fix: Beta mode status validation and EditorPrefs Manager improvements (#671)
* fix: Use beta server args for Claude Code registration Claude Code registration was still using gitUrl directly instead of GetBetaServerFromArgs(), meaning the beta server toggle had no effect on registration. Now uses GetBetaServerFromArgs(quoteFromPath: true) to properly generate --prerelease args for beta users. Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: EditorPrefs Manager shows 'Unset' for keys that haven't been set - Show "Unset. Default: [value]" for EditorPrefs that haven't been explicitly set - Disable value field and save button for unset items (grayed out at 60% opacity) - Fix search filter bug where reloading window showed filtered results with empty search field Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> * fix: Claude Code status check now accounts for beta server mode The version validation was comparing the registered package source (mcpforunityserver>=0.0.0a0 for beta) against the exact package version (mcpforunityserver==9.4.0-beta.1), causing false "Incorrect Path" status. Now both CheckStatus paths (main thread and background thread) use the same logic that considers: - Explicit GitUrlOverride - UseBetaServer setting (with dynamic default for prerelease packages) - Standard pinned version as fallback Co-Authored-By: Claude Opus 4.5 <noreply@anthropic.com> --------- Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>beta
parent
2671f2091a
commit
74236b6f66
|
|
@ -411,7 +411,8 @@ namespace MCPForUnity.Editor.Clients
|
|||
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 @@ namespace MCPForUnity.Editor.Clients
|
|||
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 @@ namespace MCPForUnity.Editor.Clients
|
|||
else
|
||||
{
|
||||
RegisterWithCapturedValues(projectDir, claudePath, pathPrepend,
|
||||
useHttpTransport, httpUrl, uvxPath, gitUrl, packageName, shouldForceRefresh,
|
||||
useHttpTransport, httpUrl, uvxPath, fromArgs, packageName, shouldForceRefresh,
|
||||
apiKey, serverTransport);
|
||||
}
|
||||
}
|
||||
|
|
@ -621,7 +622,7 @@ namespace MCPForUnity.Editor.Clients
|
|||
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 @@ namespace MCPForUnity.Editor.Clients
|
|||
// 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 @@ namespace MCPForUnity.Editor.Clients
|
|||
}
|
||||
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 @@ namespace MCPForUnity.Editor.Clients
|
|||
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 @@ namespace MCPForUnity.Editor.Clients
|
|||
return sb.ToString();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Extracts the package source (--from argument value) from claude mcp get output.
|
||||
/// The output format includes args like: --from "mcpforunityserver==9.0.1"
|
||||
|
|
|
|||
|
|
@ -293,7 +293,8 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
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 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
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 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
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 @@ namespace MCPForUnity.Editor.Windows.Components.ClientConfig
|
|||
// Notify listeners about the client's configured transport
|
||||
OnClientTransportDetected?.Invoke(client.DisplayName, client.ConfiguredTransport);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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.
|
||||
/// </summary>
|
||||
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();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -92,6 +92,9 @@ namespace MCPForUnity.Editor.Windows
|
|||
|
||||
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 @@ namespace MCPForUnity.Editor.Windows
|
|||
// 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 @@ namespace MCPForUnity.Editor.Windows
|
|||
// Buttons
|
||||
var saveButton = itemElement.Q<Button>("save-button");
|
||||
|
||||
// Style unset items
|
||||
if (item.IsUnset)
|
||||
{
|
||||
valueField.SetEnabled(false);
|
||||
valueField.style.opacity = 0.6f;
|
||||
saveButton.SetEnabled(false);
|
||||
}
|
||||
|
||||
// Callbacks
|
||||
saveButton.clicked += () => SavePref(item, valueField.value, (EditorPrefType)typeDropdown.index);
|
||||
|
||||
|
|
@ -389,6 +403,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
public string Value { get; set; }
|
||||
public EditorPrefType Type { get; set; }
|
||||
public bool IsKnown { get; set; }
|
||||
public bool IsUnset { get; set; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
|
|
|||
Loading…
Reference in New Issue