diff --git a/MCPForUnity/Editor/Services/IServerManagementService.cs b/MCPForUnity/Editor/Services/IServerManagementService.cs index f38014a88..299fad511 100644 --- a/MCPForUnity/Editor/Services/IServerManagementService.cs +++ b/MCPForUnity/Editor/Services/IServerManagementService.cs @@ -35,6 +35,12 @@ public interface IServerManagementService /// bool IsLocalHttpServerRunning(); + /// + /// Fast reachability check: returns true if a local TCP listener is accepting connections + /// for the configured local URL/port (used for UI state without process inspection). + /// + bool IsLocalHttpServerReachable(); + /// /// Attempts to get the command that will be executed when starting the local HTTP server /// diff --git a/MCPForUnity/Editor/Services/ServerManagementService.cs b/MCPForUnity/Editor/Services/ServerManagementService.cs index b77bbd357..cd62258d3 100644 --- a/MCPForUnity/Editor/Services/ServerManagementService.cs +++ b/MCPForUnity/Editor/Services/ServerManagementService.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Collections.Generic; using System.Globalization; +using System.Net.Sockets; using System.Security.Cryptography; using System.Text; using MCPForUnity.Editor.Constants; @@ -600,6 +601,75 @@ public bool IsLocalHttpServerRunning() } } + public bool IsLocalHttpServerReachable() + { + try + { + string httpUrl = HttpEndpointUtility.GetBaseUrl(); + if (!IsLocalUrl(httpUrl)) + { + return false; + } + + if (!Uri.TryCreate(httpUrl, UriKind.Absolute, out var uri) || uri.Port <= 0) + { + return false; + } + + return TryConnectToLocalPort(uri.Host, uri.Port, timeoutMs: 50); + } + catch + { + return false; + } + } + + private static bool TryConnectToLocalPort(string host, int port, int timeoutMs) + { + try + { + if (string.IsNullOrEmpty(host)) + { + host = "127.0.0.1"; + } + + var hosts = new HashSet(StringComparer.OrdinalIgnoreCase) { host }; + if (host == "localhost" || host == "0.0.0.0") + { + hosts.Add("127.0.0.1"); + } + if (host == "::" || host == "0:0:0:0:0:0:0:0") + { + hosts.Add("::1"); + } + + foreach (var target in hosts) + { + try + { + using (var client = new TcpClient()) + { + var connectTask = client.ConnectAsync(target, port); + if (connectTask.Wait(timeoutMs) && client.Connected) + { + return true; + } + } + } + catch + { + // Ignore per-host failures. + } + } + } + catch + { + // Ignore probe failures and treat as unreachable. + } + + return false; + } + private bool StopLocalHttpServerInternal(bool quiet, int? portOverride = null, bool allowNonLocalUrl = false) { string httpUrl = HttpEndpointUtility.GetBaseUrl(); diff --git a/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs b/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs index 282280324..ab5c9036a 100644 --- a/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs +++ b/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs @@ -1,5 +1,4 @@ using System; -using System.Net.Sockets; using System.Threading.Tasks; using MCPForUnity.Editor.Constants; using MCPForUnity.Editor.Helpers; @@ -482,14 +481,14 @@ private void UpdateStartHttpButtonState() bool canStartLocalServer = httpLocalSelected && MCPServiceLocator.Server.IsLocalUrl(); bool localServerRunning = false; - // Avoid running expensive port/PID checks every UI tick. + // Avoid running expensive port/PID checks every UI tick; use a fast socket probe for UI state. if (httpLocalSelected) { double now = EditorApplication.timeSinceStartup; if ((now - lastLocalServerRunningPollTime) > 0.75f || httpServerToggleInProgress) { lastLocalServerRunningPollTime = now; - lastLocalServerRunning = MCPServiceLocator.Server.IsLocalHttpServerRunning(); + lastLocalServerRunning = MCPServiceLocator.Server.IsLocalHttpServerReachable(); } localServerRunning = lastLocalServerRunning; } @@ -531,7 +530,7 @@ private async void OnHttpServerToggleClicked() try { // Check if a local server is running. - bool serverRunning = IsHttpLocalSelected() && MCPServiceLocator.Server.IsLocalHttpServerRunning(); + bool serverRunning = IsHttpLocalSelected() && MCPServiceLocator.Server.IsLocalHttpServerReachable(); if (serverRunning) { @@ -591,7 +590,7 @@ private async Task TryAutoStartSessionAsync() var delay = attempt < 6 ? shortDelay : longDelay; // Check if server is actually accepting connections - bool serverDetected = MCPServiceLocator.Server.IsLocalHttpServerRunning(); + bool serverDetected = MCPServiceLocator.Server.IsLocalHttpServerReachable(); if (serverDetected) {