@@ -67,8 +67,19 @@ public static void EnsureServerInstalled()
6767 || ( ! string . IsNullOrEmpty ( embeddedVer ) && CompareSemverSafe ( legacyVer , embeddedVer ) < 0 ) ;
6868 if ( legacyOlder )
6969 {
70- // Skip deletion if this path is still referenced by prefs or known client configs
71- if ( IsPathPossiblyReferencedByPrefsOrKnownConfigs ( legacySrc ) )
70+ // If referenced, attempt to rewire known configs (EditorPrefs, Cursor) to canonical
71+ bool stillRef = IsPathPossiblyReferencedByPrefsOrKnownConfigs ( legacySrc ) ;
72+ if ( stillRef )
73+ {
74+ bool rewired = TryRewriteKnownConfigsToCanonical ( legacySrc , destSrc ) ;
75+ if ( rewired )
76+ {
77+ McpLog . Info ( $ "Rewired configs from legacy '{ legacySrc } ' to canonical '{ destSrc } '.", always : false ) ;
78+ }
79+ stillRef = IsPathPossiblyReferencedByPrefsOrKnownConfigs ( legacySrc ) ;
80+ }
81+ // If still referenced after rewrite attempts, skip deletion
82+ if ( stillRef )
7283 {
7384 McpLog . Info ( $ "Skipping removal of legacy server at '{ legacyRoot } ' (still referenced).", always : false ) ;
7485 continue ;
@@ -321,6 +332,121 @@ private static bool IsPathPossiblyReferencedByPrefsOrKnownConfigs(string serverS
321332 return false ;
322333 }
323334
335+ private static bool TryRewriteKnownConfigsToCanonical ( string legacySrc , string canonicalSrc )
336+ {
337+ bool changed = false ;
338+ try
339+ {
340+ // Normalize for comparison
341+ string normLegacy = NormalizePathSafe ( legacySrc ) ;
342+ string normCanon = NormalizePathSafe ( canonicalSrc ) ;
343+
344+ // EditorPrefs
345+ try
346+ {
347+ string prefServerSrc = EditorPrefs . GetString ( "MCPForUnity.ServerSrc" , string . Empty ) ?? string . Empty ;
348+ if ( ! string . IsNullOrEmpty ( prefServerSrc ) && PathsEqualSafe ( prefServerSrc , normLegacy ) )
349+ {
350+ EditorPrefs . SetString ( "MCPForUnity.ServerSrc" , normCanon ) ;
351+ changed = true ;
352+ }
353+ string prefOverride = EditorPrefs . GetString ( "MCPForUnity.PythonDirOverride" , string . Empty ) ?? string . Empty ;
354+ if ( ! string . IsNullOrEmpty ( prefOverride ) && PathsEqualSafe ( prefOverride , normLegacy ) )
355+ {
356+ EditorPrefs . SetString ( "MCPForUnity.PythonDirOverride" , normCanon ) ;
357+ changed = true ;
358+ }
359+ }
360+ catch { }
361+
362+ // Cursor config (~/.cursor/mcp.json)
363+ try
364+ {
365+ string user = Environment . GetFolderPath ( Environment . SpecialFolder . UserProfile ) ?? string . Empty ;
366+ string cursorCfg = Path . Combine ( user , ".cursor" , "mcp.json" ) ;
367+ if ( File . Exists ( cursorCfg ) )
368+ {
369+ string json = File . ReadAllText ( cursorCfg ) ;
370+ string currentDir = ExtractDirectoryArgFromJson ( json ) ;
371+ if ( ! string . IsNullOrEmpty ( currentDir ) && PathsEqualSafe ( currentDir , normLegacy ) )
372+ {
373+ string updated = ReplaceDirectoryArgInJson ( json , normCanon ) ;
374+ if ( ! string . IsNullOrEmpty ( updated ) && ! string . Equals ( updated , json , StringComparison . Ordinal ) )
375+ {
376+ try
377+ {
378+ string backup = cursorCfg + ".bak" ;
379+ File . Copy ( cursorCfg , backup , overwrite : true ) ;
380+ }
381+ catch { }
382+ File . WriteAllText ( cursorCfg , updated ) ;
383+ changed = true ;
384+ }
385+ }
386+ }
387+ }
388+ catch { }
389+ }
390+ catch { }
391+ return changed ;
392+ }
393+
394+ // Best-effort: rewrite the value following --directory in the first args array found
395+ private static string ReplaceDirectoryArgInJson ( string json , string newDirectory )
396+ {
397+ try
398+ {
399+ if ( string . IsNullOrEmpty ( json ) ) return json ;
400+ int argsIdx = json . IndexOf ( "\" args\" " , StringComparison . OrdinalIgnoreCase ) ;
401+ if ( argsIdx < 0 ) return json ;
402+ int arrStart = json . IndexOf ( '[' , argsIdx ) ;
403+ if ( arrStart < 0 ) return json ;
404+ int depth = 0 ;
405+ int arrEnd = - 1 ;
406+ for ( int i = arrStart ; i < json . Length ; i ++ )
407+ {
408+ char c = json [ i ] ;
409+ if ( c == '[' ) depth ++ ;
410+ else if ( c == ']' ) { depth -- ; if ( depth == 0 ) { arrEnd = i ; break ; } }
411+ }
412+ if ( arrEnd <= arrStart ) return json ;
413+
414+ string arrBody = json . Substring ( arrStart + 1 , arrEnd - arrStart - 1 ) ;
415+ // Split simple string array by commas at top level
416+ string [ ] raw = arrBody . Split ( new [ ] { ',' } , StringSplitOptions . RemoveEmptyEntries ) ;
417+ var parts = new List < string > ( raw . Length ) ;
418+ foreach ( var r in raw )
419+ {
420+ string s = r . Trim ( ) ;
421+ if ( s . Length >= 2 && s [ 0 ] == '"' && s [ s . Length - 1 ] == '"' )
422+ {
423+ s = s . Substring ( 1 , s . Length - 2 ) ;
424+ }
425+ parts . Add ( s ) ;
426+ }
427+
428+ for ( int i = 0 ; i < parts . Count - 1 ; i ++ )
429+ {
430+ if ( string . Equals ( parts [ i ] , "--directory" , StringComparison . OrdinalIgnoreCase ) )
431+ {
432+ parts [ i + 1 ] = newDirectory ;
433+ // Rebuild array JSON
434+ var sb = new StringBuilder ( ) ;
435+ for ( int j = 0 ; j < parts . Count ; j ++ )
436+ {
437+ if ( j > 0 ) sb . Append ( ", " ) ;
438+ sb . Append ( '"' ) . Append ( parts [ j ] . Replace ( "\\ " , "\\ \\ " ) . Replace ( "\" " , "\\ \" " ) ) . Append ( '"' ) ;
439+ }
440+ string newArr = sb . ToString ( ) ;
441+ string rebuilt = json . Substring ( 0 , arrStart + 1 ) + newArr + json . Substring ( arrEnd ) ;
442+ return rebuilt ;
443+ }
444+ }
445+ }
446+ catch { }
447+ return json ;
448+ }
449+
324450 // Minimal helper to extract the value following a --directory token in a plausible JSON args array
325451 private static string ExtractDirectoryArgFromJson ( string json )
326452 {
0 commit comments