feat: installer cleanup, auto-migration, logging normalization
parent
97fb4a775e
commit
ee23346ca2
|
|
@ -0,0 +1,33 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
internal static class McpLog
|
||||
{
|
||||
private const string Prefix = "<b><color=#2EA3FF>MCP-FOR-UNITY</color></b>:";
|
||||
|
||||
private static bool IsDebugEnabled()
|
||||
{
|
||||
try { return EditorPrefs.GetBool("MCPForUnity.DebugLogs", false); } catch { return false; }
|
||||
}
|
||||
|
||||
public static void Info(string message, bool always = true)
|
||||
{
|
||||
if (!always && !IsDebugEnabled()) return;
|
||||
Debug.Log($"{Prefix} {message}");
|
||||
}
|
||||
|
||||
public static void Warn(string message)
|
||||
{
|
||||
Debug.LogWarning($"<color=#cc7a00>{Prefix} {message}</color>");
|
||||
}
|
||||
|
||||
public static void Error(string message)
|
||||
{
|
||||
Debug.LogError($"<color=#cc3333>{Prefix} {message}</color>");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,13 @@
|
|||
fileFormatVersion: 2
|
||||
guid: 9e2c3f8a4f4f48d8a4c1b7b8e3f5a1c2
|
||||
MonoImporter:
|
||||
externalObjects: {}
|
||||
serializedVersion: 2
|
||||
defaultReferences: []
|
||||
executionOrder: 0
|
||||
icon: {instanceID: 0}
|
||||
userData:
|
||||
assetBundleName:
|
||||
assetBundleVariant:
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,96 @@
|
|||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
namespace MCPForUnity.Editor.Helpers
|
||||
{
|
||||
/// <summary>
|
||||
/// Auto-runs legacy/older install detection on package load/update (log-only).
|
||||
/// Runs once per embedded server version using an EditorPrefs version-scoped key.
|
||||
/// </summary>
|
||||
[InitializeOnLoad]
|
||||
public static class PackageDetector
|
||||
{
|
||||
private const string DetectOnceFlagKeyPrefix = "MCPForUnity.LegacyDetectLogged:";
|
||||
|
||||
static PackageDetector()
|
||||
{
|
||||
try
|
||||
{
|
||||
string pkgVer = ReadPackageVersionOrFallback();
|
||||
string key = DetectOnceFlagKeyPrefix + pkgVer;
|
||||
|
||||
// Always force-run if legacy roots exist or canonical install is missing
|
||||
bool legacyPresent = LegacyRootsExist();
|
||||
bool canonicalMissing = !System.IO.File.Exists(System.IO.Path.Combine(ServerInstaller.GetServerPath(), "server.py"));
|
||||
|
||||
if (!EditorPrefs.GetBool(key, false) || legacyPresent || canonicalMissing)
|
||||
{
|
||||
EditorApplication.delayCall += () =>
|
||||
{
|
||||
try
|
||||
{
|
||||
ServerInstaller.EnsureServerInstalled();
|
||||
}
|
||||
catch (System.Exception ex)
|
||||
{
|
||||
Debug.LogWarning("MCP for Unity: Auto-detect on load failed: " + ex.Message);
|
||||
}
|
||||
finally
|
||||
{
|
||||
EditorPrefs.SetBool(key, true);
|
||||
}
|
||||
};
|
||||
}
|
||||
}
|
||||
catch { /* ignore */ }
|
||||
}
|
||||
|
||||
private static string ReadEmbeddedVersionOrFallback()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ServerPathResolver.TryFindEmbeddedServerSource(out var embeddedSrc))
|
||||
{
|
||||
var p = System.IO.Path.Combine(embeddedSrc, "server_version.txt");
|
||||
if (System.IO.File.Exists(p))
|
||||
return (System.IO.File.ReadAllText(p)?.Trim() ?? "unknown");
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private static string ReadPackageVersionOrFallback()
|
||||
{
|
||||
try
|
||||
{
|
||||
var info = UnityEditor.PackageManager.PackageInfo.FindForAssembly(typeof(PackageDetector).Assembly);
|
||||
if (info != null && !string.IsNullOrEmpty(info.version)) return info.version;
|
||||
}
|
||||
catch { }
|
||||
// Fallback to embedded server version if package info unavailable
|
||||
return ReadEmbeddedVersionOrFallback();
|
||||
}
|
||||
|
||||
private static bool LegacyRootsExist()
|
||||
{
|
||||
try
|
||||
{
|
||||
string home = System.Environment.GetFolderPath(System.Environment.SpecialFolder.UserProfile) ?? string.Empty;
|
||||
string[] roots =
|
||||
{
|
||||
System.IO.Path.Combine(home, ".config", "UnityMCP", "UnityMcpServer", "src"),
|
||||
System.IO.Path.Combine(home, ".local", "share", "UnityMCP", "UnityMcpServer", "src")
|
||||
};
|
||||
foreach (var r in roots)
|
||||
{
|
||||
try { if (System.IO.File.Exists(System.IO.Path.Combine(r, "server.py"))) return true; } catch { }
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
|
@ -0,0 +1,2 @@
|
|||
fileFormatVersion: 2
|
||||
guid: b82eaef548d164ca095f17db64d15af8
|
||||
|
|
@ -2,6 +2,7 @@ using System;
|
|||
using System.IO;
|
||||
using System.Runtime.InteropServices;
|
||||
using System.Text;
|
||||
using System.Collections.Generic;
|
||||
using UnityEditor;
|
||||
using UnityEngine;
|
||||
|
||||
|
|
@ -11,6 +12,7 @@ namespace MCPForUnity.Editor.Helpers
|
|||
{
|
||||
private const string RootFolder = "UnityMCP";
|
||||
private const string ServerFolder = "UnityMcpServer";
|
||||
private const string VersionFileName = "server_version.txt";
|
||||
|
||||
/// <summary>
|
||||
/// Ensures the mcp-for-unity-server is installed locally by copying from the embedded package source.
|
||||
|
|
@ -24,22 +26,70 @@ namespace MCPForUnity.Editor.Helpers
|
|||
string destRoot = Path.Combine(saveLocation, ServerFolder);
|
||||
string destSrc = Path.Combine(destRoot, "src");
|
||||
|
||||
if (File.Exists(Path.Combine(destSrc, "server.py")))
|
||||
{
|
||||
return; // Already installed
|
||||
}
|
||||
// Detect legacy installs and version state (logs)
|
||||
DetectAndLogLegacyInstallStates(destRoot);
|
||||
|
||||
// Resolve embedded source and versions
|
||||
if (!TryGetEmbeddedServerSource(out string embeddedSrc))
|
||||
{
|
||||
throw new Exception("Could not find embedded UnityMcpServer/src in the package.");
|
||||
}
|
||||
string embeddedVer = ReadVersionFile(Path.Combine(embeddedSrc, VersionFileName)) ?? "unknown";
|
||||
string installedVer = ReadVersionFile(Path.Combine(destSrc, VersionFileName));
|
||||
|
||||
bool destHasServer = File.Exists(Path.Combine(destSrc, "server.py"));
|
||||
bool needOverwrite = !destHasServer
|
||||
|| string.IsNullOrEmpty(installedVer)
|
||||
|| (!string.IsNullOrEmpty(embeddedVer) && CompareSemverSafe(installedVer, embeddedVer) < 0);
|
||||
|
||||
// Ensure destination exists
|
||||
Directory.CreateDirectory(destRoot);
|
||||
|
||||
// Copy the entire UnityMcpServer folder (parent of src)
|
||||
string embeddedRoot = Path.GetDirectoryName(embeddedSrc) ?? embeddedSrc; // go up from src to UnityMcpServer
|
||||
CopyDirectoryRecursive(embeddedRoot, destRoot);
|
||||
if (needOverwrite)
|
||||
{
|
||||
// Copy the entire UnityMcpServer folder (parent of src)
|
||||
string embeddedRoot = Path.GetDirectoryName(embeddedSrc) ?? embeddedSrc; // go up from src to UnityMcpServer
|
||||
CopyDirectoryRecursive(embeddedRoot, destRoot);
|
||||
// Write/refresh version file
|
||||
try { File.WriteAllText(Path.Combine(destSrc, VersionFileName), embeddedVer ?? "unknown"); } catch { }
|
||||
McpLog.Info($"Installed/updated server to {destRoot} (version {embeddedVer}).");
|
||||
}
|
||||
|
||||
// Cleanup legacy installs that are missing version or older than embedded
|
||||
foreach (var legacyRoot in GetLegacyRootsForDetection())
|
||||
{
|
||||
try
|
||||
{
|
||||
string legacySrc = Path.Combine(legacyRoot, "src");
|
||||
if (!File.Exists(Path.Combine(legacySrc, "server.py"))) continue;
|
||||
string legacyVer = ReadVersionFile(Path.Combine(legacySrc, VersionFileName));
|
||||
bool legacyOlder = string.IsNullOrEmpty(legacyVer)
|
||||
|| (!string.IsNullOrEmpty(embeddedVer) && CompareSemverSafe(legacyVer, embeddedVer) < 0);
|
||||
if (legacyOlder)
|
||||
{
|
||||
TryKillUvForPath(legacySrc);
|
||||
try
|
||||
{
|
||||
Directory.Delete(legacyRoot, recursive: true);
|
||||
McpLog.Info($"Removed legacy server at '{legacyRoot}'.");
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
McpLog.Warn($"Failed to remove legacy server at '{legacyRoot}': {ex.Message}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
// Clear overrides that might point at legacy locations
|
||||
try
|
||||
{
|
||||
EditorPrefs.DeleteKey("MCPForUnity.ServerSrc");
|
||||
EditorPrefs.DeleteKey("MCPForUnity.PythonDirOverride");
|
||||
}
|
||||
catch { }
|
||||
return;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -49,11 +99,11 @@ namespace MCPForUnity.Editor.Helpers
|
|||
|
||||
if (hasInstalled || TryGetEmbeddedServerSource(out _))
|
||||
{
|
||||
Debug.LogWarning($"MCP for Unity: Using existing server; skipped install. Details: {ex.Message}");
|
||||
McpLog.Warn($"Using existing server; skipped install. Details: {ex.Message}");
|
||||
return;
|
||||
}
|
||||
|
||||
Debug.LogError($"Failed to ensure server installation: {ex.Message}");
|
||||
McpLog.Error($"Failed to ensure server installation: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -69,9 +119,10 @@ namespace MCPForUnity.Editor.Helpers
|
|||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
// Use per-user LocalApplicationData for canonical install location
|
||||
var localAppData = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData)
|
||||
?? Path.Combine(Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty, "AppData", "Local");
|
||||
return Path.Combine(localAppData, "Programs", RootFolder);
|
||||
return Path.Combine(localAppData, RootFolder);
|
||||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.Linux))
|
||||
{
|
||||
|
|
@ -85,11 +136,15 @@ namespace MCPForUnity.Editor.Helpers
|
|||
}
|
||||
else if (RuntimeInformation.IsOSPlatform(OSPlatform.OSX))
|
||||
{
|
||||
// Use Application Support for a stable, user-writable location
|
||||
return Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
|
||||
RootFolder
|
||||
);
|
||||
// On macOS, use LocalApplicationData (~/Library/Application Support)
|
||||
var localAppSupport = Environment.GetFolderPath(Environment.SpecialFolder.LocalApplicationData);
|
||||
if (string.IsNullOrEmpty(localAppSupport))
|
||||
{
|
||||
// Fallback: construct from $HOME
|
||||
var home = Environment.GetFolderPath(Environment.SpecialFolder.Personal) ?? string.Empty;
|
||||
localAppSupport = Path.Combine(home, "Library", "Application Support");
|
||||
}
|
||||
return Path.Combine(localAppSupport, RootFolder);
|
||||
}
|
||||
throw new Exception("Unsupported operating system.");
|
||||
}
|
||||
|
|
@ -117,6 +172,173 @@ namespace MCPForUnity.Editor.Helpers
|
|||
&& File.Exists(Path.Combine(location, ServerFolder, "src", "server.py"));
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Detects legacy installs or older versions and logs findings (no deletion yet).
|
||||
/// </summary>
|
||||
private static void DetectAndLogLegacyInstallStates(string canonicalRoot)
|
||||
{
|
||||
try
|
||||
{
|
||||
string canonicalSrc = Path.Combine(canonicalRoot, "src");
|
||||
// Normalize canonical root for comparisons
|
||||
string normCanonicalRoot = NormalizePathSafe(canonicalRoot);
|
||||
string embeddedSrc = null;
|
||||
TryGetEmbeddedServerSource(out embeddedSrc);
|
||||
|
||||
string embeddedVer = ReadVersionFile(Path.Combine(embeddedSrc ?? string.Empty, VersionFileName));
|
||||
string installedVer = ReadVersionFile(Path.Combine(canonicalSrc, VersionFileName));
|
||||
|
||||
// Legacy paths (macOS/Linux .config; Windows roaming as example)
|
||||
foreach (var legacyRoot in GetLegacyRootsForDetection())
|
||||
{
|
||||
// Skip logging for the canonical root itself
|
||||
if (PathsEqualSafe(legacyRoot, normCanonicalRoot))
|
||||
continue;
|
||||
string legacySrc = Path.Combine(legacyRoot, "src");
|
||||
bool hasServer = File.Exists(Path.Combine(legacySrc, "server.py"));
|
||||
string legacyVer = ReadVersionFile(Path.Combine(legacySrc, VersionFileName));
|
||||
|
||||
if (hasServer)
|
||||
{
|
||||
// Case 1: No version file
|
||||
if (string.IsNullOrEmpty(legacyVer))
|
||||
{
|
||||
McpLog.Info("Detected legacy install without version file at: " + legacyRoot, always: false);
|
||||
}
|
||||
|
||||
// Case 2: Lives in legacy path
|
||||
McpLog.Info("Detected legacy install path: " + legacyRoot, always: false);
|
||||
|
||||
// Case 3: Has version but appears older than embedded
|
||||
if (!string.IsNullOrEmpty(embeddedVer) && !string.IsNullOrEmpty(legacyVer) && CompareSemverSafe(legacyVer, embeddedVer) < 0)
|
||||
{
|
||||
McpLog.Info($"Legacy install version {legacyVer} is older than embedded {embeddedVer}", always: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Also log if canonical is missing version (treated as older)
|
||||
if (Directory.Exists(canonicalRoot))
|
||||
{
|
||||
if (string.IsNullOrEmpty(installedVer))
|
||||
{
|
||||
McpLog.Info("Canonical install missing version file (treat as older). Path: " + canonicalRoot, always: false);
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(embeddedVer) && CompareSemverSafe(installedVer, embeddedVer) < 0)
|
||||
{
|
||||
McpLog.Info($"Canonical install version {installedVer} is older than embedded {embeddedVer}", always: false);
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
McpLog.Warn("Detect legacy/version state failed: " + ex.Message);
|
||||
}
|
||||
}
|
||||
|
||||
private static string NormalizePathSafe(string path)
|
||||
{
|
||||
try { return string.IsNullOrEmpty(path) ? path : Path.GetFullPath(path.Trim()); }
|
||||
catch { return path; }
|
||||
}
|
||||
|
||||
private static bool PathsEqualSafe(string a, string b)
|
||||
{
|
||||
if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) return false;
|
||||
string na = NormalizePathSafe(a);
|
||||
string nb = NormalizePathSafe(b);
|
||||
try
|
||||
{
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows))
|
||||
{
|
||||
return string.Equals(na, nb, StringComparison.OrdinalIgnoreCase);
|
||||
}
|
||||
return string.Equals(na, nb, StringComparison.Ordinal);
|
||||
}
|
||||
catch { return false; }
|
||||
}
|
||||
|
||||
private static IEnumerable<string> GetLegacyRootsForDetection()
|
||||
{
|
||||
var roots = new System.Collections.Generic.List<string>();
|
||||
string home = Environment.GetFolderPath(Environment.SpecialFolder.UserProfile) ?? string.Empty;
|
||||
// macOS/Linux legacy
|
||||
roots.Add(Path.Combine(home, ".config", "UnityMCP", "UnityMcpServer"));
|
||||
roots.Add(Path.Combine(home, ".local", "share", "UnityMCP", "UnityMcpServer"));
|
||||
// Windows roaming example
|
||||
try
|
||||
{
|
||||
string roaming = Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData) ?? string.Empty;
|
||||
if (!string.IsNullOrEmpty(roaming))
|
||||
roots.Add(Path.Combine(roaming, "UnityMCP", "UnityMcpServer"));
|
||||
}
|
||||
catch { }
|
||||
return roots;
|
||||
}
|
||||
|
||||
private static void TryKillUvForPath(string serverSrcPath)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(serverSrcPath)) return;
|
||||
if (RuntimeInformation.IsOSPlatform(OSPlatform.Windows)) return;
|
||||
|
||||
var psi = new System.Diagnostics.ProcessStartInfo
|
||||
{
|
||||
FileName = "/usr/bin/pgrep",
|
||||
Arguments = $"-f \"uv .*--directory {serverSrcPath}\"",
|
||||
UseShellExecute = false,
|
||||
RedirectStandardOutput = true,
|
||||
RedirectStandardError = true,
|
||||
CreateNoWindow = true
|
||||
};
|
||||
using var p = System.Diagnostics.Process.Start(psi);
|
||||
if (p == null) return;
|
||||
string outp = p.StandardOutput.ReadToEnd();
|
||||
p.WaitForExit(1500);
|
||||
if (p.ExitCode == 0 && !string.IsNullOrEmpty(outp))
|
||||
{
|
||||
foreach (var line in outp.Split(new[] { '\n', '\r' }, StringSplitOptions.RemoveEmptyEntries))
|
||||
{
|
||||
if (int.TryParse(line.Trim(), out int pid))
|
||||
{
|
||||
try { System.Diagnostics.Process.GetProcessById(pid).Kill(); } catch { }
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
}
|
||||
|
||||
private static string ReadVersionFile(string path)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(path) || !File.Exists(path)) return null;
|
||||
string v = File.ReadAllText(path).Trim();
|
||||
return string.IsNullOrEmpty(v) ? null : v;
|
||||
}
|
||||
catch { return null; }
|
||||
}
|
||||
|
||||
private static int CompareSemverSafe(string a, string b)
|
||||
{
|
||||
try
|
||||
{
|
||||
if (string.IsNullOrEmpty(a) || string.IsNullOrEmpty(b)) return 0;
|
||||
var ap = a.Split('.');
|
||||
var bp = b.Split('.');
|
||||
for (int i = 0; i < Math.Max(ap.Length, bp.Length); i++)
|
||||
{
|
||||
int ai = (i < ap.Length && int.TryParse(ap[i], out var t1)) ? t1 : 0;
|
||||
int bi = (i < bp.Length && int.TryParse(bp[i], out var t2)) ? t2 : 0;
|
||||
if (ai != bi) return ai.CompareTo(bi);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
catch { return 0; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Attempts to locate the embedded UnityMcpServer/src directory inside the installed package
|
||||
/// or common development locations.
|
||||
|
|
|
|||
|
|
@ -58,6 +58,10 @@ namespace MCPForUnity.Editor.Windows
|
|||
isUnityBridgeRunning = MCPForUnityBridge.IsRunning;
|
||||
autoRegisterEnabled = EditorPrefs.GetBool("MCPForUnity.AutoRegisterEnabled", true);
|
||||
debugLogsEnabled = EditorPrefs.GetBool("MCPForUnity.DebugLogs", false);
|
||||
if (debugLogsEnabled)
|
||||
{
|
||||
LogDebugPrefsState();
|
||||
}
|
||||
foreach (McpClient mcpClient in mcpClients.clients)
|
||||
{
|
||||
CheckMcpConfiguration(mcpClient);
|
||||
|
|
@ -242,10 +246,79 @@ namespace MCPForUnity.Editor.Windows
|
|||
{
|
||||
debugLogsEnabled = newDebug;
|
||||
EditorPrefs.SetBool("MCPForUnity.DebugLogs", debugLogsEnabled);
|
||||
if (debugLogsEnabled)
|
||||
{
|
||||
LogDebugPrefsState();
|
||||
}
|
||||
}
|
||||
EditorGUILayout.Space(15);
|
||||
}
|
||||
|
||||
private void LogDebugPrefsState()
|
||||
{
|
||||
try
|
||||
{
|
||||
string pythonDirOverridePref = SafeGetPrefString("MCPForUnity.PythonDirOverride");
|
||||
string uvPathPref = SafeGetPrefString("MCPForUnity.UvPath");
|
||||
string serverSrcPref = SafeGetPrefString("MCPForUnity.ServerSrc");
|
||||
bool useEmbedded = SafeGetPrefBool("MCPForUnity.UseEmbeddedServer");
|
||||
|
||||
// Version-scoped detection key
|
||||
string embeddedVer = ReadEmbeddedVersionOrFallback();
|
||||
string detectKey = $"MCPForUnity.LegacyDetectLogged:{embeddedVer}";
|
||||
bool detectLogged = SafeGetPrefBool(detectKey);
|
||||
|
||||
// Project-scoped auto-register key
|
||||
string projectPath = Application.dataPath ?? string.Empty;
|
||||
string autoKey = $"MCPForUnity.AutoRegistered.{ComputeSha1(projectPath)}";
|
||||
bool autoRegistered = SafeGetPrefBool(autoKey);
|
||||
|
||||
MCPForUnity.Editor.Helpers.McpLog.Info(
|
||||
"MCP Debug Prefs:\n" +
|
||||
$" DebugLogs: {debugLogsEnabled}\n" +
|
||||
$" PythonDirOverride: '{pythonDirOverridePref}'\n" +
|
||||
$" UvPath: '{uvPathPref}'\n" +
|
||||
$" ServerSrc: '{serverSrcPref}'\n" +
|
||||
$" UseEmbeddedServer: {useEmbedded}\n" +
|
||||
$" DetectOnceKey: '{detectKey}' => {detectLogged}\n" +
|
||||
$" AutoRegisteredKey: '{autoKey}' => {autoRegistered}",
|
||||
always: false
|
||||
);
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"MCP Debug Prefs logging failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
private static string SafeGetPrefString(string key)
|
||||
{
|
||||
try { return EditorPrefs.GetString(key, string.Empty) ?? string.Empty; } catch { return string.Empty; }
|
||||
}
|
||||
|
||||
private static bool SafeGetPrefBool(string key)
|
||||
{
|
||||
try { return EditorPrefs.GetBool(key, false); } catch { return false; }
|
||||
}
|
||||
|
||||
private static string ReadEmbeddedVersionOrFallback()
|
||||
{
|
||||
try
|
||||
{
|
||||
if (ServerPathResolver.TryFindEmbeddedServerSource(out var embeddedSrc))
|
||||
{
|
||||
var p = Path.Combine(embeddedSrc, "server_version.txt");
|
||||
if (File.Exists(p))
|
||||
{
|
||||
var s = File.ReadAllText(p)?.Trim();
|
||||
if (!string.IsNullOrEmpty(s)) return s;
|
||||
}
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
return "unknown";
|
||||
}
|
||||
|
||||
private void DrawServerStatusSection()
|
||||
{
|
||||
EditorGUILayout.BeginVertical(EditorStyles.helpBox);
|
||||
|
|
@ -504,7 +577,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Auto-setup client '{client.name}' failed: {ex.Message}");
|
||||
MCPForUnity.Editor.Helpers.McpLog.Warn($"Auto-setup client '{client.name}' failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
lastClientRegisteredOk = anyRegistered || IsCursorConfigured(pythonDir) || IsClaudeConfigured();
|
||||
|
|
@ -521,7 +594,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Auto-setup StartAutoConnect failed: {ex.Message}");
|
||||
MCPForUnity.Editor.Helpers.McpLog.Warn($"Auto-setup StartAutoConnect failed: {ex.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -532,7 +605,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"MCP for Unity auto-setup skipped: {e.Message}");
|
||||
MCPForUnity.Editor.Helpers.McpLog.Warn($"MCP for Unity auto-setup skipped: {e.Message}");
|
||||
}
|
||||
}
|
||||
|
||||
|
|
@ -1000,7 +1073,7 @@ namespace MCPForUnity.Editor.Windows
|
|||
return true;
|
||||
}
|
||||
|
||||
private string WriteToConfig(string pythonDir, string configPath, McpClient mcpClient = null)
|
||||
private string WriteToConfig(string pythonDir, string configPath, McpClient mcpClient = null)
|
||||
{
|
||||
// 0) Respect explicit lock (hidden pref or UI toggle)
|
||||
try { if (UnityEditor.EditorPrefs.GetBool("MCPForUnity.LockCursorConfig", false)) return "Skipped (locked)"; } catch { }
|
||||
|
|
|
|||
|
|
@ -0,0 +1,2 @@
|
|||
3.0.1
|
||||
|
||||
|
|
@ -1,6 +1,6 @@
|
|||
{
|
||||
"name": "com.coplaydev.unity-mcp",
|
||||
"version": "3.0.0",
|
||||
"version": "3.0.1",
|
||||
"displayName": "MCP for Unity",
|
||||
"description": "A bridge that connects an LLM to Unity via the MCP (Model Context Protocol). This allows MCP Clients like Claude Desktop or Cursor to directly control your Unity Editor.\n\nJoin Our Discord: https://discord.gg/y4p8KfzrN4",
|
||||
"unity": "2021.3",
|
||||
|
|
|
|||
Loading…
Reference in New Issue