diff --git a/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs b/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
index 2a201995d..afa8b2563 100644
--- a/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
+++ b/MCPForUnity/Editor/Clients/McpClientConfiguratorBase.cs
@@ -411,7 +411,8 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
string claudePath = MCPServiceLocator.Paths.GetClaudeCliPath();
RuntimePlatform platform = Application.platform;
bool isRemoteScope = HttpEndpointUtility.IsRemoteScope();
- string expectedPackageSource = AssetPathUtility.GetMcpServerPackageSource();
+ // Get expected package source considering beta mode (matches what Register() would use)
+ string expectedPackageSource = GetExpectedPackageSourceForValidation();
return CheckStatusWithProjectDir(projectDir, useHttpTransport, claudePath, platform, isRemoteScope, expectedPackageSource, attemptAutoRewrite);
}
@@ -599,7 +600,7 @@ public override void Configure()
public void ConfigureWithCapturedValues(
string projectDir, string claudePath, string pathPrepend,
bool useHttpTransport, string httpUrl,
- string uvxPath, string gitUrl, string packageName, bool shouldForceRefresh,
+ string uvxPath, string fromArgs, string packageName, bool shouldForceRefresh,
string apiKey,
Models.ConfiguredTransport serverTransport)
{
@@ -610,7 +611,7 @@ public void ConfigureWithCapturedValues(
else
{
RegisterWithCapturedValues(projectDir, claudePath, pathPrepend,
- useHttpTransport, httpUrl, uvxPath, gitUrl, packageName, shouldForceRefresh,
+ useHttpTransport, httpUrl, uvxPath, fromArgs, packageName, shouldForceRefresh,
apiKey, serverTransport);
}
}
@@ -621,7 +622,7 @@ public void ConfigureWithCapturedValues(
private void RegisterWithCapturedValues(
string projectDir, string claudePath, string pathPrepend,
bool useHttpTransport, string httpUrl,
- string uvxPath, string gitUrl, string packageName, bool shouldForceRefresh,
+ string uvxPath, string fromArgs, string packageName, bool shouldForceRefresh,
string apiKey,
Models.ConfiguredTransport serverTransport)
{
@@ -650,7 +651,7 @@ private void RegisterWithCapturedValues(
// Note: --reinstall is not supported by uvx, use --no-cache --refresh instead
string devFlags = shouldForceRefresh ? "--no-cache --refresh " : string.Empty;
// Use --scope local to register in the project-local config, avoiding conflicts with user-level config (#664)
- args = $"mcp add --scope local --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{gitUrl}\" {packageName}";
+ args = $"mcp add --scope local --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}{fromArgs} {packageName}";
}
// Remove any existing registrations from ALL scopes to prevent stale config conflicts (#664)
@@ -724,12 +725,13 @@ private void Register()
}
else
{
- var (uvxPath, gitUrl, packageName) = AssetPathUtility.GetUvxCommandParts();
+ var (uvxPath, _, 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;
+ string fromArgs = AssetPathUtility.GetBetaServerFromArgs(quoteFromPath: true);
// Use --scope local to register in the project-local config, avoiding conflicts with user-level config (#664)
- args = $"mcp add --scope local --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{gitUrl}\" {packageName}";
+ args = $"mcp add --scope local --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}{fromArgs} {packageName}";
}
string projectDir = Path.GetDirectoryName(Application.dataPath);
@@ -834,13 +836,13 @@ 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;
+ string fromArgs = AssetPathUtility.GetBetaServerFromArgs(quoteFromPath: true);
return "# Register the MCP server with Claude Code:\n" +
- $"claude mcp add --scope local --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}--from \"{packageSource}\" mcp-for-unity\n\n" +
+ $"claude mcp add --scope local --transport stdio UnityMCP -- \"{uvxPath}\" {devFlags}{fromArgs} mcp-for-unity\n\n" +
"# Unregister the MCP server (from all scopes to clean up any stale configs):\n" +
"claude mcp remove --scope local UnityMCP\n" +
"claude mcp remove --scope user UnityMCP\n" +
@@ -909,6 +911,43 @@ private static string SanitizeShellHeaderValue(string value)
return sb.ToString();
}
+ ///
+ /// Gets the expected package source for validation, accounting for beta mode.
+ /// This should match what Register() would actually use for the --from argument.
+ /// MUST be called from the main thread due to EditorPrefs access.
+ ///
+ private static string GetExpectedPackageSourceForValidation()
+ {
+ // Check for explicit override first
+ string gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
+ if (!string.IsNullOrEmpty(gitUrlOverride))
+ {
+ return gitUrlOverride;
+ }
+
+ // Check beta mode using the same logic as GetUseBetaServerWithDynamicDefault
+ // (bypass cache to ensure fresh read)
+ bool useBetaServer;
+ bool hasPrefKey = EditorPrefs.HasKey(EditorPrefKeys.UseBetaServer);
+ if (hasPrefKey)
+ {
+ useBetaServer = EditorPrefs.GetBool(EditorPrefKeys.UseBetaServer, false);
+ }
+ else
+ {
+ // Dynamic default based on package version
+ useBetaServer = AssetPathUtility.IsPreReleaseVersion();
+ }
+
+ if (useBetaServer)
+ {
+ return "mcpforunityserver>=0.0.0a0";
+ }
+
+ // Standard mode uses exact version from package.json
+ return AssetPathUtility.GetMcpServerPackageSource();
+ }
+
///
/// Extracts the package source (--from argument value) from claude mcp get output.
/// The output format includes args like: --from "mcpforunityserver==9.0.1"
diff --git a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs
index 0e4d5fee2..21d2f69c8 100644
--- a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs
+++ b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs
@@ -293,7 +293,8 @@ private void ConfigureClaudeCliAsync(IMcpClientConfigurator client)
bool useHttpTransport = EditorConfigurationCache.Instance.UseHttpTransport;
string claudePath = MCPServiceLocator.Paths.GetClaudeCliPath();
string httpUrl = HttpEndpointUtility.GetMcpRpcUrl();
- var (uvxPath, gitUrl, packageName) = AssetPathUtility.GetUvxCommandParts();
+ var (uvxPath, _, packageName) = AssetPathUtility.GetUvxCommandParts();
+ string fromArgs = AssetPathUtility.GetBetaServerFromArgs(quoteFromPath: true);
bool shouldForceRefresh = AssetPathUtility.ShouldForceUvxRefresh();
string apiKey = EditorPrefs.GetString(EditorPrefKeys.ApiKey, string.Empty);
@@ -321,7 +322,7 @@ private void ConfigureClaudeCliAsync(IMcpClientConfigurator client)
cliConfigurator.ConfigureWithCapturedValues(
projectDir, claudePath, pathPrepend,
useHttpTransport, httpUrl,
- uvxPath, gitUrl, packageName, shouldForceRefresh,
+ uvxPath, fromArgs, packageName, shouldForceRefresh,
apiKey, serverTransport);
}
return (success: true, error: (string)null);
@@ -480,7 +481,8 @@ private void RefreshClaudeCliStatus(IMcpClientConfigurator client, bool forceImm
string claudePath = MCPServiceLocator.Paths.GetClaudeCliPath();
RuntimePlatform platform = Application.platform;
bool isRemoteScope = HttpEndpointUtility.IsRemoteScope();
- string expectedPackageSource = AssetPathUtility.GetMcpServerPackageSource();
+ // Get expected package source considering beta mode (bypass cache for fresh read)
+ string expectedPackageSource = GetExpectedPackageSourceForBetaMode();
Task.Run(() =>
{
@@ -593,5 +595,41 @@ private void ApplyStatusToUi(IMcpClientConfigurator client, bool showChecking =
// Notify listeners about the client's configured transport
OnClientTransportDetected?.Invoke(client.DisplayName, client.ConfiguredTransport);
}
+
+ ///
+ /// Gets the expected package source for validation, accounting for beta mode.
+ /// Uses the same logic as registration to ensure validation matches what was registered.
+ /// MUST be called from the main thread due to EditorPrefs access.
+ ///
+ private static string GetExpectedPackageSourceForBetaMode()
+ {
+ // Check for explicit override first
+ string gitUrlOverride = EditorPrefs.GetString(EditorPrefKeys.GitUrlOverride, "");
+ if (!string.IsNullOrEmpty(gitUrlOverride))
+ {
+ return gitUrlOverride;
+ }
+
+ // Check beta mode using the same logic as GetUseBetaServerWithDynamicDefault
+ // (bypass cache to ensure fresh read)
+ bool useBetaServer;
+ if (EditorPrefs.HasKey(EditorPrefKeys.UseBetaServer))
+ {
+ useBetaServer = EditorPrefs.GetBool(EditorPrefKeys.UseBetaServer, false);
+ }
+ else
+ {
+ // Dynamic default based on package version
+ useBetaServer = AssetPathUtility.IsPreReleaseVersion();
+ }
+
+ if (useBetaServer)
+ {
+ return "mcpforunityserver>=0.0.0a0";
+ }
+
+ // Standard mode uses exact version from package.json
+ return AssetPathUtility.GetMcpServerPackageSource();
+ }
}
}
diff --git a/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs b/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
index 9a2b86ad4..b36125d4f 100644
--- a/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
+++ b/MCPForUnity/Editor/Windows/EditorPrefs/EditorPrefsWindow.cs
@@ -92,6 +92,9 @@ public static void ShowWindow()
public void CreateGUI()
{
+ // Clear search filter on GUI recreation to avoid stale filtered results
+ searchFilter = "";
+
string basePath = AssetPathUtility.GetMcpPackageRootPath();
// Load UXML
@@ -245,24 +248,27 @@ private EditorPrefItem CreateEditorPrefItem(string key)
// Check if we know the type of this pref
if (knownPrefTypes.TryGetValue(key, out var knownType))
{
+ // Check if the key actually exists
+ item.IsUnset = !EditorPrefs.HasKey(key);
+
// Use the known type
switch (knownType)
{
case EditorPrefType.Bool:
item.Type = EditorPrefType.Bool;
- item.Value = EditorPrefs.GetBool(key, false).ToString();
+ item.Value = item.IsUnset ? "Unset. Default: False" : EditorPrefs.GetBool(key, false).ToString();
break;
case EditorPrefType.Int:
item.Type = EditorPrefType.Int;
- item.Value = EditorPrefs.GetInt(key, 0).ToString();
+ item.Value = item.IsUnset ? "Unset. Default: 0" : EditorPrefs.GetInt(key, 0).ToString();
break;
case EditorPrefType.Float:
item.Type = EditorPrefType.Float;
- item.Value = EditorPrefs.GetFloat(key, 0f).ToString();
+ item.Value = item.IsUnset ? "Unset. Default: 0" : EditorPrefs.GetFloat(key, 0f).ToString();
break;
case EditorPrefType.String:
item.Type = EditorPrefType.String;
- item.Value = EditorPrefs.GetString(key, "");
+ item.Value = item.IsUnset ? "Unset. Default: (empty)" : EditorPrefs.GetString(key, "");
break;
}
}
@@ -324,6 +330,14 @@ private VisualElement CreateItemUI(EditorPrefItem item)
// Buttons
var saveButton = itemElement.Q