Skip to content

Commit ce1104e

Browse files
committed
feat(installer): rewire known configs (EditorPrefs, Cursor mcp.json) to canonical path; then remove legacy if unreferenced
1 parent 755e1d7 commit ce1104e

File tree

1 file changed

+128
-2
lines changed

1 file changed

+128
-2
lines changed

UnityMcpBridge/Editor/Helpers/ServerInstaller.cs

Lines changed: 128 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)