diff --git a/MCPForUnity/Editor/Clients/Configurators/RiderConfigurator.cs b/MCPForUnity/Editor/Clients/Configurators/RiderConfigurator.cs
index 5db02acaf..ea1087ea4 100644
--- a/MCPForUnity/Editor/Clients/Configurators/RiderConfigurator.cs
+++ b/MCPForUnity/Editor/Clients/Configurators/RiderConfigurator.cs
@@ -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
})
{ }
diff --git a/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs b/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
index 897e7dfd8..5a0bb0317 100644
--- a/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
+++ b/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
@@ -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)
@@ -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);
@@ -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" +
@@ -763,5 +760,24 @@ private static string ExtractPackageSourceFromCliOutput(string cliOutput)
return null;
}
+
+ ///
+ /// 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
+ ///
+ 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;
+ }
}
}
diff --git a/MCPForUnity/Editor/Helpers/AssetPathUtility.cs b/MCPForUnity/Editor/Helpers/AssetPathUtility.cs
index b65cf21ea..1669bf52c 100644
--- a/MCPForUnity/Editor/Helpers/AssetPathUtility.cs
+++ b/MCPForUnity/Editor/Helpers/AssetPathUtility.cs
@@ -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.
///
- /// Package source string for uvx --from argument
+ /// Package source string for uvx --from argument (guaranteed non-null)
public static string GetMcpServerPackageSource()
{
// Check for override first (supports git URLs, file:// paths, local paths)
@@ -182,13 +182,25 @@ public static string GetMcpServerPackageSource()
public static string GetMcpServerGitUrl() => GetMcpServerPackageSource();
///
- /// Gets structured uvx command parts for different client configurations
+ /// Gets structured uvx command parts for different client configurations.
///
- /// Tuple containing (uvxPath, fromUrl, packageName)
+ /// Tuple containing (uvxPath, fromUrl, packageName) - fromUrl is guaranteed non-null
+ /// Thrown when uvx path cannot be determined
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);
diff --git a/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs b/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs
index 10f2bacb3..32dae58d0 100644
--- a/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs
+++ b/MCPForUnity/Editor/Helpers/CodexConfigHelper.cs
@@ -27,6 +27,33 @@ private static void AddDevModeArgs(TomlArray args)
args.Add(new TomlString { Value = "--refresh" });
}
+ ///
+ /// 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
+ ///
+ /// The TOML array to add arguments to
+ /// Package source for --from argument (guaranteed non-null from GetUvxCommandParts)
+ /// Executable name to run
+ 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();
@@ -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" });
@@ -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;
diff --git a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs
index dd00d73d0..bc21f878c 100644
--- a/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs
+++ b/MCPForUnity/Editor/Helpers/ConfigJsonBuilder.cs
@@ -161,7 +161,6 @@ private static IList 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 , then package name.
var args = new List();
// Use central helper that checks both DevModeForceServerRefresh AND local path detection.
@@ -170,13 +169,21 @@ private static IList 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");