diff --git a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs index dbdfb74..aa84589 100644 --- a/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs +++ b/UnityMcpBridge/Editor/Helpers/ServerInstaller.cs @@ -270,19 +270,29 @@ namespace UnityMcpBridge.Editor.Helpers string[] candidates; if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) { + string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty; + string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) ?? string.Empty; + string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty; + candidates = new[] { + // Preferred: WinGet Links shims (stable entrypoints) + Path.Combine(localAppData, "Microsoft", "WinGet", "Links", "uv.exe"), + Path.Combine(programFiles, "WinGet", "Links", "uv.exe"), + // Common per-user installs - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python313\Scripts\uv.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python312\Scripts\uv.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python311\Scripts\uv.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty, @"Programs\Python\Python310\Scripts\uv.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python313\Scripts\uv.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python312\Scripts\uv.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python311\Scripts\uv.exe"), - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty, @"Python\Python310\Scripts\uv.exe"), + Path.Combine(localAppData, @"Programs\Python\Python313\Scripts\uv.exe"), + Path.Combine(localAppData, @"Programs\Python\Python312\Scripts\uv.exe"), + Path.Combine(localAppData, @"Programs\Python\Python311\Scripts\uv.exe"), + Path.Combine(localAppData, @"Programs\Python\Python310\Scripts\uv.exe"), + Path.Combine(appData, @"Python\Python313\Scripts\uv.exe"), + Path.Combine(appData, @"Python\Python312\Scripts\uv.exe"), + Path.Combine(appData, @"Python\Python311\Scripts\uv.exe"), + Path.Combine(appData, @"Python\Python310\Scripts\uv.exe"), + // Program Files style installs (if a native installer was used) - Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) ?? string.Empty, @"uv\uv.exe"), + Path.Combine(programFiles, @"uv\uv.exe"), + // Try simple name resolution later via PATH "uv.exe", "uv" diff --git a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs index e5354ba..821e03c 100644 --- a/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs +++ b/UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs @@ -1023,6 +1023,68 @@ namespace UnityMcpBridge.Editor.Windows break; } + // If config already has a working absolute uv path, avoid rewriting it on refresh + try + { + if (mcpClient?.mcpType != McpTypes.ClaudeCode) + { + // Inspect existing command for stability (Windows absolute path that exists) + string existingCommand = null; + if (mcpClient?.mcpType == McpTypes.VSCode) + { + existingCommand = existingConfig?.servers?.unityMCP?.command?.ToString(); + } + else + { + existingCommand = existingConfig?.mcpServers?.unityMCP?.command?.ToString(); + } + + if (!string.IsNullOrEmpty(existingCommand)) + { + bool keep = false; + if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) + { + // Consider absolute, existing paths as stable; prefer WinGet Links + if (Path.IsPathRooted(existingCommand) && File.Exists(existingCommand)) + { + keep = true; + } + } + else + { + // On Unix, keep absolute existing path as well + if (Path.IsPathRooted(existingCommand) && File.Exists(existingCommand)) + { + keep = true; + } + } + + if (keep) + { + // Merge without replacing the existing command + if (mcpClient?.mcpType == McpTypes.VSCode) + { + existingConfig.servers.unityMCP.args = + JsonConvert.DeserializeObject( + JsonConvert.SerializeObject(unityMCPConfig.args) + ); + } + else + { + existingConfig.mcpServers.unityMCP.args = + JsonConvert.DeserializeObject( + JsonConvert.SerializeObject(unityMCPConfig.args) + ); + } + string mergedKeep = JsonConvert.SerializeObject(existingConfig, jsonSettings); + File.WriteAllText(configPath, mergedKeep); + return "Configured successfully"; + } + } + } + } + catch { /* fall back to normal write */ } + // Write the merged configuration back to file string mergedJson = JsonConvert.SerializeObject(existingConfig, jsonSettings); File.WriteAllText(configPath, mergedJson);