Merge pull request #208 from dsarno/feature/claude-cli-detection-ui
UX and Fix: Claude Code detection + UV gating (Cursor/Windsurf) + VSCode mcp.json schemamain
commit
ce8ab83256
|
|
@ -87,7 +87,7 @@ namespace UnityMcpBridge.Editor.Data
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||||
"Code",
|
"Code",
|
||||||
"User",
|
"User",
|
||||||
"settings.json"
|
"mcp.json"
|
||||||
),
|
),
|
||||||
linuxConfigPath = Path.Combine(
|
linuxConfigPath = Path.Combine(
|
||||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||||
|
|
@ -95,7 +95,7 @@ namespace UnityMcpBridge.Editor.Data
|
||||||
"Application Support",
|
"Application Support",
|
||||||
"Code",
|
"Code",
|
||||||
"User",
|
"User",
|
||||||
"settings.json"
|
"mcp.json"
|
||||||
),
|
),
|
||||||
mcpType = McpTypes.VSCode,
|
mcpType = McpTypes.VSCode,
|
||||||
configStatus = "Not Configured",
|
configStatus = "Not Configured",
|
||||||
|
|
|
||||||
|
|
@ -35,6 +35,9 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
Path.Combine(home, ".local", "bin", "claude"),
|
Path.Combine(home, ".local", "bin", "claude"),
|
||||||
};
|
};
|
||||||
foreach (string c in candidates) { if (File.Exists(c)) return c; }
|
foreach (string c in candidates) { if (File.Exists(c)) return c; }
|
||||||
|
// Try NVM-installed claude under ~/.nvm/versions/node/*/bin/claude
|
||||||
|
string nvmClaude = ResolveClaudeFromNvm(home);
|
||||||
|
if (!string.IsNullOrEmpty(nvmClaude)) return nvmClaude;
|
||||||
#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX
|
#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX
|
||||||
return Which("claude", "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin");
|
return Which("claude", "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin");
|
||||||
#else
|
#else
|
||||||
|
|
@ -70,6 +73,9 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
Path.Combine(home, ".local", "bin", "claude"),
|
Path.Combine(home, ".local", "bin", "claude"),
|
||||||
};
|
};
|
||||||
foreach (string c in candidates) { if (File.Exists(c)) return c; }
|
foreach (string c in candidates) { if (File.Exists(c)) return c; }
|
||||||
|
// Try NVM-installed claude under ~/.nvm/versions/node/*/bin/claude
|
||||||
|
string nvmClaude = ResolveClaudeFromNvm(home);
|
||||||
|
if (!string.IsNullOrEmpty(nvmClaude)) return nvmClaude;
|
||||||
#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX
|
#if UNITY_EDITOR_OSX || UNITY_EDITOR_LINUX
|
||||||
return Which("claude", "/usr/local/bin:/usr/bin:/bin");
|
return Which("claude", "/usr/local/bin:/usr/bin:/bin");
|
||||||
#else
|
#else
|
||||||
|
|
@ -78,6 +84,75 @@ namespace UnityMcpBridge.Editor.Helpers
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Attempt to resolve claude from NVM-managed Node installations, choosing the newest version
|
||||||
|
private static string ResolveClaudeFromNvm(string home)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrEmpty(home)) return null;
|
||||||
|
string nvmNodeDir = Path.Combine(home, ".nvm", "versions", "node");
|
||||||
|
if (!Directory.Exists(nvmNodeDir)) return null;
|
||||||
|
|
||||||
|
string bestPath = null;
|
||||||
|
Version bestVersion = null;
|
||||||
|
foreach (string versionDir in Directory.EnumerateDirectories(nvmNodeDir))
|
||||||
|
{
|
||||||
|
string name = Path.GetFileName(versionDir);
|
||||||
|
if (string.IsNullOrEmpty(name)) continue;
|
||||||
|
if (name.StartsWith("v", StringComparison.OrdinalIgnoreCase))
|
||||||
|
{
|
||||||
|
// Extract numeric portion: e.g., v18.19.0-nightly -> 18.19.0
|
||||||
|
string versionStr = name.Substring(1);
|
||||||
|
int dashIndex = versionStr.IndexOf('-');
|
||||||
|
if (dashIndex > 0)
|
||||||
|
{
|
||||||
|
versionStr = versionStr.Substring(0, dashIndex);
|
||||||
|
}
|
||||||
|
if (Version.TryParse(versionStr, out Version parsed))
|
||||||
|
{
|
||||||
|
string candidate = Path.Combine(versionDir, "bin", "claude");
|
||||||
|
if (File.Exists(candidate))
|
||||||
|
{
|
||||||
|
if (bestVersion == null || parsed > bestVersion)
|
||||||
|
{
|
||||||
|
bestVersion = parsed;
|
||||||
|
bestPath = candidate;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return bestPath;
|
||||||
|
}
|
||||||
|
catch { return null; }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Explicitly set the Claude CLI absolute path override in EditorPrefs
|
||||||
|
internal static void SetClaudeCliPath(string absolutePath)
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (!string.IsNullOrEmpty(absolutePath) && File.Exists(absolutePath))
|
||||||
|
{
|
||||||
|
EditorPrefs.SetString(PrefClaude, absolutePath);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
|
// Clear any previously set Claude CLI override path
|
||||||
|
internal static void ClearClaudeCliPath()
|
||||||
|
{
|
||||||
|
try
|
||||||
|
{
|
||||||
|
if (EditorPrefs.HasKey(PrefClaude))
|
||||||
|
{
|
||||||
|
EditorPrefs.DeleteKey(PrefClaude);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { }
|
||||||
|
}
|
||||||
|
|
||||||
// Use existing UV resolver; returns absolute path or null.
|
// Use existing UV resolver; returns absolute path or null.
|
||||||
internal static string ResolveUv()
|
internal static string ResolveUv()
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -11,5 +11,9 @@ namespace UnityMcpBridge.Editor.Models
|
||||||
|
|
||||||
[JsonProperty("args")]
|
[JsonProperty("args")]
|
||||||
public string[] args;
|
public string[] args;
|
||||||
|
|
||||||
|
// VSCode expects a transport type; include only when explicitly set
|
||||||
|
[JsonProperty("type", NullValueHandling = NullValueHandling.Ignore)]
|
||||||
|
public string type;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -438,14 +438,7 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
EditorGUILayout.LabelField("MCP Client Configuration", sectionTitleStyle);
|
EditorGUILayout.LabelField("MCP Client Configuration", sectionTitleStyle);
|
||||||
EditorGUILayout.Space(10);
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
// Auto-connect toggle (moved from Server Status)
|
// (Auto-connect toggle removed per design)
|
||||||
bool newAuto = EditorGUILayout.ToggleLeft("Auto-connect to MCP Clients", autoRegisterEnabled);
|
|
||||||
if (newAuto != autoRegisterEnabled)
|
|
||||||
{
|
|
||||||
autoRegisterEnabled = newAuto;
|
|
||||||
EditorPrefs.SetBool("UnityMCP.AutoRegisterEnabled", autoRegisterEnabled);
|
|
||||||
}
|
|
||||||
EditorGUILayout.Space(6);
|
|
||||||
|
|
||||||
// Client selector
|
// Client selector
|
||||||
string[] clientNames = mcpClients.clients.Select(c => c.name).ToArray();
|
string[] clientNames = mcpClients.clients.Select(c => c.name).ToArray();
|
||||||
|
|
@ -697,6 +690,31 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
|
|
||||||
private void DrawClientConfigurationCompact(McpClient mcpClient)
|
private void DrawClientConfigurationCompact(McpClient mcpClient)
|
||||||
{
|
{
|
||||||
|
// Special pre-check for Claude Code: if CLI missing, reflect in status UI
|
||||||
|
if (mcpClient.mcpType == McpTypes.ClaudeCode)
|
||||||
|
{
|
||||||
|
string claudeCheck = ExecPath.ResolveClaude();
|
||||||
|
if (string.IsNullOrEmpty(claudeCheck))
|
||||||
|
{
|
||||||
|
mcpClient.configStatus = "Claude Not Found";
|
||||||
|
mcpClient.status = McpStatus.NotConfigured;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Pre-check for clients that require uv (all except Claude Code)
|
||||||
|
bool uvRequired = mcpClient.mcpType != McpTypes.ClaudeCode;
|
||||||
|
bool uvMissingEarly = false;
|
||||||
|
if (uvRequired)
|
||||||
|
{
|
||||||
|
string uvPathEarly = FindUvPath();
|
||||||
|
if (string.IsNullOrEmpty(uvPathEarly))
|
||||||
|
{
|
||||||
|
uvMissingEarly = true;
|
||||||
|
mcpClient.configStatus = "uv Not Found";
|
||||||
|
mcpClient.status = McpStatus.NotConfigured;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
// Status display
|
// Status display
|
||||||
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.BeginHorizontal();
|
||||||
Rect statusRect = GUILayoutUtility.GetRect(0, 28, GUILayout.Width(24));
|
Rect statusRect = GUILayoutUtility.GetRect(0, 28, GUILayout.Width(24));
|
||||||
|
|
@ -710,8 +728,64 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
};
|
};
|
||||||
EditorGUILayout.LabelField(mcpClient.configStatus, clientStatusStyle, GUILayout.Height(28));
|
EditorGUILayout.LabelField(mcpClient.configStatus, clientStatusStyle, GUILayout.Height(28));
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
|
// When Claude CLI is missing, show a clear install hint directly below status
|
||||||
EditorGUILayout.Space(10);
|
if (mcpClient.mcpType == McpTypes.ClaudeCode && string.IsNullOrEmpty(ExecPath.ResolveClaude()))
|
||||||
|
{
|
||||||
|
GUIStyle installHintStyle = new GUIStyle(clientStatusStyle);
|
||||||
|
installHintStyle.normal.textColor = new Color(1f, 0.5f, 0f); // orange
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUIContent installText = new GUIContent("Make sure Claude Code is installed!");
|
||||||
|
Vector2 textSize = installHintStyle.CalcSize(installText);
|
||||||
|
EditorGUILayout.LabelField(installText, installHintStyle, GUILayout.Height(22), GUILayout.Width(textSize.x + 2), GUILayout.ExpandWidth(false));
|
||||||
|
GUIStyle helpLinkStyle = new GUIStyle(EditorStyles.linkLabel) { fontStyle = FontStyle.Bold };
|
||||||
|
GUILayout.Space(6);
|
||||||
|
if (GUILayout.Button("[HELP]", helpLinkStyle, GUILayout.Height(22), GUILayout.ExpandWidth(false)))
|
||||||
|
{
|
||||||
|
Application.OpenURL("https://github.com/CoplayDev/unity-mcp/wiki/Troubleshooting-Unity-MCP-and-Claude-Code");
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
}
|
||||||
|
|
||||||
|
EditorGUILayout.Space(10);
|
||||||
|
|
||||||
|
// If uv is missing for required clients, show hint and picker then exit early to avoid showing other controls
|
||||||
|
if (uvRequired && uvMissingEarly)
|
||||||
|
{
|
||||||
|
GUIStyle installHintStyle2 = new GUIStyle(EditorStyles.label)
|
||||||
|
{
|
||||||
|
fontSize = 12,
|
||||||
|
fontStyle = FontStyle.Bold,
|
||||||
|
wordWrap = false
|
||||||
|
};
|
||||||
|
installHintStyle2.normal.textColor = new Color(1f, 0.5f, 0f);
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUIContent installText2 = new GUIContent("Make sure uv is installed!");
|
||||||
|
Vector2 sz = installHintStyle2.CalcSize(installText2);
|
||||||
|
EditorGUILayout.LabelField(installText2, installHintStyle2, GUILayout.Height(22), GUILayout.Width(sz.x + 2), GUILayout.ExpandWidth(false));
|
||||||
|
GUIStyle helpLinkStyle2 = new GUIStyle(EditorStyles.linkLabel) { fontStyle = FontStyle.Bold };
|
||||||
|
GUILayout.Space(6);
|
||||||
|
if (GUILayout.Button("[HELP]", helpLinkStyle2, GUILayout.Height(22), GUILayout.ExpandWidth(false)))
|
||||||
|
{
|
||||||
|
Application.OpenURL("https://github.com/CoplayDev/unity-mcp/wiki/Troubleshooting-Unity-MCP-and-Cursor,-VSCode-&-Windsurf");
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
|
EditorGUILayout.Space(8);
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (GUILayout.Button("Choose UV Install Location", GUILayout.Width(260), GUILayout.Height(22)))
|
||||||
|
{
|
||||||
|
string suggested = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "/opt/homebrew/bin" : Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||||
|
string picked = EditorUtility.OpenFilePanel("Select 'uv' binary", suggested, "");
|
||||||
|
if (!string.IsNullOrEmpty(picked))
|
||||||
|
{
|
||||||
|
EditorPrefs.SetString("UnityMCP.UvPath", picked);
|
||||||
|
ConfigureMcpClient(mcpClient);
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
// Action buttons in horizontal layout
|
// Action buttons in horizontal layout
|
||||||
EditorGUILayout.BeginHorizontal();
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
|
@ -723,23 +797,57 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
ConfigureMcpClient(mcpClient);
|
ConfigureMcpClient(mcpClient);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
else if (mcpClient.mcpType == McpTypes.ClaudeCode)
|
else if (mcpClient.mcpType == McpTypes.ClaudeCode)
|
||||||
{
|
{
|
||||||
bool isConfigured = mcpClient.status == McpStatus.Configured;
|
bool claudeAvailable = !string.IsNullOrEmpty(ExecPath.ResolveClaude());
|
||||||
string buttonText = isConfigured ? "Unregister UnityMCP with Claude Code" : "Register with Claude Code";
|
if (claudeAvailable)
|
||||||
if (GUILayout.Button(buttonText, GUILayout.Height(32)))
|
{
|
||||||
{
|
bool isConfigured = mcpClient.status == McpStatus.Configured;
|
||||||
if (isConfigured)
|
string buttonText = isConfigured ? "Unregister UnityMCP with Claude Code" : "Register with Claude Code";
|
||||||
{
|
if (GUILayout.Button(buttonText, GUILayout.Height(32)))
|
||||||
UnregisterWithClaudeCode();
|
{
|
||||||
}
|
if (isConfigured)
|
||||||
else
|
{
|
||||||
{
|
UnregisterWithClaudeCode();
|
||||||
string pythonDir = FindPackagePythonDirectory();
|
}
|
||||||
RegisterWithClaudeCode(pythonDir);
|
else
|
||||||
}
|
{
|
||||||
}
|
string pythonDir = FindPackagePythonDirectory();
|
||||||
}
|
RegisterWithClaudeCode(pythonDir);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
// Hide the picker once a valid binary is available
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
GUIStyle pathLabelStyle = new GUIStyle(EditorStyles.miniLabel) { wordWrap = true };
|
||||||
|
string resolvedClaude = ExecPath.ResolveClaude();
|
||||||
|
EditorGUILayout.LabelField($"Claude CLI: {resolvedClaude}", pathLabelStyle);
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
}
|
||||||
|
// CLI picker row (only when not found)
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
if (!claudeAvailable)
|
||||||
|
{
|
||||||
|
// Only show the picker button in not-found state (no redundant "not found" label)
|
||||||
|
if (GUILayout.Button("Choose Claude Install Location", GUILayout.Width(260), GUILayout.Height(22)))
|
||||||
|
{
|
||||||
|
string suggested = RuntimeInformation.IsOSPlatform(OSPlatform.OSX) ? "/opt/homebrew/bin" : Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles);
|
||||||
|
string picked = EditorUtility.OpenFilePanel("Select 'claude' CLI", suggested, "");
|
||||||
|
if (!string.IsNullOrEmpty(picked))
|
||||||
|
{
|
||||||
|
ExecPath.SetClaudeCliPath(picked);
|
||||||
|
// Auto-register after setting a valid path
|
||||||
|
string pythonDir = FindPackagePythonDirectory();
|
||||||
|
RegisterWithClaudeCode(pythonDir);
|
||||||
|
Repaint();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
EditorGUILayout.EndHorizontal();
|
||||||
|
EditorGUILayout.BeginHorizontal();
|
||||||
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
if (GUILayout.Button($"Auto Configure", GUILayout.Height(32)))
|
if (GUILayout.Button($"Auto Configure", GUILayout.Height(32)))
|
||||||
|
|
@ -793,13 +901,19 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
|
|
||||||
EditorGUILayout.EndHorizontal();
|
EditorGUILayout.EndHorizontal();
|
||||||
|
|
||||||
EditorGUILayout.Space(8);
|
EditorGUILayout.Space(8);
|
||||||
// Quick info
|
// Quick info (hide when Claude is not found to avoid confusion)
|
||||||
GUIStyle configInfoStyle = new GUIStyle(EditorStyles.miniLabel)
|
bool hideConfigInfo =
|
||||||
{
|
(mcpClient.mcpType == McpTypes.ClaudeCode && string.IsNullOrEmpty(ExecPath.ResolveClaude()))
|
||||||
fontSize = 10
|
|| ((mcpClient.mcpType != McpTypes.ClaudeCode) && string.IsNullOrEmpty(FindUvPath()));
|
||||||
};
|
if (!hideConfigInfo)
|
||||||
EditorGUILayout.LabelField($"Config: {Path.GetFileName(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? mcpClient.windowsConfigPath : mcpClient.linuxConfigPath)}", configInfoStyle);
|
{
|
||||||
|
GUIStyle configInfoStyle = new GUIStyle(EditorStyles.miniLabel)
|
||||||
|
{
|
||||||
|
fontSize = 10
|
||||||
|
};
|
||||||
|
EditorGUILayout.LabelField($"Config: {Path.GetFileName(RuntimeInformation.IsOSPlatform(OSPlatform.Windows) ? mcpClient.windowsConfigPath : mcpClient.linuxConfigPath)}", configInfoStyle);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private void ToggleUnityBridge()
|
private void ToggleUnityBridge()
|
||||||
|
|
@ -831,6 +945,10 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
command = uvPath,
|
command = uvPath,
|
||||||
args = new[] { "--directory", pythonDir, "run", "server.py" },
|
args = new[] { "--directory", pythonDir, "run", "server.py" },
|
||||||
};
|
};
|
||||||
|
if (mcpClient?.mcpType == McpTypes.VSCode)
|
||||||
|
{
|
||||||
|
unityMCPConfig.type = "stdio";
|
||||||
|
}
|
||||||
|
|
||||||
JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };
|
JsonSerializerSettings jsonSettings = new() { Formatting = Formatting.Indented };
|
||||||
|
|
||||||
|
|
@ -849,29 +967,41 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
}
|
}
|
||||||
|
|
||||||
// Parse the existing JSON while preserving all properties
|
// Parse the existing JSON while preserving all properties
|
||||||
dynamic existingConfig = JsonConvert.DeserializeObject(existingJson);
|
dynamic existingConfig;
|
||||||
existingConfig ??= new Newtonsoft.Json.Linq.JObject();
|
try
|
||||||
|
{
|
||||||
|
if (string.IsNullOrWhiteSpace(existingJson))
|
||||||
|
{
|
||||||
|
existingConfig = new Newtonsoft.Json.Linq.JObject();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
existingConfig = JsonConvert.DeserializeObject(existingJson) ?? new Newtonsoft.Json.Linq.JObject();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch
|
||||||
|
{
|
||||||
|
// If user has partial/invalid JSON (e.g., mid-edit), start from a fresh object
|
||||||
|
if (!string.IsNullOrWhiteSpace(existingJson))
|
||||||
|
{
|
||||||
|
UnityEngine.Debug.LogWarning("UnityMCP: VSCode mcp.json could not be parsed; rewriting servers block.");
|
||||||
|
}
|
||||||
|
existingConfig = new Newtonsoft.Json.Linq.JObject();
|
||||||
|
}
|
||||||
|
|
||||||
// Handle different client types with a switch statement
|
// Handle different client types with a switch statement
|
||||||
//Comments: Interestingly, VSCode has mcp.servers.unityMCP while others have mcpServers.unityMCP, which is why we need to prevent this
|
//Comments: Interestingly, VSCode has mcp.servers.unityMCP while others have mcpServers.unityMCP, which is why we need to prevent this
|
||||||
switch (mcpClient?.mcpType)
|
switch (mcpClient?.mcpType)
|
||||||
{
|
{
|
||||||
case McpTypes.VSCode:
|
case McpTypes.VSCode:
|
||||||
// VSCode specific configuration
|
// VSCode-specific configuration (top-level "servers")
|
||||||
// Ensure mcp object exists
|
if (existingConfig.servers == null)
|
||||||
if (existingConfig.mcp == null)
|
|
||||||
{
|
{
|
||||||
existingConfig.mcp = new Newtonsoft.Json.Linq.JObject();
|
existingConfig.servers = new Newtonsoft.Json.Linq.JObject();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Ensure mcp.servers object exists
|
// Add/update UnityMCP server in VSCode mcp.json
|
||||||
if (existingConfig.mcp.servers == null)
|
existingConfig.servers.unityMCP =
|
||||||
{
|
|
||||||
existingConfig.mcp.servers = new Newtonsoft.Json.Linq.JObject();
|
|
||||||
}
|
|
||||||
|
|
||||||
// Add/update UnityMCP server in VSCode settings
|
|
||||||
existingConfig.mcp.servers.unityMCP =
|
|
||||||
JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>(
|
JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>(
|
||||||
JsonConvert.SerializeObject(unityMCPConfig)
|
JsonConvert.SerializeObject(unityMCPConfig)
|
||||||
);
|
);
|
||||||
|
|
@ -923,24 +1053,29 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
// Use switch statement to handle different client types
|
// Use switch statement to handle different client types
|
||||||
switch (mcpClient.mcpType)
|
switch (mcpClient.mcpType)
|
||||||
{
|
{
|
||||||
case McpTypes.VSCode:
|
case McpTypes.VSCode:
|
||||||
// Create VSCode-specific configuration with proper format
|
// Resolve uv so VSCode launches the correct executable even if not on PATH
|
||||||
var vscodeConfig = new
|
string uvPathManual = FindUvPath();
|
||||||
{
|
if (uvPathManual == null)
|
||||||
mcp = new
|
{
|
||||||
{
|
UnityEngine.Debug.LogError("UV package manager not found. Cannot generate manual configuration.");
|
||||||
servers = new
|
return;
|
||||||
{
|
}
|
||||||
unityMCP = new
|
// Create VSCode-specific configuration with proper format
|
||||||
{
|
var vscodeConfig = new
|
||||||
command = "uv",
|
{
|
||||||
args = new[] { "--directory", pythonDir, "run", "server.py" }
|
servers = new
|
||||||
}
|
{
|
||||||
}
|
unityMCP = new
|
||||||
}
|
{
|
||||||
};
|
command = uvPathManual,
|
||||||
manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings);
|
args = new[] { "--directory", pythonDir, "run", "server.py" },
|
||||||
break;
|
type = "stdio"
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
manualConfigJson = JsonConvert.SerializeObject(vscodeConfig, jsonSettings);
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
default:
|
||||||
// Create standard MCP configuration for other clients
|
// Create standard MCP configuration for other clients
|
||||||
|
|
@ -1244,9 +1379,15 @@ namespace UnityMcpBridge.Editor.Windows
|
||||||
case McpTypes.VSCode:
|
case McpTypes.VSCode:
|
||||||
dynamic config = JsonConvert.DeserializeObject(configJson);
|
dynamic config = JsonConvert.DeserializeObject(configJson);
|
||||||
|
|
||||||
if (config?.mcp?.servers?.unityMCP != null)
|
// New schema: top-level servers
|
||||||
|
if (config?.servers?.unityMCP != null)
|
||||||
|
{
|
||||||
|
args = config.servers.unityMCP.args.ToObject<string[]>();
|
||||||
|
configExists = true;
|
||||||
|
}
|
||||||
|
// Back-compat: legacy mcp.servers
|
||||||
|
else if (config?.mcp?.servers?.unityMCP != null)
|
||||||
{
|
{
|
||||||
// Extract args from VSCode config format
|
|
||||||
args = config.mcp.servers.unityMCP.args.ToObject<string[]>();
|
args = config.mcp.servers.unityMCP.args.ToObject<string[]>();
|
||||||
configExists = true;
|
configExists = true;
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
{
|
{
|
||||||
"name": "com.coplaydev.unity-mcp",
|
"name": "com.coplaydev.unity-mcp",
|
||||||
"version": "2.0.1",
|
"version": "2.0.2",
|
||||||
"displayName": "Unity MCP Bridge",
|
"displayName": "Unity MCP Bridge",
|
||||||
"description": "A bridge that manages and communicates with the sister application, Unity MCP Server, which allows for communications with MCP Clients like Claude Desktop or Cursor.",
|
"description": "A bridge that manages and communicates with the sister application, Unity MCP Server, which allows for communications with MCP Clients like Claude Desktop or Cursor.",
|
||||||
"unity": "2020.3",
|
"unity": "2020.3",
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue