Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
204e95a
fix: resolve UV path override not being detected in System Requirements
whatevertogo Jan 11, 2026
25e5d05
fix: improve uv/uvx detection robustness on macOS and Linux
whatevertogo Jan 11, 2026
cc22320
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 11, 2026
8d5fa2f
refactor: unify process execution with ExecPath.TryRun and add Window…
whatevertogo Jan 11, 2026
84cee8c
fix: improve version parsing to handle both spaces and parentheses
whatevertogo Jan 11, 2026
510e631
refactor: improve platform detectors with absolute path resolution
whatevertogo Jan 11, 2026
bf41479
fix: improve error handling in PathResolverService by logging exceptions
whatevertogo Jan 11, 2026
f39857c
Remove .meta files added after fork and update .gitignore
whatevertogo Jan 11, 2026
c4be11c
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 12, 2026
1832715
Update .gitignore
whatevertogo Jan 12, 2026
554ddd0
save .meta
whatevertogo Jan 12, 2026
fb5909d
refactor: unify uv/uvx naming and path detection across platforms
whatevertogo Jan 12, 2026
254125a
fix: improve validation light(uvxPathStatus) logic for UVX path overr…
whatevertogo Jan 12, 2026
f9ae5d5
refactor: streamline UV version validation and unify path detection m…
whatevertogo Jan 12, 2026
84cb9c6
fix: add type handling for Claude Code client in config JSON builder
whatevertogo Jan 12, 2026
ee33077
fix: correct command from 'uvx' to 'uv' for Python version listing in…
whatevertogo Jan 12, 2026
2c3ebcd
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 14, 2026
66fe194
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 15, 2026
79bce47
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 16, 2026
1877cc2
feat: add uvx path fallback with warning UI
whatevertogo Jan 17, 2026
f9b0563
refactor: remove GetDetails method from PlatformDetectorBase
whatevertogo Jan 17, 2026
c86eb78
Update ExecPath.cs
Scriptwonder Jan 17, 2026
a05d1c7
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 18, 2026
396f248
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 19, 2026
a0c07ec
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 20, 2026
4916b12
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 21, 2026
a3c2f30
Merge branch 'CoplayDev:main' into main
whatevertogo Jan 21, 2026
2d3dd3e
refactor: update configuration paths for Rider GitHub Copilot and str…
whatevertogo Jan 21, 2026
293db3b
fix: enhance error handling for package source and uvx command arguments
whatevertogo Jan 21, 2026
e0bfd96
Update MCPForUnity/Editor/Clients/Configurators/RiderConfigurator.cs
whatevertogo Jan 21, 2026
9e83f83
Update MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
whatevertogo Jan 21, 2026
88deaf0
chore: remove unnecessary exception documentation from GetMcpServerPa…
whatevertogo Jan 21, 2026
cde5795
Merge branch 'main' of github.com:whatevertogo/unity-mcp
whatevertogo Jan 21, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 4 additions & 3 deletions MCPForUnity/Editor/Clients/Configurators/RiderConfigurator.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,9 +10,10 @@ public class RiderConfigurator : JsonFileMcpConfigurator
public RiderConfigurator() : base(new McpClient
{
name = "Rider GitHub Copilot",
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData), "JetBrains", "Rider", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "JetBrains", "Rider", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".config", "JetBrains", "Rider", "mcp.json"),
// Rider GitHub Copilot uses github-copilot/intellij path under appropriate per-OS data directories
windowsConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData), "github-copilot", "intellij", "mcp.json"),
macConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), "Library", "Application Support", "github-copilot", "intellij", "mcp.json"),
linuxConfigPath = Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile), ".local", "share", "github-copilot", "intellij", "mcp.json"),
IsVsCodeLayout = true
})
{ }
Expand Down
40 changes: 28 additions & 12 deletions MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -540,9 +540,8 @@ private void RegisterWithCapturedValues(
}
else
{
// Note: --reinstall is not supported by uvx, use --no-cache --refresh instead
string devFlags = shouldForceRefresh ? "--no-cache --refresh " : string.Empty;
args = $"mcp add --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{gitUrl}\" {packageName}";
string packageArgs = BuildClaudeUvxArgs(uvxPath, gitUrl, packageName, shouldForceRefresh);
args = $"mcp add --transport stdio UnityMCP -- {packageArgs}";
}

// Remove any existing registrations - handle both "UnityMCP" and "unityMCP" (legacy)
Expand Down Expand Up @@ -599,10 +598,9 @@ private void Register()
else
{
var (uvxPath, gitUrl, packageName) = AssetPathUtility.GetUvxCommandParts();
// Use central helper that checks both DevModeForceServerRefresh AND local path detection.
// Note: --reinstall is not supported by uvx, use --no-cache --refresh instead
string devFlags = AssetPathUtility.ShouldForceUvxRefresh() ? "--no-cache --refresh " : string.Empty;
args = $"mcp add --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{gitUrl}\" {packageName}";
bool shouldForceRefresh = AssetPathUtility.ShouldForceUvxRefresh();
string packageArgs = BuildClaudeUvxArgs(uvxPath, gitUrl, packageName, shouldForceRefresh);
args = $"mcp add --transport stdio UnityMCP -- {packageArgs}";
}

string projectDir = Path.GetDirectoryName(Application.dataPath);
Expand Down Expand Up @@ -698,13 +696,12 @@ public override string GetManualSnippet()
return "# Error: Configuration not available - check paths in Advanced Settings";
}

string packageSource = AssetPathUtility.GetMcpServerPackageSource();
// Use central helper that checks both DevModeForceServerRefresh AND local path detection.
// Note: --reinstall is not supported by uvx, use --no-cache --refresh instead
string devFlags = AssetPathUtility.ShouldForceUvxRefresh() ? "--no-cache --refresh " : string.Empty;
var (_, gitUrl, packageName) = AssetPathUtility.GetUvxCommandParts();
bool shouldForceRefresh = AssetPathUtility.ShouldForceUvxRefresh();
string packageArgs = BuildClaudeUvxArgs(uvxPath, gitUrl, packageName, shouldForceRefresh);

return "# Register the MCP server with Claude Code:\n" +
$"claude mcp add --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{packageSource}\" mcp-for-unity\n\n" +
$"claude mcp add --transport stdio UnityMCP -- {packageArgs}\n\n" +
"# Unregister the MCP server:\n" +
"claude mcp remove UnityMCP\n\n" +
"# List registered servers:\n" +
Expand Down Expand Up @@ -763,5 +760,24 @@ private static string ExtractPackageSourceFromCliOutput(string cliOutput)

return null;
}

/// <summary>
/// Builds the correct uvx package arguments for Claude CLI mcp add command.
/// IMPORTANT: We always need --from because the PyPI package name (mcpforunityserver)
/// differs from the executable name (mcp-for-unity). The uvx command format is:
/// uvx --from PACKAGE_SOURCE EXECUTABLE_NAME --transport stdio
/// Example: uvx --from mcpforunityserver==9.0.8 mcp-for-unity --transport stdio
/// </summary>
private static string BuildClaudeUvxArgs(string uvxPath, string fromUrl, string packageName, bool shouldForceRefresh)
{
// Dev flags
string devFlags = shouldForceRefresh ? "--no-cache --refresh " : string.Empty;

// Always use --from because package name != executable name
// Example: uvx --from mcpforunityserver==9.0.8 mcp-for-unity
string packageArgs = $"\"{uvxPath}\" {devFlags}--from \"{fromUrl}\" {packageName}";

return packageArgs;
}
}
}
18 changes: 15 additions & 3 deletions MCPForUnity/Editor/Helpers/AssetPathUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ public static JObject GetPackageJson()
/// Checks for EditorPrefs override first (supports git URLs, file:// paths, etc.),
/// then falls back to PyPI package reference.
/// </summary>
/// <returns>Package source string for uvx --from argument</returns>
/// <returns>Package source string for uvx --from argument (guaranteed non-null)</returns>
public static string GetMcpServerPackageSource()
{
// Check for override first (supports git URLs, file:// paths, local paths)
Expand Down Expand Up @@ -182,13 +182,25 @@ public static string GetMcpServerPackageSource()
public static string GetMcpServerGitUrl() => GetMcpServerPackageSource();

/// <summary>
/// Gets structured uvx command parts for different client configurations
/// Gets structured uvx command parts for different client configurations.
/// </summary>
/// <returns>Tuple containing (uvxPath, fromUrl, packageName)</returns>
/// <returns>Tuple containing (uvxPath, fromUrl, packageName) - fromUrl is guaranteed non-null</returns>
/// <exception cref="InvalidOperationException">Thrown when uvx path cannot be determined</exception>
public static (string uvxPath, string fromUrl, string packageName) GetUvxCommandParts()
{
string uvxPath = MCPServiceLocator.Paths.GetUvxPath();
if (string.IsNullOrEmpty(uvxPath))
{
throw new InvalidOperationException("Cannot determine uvx path. Please ensure uv is installed and configured.");
}

string fromUrl = GetMcpServerPackageSource();
// GetMcpServerPackageSource() guarantees non-null, but validate for defense in depth
if (string.IsNullOrEmpty(fromUrl))
{
throw new InvalidOperationException("Cannot determine MCP server package source. This should never happen - please report this bug.");
}

string packageName = "mcp-for-unity";

return (uvxPath, fromUrl, packageName);
Expand Down
41 changes: 29 additions & 12 deletions MCPForUnity/Editor/Helpers/CodexConfigHelper.cs
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,33 @@ private static void AddDevModeArgs(TomlArray args)
args.Add(new TomlString { Value = "--refresh" });
}

/// <summary>
/// Adds package arguments to the TOML array.
/// IMPORTANT: We always need --from because the PyPI package name (mcpforunityserver)
/// differs from the executable name (mcp-for-unity). The uvx command format is:
/// uvx --from PACKAGE_SOURCE EXECUTABLE_NAME --transport stdio
/// Example: uvx --from mcpforunityserver==9.0.8 mcp-for-unity --transport stdio
/// </summary>
/// <param name="args">The TOML array to add arguments to</param>
/// <param name="fromUrl">Package source for --from argument (guaranteed non-null from GetUvxCommandParts)</param>
/// <param name="packageName">Executable name to run</param>
private static void AddPackageArgs(TomlArray args, string fromUrl, string packageName)
{
if (args == null) return;

// fromUrl is guaranteed non-null by GetUvxCommandParts(), but validate for defense in depth
if (string.IsNullOrEmpty(fromUrl))
{
throw new InvalidOperationException("Package source (--from argument) cannot be empty. This should never happen - please report this bug.");
}

// Always use --from because package name != executable name
args.Add(new TomlString { Value = "--from" });
args.Add(new TomlString { Value = fromUrl });

args.Add(new TomlString { Value = packageName });
}

public static string BuildCodexServerBlock(string uvPath)
{
var table = new TomlTable();
Expand Down Expand Up @@ -54,12 +81,7 @@ public static string BuildCodexServerBlock(string uvPath)

var args = new TomlArray();
AddDevModeArgs(args);
if (!string.IsNullOrEmpty(fromUrl))
{
args.Add(new TomlString { Value = "--from" });
args.Add(new TomlString { Value = fromUrl });
}
args.Add(new TomlString { Value = packageName });
AddPackageArgs(args, fromUrl, packageName);
args.Add(new TomlString { Value = "--transport" });
args.Add(new TomlString { Value = "stdio" });

Expand Down Expand Up @@ -203,12 +225,7 @@ private static TomlTable CreateUnityMcpTable(string uvPath)

var argsArray = new TomlArray();
AddDevModeArgs(argsArray);
if (!string.IsNullOrEmpty(fromUrl))
{
argsArray.Add(new TomlString { Value = "--from" });
argsArray.Add(new TomlString { Value = fromUrl });
}
argsArray.Add(new TomlString { Value = packageName });
AddPackageArgs(argsArray, fromUrl, packageName);
argsArray.Add(new TomlString { Value = "--transport" });
argsArray.Add(new TomlString { Value = "stdio" });
unityMCP["args"] = argsArray;
Expand Down
17 changes: 12 additions & 5 deletions MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs
Original file line number Diff line number Diff line change
Expand Up @@ -161,7 +161,6 @@ private static IList<string> BuildUvxArgs(string fromUrl, string packageName)
// Dev mode: force a fresh install/resolution (avoids stale cached builds while iterating).
// `--no-cache` avoids reading from cache; `--refresh` ensures metadata is revalidated.
// Note: --reinstall is not supported by uvx and will cause a warning.
// Keep ordering consistent with other uvx builders: dev flags first, then --from <url>, then package name.
var args = new List<string>();

// Use central helper that checks both DevModeForceServerRefresh AND local path detection.
Expand All @@ -170,13 +169,21 @@ private static IList<string> BuildUvxArgs(string fromUrl, string packageName)
args.Add("--no-cache");
args.Add("--refresh");
}
if (!string.IsNullOrEmpty(fromUrl))

// IMPORTANT: We always need --from because the PyPI package name (mcpforunityserver)
// differs from the executable name (mcp-for-unity). The uvx command format is:
// uvx --from PACKAGE_SOURCE EXECUTABLE_NAME --transport stdio
// Example: uvx --from mcpforunityserver==9.0.8 mcp-for-unity --transport stdio
// fromUrl is guaranteed non-null by GetUvxCommandParts(), but validate for defense in depth
if (string.IsNullOrEmpty(fromUrl))
{
args.Add("--from");
args.Add(fromUrl);
throw new InvalidOperationException("Package source (--from argument) cannot be empty. This should never happen - please report this bug.");
}
args.Add(packageName);

args.Add("--from");
args.Add(fromUrl);

args.Add(packageName);
args.Add("--transport");
args.Add("stdio");

Expand Down