diff --git a/MCPForUnity/Editor/Helpers/HttpEndpointUtility.cs b/MCPForUnity/Editor/Helpers/HttpEndpointUtility.cs index 94e2f3be9..8678676d8 100644 --- a/MCPForUnity/Editor/Helpers/HttpEndpointUtility.cs +++ b/MCPForUnity/Editor/Helpers/HttpEndpointUtility.cs @@ -47,10 +47,13 @@ public static void SaveBaseUrl(string userValue) } /// - /// Returns the normalized local HTTP base URL (always reads local pref). + /// Returns the normalized local HTTP base URL. UNITY_MCP_HTTP_PORT wins when set, + /// so a CI editor keeps its own port instead of the one a sibling last wrote. /// public static string GetLocalBaseUrl() { + if (McpHttpCiBoot.TryGetCiPort(out int ciPort)) return $"http://127.0.0.1:{ciPort}"; + string stored = EditorPrefs.GetString(LocalPrefKey, DefaultLocalBaseUrl); return NormalizeBaseUrl(stored, DefaultLocalBaseUrl, remoteScope: false); } @@ -128,9 +131,12 @@ public static string GetRegisterToolsUrl() /// /// Returns true if the active HTTP transport scope is "remote". + /// Always false under UNITY_MCP_HTTP_PORT: the CI endpoint is local by construction. /// public static bool IsRemoteScope() { + if (McpHttpCiBoot.TryGetCiPort(out _)) return false; + string scope = EditorConfigurationCache.Instance.HttpTransportScope; return string.Equals(scope, "remote", StringComparison.OrdinalIgnoreCase); } diff --git a/MCPForUnity/Editor/McpHttpCiBoot.cs b/MCPForUnity/Editor/McpHttpCiBoot.cs new file mode 100644 index 000000000..6366ac501 --- /dev/null +++ b/MCPForUnity/Editor/McpHttpCiBoot.cs @@ -0,0 +1,49 @@ +using System; +using MCPForUnity.Editor.Helpers; +using MCPForUnity.Editor.Services; +using MCPForUnity.Editor.Services.Transport; +using UnityEditor; + +namespace MCPForUnity.Editor +{ + // HTTP is a plugin-hub pull model: the editor dials ws://127.0.0.1:/hub/plugin + // StartStdioForCi only listens, so an editor booted that way never registers + public static class McpHttpCiBoot + { + private const string PortEnv = "UNITY_MCP_HTTP_PORT"; + + // Never persisted: sibling checkouts share one EditorPrefs file, so the last writer wins + public static bool TryGetCiPort(out int port) + { + port = 0; + string raw = Environment.GetEnvironmentVariable(PortEnv); + return !string.IsNullOrWhiteSpace(raw) + && int.TryParse(raw, out port) + && port > 0 + && port <= 65535; + } + + public static void StartHttpForCi() + { + if (!TryGetCiPort(out _)) + { + McpLog.Error($"[MCPForUnity] StartHttpForCi: {PortEnv} not set or invalid; cannot start HTTP transport"); + return; + } + + // Defer the connect so MCPServiceLocator and friends are initialized. + EditorApplication.delayCall += async () => + { + try + { + if (!await MCPServiceLocator.TransportManager.StartAsync(TransportMode.Http)) + McpLog.Error("[MCPForUnity] StartHttpForCi: HTTP transport failed to start"); + } + catch (Exception e) + { + McpLog.Error($"[MCPForUnity] StartHttpForCi: HTTP transport threw on start: {e}"); + } + }; + } + } +} diff --git a/MCPForUnity/Editor/McpHttpCiBoot.cs.meta b/MCPForUnity/Editor/McpHttpCiBoot.cs.meta new file mode 100644 index 000000000..1bd4658ac --- /dev/null +++ b/MCPForUnity/Editor/McpHttpCiBoot.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 3fff0044fbf5420d8be6c5bb79828276 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: