diff --git a/UnityMcpBridge/Editor/Helpers/ExecPath.cs b/UnityMcpBridge/Editor/Helpers/ExecPath.cs index 1848dca..537962e 100644 --- a/UnityMcpBridge/Editor/Helpers/ExecPath.cs +++ b/UnityMcpBridge/Editor/Helpers/ExecPath.cs @@ -2,6 +2,7 @@ using System; using System.Diagnostics; using System.IO; using System.Linq; +using System.Text; using System.Runtime.InteropServices; using UnityEditor; @@ -43,7 +44,7 @@ namespace UnityMcpBridge.Editor.Helpers if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { -#if UNITY_EDITOR_WINDOWS +#if UNITY_EDITOR_WIN // Common npm global locations string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty; string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty; @@ -109,16 +110,35 @@ namespace UnityMcpBridge.Editor.Helpers if (!string.IsNullOrEmpty(extraPathPrepend)) { string currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; - psi.Environment["PATH"] = string.IsNullOrEmpty(currentPath) + psi.EnvironmentVariables["PATH"] = string.IsNullOrEmpty(currentPath) ? extraPathPrepend : (extraPathPrepend + System.IO.Path.PathSeparator + currentPath); } - using var p = Process.Start(psi); - if (p == null) return false; - stdout = p.StandardOutput.ReadToEnd(); - stderr = p.StandardError.ReadToEnd(); - if (!p.WaitForExit(timeoutMs)) { try { p.Kill(); } catch { } return false; } - return p.ExitCode == 0; + + using var process = new Process { StartInfo = psi, EnableRaisingEvents = false }; + + var so = new StringBuilder(); + var se = new StringBuilder(); + process.OutputDataReceived += (_, e) => { if (e.Data != null) so.AppendLine(e.Data); }; + process.ErrorDataReceived += (_, e) => { if (e.Data != null) se.AppendLine(e.Data); }; + + if (!process.Start()) return false; + + process.BeginOutputReadLine(); + process.BeginErrorReadLine(); + + if (!process.WaitForExit(timeoutMs)) + { + try { process.Kill(); } catch { } + return false; + } + + // Ensure async buffers are flushed + process.WaitForExit(); + + stdout = so.ToString(); + stderr = se.ToString(); + return process.ExitCode == 0; } catch { @@ -138,7 +158,7 @@ namespace UnityMcpBridge.Editor.Helpers CreateNoWindow = true, }; string path = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; - psi.Environment["PATH"] = string.IsNullOrEmpty(path) ? prependPath : (prependPath + Path.PathSeparator + path); + psi.EnvironmentVariables["PATH"] = string.IsNullOrEmpty(path) ? prependPath : (prependPath + Path.PathSeparator + path); using var p = Process.Start(psi); string output = p?.StandardOutput.ReadToEnd().Trim(); p?.WaitForExit(1500); @@ -148,7 +168,7 @@ namespace UnityMcpBridge.Editor.Helpers } #endif -#if UNITY_EDITOR_WINDOWS +#if UNITY_EDITOR_WIN private static string Where(string exe) { try diff --git a/UnityMcpBridge/Editor/Helpers/ExecPath.cs.meta b/UnityMcpBridge/Editor/Helpers/ExecPath.cs.meta index 452749e..aba921e 100644 --- a/UnityMcpBridge/Editor/Helpers/ExecPath.cs.meta +++ b/UnityMcpBridge/Editor/Helpers/ExecPath.cs.meta @@ -8,6 +8,4 @@ MonoImporter: icon: {instanceID: 0} userData: assetBundleName: - assetBundleVariant: -fileFormatVersion: 2 -guid: 3f130216be0fd4a57ab7d646a85c6d54 \ No newline at end of file + assetBundleVariant: \ No newline at end of file diff --git a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs index 1724e0c..dbdfb74 100644 --- a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs +++ b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs @@ -1,6 +1,7 @@ using System; using System.IO; using System.Runtime.InteropServices; +using System.Text; using System.Reflection; using UnityEditor; using UnityEngine; @@ -206,12 +207,35 @@ namespace UnityMcpBridge.Editor.Helpers CreateNoWindow = true }; - using var p = System.Diagnostics.Process.Start(psi); - string stdout = p.StandardOutput.ReadToEnd(); - string stderr = p.StandardError.ReadToEnd(); - p.WaitForExit(60000); + using var proc = new System.Diagnostics.Process { StartInfo = psi }; + var sbOut = new StringBuilder(); + var sbErr = new StringBuilder(); + proc.OutputDataReceived += (_, e) => { if (e.Data != null) sbOut.AppendLine(e.Data); }; + proc.ErrorDataReceived += (_, e) => { if (e.Data != null) sbErr.AppendLine(e.Data); }; - if (p.ExitCode != 0) + if (!proc.Start()) + { + Debug.LogError("Failed to start uv process."); + return false; + } + + proc.BeginOutputReadLine(); + proc.BeginErrorReadLine(); + + if (!proc.WaitForExit(60000)) + { + try { proc.Kill(); } catch { } + Debug.LogError("uv sync timed out."); + return false; + } + + // Ensure async buffers flushed + proc.WaitForExit(); + + string stdout = sbOut.ToString(); + string stderr = sbErr.ToString(); + + if (proc.ExitCode != 0) { Debug.LogError($"uv sync failed: {stderr}\n{stdout}"); return false; @@ -341,7 +365,7 @@ namespace UnityMcpBridge.Editor.Helpers "/bin" }); string currentPath = Environment.GetEnvironmentVariable("PATH") ?? string.Empty; - whichPsi.Environment["PATH"] = string.IsNullOrEmpty(currentPath) ? prepend : (prepend + ":" + currentPath); + whichPsi.EnvironmentVariables["PATH"] = string.IsNullOrEmpty(currentPath) ? prepend : (prepend + ":" + currentPath); } catch { } using var wp = System.Diagnostics.Process.Start(whichPsi); diff --git a/UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs.meta b/UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs.meta index e0a835a..d02df60 100644 --- a/UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs.meta +++ b/UnityMcpBridge/Editor/Helpers/ServerPathResolver.cs.meta @@ -8,6 +8,4 @@ MonoImporter: icon: {instanceID: 0} userData: assetBundleName: - assetBundleVariant: -fileFormatVersion: 2 -guid: 9ac156bc74460420290ab50ed91d3a15 \ No newline at end of file + assetBundleVariant: \ No newline at end of file diff --git a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs index f86acd1..17af978 100644 --- a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs +++ b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs @@ -1320,10 +1320,14 @@ namespace UnityMcpBridge.Editor.Windows string args = $"mcp add UnityMCP -- \"{uvPath}\" run --directory \"{srcDir}\" server.py"; string projectDir = Path.GetDirectoryName(Application.dataPath); - // Ensure PATH includes common Node/npm locations so claude can spawn node internally if needed - string pathPrepend = Application.platform == RuntimePlatform.OSXEditor - ? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" - : "/usr/local/bin:/usr/bin:/bin"; + // Ensure PATH includes common locations on Unix; on Windows leave PATH as-is + string pathPrepend = null; + if (Application.platform == RuntimePlatform.OSXEditor || Application.platform == RuntimePlatform.LinuxEditor) + { + pathPrepend = Application.platform == RuntimePlatform.OSXEditor + ? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" + : "/usr/local/bin:/usr/bin:/bin"; + } if (!ExecPath.TryRun(claudePath, args, projectDir, out var stdout, out var stderr, 15000, pathPrepend)) { UnityEngine.Debug.LogError($"UnityMCP: Failed to start Claude CLI.\n{stderr}\n{stdout}");