Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
8 changes: 4 additions & 4 deletions MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -148,7 +148,7 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
bool matches = false;
if (args != null && args.Length > 0)
{
string expectedUvxUrl = AssetPathUtility.GetMcpServerGitUrl();
string expectedUvxUrl = AssetPathUtility.GetMcpServerPackageSource();
string configuredUvxUrl = McpConfigurationHelper.ExtractUvxUrl(args);
matches = !string.IsNullOrEmpty(configuredUvxUrl) &&
McpConfigurationHelper.PathsEqual(configuredUvxUrl, expectedUvxUrl);
Expand Down Expand Up @@ -250,7 +250,7 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
}
else if (args != null && args.Length > 0)
{
string expected = AssetPathUtility.GetMcpServerGitUrl();
string expected = AssetPathUtility.GetMcpServerPackageSource();
string configured = McpConfigurationHelper.ExtractUvxUrl(args);
matches = !string.IsNullOrEmpty(configured) &&
McpConfigurationHelper.PathsEqual(configured, expected);
Expand Down Expand Up @@ -585,12 +585,12 @@ public override string GetManualSnippet()
return "# Error: Configuration not available - check paths in Advanced Settings";
}

string gitUrl = AssetPathUtility.GetMcpServerGitUrl();
string packageSource = AssetPathUtility.GetMcpServerPackageSource();
// Use central helper that checks both DevModeForceServerRefresh AND local path detection.
string devFlags = AssetPathUtility.ShouldForceUvxRefresh() ? "--no-cache --refresh " : string.Empty;

return "# Register the MCP server with Claude Code:\n" +
$"claude mcp add --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{gitUrl}\" mcp-for-unity\n\n" +
$"claude mcp add --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{packageSource}\" mcp-for-unity\n\n" +
"# Unregister the MCP server:\n" +
"claude mcp remove UnityMCP\n\n" +
"# List registered servers:\n" +
Expand Down
38 changes: 23 additions & 15 deletions MCPForUnity/Editor/Helpers/AssetPathUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -149,38 +149,46 @@ public static JObject GetPackageJson()
}

/// <summary>
/// Gets just the git URL part for the MCP server package
/// Checks for EditorPrefs override first, then falls back to package version
/// Gets the package source for the MCP server (used with uvx --from).
/// Checks for EditorPrefs override first (supports git URLs, file:// paths, etc.),
/// then falls back to PyPI package reference.
/// </summary>
/// <returns>Git URL string, or empty string if version is unknown and no override</returns>
public static string GetMcpServerGitUrl()
/// <returns>Package source string for uvx --from argument</returns>
public static string GetMcpServerPackageSource()
{
// Check for Git URL override first
string gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
if (!string.IsNullOrEmpty(gitUrlOverride))
// Check for override first (supports git URLs, file:// paths, local paths)
string sourceOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
if (!string.IsNullOrEmpty(sourceOverride))
{
return gitUrlOverride;
return sourceOverride;
}

// Fall back to default package version
// Default to PyPI package (avoids Windows long path issues with git clone)
string version = GetPackageVersion();
if (version == "unknown")
{
// Fall back to main repo without pinned version so configs remain valid in test scenarios
return "git+https://github.com/CoplayDev/unity-mcp#subdirectory=Server";
// Fall back to latest PyPI version so configs remain valid in test scenarios
return "mcpforunityserver";
}

return $"git+https://github.com/CoplayDev/unity-mcp@v{version}#subdirectory=Server";
return $"mcpforunityserver=={version}";
}

/// <summary>
/// Deprecated: Use GetMcpServerPackageSource() instead.
/// Kept for backwards compatibility.
/// </summary>
[System.Obsolete("Use GetMcpServerPackageSource() instead")]
public static string GetMcpServerGitUrl() => GetMcpServerPackageSource();

