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
1 change: 1 addition & 0 deletions MCPForUnity/Editor/Constants/EditorPrefKeys.cs
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ internal static class EditorPrefKeys
internal const string ValidationLevel = "MCPForUnity.ValidationLevel";
internal const string UnitySocketPort = "MCPForUnity.UnitySocketPort";
internal const string ResumeHttpAfterReload = "MCPForUnity.ResumeHttpAfterReload";
internal const string ResumeStdioAfterReload = "MCPForUnity.ResumeStdioAfterReload";

internal const string UvxPathOverride = "MCPForUnity.UvxPath";
internal const string ClaudeCliPathOverride = "MCPForUnity.ClaudeCliPath";
Expand Down
29 changes: 19 additions & 10 deletions MCPForUnity/Editor/Services/BridgeControlService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,21 @@ private static BridgeVerificationResult BuildVerificationResult(TransportState s
};
}

public bool IsRunning => _transportManager.GetState().IsConnected;
public bool IsRunning
{
get
{
var mode = ResolvePreferredMode();
return _transportManager.IsRunning(mode);
}
}

public int CurrentPort
{
get
{
var state = _transportManager.GetState();
var mode = ResolvePreferredMode();
var state = _transportManager.GetState(mode);
if (state.Port.HasValue)
{
return state.Port.Value;
Expand All @@ -67,7 +75,7 @@ public int CurrentPort
}

public bool IsAutoConnectMode => StdioBridgeHost.IsAutoConnectMode();
public TransportMode? ActiveMode => _transportManager.ActiveMode;
public TransportMode? ActiveMode => ResolvePreferredMode();

public async Task<bool> StartAsync()
{
Expand All @@ -92,7 +100,8 @@ public async Task StopAsync()
{
try
{
await _transportManager.StopAsync();
var mode = ResolvePreferredMode();
await _transportManager.StopAsync(mode);
}
catch (Exception ex)
{
Expand All @@ -102,17 +111,17 @@ public async Task StopAsync()

public async Task<BridgeVerificationResult> VerifyAsync()
{
var mode = _transportManager.ActiveMode ?? ResolvePreferredMode();
bool pingSucceeded = await _transportManager.VerifyAsync();
var state = _transportManager.GetState();
var mode = ResolvePreferredMode();
bool pingSucceeded = await _transportManager.VerifyAsync(mode);
var state = _transportManager.GetState(mode);
return BuildVerificationResult(state, mode, pingSucceeded);
}

public BridgeVerificationResult Verify(int port)
{
var mode = _transportManager.ActiveMode ?? ResolvePreferredMode();
bool pingSucceeded = _transportManager.VerifyAsync().GetAwaiter().GetResult();
var state = _transportManager.GetState();
var mode = ResolvePreferredMode();
bool pingSucceeded = _transportManager.VerifyAsync(mode).GetAwaiter().GetResult();
var state = _transportManager.GetState(mode);

if (mode == TransportMode.Stdio)
{
Expand Down
16 changes: 9 additions & 7 deletions MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -24,8 +24,8 @@ private static void OnBeforeAssemblyReload()
{
try
{
var bridge = MCPServiceLocator.Bridge;
bool shouldResume = bridge.IsRunning && bridge.ActiveMode == TransportMode.Http;
var transport = MCPServiceLocator.TransportManager;
bool shouldResume = transport.IsRunning(TransportMode.Http);

if (shouldResume)
{
Expand All @@ -36,9 +36,9 @@ private static void OnBeforeAssemblyReload()
EditorPrefs.DeleteKey(EditorPrefKeys.ResumeHttpAfterReload);
}

if (bridge.IsRunning)
if (shouldResume)
{
var stopTask = bridge.StopAsync();
var stopTask = transport.StopAsync(TransportMode.Http);
stopTask.ContinueWith(t =>
{
if (t.IsFaulted && t.Exception != null)
Expand All @@ -59,7 +59,9 @@ private static void OnAfterAssemblyReload()
bool resume = false;
try
{
resume = EditorPrefs.GetBool(EditorPrefKeys.ResumeHttpAfterReload, false);
// Only resume HTTP if it is still the selected transport.
bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
resume = useHttp && EditorPrefs.GetBool(EditorPrefKeys.ResumeHttpAfterReload, false);
if (resume)
{
EditorPrefs.DeleteKey(EditorPrefKeys.ResumeHttpAfterReload);
Expand Down Expand Up @@ -90,7 +92,7 @@ private static void OnAfterAssemblyReload()
{
try
{
var startTask = MCPServiceLocator.Bridge.StartAsync();
var startTask = MCPServiceLocator.TransportManager.StartAsync(TransportMode.Http);
startTask.ContinueWith(t =>
{
if (t.IsFaulted)
Expand Down Expand Up @@ -123,7 +125,7 @@ private static void OnAfterAssemblyReload()
{
try
{
bool started = await MCPServiceLocator.Bridge.StartAsync();
bool started = await MCPServiceLocator.TransportManager.StartAsync(TransportMode.Http);
if (!started)
{
McpLog.Warn("Failed to resume HTTP MCP bridge after domain reload");
Expand Down
104 changes: 104 additions & 0 deletions MCPForUnity/Editor/Services/StdioBridgeReloadHandler.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
using System;
using UnityEditor;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Services.Transport;
using MCPForUnity.Editor.Services.Transport.Transports;

namespace MCPForUnity.Editor.Services
{
/// <summary>
/// Ensures the legacy stdio bridge resumes after domain reloads, mirroring the HTTP handler.
/// </summary>
[InitializeOnLoad]
internal static class StdioBridgeReloadHandler
{
static StdioBridgeReloadHandler()
{
AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
}

private static void OnBeforeAssemblyReload()
{
try
{
// Only persist resume intent when stdio is the active transport and the bridge is running.
bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
bool isRunning = MCPServiceLocator.TransportManager.IsRunning(TransportMode.Stdio);
bool shouldResume = !useHttp && isRunning;

if (shouldResume)
{
EditorPrefs.SetBool(EditorPrefKeys.ResumeStdioAfterReload, true);

// Stop only the stdio bridge; leave HTTP untouched if it is running concurrently.
var stopTask = MCPServiceLocator.TransportManager.StopAsync(TransportMode.Stdio);
stopTask.ContinueWith(t =>
{
if (t.IsFaulted && t.Exception != null)
{
McpLog.Warn($"Error stopping stdio bridge before reload: {t.Exception.GetBaseException()?.Message}");
}
}, System.Threading.Tasks.TaskScheduler.Default);
}
else
{
EditorPrefs.DeleteKey(EditorPrefKeys.ResumeStdioAfterReload);
}
}
catch (Exception ex)
{
McpLog.Warn($"Failed to persist stdio reload flag: {ex.Message}");
}
}

private static void OnAfterAssemblyReload()
{
bool resume = false;
try
{
resume = EditorPrefs.GetBool(EditorPrefKeys.ResumeStdioAfterReload, false);
bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
resume = resume && !useHttp;
if (resume)
{
EditorPrefs.DeleteKey(EditorPrefKeys.ResumeStdioAfterReload);
}
}
catch (Exception ex)
{
McpLog.Warn($"Failed to read stdio reload flag: {ex.Message}");
}

if (!resume)
{
return;
}

// Restart via TransportManager so state stays in sync; if it fails (port busy), rely on UI to retry.
TryStartBridgeImmediate();
}

private static void TryStartBridgeImmediate()
{
var startTask = MCPServiceLocator.TransportManager.StartAsync(TransportMode.Stdio);
startTask.ContinueWith(t =>
{
if (t.IsFaulted)
{
var baseEx = t.Exception?.GetBaseException();
McpLog.Warn($"Failed to resume stdio bridge after reload: {baseEx?.Message}");
return;
}
if (!t.Result)
{
McpLog.Warn("Failed to resume stdio bridge after domain reload");
return;
}

MCPForUnity.Editor.Windows.MCPForUnityEditorWindow.RequestHealthVerification();
}, System.Threading.Tasks.TaskScheduler.Default);
}
}
}
11 changes: 11 additions & 0 deletions MCPForUnity/Editor/Services/StdioBridgeReloadHandler.cs.meta

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading