Skip to content

Commit ab86d5b

Browse files
committed
Fix Claude Windows config and CLI status refresh
1 parent 839665b commit ab86d5b

File tree

2 files changed

+175
-39
lines changed

2 files changed

+175
-39
lines changed

MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs

Lines changed: 54 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,14 @@
11
using System;
22
using System.Collections.Generic;
3+
using System.IO;
4+
using System.Linq;
35
using MCPForUnity.Editor.Constants;
46
using MCPForUnity.Editor.Helpers;
57
using MCPForUnity.Editor.Models;
68
using Newtonsoft.Json;
79
using Newtonsoft.Json.Linq;
810
using UnityEditor;
11+
using UnityEngine;
912

1013
namespace MCPForUnity.Editor.Helpers
1114
{
@@ -77,19 +80,22 @@ private static void PopulateUnityNode(JObject unity, string uvPath, McpClient cl
7780
// Stdio mode: Use uvx command
7881
var (uvxPath, fromUrl, packageName) = AssetPathUtility.GetUvxCommandParts();
7982

80-
unity["command"] = uvxPath;
83+
var toolArgs = BuildUvxArgs(fromUrl, packageName);
8184

82-
var args = new List<string> { packageName };
83-
if (!string.IsNullOrEmpty(fromUrl))
85+
if (ShouldUseWindowsCmdShim(client))
8486
{
85-
args.Insert(0, fromUrl);
86-
args.Insert(0, "--from");
87-
}
87+
unity["command"] = ResolveCmdPath();
8888

89-
args.Add("--transport");
90-
args.Add("stdio");
89+
var cmdArgs = new List<string> { "/c", uvxPath };
90+
cmdArgs.AddRange(toolArgs);
9191

92-
unity["args"] = JArray.FromObject(args.ToArray());
92+
unity["args"] = JArray.FromObject(cmdArgs.ToArray());
93+
}
94+
else
95+
{
96+
unity["command"] = uvxPath;
97+
unity["args"] = JArray.FromObject(toolArgs.ToArray());
98+
}
9399

94100
// Remove url/serverUrl if they exist from previous config
95101
if (unity["url"] != null) unity.Remove("url");
@@ -145,5 +151,44 @@ private static JObject EnsureObject(JObject parent, string name)
145151
parent[name] = created;
146152
return created;
147153
}
154+
155+
private static IList<string> BuildUvxArgs(string fromUrl, string packageName)
156+
{
157+
var args = new List<string> { packageName };
158+
159+
if (!string.IsNullOrEmpty(fromUrl))
160+
{
161+
args.Insert(0, fromUrl);
162+
args.Insert(0, "--from");
163+
}
164+
165+
args.Add("--transport");
166+
args.Add("stdio");
167+
168+
return args;
169+
}
170+
171+
private static bool ShouldUseWindowsCmdShim(McpClient client)
172+
{
173+
if (client == null)
174+
{
175+
return false;
176+
}
177+
178+
return Application.platform == RuntimePlatform.WindowsEditor &&
179+
string.Equals(client.name, "Claude Desktop", StringComparison.OrdinalIgnoreCase);
180+
}
181+
182+
private static string ResolveCmdPath()
183+
{
184+
var comSpec = Environment.GetEnvironmentVariable("ComSpec");
185+
if (!string.IsNullOrEmpty(comSpec) && File.Exists(comSpec))
186+
{
187+
return comSpec;
188+
}
189+
190+
string system32Cmd = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.System), "cmd.exe");
191+
return File.Exists(system32Cmd) ? system32Cmd : "cmd.exe";
192+
}
148193
}
149194
}

MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs

Lines changed: 121 additions & 30 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
using System.IO;
55
using System.Linq;
66
using System.Runtime.InteropServices;
7+
using System.Threading.Tasks;
78
using MCPForUnity.Editor.Clients;
89
using MCPForUnity.Editor.Helpers;
910
using MCPForUnity.Editor.Models;
@@ -38,6 +39,9 @@ public class McpClientConfigSection
3839

3940
// Data
4041
private readonly List<IMcpClientConfigurator> configurators;
42+
private readonly Dictionary<IMcpClientConfigurator, DateTime> lastStatusChecks = new();
43+
private readonly HashSet<IMcpClientConfigurator> statusRefreshInFlight = new();
44+
private static readonly TimeSpan StatusRefreshInterval = TimeSpan.FromSeconds(45);
4145
private int selectedClientIndex = 0;
4246

4347
public VisualElement Root { get; private set; }
@@ -105,33 +109,7 @@ public void UpdateClientStatus()
105109
return;
106110

107111
var client = configurators[selectedClientIndex];
108-
MCPServiceLocator.Client.CheckClientStatus(client);
109-
110-
clientStatusLabel.text = GetStatusDisplayString(client.Status);
111-
clientStatusLabel.style.color = StyleKeyword.Null;
112-
113-
clientStatusIndicator.RemoveFromClassList("configured");
114-
clientStatusIndicator.RemoveFromClassList("not-configured");
115-
clientStatusIndicator.RemoveFromClassList("warning");
116-
117-
switch (client.Status)
118-
{
119-
case McpStatus.Configured:
120-
case McpStatus.Running:
121-
case McpStatus.Connected:
122-
clientStatusIndicator.AddToClassList("configured");
123-
break;
124-
case McpStatus.IncorrectPath:
125-
case McpStatus.CommunicationError:
126-
case McpStatus.NoResponse:
127-
clientStatusIndicator.AddToClassList("warning");
128-
break;
129-
default:
130-
clientStatusIndicator.AddToClassList("not-configured");
131-
break;
132-
}
133-
134-
configureButton.text = client.GetConfigureActionLabel();
112+
RefreshClientStatus(client);
135113
}
136114

