From ab86d5bc95515ad164c8a80c48ab60134118d152 Mon Sep 17 00:00:00 2001 From: dsarno Date: Mon, 1 Dec 2025 14:23:57 -0800 Subject: [PATCH 1/5] Fix Claude Windows config and CLI status refresh --- .../Editor/Helpers/ConfigJsonBuilder.cs | 63 ++++++-- .../ClientConfig/McpClientConfigSection.cs | 151 ++++++++++++++---- 2 files changed, 175 insertions(+), 39 deletions(-) diff --git a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs index 6df986ae..db620062 100644 --- a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs +++ b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs @@ -1,11 +1,14 @@ using System; using System.Collections.Generic; +using System.IO; +using System.Linq; using MCPForUnity.Editor.Constants; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Models; using Newtonsoft.Json; using Newtonsoft.Json.Linq; using UnityEditor; +using UnityEngine; namespace MCPForUnity.Editor.Helpers { @@ -77,19 +80,22 @@ private static void PopulateUnityNode(JObject unity, string uvPath, McpClient cl // Stdio mode: Use uvx command var (uvxPath, fromUrl, packageName) = AssetPathUtility.GetUvxCommandParts(); - unity["command"] = uvxPath; + var toolArgs = BuildUvxArgs(fromUrl, packageName); - var args = new List { packageName }; - if (!string.IsNullOrEmpty(fromUrl)) + if (ShouldUseWindowsCmdShim(client)) { - args.Insert(0, fromUrl); - args.Insert(0, "--from"); - } + unity["command"] = ResolveCmdPath(); - args.Add("--transport"); - args.Add("stdio"); + var cmdArgs = new List { "/c", uvxPath }; + cmdArgs.AddRange(toolArgs); - unity["args"] = JArray.FromObject(args.ToArray()); + unity["args"] = JArray.FromObject(cmdArgs.ToArray()); + } + else + { + unity["command"] = uvxPath; + unity["args"] = JArray.FromObject(toolArgs.ToArray()); + } // Remove url/serverUrl if they exist from previous config if (unity["url"] != null) unity.Remove("url"); @@ -145,5 +151,44 @@ private static JObject EnsureObject(JObject parent, string name) parent[name] = created; return created; } + + private static IList BuildUvxArgs(string fromUrl, string packageName) + { + var args = new List { packageName }; + + if (!string.IsNullOrEmpty(fromUrl)) + { + args.Insert(0, fromUrl); + args.Insert(0, "--from"); + } + + args.Add("--transport"); + args.Add("stdio"); + + return args; + } + + private static bool ShouldUseWindowsCmdShim(McpClient client) + { + if (client == null) + { + return false; + } + + return Application.platform == RuntimePlatform.WindowsEditor && + string.Equals(client.name, "Claude Desktop", StringComparison.OrdinalIgnoreCase); + } + + private static string ResolveCmdPath() + { + var comSpec = Environment.GetEnvironmentVariable("ComSpec"); + if (!string.IsNullOrEmpty(comSpec) && File.Exists(comSpec)) + { + return comSpec; + } + + string system32Cmd = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe"); + return File.Exists(system32Cmd) ? system32Cmd : "cmd.exe"; + } } } diff --git a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs index 617347cb..0a6c81ae 100644 --- a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs +++ b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs @@ -4,6 +4,7 @@ using System.IO; using System.Linq; using System.Runtime.InteropServices; +using System.Threading.Tasks; using MCPForUnity.Editor.Clients; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Models; @@ -38,6 +39,9 @@ public class McpClientConfigSection // Data private readonly List configurators; + private readonly Dictionary lastStatusChecks = new(); + private readonly HashSet statusRefreshInFlight = new(); + private static readonly TimeSpan StatusRefreshInterval = TimeSpan.FromSeconds(45); private int selectedClientIndex = 0; public VisualElement Root { get; private set; } @@ -105,33 +109,7 @@ public void UpdateClientStatus() return; var client = configurators[selectedClientIndex]; - MCPServiceLocator.Client.CheckClientStatus(client); - - clientStatusLabel.text = GetStatusDisplayString(client.Status); - clientStatusLabel.style.color = StyleKeyword.Null; - - clientStatusIndicator.RemoveFromClassList("configured"); - clientStatusIndicator.RemoveFromClassList("not-configured"); - clientStatusIndicator.RemoveFromClassList("warning"); - - switch (client.Status) - { - case McpStatus.Configured: - case McpStatus.Running: - case McpStatus.Connected: - clientStatusIndicator.AddToClassList("configured"); - break; - case McpStatus.IncorrectPath: - case McpStatus.CommunicationError: - case McpStatus.NoResponse: - clientStatusIndicator.AddToClassList("warning"); - break; - default: - clientStatusIndicator.AddToClassList("not-configured"); - break; - } - - configureButton.text = client.GetConfigureActionLabel(); + RefreshClientStatus(client); } private string GetStatusDisplayString(McpStatus status) @@ -240,7 +218,8 @@ private void OnConfigureClicked() try { MCPServiceLocator.Client.ConfigureClient(client); - UpdateClientStatus(); + lastStatusChecks.Remove(client); + RefreshClientStatus(client, forceImmediate: true); UpdateManualConfiguration(); } catch (Exception ex) @@ -314,11 +293,123 @@ public void RefreshSelectedClient() if (selectedClientIndex >= 0 && selectedClientIndex < configurators.Count) { var client = configurators[selectedClientIndex]; - MCPServiceLocator.Client.CheckClientStatus(client); - UpdateClientStatus(); + RefreshClientStatus(client, forceImmediate: true); UpdateManualConfiguration(); UpdateClaudeCliPathVisibility(); } } + + private void RefreshClientStatus(IMcpClientConfigurator client, bool forceImmediate = false) + { + if (client is ClaudeCliMcpConfigurator) + { + RefreshClaudeCliStatus(client, forceImmediate); + return; + } + + if (forceImmediate || ShouldRefreshClient(client)) + { + MCPServiceLocator.Client.CheckClientStatus(client); + lastStatusChecks[client] = DateTime.UtcNow; + } + + ApplyStatusToUi(client); + } + + private void RefreshClaudeCliStatus(IMcpClientConfigurator client, bool forceImmediate) + { + if (forceImmediate) + { + MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false); + lastStatusChecks[client] = DateTime.UtcNow; + ApplyStatusToUi(client); + return; + } + + bool hasStatus = lastStatusChecks.ContainsKey(client); + bool needsRefresh = forceImmediate || !hasStatus || ShouldRefreshClient(client); + + if (!hasStatus) + { + ApplyStatusToUi(client, showChecking: true); + } + else + { + ApplyStatusToUi(client); + } + + if (needsRefresh && !statusRefreshInFlight.Contains(client)) + { + statusRefreshInFlight.Add(client); + ApplyStatusToUi(client, showChecking: true); + + Task.Run(() => + { + MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false); + }).ContinueWith(_ => + { + EditorApplication.delayCall += () => + { + statusRefreshInFlight.Remove(client); + lastStatusChecks[client] = DateTime.UtcNow; + ApplyStatusToUi(client); + }; + }); + } + } + + private bool ShouldRefreshClient(IMcpClientConfigurator client) + { + if (!lastStatusChecks.TryGetValue(client, out var last)) + { + return true; + } + + return (DateTime.UtcNow - last) > StatusRefreshInterval; + } + + private void ApplyStatusToUi(IMcpClientConfigurator client, bool showChecking = false) + { + if (selectedClientIndex < 0 || selectedClientIndex >= configurators.Count) + return; + + if (!ReferenceEquals(configurators[selectedClientIndex], client)) + return; + + clientStatusIndicator.RemoveFromClassList("configured"); + clientStatusIndicator.RemoveFromClassList("not-configured"); + clientStatusIndicator.RemoveFromClassList("warning"); + + if (showChecking) + { + clientStatusLabel.text = "Checking..."; + clientStatusLabel.style.color = StyleKeyword.Null; + clientStatusIndicator.AddToClassList("warning"); + configureButton.text = client.GetConfigureActionLabel(); + return; + } + + clientStatusLabel.text = GetStatusDisplayString(client.Status); + clientStatusLabel.style.color = StyleKeyword.Null; + + switch (client.Status) + { + case McpStatus.Configured: + case McpStatus.Running: + case McpStatus.Connected: + clientStatusIndicator.AddToClassList("configured"); + break; + case McpStatus.IncorrectPath: + case McpStatus.CommunicationError: + case McpStatus.NoResponse: + clientStatusIndicator.AddToClassList("warning"); + break; + default: + clientStatusIndicator.AddToClassList("not-configured"); + break; + } + + configureButton.text = client.GetConfigureActionLabel(); + } } } From c3a8dab36c527bb1d60cee6f1da34ea1462b90f6 Mon Sep 17 00:00:00 2001 From: David Sarno Date: Mon, 1 Dec 2025 17:05:58 -0800 Subject: [PATCH 2/5] Fix Claude uvx path resolution --- .../ClaudeDesktopConfigurator.cs | 3 +- .../Editor/Services/PathResolverService.cs | 82 +++++++++++++++++++ .../EditMode/Helpers/WriteToConfigTests.cs | 34 ++++++++ 3 files changed, 118 insertions(+), 1 deletion(-) diff --git a/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs b/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs index e6cb41b1..d4b842d5 100644 --- a/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs +++ b/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs @@ -15,7 +15,8 @@ public ClaudeDesktopConfigurator() : base(new McpClient windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Claude", "claude_desktop_config.json"), macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Claude", "claude_desktop_config.json"), linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Claude", "claude_desktop_config.json"), - SupportsHttpTransport = false + SupportsHttpTransport = false, + StripEnvWhenNotRequired = true }) { } diff --git a/MCPForUnity/Editor/Services/PathResolverService.cs b/MCPForUnity/Editor/Services/PathResolverService.cs index 4b6b07fb..4947a16d 100644 --- a/MCPForUnity/Editor/Services/PathResolverService.cs +++ b/MCPForUnity/Editor/Services/PathResolverService.cs @@ -1,4 +1,5 @@ using System; +using System.Collections.Generic; using System.Diagnostics; using System.IO; using System.Linq; @@ -34,6 +35,12 @@ public string GetUvxPath() McpLog.Debug("No uvx path override found, falling back to default command"); } + string discovered = ResolveUvxFromSystem(); + if (!string.IsNullOrEmpty(discovered)) + { + return discovered; + } + return "uvx"; } @@ -123,6 +130,81 @@ public bool IsClaudeCliDetected() return !string.IsNullOrEmpty(GetClaudeCliPath()); } + private static string ResolveUvxFromSystem() + { + try + { + foreach (string candidate in EnumerateUvxCandidates()) + { + if (!string.IsNullOrEmpty(candidate) && File.Exists(candidate)) + { + return candidate; + } + } + } + catch + { + // fall back to bare command + } + + return null; + } + + private static IEnumerable EnumerateUvxCandidates() + { + string exeName = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? "uvx.exe" : "uvx"; + + string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile); + if (!string.IsNullOrEmpty(home)) + { + yield return Path.Combine(home, ".local", "bin", exeName); + yield return Path.Combine(home, ".cargo", "bin", exeName); + } + + if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) + { + yield return "/opt/homebrew/bin/" + exeName; + yield return "/usr/local/bin/" + exeName; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) + { + yield return "/usr/local/bin/" + exeName; + yield return "/usr/bin/" + exeName; + } + else if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData); + string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles); + + if (!string.IsNullOrEmpty(localAppData)) + { + yield return Path.Combine(localAppData, "Programs", "uv", exeName); + } + + if (!string.IsNullOrEmpty(programFiles)) + { + yield return Path.Combine(programFiles, "uv", exeName); + } + } + + string pathEnv = Environment.GetEnvironmentVariable("PATH"); + if (!string.IsNullOrEmpty(pathEnv)) + { + foreach (string rawDir in pathEnv.Split(Path.PathSeparator)) + { + if (string.IsNullOrWhiteSpace(rawDir)) continue; + string dir = rawDir.Trim(); + yield return Path.Combine(dir, exeName); + + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Some PATH entries may already contain the file without extension + yield return Path.Combine(dir, "uvx"); + } + } + } + } + public void SetUvxPathOverride(string path) { if (string.IsNullOrEmpty(path)) diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs index 65ebc879..1b41f6b1 100644 --- a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs @@ -167,6 +167,40 @@ public void DoesNotAddEnvOrDisabled_ForTrae() AssertTransportConfiguration(unity, client); } + [Test] + public void ClaudeDesktop_UsesAbsoluteUvPath_WhenOverrideProvided() + { + var configPath = Path.Combine(_tempRoot, "claude-desktop.json"); + WriteInitialConfig(configPath, isVSCode: false, command: "uvx", directory: "/old/path"); + + WithTransportPreference(false, () => + { + EditorPrefs.SetString(EditorPrefKeys.UvxPathOverride, "/abs/mock/uvx"); + try + { + var client = new McpClient + { + name = "Claude Desktop", + SupportsHttpTransport = false, + StripEnvWhenNotRequired = true + }; + + InvokeWriteToConfig(configPath, client); + + var root = JObject.Parse(File.ReadAllText(configPath)); + var unity = (JObject)root.SelectToken("mcpServers.unityMCP"); + Assert.NotNull(unity, "Expected mcpServers.unityMCP node"); + Assert.AreEqual("/abs/mock/uvx", (string)unity["command"], "Claude Desktop should use absolute uvx path"); + Assert.IsNull(unity["env"], "Claude Desktop config should not include env block when not required"); + AssertTransportConfiguration(unity, client); + } + finally + { + EditorPrefs.DeleteKey(EditorPrefKeys.UvxPathOverride); + } + }); + } + [Test] public void PreservesExistingEnvAndDisabled_ForKiro() { From d6ff2bd738c4890c43d138cc2d865082b7657ecb Mon Sep 17 00:00:00 2001 From: David Sarno Date: Mon, 1 Dec 2025 17:22:27 -0800 Subject: [PATCH 3/5] Address review feedback for Claude uvx --- .../Clients/Configurators/ClaudeDesktopConfigurator.cs | 4 +++- MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs | 3 ++- .../Components/ClientConfig/McpClientConfigSection.cs | 7 ++++++- .../Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs | 7 ++++--- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs b/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs index d4b842d5..f55f3231 100644 --- a/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs +++ b/MCPForUnity/Editor/Clients/Configurators/ClaudeDesktopConfigurator.cs @@ -9,9 +9,11 @@ namespace MCPForUnity.Editor.Clients.Configurators { public class ClaudeDesktopConfigurator : JsonFileMcpConfigurator { + public const string ClientName = "Claude Desktop"; + public ClaudeDesktopConfigurator() : base(new McpClient { - name = "Claude Desktop", + name = ClientName, windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "Claude", "claude_desktop_config.json"), macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "Claude", "claude_desktop_config.json"), linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "Claude", "claude_desktop_config.json"), diff --git a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs index db620062..9450e102 100644 --- a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs +++ b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs @@ -3,6 +3,7 @@ using System.IO; using System.Linq; using MCPForUnity.Editor.Constants; +using MCPForUnity.Editor.Clients.Configurators; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Models; using Newtonsoft.Json; @@ -176,7 +177,7 @@ private static bool ShouldUseWindowsCmdShim(McpClient client) } return Application.platform == RuntimePlatform.WindowsEditor && - string.Equals(client.name, "Claude Desktop", StringComparison.OrdinalIgnoreCase); + string.Equals(client.name, ClaudeDesktopConfigurator.ClientName, StringComparison.OrdinalIgnoreCase); } private static string ResolveCmdPath() diff --git a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs index 0a6c81ae..76b2aae2 100644 --- a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs +++ b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs @@ -346,8 +346,13 @@ private void RefreshClaudeCliStatus(IMcpClientConfigurator client, bool forceImm Task.Run(() => { MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false); - }).ContinueWith(_ => + }).ContinueWith(t => { + if (t.IsFaulted && t.Exception != null) + { + McpLog.Error($"Failed to refresh Claude CLI status: {t.Exception.GetBaseException().Message}"); + } + EditorApplication.delayCall += () => { statusRefreshInFlight.Remove(client); diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs index 1b41f6b1..f168742f 100644 --- a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Helpers/WriteToConfigTests.cs @@ -8,6 +8,7 @@ using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Models; using MCPForUnity.Editor.Constants; +using MCPForUnity.Editor.Services; namespace MCPForUnityTests.Editor.Helpers { @@ -175,7 +176,7 @@ public void ClaudeDesktop_UsesAbsoluteUvPath_WhenOverrideProvided() WithTransportPreference(false, () => { - EditorPrefs.SetString(EditorPrefKeys.UvxPathOverride, "/abs/mock/uvx"); + MCPServiceLocator.Paths.SetUvxPathOverride(_fakeUvPath); try { var client = new McpClient @@ -190,13 +191,13 @@ public void ClaudeDesktop_UsesAbsoluteUvPath_WhenOverrideProvided() var root = JObject.Parse(File.ReadAllText(configPath)); var unity = (JObject)root.SelectToken("mcpServers.unityMCP"); Assert.NotNull(unity, "Expected mcpServers.unityMCP node"); - Assert.AreEqual("/abs/mock/uvx", (string)unity["command"], "Claude Desktop should use absolute uvx path"); + Assert.AreEqual(_fakeUvPath, (string)unity["command"], "Claude Desktop should use absolute uvx path"); Assert.IsNull(unity["env"], "Claude Desktop config should not include env block when not required"); AssertTransportConfiguration(unity, client); } finally { - EditorPrefs.DeleteKey(EditorPrefKeys.UvxPathOverride); + MCPServiceLocator.Paths.ClearUvxPathOverride(); } }); } From a1c00edb952b6e6734425861e80629ecbcc2b982 Mon Sep 17 00:00:00 2001 From: David Sarno Date: Mon, 1 Dec 2025 17:30:23 -0800 Subject: [PATCH 4/5] Polish config cleanup and status errors --- MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs | 4 ---- .../ClientConfig/McpClientConfigSection.cs | 14 +++++++++++++- 2 files changed, 13 insertions(+), 5 deletions(-) diff --git a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs index 9450e102..3c0ba705 100644 --- a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs +++ b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs @@ -101,10 +101,6 @@ private static void PopulateUnityNode(JObject unity, string uvPath, McpClient cl // Remove url/serverUrl if they exist from previous config if (unity["url"] != null) unity.Remove("url"); if (unity["serverUrl"] != null) unity.Remove("serverUrl"); - foreach (var prop in urlPropsToRemove) - { - if (unity[prop] != null) unity.Remove(prop); - } if (isVSCode) { diff --git a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs index 76b2aae2..a46012e0 100644 --- a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs +++ b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs @@ -348,15 +348,27 @@ private void RefreshClaudeCliStatus(IMcpClientConfigurator client, bool forceImm MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false); }).ContinueWith(t => { + bool faulted = false; + string errorMessage = null; if (t.IsFaulted && t.Exception != null) { - McpLog.Error($"Failed to refresh Claude CLI status: {t.Exception.GetBaseException().Message}"); + var baseException = t.Exception.GetBaseException(); + errorMessage = baseException?.Message ?? "Status check failed"; + McpLog.Error($"Failed to refresh Claude CLI status: {errorMessage}"); + faulted = true; } EditorApplication.delayCall += () => { statusRefreshInFlight.Remove(client); lastStatusChecks[client] = DateTime.UtcNow; + if (faulted) + { + if (client is McpClientConfiguratorBase baseConfigurator) + { + baseConfigurator.Client.SetStatus(McpStatus.Error, errorMessage ?? "Status check failed"); + } + } ApplyStatusToUi(client); }; }); From c87c17444eea9496de5a5af503834be92c2a344a Mon Sep 17 00:00:00 2001 From: David Sarno Date: Mon, 1 Dec 2025 17:41:30 -0800 Subject: [PATCH 5/5] Tidy Claude status refresh --- .../Windows/Components/ClientConfig/McpClientConfigSection.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs index a46012e0..781d1c0e 100644 --- a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs +++ b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs @@ -327,7 +327,7 @@ private void RefreshClaudeCliStatus(IMcpClientConfigurator client, bool forceImm } bool hasStatus = lastStatusChecks.ContainsKey(client); - bool needsRefresh = forceImmediate || !hasStatus || ShouldRefreshClient(client); + bool needsRefresh = !hasStatus || ShouldRefreshClient(client); if (!hasStatus) {