chore: bump version to 2.1.0; Windows uv resolver improvements; preserve existing uv command; Claude unregister UI fix; .ps1 handling; add generic mcp_source.py

main
David Sarno 2025-08-13 14:02:19 -07:00
parent 5583327a03
commit 4e1b905ea0
5 changed files with 69 additions and 43 deletions

View File

@ -176,10 +176,16 @@ namespace UnityMcpBridge.Editor.Helpers
stderr = string.Empty; stderr = string.Empty;
try try
{ {
// Handle PowerShell scripts on Windows by invoking through powershell.exe
bool isPs1 = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) &&
file.EndsWith(".ps1", StringComparison.OrdinalIgnoreCase);
var psi = new ProcessStartInfo var psi = new ProcessStartInfo
{ {
FileName = file, FileName = isPs1 ? "powershell.exe" : file,
Arguments = args, Arguments = isPs1
? $"-NoProfile -ExecutionPolicy Bypass -File \"{file}\" {args}".Trim()
: args,
WorkingDirectory = string.IsNullOrEmpty(workingDir) ? Environment.CurrentDirectory : workingDir, WorkingDirectory = string.IsNullOrEmpty(workingDir) ? Environment.CurrentDirectory : workingDir,
UseShellExecute = false, UseShellExecute = false,
RedirectStandardOutput = true, RedirectStandardOutput = true,

View File

@ -2,7 +2,6 @@ using System;
using System.IO; using System.IO;
using System.Runtime.InteropServices; using System.Runtime.InteropServices;
using System.Text; using System.Text;
using System.Reflection;
using UnityEditor; using UnityEditor;
using UnityEngine; using UnityEngine;
@ -70,21 +69,19 @@ namespace UnityMcpBridge.Editor.Helpers
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{ {
return Path.Combine( var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty, "AppData", "Local");
"AppData", return Path.Combine(localAppData, "Programs", RootFolder);
"Local",
"Programs",
RootFolder
);
} }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux)) else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
{ {
return Path.Combine( var xdg = Environment.GetEnvironmentVariable("XDG_DATA_HOME");
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), if (string.IsNullOrEmpty(xdg))
"bin", {
RootFolder xdg = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty,
); ".local", "share");
}
return Path.Combine(xdg, RootFolder);
} }
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX)) else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
{ {
@ -273,12 +270,41 @@ namespace UnityMcpBridge.Editor.Helpers
string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty; string localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData) ?? string.Empty;
string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) ?? string.Empty; string programFiles = Environment.GetFolderPath(Environment.SpecialFolder.ProgramFiles) ?? string.Empty;
string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty; string appData = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty;
string programData = Environment.GetFolderPath(Environment.SpecialFolder.CommonApplicationData) ?? string.Empty; // optional fallback
// Fast path: resolve from PATH first
try
{
var wherePsi = new System.Diagnostics.ProcessStartInfo
{
FileName = "where",
Arguments = "uv.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
using var wp = System.Diagnostics.Process.Start(wherePsi);
string output = wp.StandardOutput.ReadToEnd().Trim();
wp.WaitForExit(1500);
if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output))
{
foreach (var line in output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
string path = line.Trim();
if (File.Exists(path) && ValidateUvBinary(path)) return path;
}
}
}
catch { }
candidates = new[] candidates = new[]
{ {
// Preferred: WinGet Links shims (stable entrypoints) // Preferred: WinGet Links shims (stable entrypoints)
Path.Combine(localAppData, "Microsoft", "WinGet", "Links", "uv.exe"), Path.Combine(localAppData, "Microsoft", "WinGet", "Links", "uv.exe"),
Path.Combine(programFiles, "WinGet", "Links", "uv.exe"), Path.Combine(programFiles, "WinGet", "Links", "uv.exe"),
// Optional low-priority fallback for atypical images
Path.Combine(programData, "Microsoft", "WinGet", "Links", "uv.exe"),
// Common per-user installs // Common per-user installs
Path.Combine(localAppData, @"Programs\Python\Python313\Scripts\uv.exe"), Path.Combine(localAppData, @"Programs\Python\Python313\Scripts\uv.exe"),
@ -325,33 +351,10 @@ namespace UnityMcpBridge.Editor.Helpers
catch { /* ignore */ } catch { /* ignore */ }
} }
// Use platform-appropriate which/where to resolve from PATH // Use platform-appropriate which/where to resolve from PATH (non-Windows handled here; Windows tried earlier)
try try
{ {
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) if (!RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
{
var wherePsi = new System.Diagnostics.ProcessStartInfo
{
FileName = "where",
Arguments = "uv.exe",
UseShellExecute = false,
RedirectStandardOutput = true,
RedirectStandardError = true,
CreateNoWindow = true
};
using var wp = System.Diagnostics.Process.Start(wherePsi);
string output = wp.StandardOutput.ReadToEnd().Trim();
wp.WaitForExit(3000);
if (wp.ExitCode == 0 && !string.IsNullOrEmpty(output))
{
foreach (var line in output.Split(new[] { '\r', '\n' }, StringSplitOptions.RemoveEmptyEntries))
{
string path = line.Trim();
if (File.Exists(path) && ValidateUvBinary(path)) return path;
}
}
}
else
{ {
var whichPsi = new System.Diagnostics.ProcessStartInfo var whichPsi = new System.Diagnostics.ProcessStartInfo
{ {

View File

@ -1064,6 +1064,14 @@ namespace UnityMcpBridge.Editor.Windows
// Merge without replacing the existing command // Merge without replacing the existing command
if (mcpClient?.mcpType == McpTypes.VSCode) if (mcpClient?.mcpType == McpTypes.VSCode)
{ {
if (existingConfig.servers == null)
{
existingConfig.servers = new Newtonsoft.Json.Linq.JObject();
}
if (existingConfig.servers.unityMCP == null)
{
existingConfig.servers.unityMCP = new Newtonsoft.Json.Linq.JObject();
}
existingConfig.servers.unityMCP.args = existingConfig.servers.unityMCP.args =
JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>( JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>(
JsonConvert.SerializeObject(unityMCPConfig.args) JsonConvert.SerializeObject(unityMCPConfig.args)
@ -1071,6 +1079,14 @@ namespace UnityMcpBridge.Editor.Windows
} }
else else
{ {
if (existingConfig.mcpServers == null)
{
existingConfig.mcpServers = new Newtonsoft.Json.Linq.JObject();
}
if (existingConfig.mcpServers.unityMCP == null)
{
existingConfig.mcpServers.unityMCP = new Newtonsoft.Json.Linq.JObject();
}
existingConfig.mcpServers.unityMCP.args = existingConfig.mcpServers.unityMCP.args =
JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>( JsonConvert.DeserializeObject<Newtonsoft.Json.Linq.JToken>(
JsonConvert.SerializeObject(unityMCPConfig.args) JsonConvert.SerializeObject(unityMCPConfig.args)
@ -1617,7 +1633,8 @@ namespace UnityMcpBridge.Editor.Windows
UnityEngine.Debug.Log($"Successfully removed MCP server: {serverName}"); UnityEngine.Debug.Log($"Successfully removed MCP server: {serverName}");
break; break;
} }
else if (!stderr.Contains("No MCP server found")) else if (!string.IsNullOrEmpty(stderr) &&
!stderr.Contains("No MCP server found", StringComparison.OrdinalIgnoreCase))
{ {
// If it's not a "not found" error, log it and stop trying // If it's not a "not found" error, log it and stop trying
UnityEngine.Debug.LogWarning($"Error removing {serverName}: {stderr}"); UnityEngine.Debug.LogWarning($"Error removing {serverName}: {stderr}");

View File

@ -1,6 +1,6 @@
{ {
"name": "com.coplaydev.unity-mcp", "name": "com.coplaydev.unity-mcp",
"version": "2.0.2", "version": "2.1.0",
"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",

View File

@ -1,7 +1,7 @@
#!/usr/bin/env python3 #!/usr/bin/env python3
""" """
Generic helper to switch the Unity MCP package source in a Unity project's Generic helper to switch the Unity MCP package source in a Unity project's
Packages/manifest.json without embedding any personal paths. Packages/manifest.json. This is useful for switching between upstream and local repos while working on the MCP.
Usage: Usage:
python mcp_source.py [--manifest /abs/path/to/manifest.json] [--repo /abs/path/to/unity-mcp] [--choice 1|2|3] python mcp_source.py [--manifest /abs/path/to/manifest.json] [--repo /abs/path/to/unity-mcp] [--choice 1|2|3]