diff --git a/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs b/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs
index 75cdb3b..ae420a2 100644
--- a/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs
+++ b/UnityMcpBridge/Editor/Helpers/PackageInstaller.cs
@@ -25,18 +25,18 @@ namespace UnityMcpBridge.Editor.Helpers
{
try
{
- Debug.Log("Unity MCP: Installing Python server...");
+ Debug.Log("UNITY-MCP: Installing Python server...");
ServerInstaller.EnsureServerInstalled();
// Mark as installed
EditorPrefs.SetBool(InstallationFlagKey, true);
- Debug.Log("Unity MCP: Python server installation completed successfully.");
+ Debug.Log("UNITY-MCP: Python server installation completed successfully.");
}
catch (System.Exception ex)
{
- Debug.LogError($"Unity MCP: Failed to install Python server: {ex.Message}");
- Debug.LogWarning("Unity MCP: You may need to manually install the Python server. Check the Unity MCP Editor Window for instructions.");
+ Debug.LogError($"UNITY-MCP: Failed to install Python server: {ex.Message}");
+ Debug.LogWarning("UNITY-MCP: You may need to manually install the Python server. Check the Unity MCP Editor Window for instructions.");
}
}
}
diff --git a/UnityMcpBridge/Editor/Helpers/PortManager.cs b/UnityMcpBridge/Editor/Helpers/PortManager.cs
index 9caeccc..376f916 100644
--- a/UnityMcpBridge/Editor/Helpers/PortManager.cs
+++ b/UnityMcpBridge/Editor/Helpers/PortManager.cs
@@ -1,5 +1,6 @@
using System;
using System.IO;
+using UnityEditor;
using System.Net;
using System.Net.Sockets;
using System.Security.Cryptography;
@@ -15,6 +16,12 @@ namespace UnityMcpBridge.Editor.Helpers
///
public static class PortManager
{
+ private static bool IsDebugEnabled()
+ {
+ try { return EditorPrefs.GetBool("UnityMCP.DebugLogs", false); }
+ catch { return false; }
+ }
+
private const int DefaultPort = 6400;
private const int MaxPortAttempts = 100;
private const string RegistryFileName = "unity-mcp-port.json";
@@ -41,7 +48,7 @@ namespace UnityMcpBridge.Editor.Helpers
string.Equals(storedConfig.project_path ?? string.Empty, Application.dataPath ?? string.Empty, StringComparison.OrdinalIgnoreCase) &&
IsPortAvailable(storedConfig.unity_port))
{
- Debug.Log($"Using stored port {storedConfig.unity_port} for current project");
+ if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Using stored port {storedConfig.unity_port} for current project");
return storedConfig.unity_port;
}
@@ -50,7 +57,7 @@ namespace UnityMcpBridge.Editor.Helpers
{
if (WaitForPortRelease(storedConfig.unity_port, 1500))
{
- Debug.Log($"Stored port {storedConfig.unity_port} became available after short wait");
+ if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Stored port {storedConfig.unity_port} became available after short wait");
return storedConfig.unity_port;
}
// Prefer sticking to the same port; let the caller handle bind retries/fallbacks
@@ -71,7 +78,7 @@ namespace UnityMcpBridge.Editor.Helpers
{
int newPort = FindAvailablePort();
SavePort(newPort);
- Debug.Log($"Discovered and saved new port: {newPort}");
+ if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Discovered and saved new port: {newPort}");
return newPort;
}
@@ -84,18 +91,18 @@ namespace UnityMcpBridge.Editor.Helpers
// Always try default port first
if (IsPortAvailable(DefaultPort))
{
- Debug.Log($"Using default port {DefaultPort}");
+ if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Using default port {DefaultPort}");
return DefaultPort;
}
- Debug.Log($"Default port {DefaultPort} is in use, searching for alternative...");
+ if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Default port {DefaultPort} is in use, searching for alternative...");
// Search for alternatives
for (int port = DefaultPort + 1; port < DefaultPort + MaxPortAttempts; port++)
{
if (IsPortAvailable(port))
{
- Debug.Log($"Found available port {port}");
+ if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Found available port {port}");
return port;
}
}
@@ -204,7 +211,7 @@ namespace UnityMcpBridge.Editor.Helpers
string legacy = Path.Combine(GetRegistryDirectory(), RegistryFileName);
File.WriteAllText(legacy, json);
- Debug.Log($"Saved port {port} to storage");
+ if (IsDebugEnabled()) Debug.Log($"UNITY-MCP: Saved port {port} to storage");
}
catch (Exception ex)
{
diff --git a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs
index ed92786..03b753f 100644
--- a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs
+++ b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs
@@ -281,7 +281,7 @@ namespace UnityMcpBridge.Editor.Helpers
return false;
}
- Debug.Log("Unity MCP: Python environment repaired successfully.");
+ Debug.Log("UNITY-MCP: Python environment repaired successfully.");
return true;
}
catch (Exception ex)
@@ -305,47 +305,100 @@ namespace UnityMcpBridge.Editor.Helpers
catch { }
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
- string[] candidates =
+
+ // Platform-specific candidate lists
+ string[] candidates;
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- "/opt/homebrew/bin/uv",
- "/usr/local/bin/uv",
- "/usr/bin/uv",
- "/opt/local/bin/uv",
- Path.Combine(home, ".local", "bin", "uv"),
- "/opt/homebrew/opt/uv/bin/uv",
- // Framework Python installs
- "/Library/Frameworks/Python.framework/Versions/3.13/bin/uv",
- "/Library/Frameworks/Python.framework/Versions/3.12/bin/uv",
- // Fallback to PATH resolution by name
- "uv"
- };
+ candidates = new[]
+ {
+ // Common per-user installs
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python313\Scripts\uv.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python312\Scripts\uv.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python311\Scripts\uv.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python310\Scripts\uv.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python313\Scripts\uv.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python312\Scripts\uv.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python311\Scripts\uv.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python310\Scripts\uv.exe"),
+ // Program Files style installs (if a native installer was used)
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) ?? string.Empty, @"uv\uv.exe"),
+ // Try simple name resolution later via PATH
+ "uv.exe",
+ "uv"
+ };
+ }
+ else
+ {
+ candidates = new[]
+ {
+ "/opt/homebrew/bin/uv",
+ "/usr/local/bin/uv",
+ "/usr/bin/uv",
+ "/opt/local/bin/uv",
+ Path.Combine(home, ".local", "bin", "uv"),
+ "/opt/homebrew/opt/uv/bin/uv",
+ // Framework Python installs
+ "/Library/Frameworks/Python.framework/Versions/3.13/bin/uv",
+ "/Library/Frameworks/Python.framework/Versions/3.12/bin/uv",
+ // Fallback to PATH resolution by name
+ "uv"
+ };
+ }
+
foreach (string c in candidates)
{
try
{
- if (ValidateUvBinary(c)) return c;
+ if (File.Exists(c) && ValidateUvBinary(c)) return c;
}
catch { /* ignore */ }
}
- // Try which uv (explicit path)
+ // Use platform-appropriate which/where to resolve from PATH
try
{
- var whichPsi = new System.Diagnostics.ProcessStartInfo
+ if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
- FileName = "/usr/bin/which",
- Arguments = "uv",
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true
- };
- using var wp = System.Diagnostics.Process.Start(whichPsi);
- string output = wp.StandardOutput.ReadToEnd().Trim();
- wp.WaitForExit(3000);
- if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output) && File.Exists(output))
+ var wherePsi = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = "where",
+ Arguments = "uv.exe",
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true
+ };
+ using var wp = System.Diagnostics.Process.Start(wherePsi);
+ string output = wp.StandardOutput.ReadToEnd().Trim();
+ wp.WaitForExit(3000);
+ if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output))
+ {
+ foreach (var line in output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
+ {
+ string path = line.Trim();
+ if (File.Exists(path) && ValidateUvBinary(path)) return path;
+ }
+ }
+ }
+ else
{
- if (ValidateUvBinary(output)) return output;
+ var whichPsi = new System.Diagnostics.ProcessStartInfo
+ {
+ FileName = "/usr/bin/which",
+ Arguments = "uv",
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true
+ };
+ using var wp = System.Diagnostics.Process.Start(whichPsi);
+ string output = wp.StandardOutput.ReadToEnd().Trim();
+ wp.WaitForExit(3000);
+ if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output) && File.Exists(output))
+ {
+ if (ValidateUvBinary(output)) return output;
+ }
}
}
catch { }
@@ -359,8 +412,11 @@ namespace UnityMcpBridge.Editor.Helpers
{
try
{
- string candidate = Path.Combine(part, "uv");
- if (File.Exists(candidate) && ValidateUvBinary(candidate)) return candidate;
+ // Check both uv and uv.exe
+ string candidateUv = Path.Combine(part, "uv");
+ string candidateUvExe = Path.Combine(part, "uv.exe");
+ if (File.Exists(candidateUv) && ValidateUvBinary(candidateUv)) return candidateUv;
+ if (File.Exists(candidateUvExe) && ValidateUvBinary(candidateUvExe)) return candidateUvExe;
}
catch { }
}
diff --git a/UnityMcpBridge/Editor/UnityMcpBridge.cs b/UnityMcpBridge/Editor/UnityMcpBridge.cs
index b7e4af2..89bae4e 100644
--- a/UnityMcpBridge/Editor/UnityMcpBridge.cs
+++ b/UnityMcpBridge/Editor/UnityMcpBridge.cs
@@ -128,7 +128,7 @@ namespace UnityMcpBridge.Editor
// Don't restart if already running on a working port
if (isRunning && listener != null)
{
- Debug.Log($"UnityMcpBridge already running on port {currentUnityPort}");
+ Debug.Log($"UNITY-MCP: UnityMcpBridge already running on port {currentUnityPort}");
return;
}
@@ -194,7 +194,7 @@ namespace UnityMcpBridge.Editor
isRunning = true;
isAutoConnectMode = false;
- Debug.Log($"UnityMcpBridge started on port {currentUnityPort}.");
+ Debug.Log($"UNITY-MCP: UnityMcpBridge started on port {currentUnityPort}.");
Task.Run(ListenerLoop);
EditorApplication.update += ProcessCommands;
// Write initial heartbeat immediately
@@ -226,7 +226,7 @@ namespace UnityMcpBridge.Editor
listener?.Stop();
listener = null;
EditorApplication.update -= ProcessCommands;
- Debug.Log("UnityMcpBridge stopped.");
+ Debug.Log("UNITY-MCP: UnityMcpBridge stopped.");
}
catch (Exception ex)
{
diff --git a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs
index 408a63b..f85aa6f 100644
--- a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs
+++ b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs
@@ -1990,37 +1990,91 @@ namespace UnityMcpBridge.Editor.Windows
{
try
{
- // Common absolute paths
- string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
- string[] candidates =
+ // Windows-specific Python detection
+ if (Application.platform == RuntimePlatform.WindowsEditor)
{
- "/opt/homebrew/bin/python3",
- "/usr/local/bin/python3",
- "/usr/bin/python3",
- "/opt/local/bin/python3",
- Path.Combine(home, ".local", "bin", "python3"),
- "/Library/Frameworks/Python.framework/Versions/3.13/bin/python3",
- "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
- };
- foreach (string c in candidates)
- {
- if (File.Exists(c)) return true;
- }
+ // Common Windows Python installation paths
+ string[] windowsCandidates =
+ {
+ @"C:\Python313\python.exe",
+ @"C:\Python312\python.exe",
+ @"C:\Python311\python.exe",
+ @"C:\Python310\python.exe",
+ @"C:\Python39\python.exe",
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python313\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python312\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python311\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python310\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), @"Programs\Python\Python39\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python313\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python312\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python311\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python310\python.exe"),
+ Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles), @"Python39\python.exe"),
+ };
+
+ foreach (string c in windowsCandidates)
+ {
+ if (File.Exists(c)) return true;
+ }
- // Try 'which python3'
- var psi = new ProcessStartInfo
+ // Try 'where python' command (Windows equivalent of 'which')
+ var psi = new ProcessStartInfo
+ {
+ FileName = "where",
+ Arguments = "python",
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true
+ };
+ using var p = Process.Start(psi);
+ string outp = p.StandardOutput.ReadToEnd().Trim();
+ p.WaitForExit(2000);
+ if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp))
+ {
+ string[] lines = outp.Split('\n');
+ foreach (string line in lines)
+ {
+ string trimmed = line.Trim();
+ if (File.Exists(trimmed)) return true;
+ }
+ }
+ }
+ else
{
- FileName = "/usr/bin/which",
- Arguments = "python3",
- UseShellExecute = false,
- RedirectStandardOutput = true,
- RedirectStandardError = true,
- CreateNoWindow = true
- };
- using var p = Process.Start(psi);
- string outp = p.StandardOutput.ReadToEnd().Trim();
- p.WaitForExit(2000);
- if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp) && File.Exists(outp)) return true;
+ // macOS/Linux detection (existing code)
+ string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
+ string[] candidates =
+ {
+ "/opt/homebrew/bin/python3",
+ "/usr/local/bin/python3",
+ "/usr/bin/python3",
+ "/opt/local/bin/python3",
+ Path.Combine(home, ".local", "bin", "python3"),
+ "/Library/Frameworks/Python.framework/Versions/3.13/bin/python3",
+ "/Library/Frameworks/Python.framework/Versions/3.12/bin/python3",
+ };
+ foreach (string c in candidates)
+ {
+ if (File.Exists(c)) return true;
+ }
+
+ // Try 'which python3'
+ var psi = new ProcessStartInfo
+ {
+ FileName = "/usr/bin/which",
+ Arguments = "python3",
+ UseShellExecute = false,
+ RedirectStandardOutput = true,
+ RedirectStandardError = true,
+ CreateNoWindow = true
+ };
+ using var p = Process.Start(psi);
+ string outp = p.StandardOutput.ReadToEnd().Trim();
+ p.WaitForExit(2000);
+ if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp) && File.Exists(outp)) return true;
+ }
}
catch { }
return false;