@@ -102,6 +102,8 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
102102 string configuredUrl = null ;
103103 bool configExists = false ;
104104
105+ string command = null ;
106+
105107 if ( client . IsVsCodeLayout )
106108 {
107109 var vsConfig = JsonConvert . DeserializeObject < JToken > ( configJson ) as JObject ;
@@ -115,6 +117,12 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
115117 {
116118 configExists = true ;
117119
120+ var commandToken = unityObj [ "command" ] ;
121+ if ( commandToken != null && commandToken . Type == JTokenType . String )
122+ {
123+ command = commandToken . ToString ( ) ;
124+ }
125+
118126 var argsToken = unityObj [ "args" ] ;
119127 if ( argsToken is JArray )
120128 {
@@ -131,10 +139,31 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
131139 }
132140 else
133141 {
134- McpConfig standardConfig = JsonConvert . DeserializeObject < McpConfig > ( configJson ) ;
135- if ( standardConfig ? . mcpServers ? . unityMCP != null )
142+ // Use JObject parsing for flexibility (handling both url and serverUrl without strict model dependency)
143+ var rootObj = JsonConvert . DeserializeObject < JToken > ( configJson ) as JObject ;
144+ var unityToken = rootObj ? [ "mcpServers" ] ? [ "unityMCP" ] ;
145+
146+ if ( unityToken is JObject unityObj )
136147 {
137- args = standardConfig . mcpServers . unityMCP . args ;
148+ var commandToken = unityObj [ "command" ] ;
149+ if ( commandToken != null && commandToken . Type == JTokenType . String )
150+ {
151+ command = commandToken . ToString ( ) ;
152+ }
153+
154+ var argsToken = unityObj [ "args" ] ;
155+ if ( argsToken is JArray )
156+ {
157+ args = argsToken . ToObject < string [ ] > ( ) ;
158+ }
159+
160+ // Check for both 'url' (standard) and 'serverUrl' (Antigravity)
161+ var urlToken = unityObj [ "url" ] ?? unityObj [ "serverUrl" ] ;
162+ if ( urlToken != null && urlToken . Type != JTokenType . Null )
163+ {
164+ configuredUrl = urlToken . ToString ( ) ;
165+ }
166+
138167 configExists = true ;
139168 }
140169 }
@@ -146,17 +175,39 @@ public override McpStatus CheckStatus(bool attemptAutoRewrite = true)
146175 }
147176
148177 bool matches = false ;
149- if ( args != null && args . Length > 0 )
178+ bool useHttpTransport = EditorPrefs . GetBool ( EditorPrefKeys . UseHttpTransport , true ) ;
179+
180+ if ( useHttpTransport )
150181 {
151- string expectedUvxUrl = AssetPathUtility . GetMcpServerGitUrl ( ) ;
152- string configuredUvxUrl = McpConfigurationHelper . ExtractUvxUrl ( args ) ;
153- matches = ! string . IsNullOrEmpty ( configuredUvxUrl ) &&
154- McpConfigurationHelper . PathsEqual ( configuredUvxUrl , expectedUvxUrl ) ;
182+ // Check 3: HTTP (Only check this if we are in HTTP mode)
183+ if ( ! string . IsNullOrEmpty ( configuredUrl ) )
184+ {
185+ string expectedUrl = HttpEndpointUtility . GetMcpRpcUrl ( ) ;
186+ matches = UrlsEqual ( configuredUrl , expectedUrl ) ;
187+ }
155188 }
156- else if ( ! string . IsNullOrEmpty ( configuredUrl ) )
189+ else
157190 {
158- string expectedUrl = HttpEndpointUtility . GetMcpRpcUrl ( ) ;
159- matches = UrlsEqual ( configuredUrl , expectedUrl ) ;
191+ // Check 1: Node.js Wrapper (Stdio) (Only check this if we are in Stdio mode)
192+ string expectedWrapper = AssetPathUtility . GetWrapperJsPath ( ) ;
193+ if ( ! string . IsNullOrEmpty ( command ) &&
194+ Path . GetFileNameWithoutExtension ( command ) . Equals ( "node" , StringComparison . OrdinalIgnoreCase ) &&
195+ args != null && args . Length > 0 &&
196+ ! string . IsNullOrEmpty ( expectedWrapper ) )
197+ {
198+ if ( McpConfigurationHelper . PathsEqual ( args [ 0 ] , expectedWrapper ) )
199+ {
200+ matches = true ;
201+ }
202+ }
203+ // Check 2: UVX (Stdio - Legacy/Fallback)
204+ else if ( args != null && args . Length > 0 )
205+ {
206+ string expectedUvxUrl = AssetPathUtility . GetMcpServerGitUrl ( ) ;
207+ string configuredUvxUrl = McpConfigurationHelper . ExtractUvxUrl ( args ) ;
208+ matches = ! string . IsNullOrEmpty ( configuredUvxUrl ) &&
209+ McpConfigurationHelper . PathsEqual ( configuredUvxUrl , expectedUvxUrl ) ;
210+ }
160211 }
161212
162213 if ( matches )
@@ -327,6 +378,9 @@ public override string GetManualSnippet()
327378 /// <summary>CLI-based configurator (Claude Code).</summary>
328379 public abstract class ClaudeCliMcpConfigurator : McpClientConfiguratorBase
329380 {
381+ private static readonly object _claudeCliLock = new object ( ) ;
382+ private static bool _isClaudeCliRunning = false ;
383+
330384 public ClaudeCliMcpConfigurator ( McpClient client ) : base ( client ) { }
331385
332386 public override bool SupportsAutoConfigure => true ;
@@ -405,114 +459,152 @@ public override void Configure()
405459
406460 private void Register ( )
407461 {
408- var pathService = MCPServiceLocator . Paths ;
409- string claudePath = pathService . GetClaudeCliPath ( ) ;
410- if ( string . IsNullOrEmpty ( claudePath ) )
462+ lock ( _claudeCliLock )
411463 {
412- throw new InvalidOperationException ( "Claude CLI not found. Please install Claude Code first." ) ;
464+ if ( _isClaudeCliRunning )
465+ {
466+ throw new InvalidOperationException ( "Claude CLI operation already in progress. Please wait." ) ;
467+ }
468+ _isClaudeCliRunning = true ;
413469 }
470+
471+ try
472+ {
473+ var pathService = MCPServiceLocator . Paths ;
474+ string claudePath = pathService . GetClaudeCliPath ( ) ;
475+ if ( string . IsNullOrEmpty ( claudePath ) )
476+ {
477+ throw new InvalidOperationException ( "Claude CLI not found. Please install Claude Code first." ) ;
478+ }
414479
415- bool useHttpTransport = EditorPrefs . GetBool ( EditorPrefKeys . UseHttpTransport , true ) ;
480+ bool useHttpTransport = EditorPrefs . GetBool ( EditorPrefKeys . UseHttpTransport , true ) ;
416481
417- string args ;
418- if ( useHttpTransport )
419- {
420- string httpUrl = HttpEndpointUtility . GetMcpRpcUrl ( ) ;
421- args = $ "mcp add --transport http UnityMCP { httpUrl } ";
422- }
423- else
424- {
425- var ( uvxPath , gitUrl , packageName ) = AssetPathUtility . GetUvxCommandParts ( ) ;
426- args = $ "mcp add --transport stdio UnityMCP -- \" { uvxPath } \" --from \" { gitUrl } \" { packageName } ";
427- }
482+ string args ;
483+ if ( useHttpTransport )
484+ {
485+ string httpUrl = HttpEndpointUtility . GetMcpRpcUrl ( ) ;
486+ args = $ "mcp add --transport http UnityMCP { httpUrl } ";
487+ }
488+ else
489+ {
490+ var ( uvxPath , gitUrl , packageName ) = AssetPathUtility . GetUvxCommandParts ( ) ;
491+ args = $ "mcp add --transport stdio UnityMCP -- \" { uvxPath } \" --from \" { gitUrl } \" { packageName } ";
492+ }
428493
429- string projectDir = Path . GetDirectoryName ( Application . dataPath ) ;
494+ string projectDir = Path . GetDirectoryName ( Application . dataPath ) ;
430495
431- string pathPrepend = null ;
432- if ( Application . platform == RuntimePlatform . OSXEditor )
433- {
434- pathPrepend = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" ;
435- }
436- else if ( Application . platform == RuntimePlatform . LinuxEditor )
437- {
438- pathPrepend = "/usr/local/bin:/usr/bin:/bin" ;
439- }
496+ string pathPrepend = null ;
497+ if ( Application . platform == RuntimePlatform . OSXEditor )
498+ {
499+ pathPrepend = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" ;
500+ }
501+ else if ( Application . platform == RuntimePlatform . LinuxEditor )
502+ {
503+ pathPrepend = "/usr/local/bin:/usr/bin:/bin" ;
504+ }
440505
441- try
442- {
443- string claudeDir = Path . GetDirectoryName ( claudePath ) ;
444- if ( ! string . IsNullOrEmpty ( claudeDir ) )
506+ try
445507 {
446- pathPrepend = string . IsNullOrEmpty ( pathPrepend )
447- ? claudeDir
448- : $ "{ claudeDir } :{ pathPrepend } ";
508+ string claudeDir = Path . GetDirectoryName ( claudePath ) ;
509+ if ( ! string . IsNullOrEmpty ( claudeDir ) )
510+ {
511+ pathPrepend = string . IsNullOrEmpty ( pathPrepend )
512+ ? claudeDir
513+ : $ "{ claudeDir } :{ pathPrepend } ";
514+ }
449515 }
450- }
451- catch { }
516+ catch { }
452517
453- bool already = false ;
454- if ( ! ExecPath . TryRun ( claudePath , args , projectDir , out var stdout , out var stderr , 15000 , pathPrepend ) )
455- {
456- string combined = ( $ "{ stdout } \n { stderr } ") ?? string . Empty ;
457- if ( combined . IndexOf ( "already exists" , StringComparison . OrdinalIgnoreCase ) >= 0 )
518+ bool already = false ;
519+ if ( ! ExecPath . TryRun ( claudePath , args , projectDir , out var stdout , out var stderr , 15000 , pathPrepend ) )
458520 {
459- already = true ;
521+ string combined = ( $ "{ stdout } \n { stderr } ") ?? string . Empty ;
522+ if ( combined . IndexOf ( "already exists" , StringComparison . OrdinalIgnoreCase ) >= 0 )
523+ {
524+ already = true ;
525+ }
526+ else
527+ {
528+ throw new InvalidOperationException ( $ "Failed to register with Claude Code:\n { stderr } \n { stdout } ") ;
529+ }
460530 }
461- else
531+
532+ if ( ! already )
462533 {
463- throw new InvalidOperationException ( $ "Failed to register with Claude Code: \n { stderr } \n { stdout } ") ;
534+ McpLog . Info ( "Successfully registered with Claude Code. ") ;
464535 }
465- }
466536
467- if ( ! already )
537+ CheckStatus ( ) ;
538+ }
539+ finally
468540 {
469- McpLog . Info ( "Successfully registered with Claude Code." ) ;
541+ lock ( _claudeCliLock )
542+ {
543+ _isClaudeCliRunning = false ;
544+ }
470545 }
471-
472- CheckStatus ( ) ;
473546 }
474547
475548 private void Unregister ( )
476549 {
477- var pathService = MCPServiceLocator . Paths ;
478- string claudePath = pathService . GetClaudeCliPath ( ) ;
479-
480- if ( string . IsNullOrEmpty ( claudePath ) )
481- {
482- throw new InvalidOperationException ( "Claude CLI not found. Please install Claude Code first." ) ;
483- }
484-
485- string projectDir = Path . GetDirectoryName ( Application . dataPath ) ;
486- string pathPrepend = null ;
487- if ( Application . platform == RuntimePlatform . OSXEditor )
550+ lock ( _claudeCliLock )
488551 {
489- pathPrepend = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" ;
552+ if ( _isClaudeCliRunning )
553+ {
554+ throw new InvalidOperationException ( "Claude CLI operation already in progress. Please wait." ) ;
555+ }
556+ _isClaudeCliRunning = true ;
490557 }
491- else if ( Application . platform == RuntimePlatform . LinuxEditor )
558+
559+ try
492560 {
493- pathPrepend = "/usr/local/bin:/usr/bin:/bin" ;
494- }
561+ var pathService = MCPServiceLocator . Paths ;
562+ string claudePath = pathService . GetClaudeCliPath ( ) ;
495563
496- bool serverExists = ExecPath . TryRun ( claudePath , "mcp get UnityMCP" , projectDir , out _ , out _ , 7000 , pathPrepend ) ;
564+ if ( string . IsNullOrEmpty ( claudePath ) )
565+ {
566+ throw new InvalidOperationException ( "Claude CLI not found. Please install Claude Code first." ) ;
567+ }
497568
498- if ( ! serverExists )
499- {
500- client . SetStatus ( McpStatus . NotConfigured ) ;
501- McpLog . Info ( "No MCP for Unity server found - already unregistered." ) ;
502- return ;
503- }
569+ string projectDir = Path . GetDirectoryName ( Application . dataPath ) ;
570+ string pathPrepend = null ;
571+ if ( Application . platform == RuntimePlatform . OSXEditor )
572+ {
573+ pathPrepend = "/opt/homebrew/bin:/usr/local/bin:/usr/bin:/bin" ;
574+ }
575+ else if ( Application . platform == RuntimePlatform . LinuxEditor )
576+ {
577+ pathPrepend = "/usr/local/bin:/usr/bin:/bin" ;
578+ }
504579
505- if ( ExecPath . TryRun ( claudePath , "mcp remove UnityMCP" , projectDir , out var stdout , out var stderr , 10000 , pathPrepend ) )
506- {
507- McpLog . Info ( "MCP server successfully unregistered from Claude Code." ) ;
580+ bool serverExists = ExecPath . TryRun ( claudePath , "mcp get UnityMCP" , projectDir , out _ , out _ , 7000 , pathPrepend ) ;
581+
582+ if ( ! serverExists )
583+ {
584+ client . SetStatus ( McpStatus . NotConfigured ) ;
585+ McpLog . Info ( "No MCP for Unity server found - already unregistered." ) ;
586+ return ;
587+ }
588+
589+ if ( ExecPath . TryRun ( claudePath , "mcp remove UnityMCP" , projectDir , out var stdout , out var stderr , 10000 , pathPrepend ) )
590+ {
591+ McpLog . Info ( "MCP server successfully unregistered from Claude Code." ) ;
592+ }
593+ else
594+ {
595+ throw new InvalidOperationException ( $ "Failed to unregister: { stderr } ") ;
596+ }
597+
598+ client . SetStatus ( McpStatus . NotConfigured ) ;
599+ CheckStatus ( ) ;
508600 }
509- else
601+ finally
510602 {
511- throw new InvalidOperationException ( $ "Failed to unregister: { stderr } ") ;
603+ lock ( _claudeCliLock )
604+ {
605+ _isClaudeCliRunning = false ;
606+ }
512607 }
513-
514- client . SetStatus ( McpStatus . NotConfigured ) ;
515- CheckStatus ( ) ;
516608 }
517609
518610 public override string GetManualSnippet ( )
0 commit comments