Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions MCPForUnity/Editor/Services/IServerManagementService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -35,6 +35,12 @@ public interface IServerManagementService
/// </summary>
bool IsLocalHttpServerRunning();

/// <summary>
/// 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).
/// </summary>
bool IsLocalHttpServerReachable();

/// <summary>
/// Attempts to get the command that will be executed when starting the local HTTP server
/// </summary>
Expand Down
70 changes: 70 additions & 0 deletions MCPForUnity/Editor/Services/ServerManagementService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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<string>(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)
Comment on lines +652 to +653
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

issue (performance): Blocking on ConnectAsync with Wait(timeoutMs) may cause editor UI hitches.

Because this runs on the editor UI update path, connectTask.Wait(timeoutMs) will block the UI thread, and slow DNS or socket delays can still cause visible stutters even with a 50ms timeout. Please either move the probe to a background task and cache the result for the UI, or use an async pattern (e.g. Task.WhenAny with cancellation) to enforce a strict timeout without blocking the main thread.

{
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();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,4 @@
using System;
using System.Net.Sockets;
using System.Threading.Tasks;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
Expand Down Expand Up @@ -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;
}
Expand Down Expand Up @@ -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)
{
Expand Down Expand Up @@ -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)
{
Expand Down