137115
private string GetStatusDisplayString(McpStatus status)
@@ -240,7 +218,8 @@ private void OnConfigureClicked()
240218
try
241219
{
242220
MCPServiceLocator.Client.ConfigureClient(client);
243-
UpdateClientStatus();
221+
lastStatusChecks.Remove(client);
222+
RefreshClientStatus(client, forceImmediate: true);
244223
UpdateManualConfiguration();
245224
}
246225
catch (Exception ex)
@@ -314,11 +293,123 @@ public void RefreshSelectedClient()
314293
if (selectedClientIndex >= 0 && selectedClientIndex < configurators.Count)
315294
{
316295
var client = configurators[selectedClientIndex];
317-
MCPServiceLocator.Client.CheckClientStatus(client);
318-
UpdateClientStatus();
296+
RefreshClientStatus(client, forceImmediate: true);
319297
UpdateManualConfiguration();
320298
UpdateClaudeCliPathVisibility();
321299
}
322300
}
301+
302+
private void RefreshClientStatus(IMcpClientConfigurator client, bool forceImmediate = false)
303+
{
304+
if (client is ClaudeCliMcpConfigurator)
305+
{
306+
RefreshClaudeCliStatus(client, forceImmediate);
307+
return;
308+
}
309+
310+
if (forceImmediate || ShouldRefreshClient(client))
311+
{
312+
MCPServiceLocator.Client.CheckClientStatus(client);
313+
lastStatusChecks[client] = DateTime.UtcNow;
314+
}
315+
316+
ApplyStatusToUi(client);
317+
}
318+
319+
private void RefreshClaudeCliStatus(IMcpClientConfigurator client, bool forceImmediate)
320+
{
321+
if (forceImmediate)
322+
{
323+
MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false);
324+
lastStatusChecks[client] = DateTime.UtcNow;
325+
ApplyStatusToUi(client);
326+
return;
327+
}
328+
329+
bool hasStatus = lastStatusChecks.ContainsKey(client);
330+
bool needsRefresh = forceImmediate || !hasStatus || ShouldRefreshClient(client);
331+
332+
if (!hasStatus)
333+
{
334+
ApplyStatusToUi(client, showChecking: true);
335+
}
336+
else
337+
{
338+
ApplyStatusToUi(client);
339+
}
340+
341+
if (needsRefresh && !statusRefreshInFlight.Contains(client))
342+
{
343+
statusRefreshInFlight.Add(client);
344+
ApplyStatusToUi(client, showChecking: true);
345+
346+
Task.Run(() =>
347+
{
348+
MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false);
349+
}).ContinueWith(_ =>
350+
{
351+
EditorApplication.delayCall += () =>
352+
{
353+
statusRefreshInFlight.Remove(client);
354+
lastStatusChecks[client] = DateTime.UtcNow;
355+
ApplyStatusToUi(client);
356+
};
357+
});
358+
}
359+
}
360+
361+
private bool ShouldRefreshClient(IMcpClientConfigurator client)
362+
{
363+
if (!lastStatusChecks.TryGetValue(client, out var last))
364+
{
365+
return true;
366+
}
367+
368+
return (DateTime.UtcNow - last) > StatusRefreshInterval;
369+
}
370+
371+
private void ApplyStatusToUi(IMcpClientConfigurator client, bool showChecking = false)
372+
{
373+
if (selectedClientIndex < 0 || selectedClientIndex >= configurators.Count)
374+
return;
375+
376+
if (!ReferenceEquals(configurators[selectedClientIndex], client))
377+
return;
378+
379+
clientStatusIndicator.RemoveFromClassList("configured");
380+
clientStatusIndicator.RemoveFromClassList("not-configured");
381+
clientStatusIndicator.RemoveFromClassList("warning");
382+
383+
if (showChecking)
384+
{
385+
clientStatusLabel.text = "Checking...";
386+
clientStatusLabel.style.color = StyleKeyword.Null;
387+
clientStatusIndicator.AddToClassList("warning");
388+
configureButton.text = client.GetConfigureActionLabel();
389+
return;
390+
}
391+
392+
clientStatusLabel.text = GetStatusDisplayString(client.Status);
393+
clientStatusLabel.style.color = StyleKeyword.Null;
394+
395+
switch (client.Status)
396+
{
397+
case McpStatus.Configured:
398+
case McpStatus.Running:
399+
case McpStatus.Connected:
400+
clientStatusIndicator.AddToClassList("configured");
401+
break;
402+
case McpStatus.IncorrectPath:
403+
case McpStatus.CommunicationError:
404+
case McpStatus.NoResponse:
405+
clientStatusIndicator.AddToClassList("warning");
406+
break;
407+
default:
408+
clientStatusIndicator.AddToClassList("not-configured");
409+
break;
410+
}
411+
412+
configureButton.text = client.GetConfigureActionLabel();
413+
}
323414
}
324415
}

0 commit comments

Comments
 (0)