Windows: robust Claude CLI resolution (prefer .cmd, fallback .ps1, where.exe); Unregister UX: use 'claude mcp get' exit codes; stop PATH prepend on Windows; safer detection when unregistered

main
dsarno 2025-08-13 12:36:24 -07:00
parent a2a14c179c
commit b6b8d47dfe
2 changed files with 82 additions and 4 deletions

View File

@ -53,11 +53,15 @@ namespace UnityMcpBridge.Editor.Helpers
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty; string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty;
string[] candidates = string[] candidates =
{ {
// Prefer .cmd (most reliable from non-interactive processes)
Path.Combine(appData, "npm", "claude.cmd"), Path.Combine(appData, "npm", "claude.cmd"),
Path.Combine(localAppData, "npm", "claude.cmd"), Path.Combine(localAppData, "npm", "claude.cmd"),
// Fall back to PowerShell shim if only .ps1 is present
Path.Combine(appData, "npm", "claude.ps1"),
Path.Combine(localAppData, "npm", "claude.ps1"),
}; };
foreach (string c in candidates) { if (File.Exists(c)) return c; } foreach (string c in candidates) { if (File.Exists(c)) return c; }
string fromWhere = Where("claude.exe") ?? Where("claude.cmd") ?? Where("claude"); string fromWhere = Where("claude.exe") ?? Where("claude.cmd") ?? Where("claude.ps1") ?? Where("claude");
if (!string.IsNullOrEmpty(fromWhere)) return fromWhere; if (!string.IsNullOrEmpty(fromWhere)) return fromWhere;
#endif #endif
return null; return null;

View File

@ -1578,9 +1578,54 @@ namespace UnityMcpBridge.Editor.Windows
string projectDir = Path.GetDirectoryName(Application.dataPath); string projectDir = Path.GetDirectoryName(Application.dataPath);
string pathPrepend = Application.platform == RuntimePlatform.OSXEditor string pathPrepend = Application.platform == RuntimePlatform.OSXEditor
? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" ? "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin"
: "/usr/local/bin:/usr/bin:/bin"; : null; // On Windows, don't modify PATH - use system PATH as-is
if (ExecPath.TryRun(claudePath, "mcp remove UnityMCP", projectDir, out var stdout, out var stderr, 10000, pathPrepend)) // Determine if Claude has a UnityMCP server registered by using exit codes from `claude mcp get <name>`
string[] candidateNamesForGet = { "UnityMCP", "unityMCP", "unity-mcp", "UnityMcpServer" };
List<string> existingNames = new List<string>();
foreach (var candidate in candidateNamesForGet)
{
if (ExecPath.TryRun(claudePath, $"mcp get {candidate}", projectDir, out var getStdout, out var getStderr, 7000, pathPrepend))
{
// Success exit code indicates the server exists
existingNames.Add(candidate);
}
}
if (existingNames.Count == 0)
{
// Nothing to unregister set status and bail early
var claudeClient = mcpClients.clients.FirstOrDefault(c => c.mcpType == McpTypes.ClaudeCode);
if (claudeClient != null)
{
claudeClient.SetStatus(McpStatus.NotConfigured);
UnityEngine.Debug.Log("Claude CLI reports no UnityMCP server via 'mcp get' setting status to NotConfigured and aborting unregister.");
Repaint();
}
return;
}
// Try different possible server names
string[] possibleNames = { "UnityMCP", "unityMCP", "unity-mcp", "UnityMcpServer" };
bool success = false;
foreach (string serverName in possibleNames)
{
if (ExecPath.TryRun(claudePath, $"mcp remove {serverName}", projectDir, out var stdout, out var stderr, 10000, pathPrepend))
{
success = true;
UnityEngine.Debug.Log($"Successfully removed MCP server: {serverName}");
break;
}
else if (!stderr.Contains("No MCP server found"))
{
// If it's not a "not found" error, log it and stop trying
UnityEngine.Debug.LogWarning($"Error removing {serverName}: {stderr}");
break;
}
}
if (success)
{ {
var claudeClient = mcpClients.clients.FirstOrDefault(c => c.mcpType == McpTypes.ClaudeCode); var claudeClient = mcpClients.clients.FirstOrDefault(c => c.mcpType == McpTypes.ClaudeCode);
if (claudeClient != null) if (claudeClient != null)
@ -1594,16 +1639,45 @@ namespace UnityMcpBridge.Editor.Windows
} }
else else
{ {
UnityEngine.Debug.LogWarning($"Claude MCP removal failed: {stderr}\n{stdout}"); // If no servers were found to remove, they're already unregistered
// Force status to NotConfigured and update the UI
UnityEngine.Debug.Log("No MCP servers found to unregister - already unregistered.");
var claudeClient = mcpClients.clients.FirstOrDefault(c => c.mcpType == McpTypes.ClaudeCode); var claudeClient = mcpClients.clients.FirstOrDefault(c => c.mcpType == McpTypes.ClaudeCode);
if (claudeClient != null) if (claudeClient != null)
{ {
claudeClient.SetStatus(McpStatus.NotConfigured);
CheckClaudeCodeConfiguration(claudeClient); CheckClaudeCodeConfiguration(claudeClient);
} }
Repaint(); Repaint();
} }
} }
private bool ParseTextOutput(string claudePath, string projectDir, string pathPrepend)
{
if (ExecPath.TryRun(claudePath, "mcp list", projectDir, out var listStdout, out var listStderr, 10000, pathPrepend))
{
UnityEngine.Debug.Log($"Claude MCP servers (text): {listStdout}");
// Check if output indicates no servers or contains UnityMCP variants
if (listStdout.Contains("No MCP servers configured") ||
listStdout.Contains("no servers") ||
listStdout.Contains("No servers") ||
string.IsNullOrWhiteSpace(listStdout) ||
listStdout.Trim().Length == 0)
{
return false;
}
// Look for UnityMCP variants in the output
return listStdout.Contains("UnityMCP") ||
listStdout.Contains("unityMCP") ||
listStdout.Contains("unity-mcp");
}
// If command failed, assume no servers
return false;
}
private string FindUvPath() private string FindUvPath()
{ {
string uvPath = null; string uvPath = null;