/// <summary>
/// Gets structured uvx command parts for different client configurations
/// </summary>
/// <returns>Tuple containing (uvxPath, fromUrl, packageName)</returns>
public static (string uvxPath, string fromUrl, string packageName) GetUvxCommandParts()
{
string uvxPath = MCPServiceLocator.Paths.GetUvxPath();
string fromUrl = GetMcpServerGitUrl();
string fromUrl = GetMcpServerPackageSource();
string packageName = "mcp-for-unity";

return (uvxPath, fromUrl, packageName);
Expand Down Expand Up @@ -208,7 +216,7 @@ public static bool ShouldForceUvxRefresh()
/// </summary>
public static bool IsLocalServerPath()
{
string fromUrl = GetMcpServerGitUrl();
string fromUrl = GetMcpServerPackageSource();
if (string.IsNullOrEmpty(fromUrl))
return false;

Expand All @@ -226,7 +234,7 @@ public static string GetLocalServerPath()
if (!IsLocalServerPath())
return null;

string fromUrl = GetMcpServerGitUrl();
string fromUrl = GetMcpServerPackageSource();
if (fromUrl.StartsWith("file://", StringComparison.OrdinalIgnoreCase))
{
// Strip file:// prefix
Expand Down
7 changes: 4 additions & 3 deletions MCPForUnity/Editor/Services/PackageDeploymentService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -69,9 +69,10 @@ public string GetTargetPath()
public string GetTargetDisplayPath()
{
string target = GetTargetPath();
return string.IsNullOrEmpty(target)
? "Not found (check Packages/manifest.json)"
: target;
if (string.IsNullOrEmpty(target))
return "Not found (check Packages/manifest.json)";
// Use forward slashes to avoid backslash escape sequence issues in UI text
return target.Replace('\\', '/');
}

public string GetLastBackupPath()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -308,7 +308,8 @@ private void UpdateDeploymentSection()
string backupPath = deployService.GetLastBackupPath();
if (deployService.HasBackup())
{
deployBackupLabel.text = $"Last backup: {backupPath}";
// Use forward slashes to avoid backslash escape sequence issues in UI text
deployBackupLabel.text = $"Last backup: {backupPath?.Replace('\\', '/')}";
}
else
{
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,8 @@
<ui:Button name="browse-git-url-button" text="Select" class="icon-button" />
<ui:Button name="clear-git-url-button" text="Clear" class="icon-button" />
</ui:VisualElement>
<ui:Label text="Examples:" class="help-text" style="margin-top: 5px;" />
<ui:Label text="• Local: /Users/you/Projects/unity-mcp/Server" class="help-text" />
<ui:Label text="• Remote: git+https://github.com/CoplayDev/[email protected]#subdirectory=Server" class="help-text" />
<ui:Label text="Override example (default uses PyPI):" class="help-text" style="margin-top: 5px;" />
<ui:Label text="• Local dev: /path/to/unity-mcp/Server" class="help-text" />

<ui:Label text="Dev Mode:" class="advanced-label" style="margin-top: 10px;" />
<ui:Label text="When enabled, generated uvx commands add '--no-cache --refresh' before launching (slower startup, but avoids stale cached builds while iterating on the Server)." class="help-text" />
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -248,7 +248,7 @@ public void BuildCodexServerBlock_OnWindows_IncludesSystemRootEnv()
var thirdArg = (args[2] as TomlString).Value;

Assert.AreEqual("--from", firstArg, "First arg should be --from");
Assert.IsTrue(secondArg.Contains("git+https://github.com/CoplayDev/unity-mcp"), "Second arg should be git URL");
Assert.IsTrue(secondArg.Contains("mcpforunityserver"), "Second arg should be PyPI package reference");
Assert.AreEqual("mcp-for-unity", thirdArg, "Third arg should be mcp-for-unity");

// Verify env.SystemRoot is present on Windows
Expand Down Expand Up @@ -313,7 +313,7 @@ public void BuildCodexServerBlock_OnNonWindows_ExcludesEnv()
var thirdArg = (args[2] as TomlString).Value;

Assert.AreEqual("--from", firstArg, "First arg should be --from");
Assert.IsTrue(secondArg.Contains("git+https://github.com/CoplayDev/unity-mcp"), "Second arg should be git URL");
Assert.IsTrue(secondArg.Contains("mcpforunityserver"), "Second arg should be PyPI package reference");
Assert.AreEqual("mcp-for-unity", thirdArg, "Third arg should be mcp-for-unity");

// Verify env is NOT present on non-Windows platforms
Expand Down Expand Up @@ -380,7 +380,7 @@ public void UpsertCodexServerBlock_OnWindows_IncludesSystemRootEnv()
var thirdArg = (args[2] as TomlString).Value;

Assert.AreEqual("--from", firstArg, "First arg should be --from");
Assert.IsTrue(secondArg.Contains("git+https://github.com/CoplayDev/unity-mcp"), "Second arg should be git URL");
Assert.IsTrue(secondArg.Contains("mcpforunityserver"), "Second arg should be PyPI package reference");
Assert.AreEqual("mcp-for-unity", thirdArg, "Third arg should be mcp-for-unity");

// Verify env.SystemRoot is present on Windows
Expand Down Expand Up @@ -454,7 +454,7 @@ public void UpsertCodexServerBlock_OnNonWindows_ExcludesEnv()
var thirdArg = (args[2] as TomlString).Value;

Assert.AreEqual("--from", firstArg, "First arg should be --from");
Assert.IsTrue(secondArg.Contains("git+https://github.com/CoplayDev/unity-mcp"), "Second arg should be git URL");
Assert.IsTrue(secondArg.Contains("mcpforunityserver"), "Second arg should be PyPI package reference");
Assert.AreEqual("mcp-for-unity", thirdArg, "Third arg should be mcp-for-unity");

// Verify env is NOT present on non-Windows platforms
Expand Down