diff --git a/MCPForUnity/Editor/Helpers/AssetPathUtility.cs b/MCPForUnity/Editor/Helpers/AssetPathUtility.cs
index a310c6e1..70eae154 100644
--- a/MCPForUnity/Editor/Helpers/AssetPathUtility.cs
+++ b/MCPForUnity/Editor/Helpers/AssetPathUtility.cs
@@ -156,10 +156,10 @@ public static string GetMcpServerGitUrl()
if (version == "unknown")
{
// Fall back to main repo without pinned version so configs remain valid in test scenarios
- return "git+https://github.com/CoplayDev/unity-mcp#subdirectory=Server";
+ return "git+https://github.com/prophecygamestudio/unity-mcp#subdirectory=Server";
}
- return $"git+https://github.com/CoplayDev/unity-mcp@v{version}#subdirectory=Server";
+ return $"git+https://github.com/prophecygamestudio/unity-mcp@v{version}#subdirectory=Server";
}
///
diff --git a/MCPForUnity/Editor/MenuItems/MCPForUnityMenu.cs b/MCPForUnity/Editor/MenuItems/MCPForUnityMenu.cs
index 10f05248..45e4fafa 100644
--- a/MCPForUnity/Editor/MenuItems/MCPForUnityMenu.cs
+++ b/MCPForUnity/Editor/MenuItems/MCPForUnityMenu.cs
@@ -1,4 +1,3 @@
-using MCPForUnity.Editor.Setup;
using MCPForUnity.Editor.Windows;
using UnityEditor;
using UnityEngine;
@@ -7,12 +6,6 @@ namespace MCPForUnity.Editor.MenuItems
{
public static class MCPForUnityMenu
{
- [MenuItem("Window/MCP For Unity/Setup Window", priority = 1)]
- public static void ShowSetupWindow()
- {
- SetupWindowService.ShowSetupWindow();
- }
-
[MenuItem("Window/MCP For Unity/Toggle MCP Window %#m", priority = 2)]
public static void ToggleMCPWindow()
{
diff --git a/MCPForUnity/Editor/Migrations/LegacyServerSrcMigration.cs b/MCPForUnity/Editor/Migrations/LegacyServerSrcMigration.cs
index e90019d8..cf8d5de9 100644
--- a/MCPForUnity/Editor/Migrations/LegacyServerSrcMigration.cs
+++ b/MCPForUnity/Editor/Migrations/LegacyServerSrcMigration.cs
@@ -38,15 +38,8 @@ private static void RunMigrationIfNeeded()
try
{
- McpLog.Info("Detected legacy embedded MCP server configuration. Updating all client configs...");
-
- var summary = MCPServiceLocator.Client.ConfigureAllDetectedClients();
-
- if (summary.FailureCount > 0 || summary.SuccessCount == 0)
- {
- McpLog.Warn($"Legacy configuration migration incomplete ({summary.GetSummaryMessage()}). Will retry next session.");
- return;
- }
+ // Client configuration is no longer supported - just clean up legacy keys
+ McpLog.Info("Detected legacy embedded MCP server configuration. Cleaning up legacy preferences...");
if (hasServerSrc)
{
@@ -60,7 +53,7 @@ private static void RunMigrationIfNeeded()
McpLog.Info(" ✓ Removed legacy key: MCPForUnity.UseEmbeddedServer");
}
- McpLog.Info($"Legacy configuration migration complete ({summary.GetSummaryMessage()})");
+ McpLog.Info("Legacy configuration migration complete. Note: Client configuration is now handled by the MCP client (e.g., Cursor's mcp.json).");
}
catch (Exception ex)
{
diff --git a/MCPForUnity/Editor/Migrations/StdIoVersionMigration.cs b/MCPForUnity/Editor/Migrations/StdIoVersionMigration.cs
index 84b63f32..f1d3747c 100644
--- a/MCPForUnity/Editor/Migrations/StdIoVersionMigration.cs
+++ b/MCPForUnity/Editor/Migrations/StdIoVersionMigration.cs
@@ -43,53 +43,17 @@ private static void RunMigrationIfNeeded()
if (string.Equals(lastUpgradeVersion, currentVersion, StringComparison.OrdinalIgnoreCase))
{
- return; // Already refreshed for this package version
- }
-
- bool hadFailures = false;
- bool touchedAny = false;
-
- var configurators = McpClientRegistry.All.OfType().ToList();
- foreach (var configurator in configurators)
- {
- try
- {
- if (!ConfigUsesStdIo(configurator.Client))
- continue;
-
- if (!configurator.SupportsAutoConfigure)
- continue;
-
- MCPServiceLocator.Client.ConfigureClient(configurator);
- touchedAny = true;
- }
- catch (Exception ex)
- {
- hadFailures = true;
- McpLog.Warn($"Failed to refresh stdio config for {configurator.DisplayName}: {ex.Message}");
- }
- }
-
- if (!touchedAny)
- {
- // Nothing needed refreshing; still record version so we don't rerun every launch
- try { EditorPrefs.SetString(LastUpgradeKey, currentVersion); } catch { }
- return;
- }
-
- if (hadFailures)
- {
- McpLog.Warn("Stdio MCP upgrade encountered errors; will retry next session.");
- return;
+ return; // Already processed for this package version
}
+ // Client configuration is no longer supported - just record the version
try
{
EditorPrefs.SetString(LastUpgradeKey, currentVersion);
}
catch { }
- McpLog.Info($"Updated stdio MCP configs to package version {currentVersion}.");
+ McpLog.Info($"Package version {currentVersion} detected. Note: Client configuration is now handled by the MCP client (e.g., Cursor's mcp.json).");
}
private static bool ConfigUsesStdIo(McpClient client)
diff --git a/MCPForUnity/Editor/Resources/Editor/Selection.cs b/MCPForUnity/Editor/Resources/Editor/Selection.cs
index 022d9c48..406b9c8f 100644
--- a/MCPForUnity/Editor/Resources/Editor/Selection.cs
+++ b/MCPForUnity/Editor/Resources/Editor/Selection.cs
@@ -21,7 +21,7 @@ public static object HandleCommand(JObject @params)
activeObject = UnityEditor.Selection.activeObject?.name,
activeGameObject = UnityEditor.Selection.activeGameObject?.name,
activeTransform = UnityEditor.Selection.activeTransform?.name,
- activeInstanceID = UnityEditor.Selection.activeInstanceID,
+ activeInstanceID = UnityEditor.Selection.activeObject?.GetInstanceID() ?? 0,
count = UnityEditor.Selection.count,
objects = UnityEditor.Selection.objects
.Select(obj => new
diff --git a/MCPForUnity/Editor/Services/BridgeControlService.cs b/MCPForUnity/Editor/Services/BridgeControlService.cs
index 0786de05..33153ec5 100644
--- a/MCPForUnity/Editor/Services/BridgeControlService.cs
+++ b/MCPForUnity/Editor/Services/BridgeControlService.cs
@@ -1,39 +1,29 @@
using System;
using System.Threading.Tasks;
-using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Services.Transport;
using MCPForUnity.Editor.Services.Transport.Transports;
-using UnityEditor;
namespace MCPForUnity.Editor.Services
{
///
- /// Bridges the editor UI to the active transport (HTTP with WebSocket push, or stdio).
+ /// Bridges the editor UI to the stdio transport.
///
public class BridgeControlService : IBridgeControlService
{
private readonly TransportManager _transportManager;
- private TransportMode _preferredMode = TransportMode.Http;
public BridgeControlService()
{
_transportManager = MCPServiceLocator.TransportManager;
}
- private TransportMode ResolvePreferredMode()
+ private static BridgeVerificationResult BuildVerificationResult(TransportState state, bool pingSucceeded, string messageOverride = null, bool? handshakeOverride = null)
{
- bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
- _preferredMode = useHttp ? TransportMode.Http : TransportMode.Stdio;
- return _preferredMode;
- }
-
- private static BridgeVerificationResult BuildVerificationResult(TransportState state, TransportMode mode, bool pingSucceeded, string messageOverride = null, bool? handshakeOverride = null)
- {
- bool handshakeValid = handshakeOverride ?? (mode == TransportMode.Stdio ? state.IsConnected : true);
+ bool handshakeValid = handshakeOverride ?? state.IsConnected;
string transportLabel = string.IsNullOrWhiteSpace(state.TransportName)
- ? mode.ToString().ToLowerInvariant()
+ ? "stdio"
: state.TransportName;
string detailSuffix = string.IsNullOrWhiteSpace(state.Details) ? string.Empty : $" [{state.Details}]";
string message = messageOverride
@@ -53,8 +43,7 @@ public bool IsRunning
{
get
{
- var mode = ResolvePreferredMode();
- return _transportManager.IsRunning(mode);
+ return _transportManager.IsRunning();
}
}
@@ -62,36 +51,32 @@ public int CurrentPort
{
get
{
- var mode = ResolvePreferredMode();
- var state = _transportManager.GetState(mode);
+ var state = _transportManager.GetState();
if (state.Port.HasValue)
{
return state.Port.Value;
}
- // Legacy fallback while the stdio bridge is still in play
return StdioBridgeHost.GetCurrentPort();
}
}
public bool IsAutoConnectMode => StdioBridgeHost.IsAutoConnectMode();
- public TransportMode? ActiveMode => ResolvePreferredMode();
public async Task StartAsync()
{
- var mode = ResolvePreferredMode();
try
{
- bool started = await _transportManager.StartAsync(mode);
+ bool started = await _transportManager.StartAsync();
if (!started)
{
- McpLog.Warn($"Failed to start MCP transport: {mode}");
+ McpLog.Warn("Failed to start MCP stdio transport");
}
return started;
}
catch (Exception ex)
{
- McpLog.Error($"Error starting MCP transport {mode}: {ex.Message}");
+ McpLog.Error($"Error starting MCP stdio transport: {ex.Message}");
return false;
}
}
@@ -100,8 +85,7 @@ public async Task StopAsync()
{
try
{
- var mode = ResolvePreferredMode();
- await _transportManager.StopAsync(mode);
+ await _transportManager.StopAsync();
}
catch (Exception ex)
{
@@ -111,28 +95,21 @@ public async Task StopAsync()
public async Task VerifyAsync()
{
- var mode = ResolvePreferredMode();
- bool pingSucceeded = await _transportManager.VerifyAsync(mode);
- var state = _transportManager.GetState(mode);
- return BuildVerificationResult(state, mode, pingSucceeded);
+ bool pingSucceeded = await _transportManager.VerifyAsync();
+ var state = _transportManager.GetState();
+ return BuildVerificationResult(state, pingSucceeded);
}
public BridgeVerificationResult Verify(int port)
{
- var mode = ResolvePreferredMode();
- bool pingSucceeded = _transportManager.VerifyAsync(mode).GetAwaiter().GetResult();
- var state = _transportManager.GetState(mode);
-
- if (mode == TransportMode.Stdio)
- {
- bool handshakeValid = state.IsConnected && port == CurrentPort;
- string message = handshakeValid
- ? $"STDIO transport listening on port {CurrentPort}"
- : $"STDIO transport port mismatch (expected {CurrentPort}, got {port})";
- return BuildVerificationResult(state, mode, pingSucceeded && handshakeValid, message, handshakeValid);
- }
-
- return BuildVerificationResult(state, mode, pingSucceeded);
+ bool pingSucceeded = _transportManager.VerifyAsync().GetAwaiter().GetResult();
+ var state = _transportManager.GetState();
+
+ bool handshakeValid = state.IsConnected && port == CurrentPort;
+ string message = handshakeValid
+ ? $"STDIO transport listening on port {CurrentPort}"
+ : $"STDIO transport port mismatch (expected {CurrentPort}, got {port})";
+ return BuildVerificationResult(state, pingSucceeded && handshakeValid, message, handshakeValid);
}
}
diff --git a/MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs b/MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs
deleted file mode 100644
index 2f5ef681..00000000
--- a/MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs
+++ /dev/null
@@ -1,145 +0,0 @@
-using System;
-using System.Threading.Tasks;
-using MCPForUnity.Editor.Constants;
-using MCPForUnity.Editor.Helpers;
-using MCPForUnity.Editor.Services.Transport;
-using MCPForUnity.Editor.Windows;
-using UnityEditor;
-
-namespace MCPForUnity.Editor.Services
-{
- ///
- /// Ensures HTTP transports resume after domain reloads similar to the legacy stdio bridge.
- ///
- [InitializeOnLoad]
- internal static class HttpBridgeReloadHandler
- {
- static HttpBridgeReloadHandler()
- {
- AssemblyReloadEvents.beforeAssemblyReload += OnBeforeAssemblyReload;
- AssemblyReloadEvents.afterAssemblyReload += OnAfterAssemblyReload;
- }
-
- private static void OnBeforeAssemblyReload()
- {
- try
- {
- var transport = MCPServiceLocator.TransportManager;
- bool shouldResume = transport.IsRunning(TransportMode.Http);
-
- if (shouldResume)
- {
- EditorPrefs.SetBool(EditorPrefKeys.ResumeHttpAfterReload, true);
- }
- else
- {
- EditorPrefs.DeleteKey(EditorPrefKeys.ResumeHttpAfterReload);
- }
-
- if (shouldResume)
- {
- var stopTask = transport.StopAsync(TransportMode.Http);
- stopTask.ContinueWith(t =>
- {
- if (t.IsFaulted && t.Exception != null)
- {
- McpLog.Warn($"Error stopping MCP bridge before reload: {t.Exception.GetBaseException().Message}");
- }
- }, TaskScheduler.Default);
- }
- }
- catch (Exception ex)
- {
- McpLog.Warn($"Failed to evaluate HTTP bridge reload state: {ex.Message}");
- }
- }
-
- private static void OnAfterAssemblyReload()
- {
- bool resume = false;
- try
- {
- // 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);
- }
- }
- catch (Exception ex)
- {
- McpLog.Warn($"Failed to read HTTP bridge reload flag: {ex.Message}");
- resume = false;
- }
-
- if (!resume)
- {
- return;
- }
-
- // If the editor is not compiling, attempt an immediate restart without relying on editor focus.
- bool isCompiling = EditorApplication.isCompiling;
- try
- {
- var pipeline = Type.GetType("UnityEditor.Compilation.CompilationPipeline, UnityEditor");
- var prop = pipeline?.GetProperty("isCompiling", System.Reflection.BindingFlags.Public | System.Reflection.BindingFlags.Static);
- if (prop != null) isCompiling |= (bool)prop.GetValue(null);
- }
- catch { }
-
- if (!isCompiling)
- {
- try
- {
- var startTask = MCPServiceLocator.TransportManager.StartAsync(TransportMode.Http);
- startTask.ContinueWith(t =>
- {
- if (t.IsFaulted)
- {
- var baseEx = t.Exception?.GetBaseException();
- McpLog.Warn($"Failed to resume HTTP MCP bridge after domain reload: {baseEx?.Message}");
- return;
- }
- bool started = t.Result;
- if (!started)
- {
- McpLog.Warn("Failed to resume HTTP MCP bridge after domain reload");
- }
- else
- {
- MCPForUnityEditorWindow.RequestHealthVerification();
- }
- }, TaskScheduler.Default);
- return;
- }
- catch (Exception ex)
- {
- McpLog.Error($"Error resuming HTTP MCP bridge: {ex.Message}");
- return;
- }
- }
-
- // Fallback when compiling: schedule on the editor loop
- EditorApplication.delayCall += async () =>
- {
- try
- {
- bool started = await MCPServiceLocator.TransportManager.StartAsync(TransportMode.Http);
- if (!started)
- {
- McpLog.Warn("Failed to resume HTTP MCP bridge after domain reload");
- }
- else
- {
- MCPForUnityEditorWindow.RequestHealthVerification();
- }
- }
- catch (Exception ex)
- {
- McpLog.Error($"Error resuming HTTP MCP bridge: {ex.Message}");
- }
- };
- }
- }
-}
diff --git a/MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs.meta b/MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs.meta
deleted file mode 100644
index ae5e9edd..00000000
--- a/MCPForUnity/Editor/Services/HttpBridgeReloadHandler.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 4c0cf970a7b494a659be151dc0124296
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/MCPForUnity/Editor/Services/IBridgeControlService.cs b/MCPForUnity/Editor/Services/IBridgeControlService.cs
index 7cc593e0..1f62fcdd 100644
--- a/MCPForUnity/Editor/Services/IBridgeControlService.cs
+++ b/MCPForUnity/Editor/Services/IBridgeControlService.cs
@@ -1,5 +1,4 @@
using System.Threading.Tasks;
-using MCPForUnity.Editor.Services.Transport;
namespace MCPForUnity.Editor.Services
{
@@ -23,11 +22,6 @@ public interface IBridgeControlService
///
bool IsAutoConnectMode { get; }
- ///
- /// Gets the currently active transport mode, if any
- ///
- TransportMode? ActiveMode { get; }
-
///
/// Starts the MCP for Unity Bridge asynchronously
///
@@ -47,7 +41,7 @@ public interface IBridgeControlService
BridgeVerificationResult Verify(int port);
///
- /// Verifies the connection asynchronously (works for both HTTP and stdio transports)
+ /// Verifies the connection asynchronously
///
/// Verification result with detailed status
Task VerifyAsync();
diff --git a/MCPForUnity/Editor/Services/MCPServiceLocator.cs b/MCPForUnity/Editor/Services/MCPServiceLocator.cs
index d537182f..e6ec8d38 100644
--- a/MCPForUnity/Editor/Services/MCPServiceLocator.cs
+++ b/MCPForUnity/Editor/Services/MCPServiceLocator.cs
@@ -11,23 +11,19 @@ namespace MCPForUnity.Editor.Services
public static class MCPServiceLocator
{
private static IBridgeControlService _bridgeService;
- private static IClientConfigurationService _clientService;
private static IPathResolverService _pathService;
private static ITestRunnerService _testRunnerService;
private static IPackageUpdateService _packageUpdateService;
private static IPlatformService _platformService;
private static IToolDiscoveryService _toolDiscoveryService;
- private static IServerManagementService _serverManagementService;
private static TransportManager _transportManager;
public static IBridgeControlService Bridge => _bridgeService ??= new BridgeControlService();
- public static IClientConfigurationService Client => _clientService ??= new ClientConfigurationService();
public static IPathResolverService Paths => _pathService ??= new PathResolverService();
public static ITestRunnerService Tests => _testRunnerService ??= new TestRunnerService();
public static IPackageUpdateService Updates => _packageUpdateService ??= new PackageUpdateService();
public static IPlatformService Platform => _platformService ??= new PlatformService();
public static IToolDiscoveryService ToolDiscovery => _toolDiscoveryService ??= new ToolDiscoveryService();
- public static IServerManagementService Server => _serverManagementService ??= new ServerManagementService();
public static TransportManager TransportManager => _transportManager ??= new TransportManager();
///
@@ -39,8 +35,6 @@ public static void Register(T implementation) where T : class
{
if (implementation is IBridgeControlService b)
_bridgeService = b;
- else if (implementation is IClientConfigurationService c)
- _clientService = c;
else if (implementation is IPathResolverService p)
_pathService = p;
else if (implementation is ITestRunnerService t)
@@ -51,8 +45,6 @@ public static void Register(T implementation) where T : class
_platformService = ps;
else if (implementation is IToolDiscoveryService td)
_toolDiscoveryService = td;
- else if (implementation is IServerManagementService sm)
- _serverManagementService = sm;
else if (implementation is TransportManager tm)
_transportManager = tm;
}
@@ -63,23 +55,19 @@ public static void Register(T implementation) where T : class
public static void Reset()
{
(_bridgeService as IDisposable)?.Dispose();
- (_clientService as IDisposable)?.Dispose();
(_pathService as IDisposable)?.Dispose();
(_testRunnerService as IDisposable)?.Dispose();
(_packageUpdateService as IDisposable)?.Dispose();
(_platformService as IDisposable)?.Dispose();
(_toolDiscoveryService as IDisposable)?.Dispose();
- (_serverManagementService as IDisposable)?.Dispose();
(_transportManager as IDisposable)?.Dispose();
_bridgeService = null;
- _clientService = null;
_pathService = null;
_testRunnerService = null;
_packageUpdateService = null;
_platformService = null;
_toolDiscoveryService = null;
- _serverManagementService = null;
_transportManager = null;
}
}
diff --git a/MCPForUnity/Editor/Services/StdioBridgeReloadHandler.cs b/MCPForUnity/Editor/Services/StdioBridgeReloadHandler.cs
index bb70ffb0..9ce5987f 100644
--- a/MCPForUnity/Editor/Services/StdioBridgeReloadHandler.cs
+++ b/MCPForUnity/Editor/Services/StdioBridgeReloadHandler.cs
@@ -1,14 +1,12 @@
using System;
using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
-using MCPForUnity.Editor.Services.Transport;
-using MCPForUnity.Editor.Services.Transport.Transports;
using UnityEditor;
namespace MCPForUnity.Editor.Services
{
///
- /// Ensures the legacy stdio bridge resumes after domain reloads, mirroring the HTTP handler.
+ /// Ensures the stdio bridge resumes after domain reloads.
///
[InitializeOnLoad]
internal static class StdioBridgeReloadHandler
@@ -23,17 +21,13 @@ 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)
+ // Persist resume intent when the bridge is running
+ bool isRunning = MCPServiceLocator.TransportManager.IsRunning();
+ if (isRunning)
{
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);
+ var stopTask = MCPServiceLocator.TransportManager.StopAsync();
stopTask.ContinueWith(t =>
{
if (t.IsFaulted && t.Exception != null)
@@ -59,8 +53,6 @@ private static void OnAfterAssemblyReload()
try
{
resume = EditorPrefs.GetBool(EditorPrefKeys.ResumeStdioAfterReload, false);
- bool useHttp = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
- resume = resume && !useHttp;
if (resume)
{
EditorPrefs.DeleteKey(EditorPrefKeys.ResumeStdioAfterReload);
@@ -82,7 +74,7 @@ private static void OnAfterAssemblyReload()
private static void TryStartBridgeImmediate()
{
- var startTask = MCPServiceLocator.TransportManager.StartAsync(TransportMode.Stdio);
+ var startTask = MCPServiceLocator.TransportManager.StartAsync();
startTask.ContinueWith(t =>
{
if (t.IsFaulted)
@@ -96,8 +88,6 @@ private static void TryStartBridgeImmediate()
McpLog.Warn("Failed to resume stdio bridge after domain reload");
return;
}
-
- MCPForUnity.Editor.Windows.MCPForUnityEditorWindow.RequestHealthVerification();
}, System.Threading.Tasks.TaskScheduler.Default);
}
}
diff --git a/MCPForUnity/Editor/Services/Transport/TransportManager.cs b/MCPForUnity/Editor/Services/Transport/TransportManager.cs
index d221ab83..5e6422ca 100644
--- a/MCPForUnity/Editor/Services/Transport/TransportManager.cs
+++ b/MCPForUnity/Editor/Services/Transport/TransportManager.cs
@@ -6,150 +6,81 @@
namespace MCPForUnity.Editor.Services.Transport
{
///
- /// Coordinates the active transport client and exposes lifecycle helpers.
+ /// Coordinates the stdio transport client and exposes lifecycle helpers.
///
public class TransportManager
{
- private IMcpTransportClient _httpClient;
private IMcpTransportClient _stdioClient;
- private TransportState _httpState = TransportState.Disconnected("http");
private TransportState _stdioState = TransportState.Disconnected("stdio");
- private Func _webSocketFactory;
- private Func _stdioFactory;
public TransportManager()
{
- Configure(
- () => new WebSocketTransportClient(MCPServiceLocator.ToolDiscovery),
- () => new StdioTransportClient());
+ _stdioClient = new StdioTransportClient();
}
- public IMcpTransportClient ActiveTransport => null; // Deprecated single-transport accessor
- public TransportMode? ActiveMode => null; // Deprecated single-transport accessor
+ public IMcpTransportClient ActiveTransport => _stdioClient;
- public void Configure(
- Func webSocketFactory,
- Func stdioFactory)
+ public async Task StartAsync()
{
- _webSocketFactory = webSocketFactory ?? throw new ArgumentNullException(nameof(webSocketFactory));
- _stdioFactory = stdioFactory ?? throw new ArgumentNullException(nameof(stdioFactory));
- }
-
- private IMcpTransportClient GetOrCreateClient(TransportMode mode)
- {
- return mode switch
- {
- TransportMode.Http => _httpClient ??= _webSocketFactory(),
- TransportMode.Stdio => _stdioClient ??= _stdioFactory(),
- _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unsupported transport mode"),
- };
- }
-
- private IMcpTransportClient GetClient(TransportMode mode)
- {
- return mode switch
+ if (_stdioClient == null)
{
- TransportMode.Http => _httpClient,
- TransportMode.Stdio => _stdioClient,
- _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unsupported transport mode"),
- };
- }
-
- public async Task StartAsync(TransportMode mode)
- {
- IMcpTransportClient client = GetOrCreateClient(mode);
+ _stdioClient = new StdioTransportClient();
+ }
- bool started = await client.StartAsync();
+ bool started = await _stdioClient.StartAsync();
if (!started)
{
try
{
- await client.StopAsync();
+ await _stdioClient.StopAsync();
}
catch (Exception ex)
{
- McpLog.Warn($"Error while stopping transport {client.TransportName}: {ex.Message}");
+ McpLog.Warn($"Error while stopping transport {_stdioClient.TransportName}: {ex.Message}");
}
- UpdateState(mode, TransportState.Disconnected(client.TransportName, "Failed to start"));
+ _stdioState = TransportState.Disconnected(_stdioClient.TransportName, "Failed to start");
return false;
}
- UpdateState(mode, client.State ?? TransportState.Connected(client.TransportName));
+ _stdioState = _stdioClient.State ?? TransportState.Connected(_stdioClient.TransportName);
return true;
}
- public async Task StopAsync(TransportMode? mode = null)
+ public async Task StopAsync()
{
- async Task StopClient(IMcpTransportClient client, TransportMode clientMode)
- {
- if (client == null) return;
- try { await client.StopAsync(); }
- catch (Exception ex) { McpLog.Warn($"Error while stopping transport {client.TransportName}: {ex.Message}"); }
- finally { UpdateState(clientMode, TransportState.Disconnected(client.TransportName)); }
+ if (_stdioClient == null) return;
+ try
+ {
+ await _stdioClient.StopAsync();
}
-
- if (mode == null)
- {
- await StopClient(_httpClient, TransportMode.Http);
- await StopClient(_stdioClient, TransportMode.Stdio);
- return;
+ catch (Exception ex)
+ {
+ McpLog.Warn($"Error while stopping transport {_stdioClient.TransportName}: {ex.Message}");
}
-
- if (mode == TransportMode.Http)
- {
- await StopClient(_httpClient, TransportMode.Http);
- }
- else
- {
- await StopClient(_stdioClient, TransportMode.Stdio);
+ finally
+ {
+ _stdioState = TransportState.Disconnected(_stdioClient.TransportName);
}
}
- public async Task VerifyAsync(TransportMode mode)
+ public async Task VerifyAsync()
{
- IMcpTransportClient client = GetClient(mode);
- if (client == null)
+ if (_stdioClient == null)
{
return false;
}
- bool ok = await client.VerifyAsync();
- var state = client.State ?? TransportState.Disconnected(client.TransportName, "No state reported");
- UpdateState(mode, state);
+ bool ok = await _stdioClient.VerifyAsync();
+ var state = _stdioClient.State ?? TransportState.Disconnected(_stdioClient.TransportName, "No state reported");
+ _stdioState = state;
return ok;
}
- public TransportState GetState(TransportMode mode)
+ public TransportState GetState()
{
- return mode switch
- {
- TransportMode.Http => _httpState,
- TransportMode.Stdio => _stdioState,
- _ => throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unsupported transport mode"),
- };
+ return _stdioState;
}
- public bool IsRunning(TransportMode mode) => GetState(mode).IsConnected;
-
- private void UpdateState(TransportMode mode, TransportState state)
- {
- switch (mode)
- {
- case TransportMode.Http:
- _httpState = state;
- break;
- case TransportMode.Stdio:
- _stdioState = state;
- break;
- default:
- throw new ArgumentOutOfRangeException(nameof(mode), mode, "Unsupported transport mode");
- }
- }
- }
-
- public enum TransportMode
- {
- Http,
- Stdio
+ public bool IsRunning() => _stdioState.IsConnected;
}
}
diff --git a/MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs b/MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs
index 31de7311..187e3bd5 100644
--- a/MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs
+++ b/MCPForUnity/Editor/Services/Transport/Transports/StdioBridgeHost.cs
@@ -208,15 +208,8 @@ private static void ScheduleInitRetry()
private static bool ShouldAutoStartBridge()
{
- try
- {
- bool useHttpTransport = EditorPrefs.GetBool(EditorPrefKeys.UseHttpTransport, true);
- return !useHttpTransport;
- }
- catch
- {
- return true;
- }
+ // Always auto-start stdio bridge when editor starts
+ return true;
}
private static void EnsureStartedOnEditorIdle()
diff --git a/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs b/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs
deleted file mode 100644
index 35011a80..00000000
--- a/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs
+++ /dev/null
@@ -1,691 +0,0 @@
-using System;
-using System.Buffers;
-using System.Collections.Generic;
-using System.IO;
-using System.Net.WebSockets;
-using System.Text;
-using System.Threading;
-using System.Threading.Tasks;
-using MCPForUnity.Editor.Config;
-using MCPForUnity.Editor.Helpers;
-using MCPForUnity.Editor.Services.Transport;
-using Newtonsoft.Json;
-using Newtonsoft.Json.Linq;
-using UnityEngine;
-
-namespace MCPForUnity.Editor.Services.Transport.Transports
-{
- ///
- /// Maintains a persistent WebSocket connection to the MCP server plugin hub.
- /// Handles registration, keep-alives, and command dispatch back into Unity via
- /// .
- ///
- public class WebSocketTransportClient : IMcpTransportClient, IDisposable
- {
- private const string TransportDisplayName = "websocket";
- private static readonly TimeSpan[] ReconnectSchedule =
- {
- TimeSpan.Zero,
- TimeSpan.FromSeconds(1),
- TimeSpan.FromSeconds(3),
- TimeSpan.FromSeconds(5),
- TimeSpan.FromSeconds(10),
- TimeSpan.FromSeconds(30)
- };
-
- private static readonly TimeSpan DefaultKeepAliveInterval = TimeSpan.FromSeconds(15);
- private static readonly TimeSpan DefaultCommandTimeout = TimeSpan.FromSeconds(30);
-
- private readonly IToolDiscoveryService _toolDiscoveryService;
- private ClientWebSocket _socket;
- private CancellationTokenSource _lifecycleCts;
- private CancellationTokenSource _connectionCts;
- private Task _receiveTask;
- private Task _keepAliveTask;
- private readonly SemaphoreSlim _sendLock = new(1, 1);
-
- private Uri _endpointUri;
- private string _sessionId;
- private string _projectHash;
- private string _projectName;
- private string _unityVersion;
- private TimeSpan _keepAliveInterval = DefaultKeepAliveInterval;
- private TimeSpan _socketKeepAliveInterval = DefaultKeepAliveInterval;
- private volatile bool _isConnected;
- private int _isReconnectingFlag;
- private TransportState _state = TransportState.Disconnected(TransportDisplayName, "Transport not started");
- private bool _disposed;
-
- public WebSocketTransportClient(IToolDiscoveryService toolDiscoveryService = null)
- {
- _toolDiscoveryService = toolDiscoveryService;
- }
-
- public bool IsConnected => _isConnected;
- public string TransportName => TransportDisplayName;
- public TransportState State => _state;
-
- public async Task StartAsync()
- {
- // Capture identity values on the main thread before any async context switching
- _projectName = ProjectIdentityUtility.GetProjectName();
- _projectHash = ProjectIdentityUtility.GetProjectHash();
- _unityVersion = Application.unityVersion;
-
- await StopAsync();
-
- _lifecycleCts = new CancellationTokenSource();
- _endpointUri = BuildWebSocketUri(HttpEndpointUtility.GetBaseUrl());
- _sessionId = null;
-
- if (!await EstablishConnectionAsync(_lifecycleCts.Token))
- {
- await StopAsync();
- return false;
- }
-
- // State is connected but session ID might be pending until 'registered' message
- _state = TransportState.Connected(TransportDisplayName, sessionId: "pending", details: _endpointUri.ToString());
- _isConnected = true;
- return true;
- }
-
- public async Task StopAsync()
- {
- if (_lifecycleCts == null)
- {
- return;
- }
-
- try
- {
- _lifecycleCts.Cancel();
- }
- catch { }
-
- await StopConnectionLoopsAsync().ConfigureAwait(false);
-
- if (_socket != null)
- {
- try
- {
- if (_socket.State == WebSocketState.Open || _socket.State == WebSocketState.CloseReceived)
- {
- await _socket.CloseAsync(WebSocketCloseStatus.NormalClosure, "Shutdown", CancellationToken.None).ConfigureAwait(false);
- }
- }
- catch { }
- finally
- {
- _socket.Dispose();
- _socket = null;
- }
- }
-
- _isConnected = false;
- _state = TransportState.Disconnected(TransportDisplayName);
-
- _lifecycleCts.Dispose();
- _lifecycleCts = null;
- }
-
- public async Task VerifyAsync()
- {
- if (_socket == null || _socket.State != WebSocketState.Open)
- {
- return false;
- }
-
- if (_lifecycleCts == null)
- {
- return false;
- }
-
- try
- {
- using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(_lifecycleCts.Token);
- timeoutCts.CancelAfter(TimeSpan.FromSeconds(5));
- await SendPongAsync(timeoutCts.Token).ConfigureAwait(false);
- return true;
- }
- catch (Exception ex)
- {
- McpLog.Warn($"[WebSocket] Verify ping failed: {ex.Message}");
- return false;
- }
- }
-
- public void Dispose()
- {
- if (_disposed)
- {
- return;
- }
-
- try
- {
- // Ensure background loops are stopped before disposing shared resources
- StopAsync().GetAwaiter().GetResult();
- }
- catch (Exception ex)
- {
- McpLog.Warn($"[WebSocket] Dispose failed to stop cleanly: {ex.Message}");
- }
-
- _sendLock?.Dispose();
- _socket?.Dispose();
- _lifecycleCts?.Dispose();
- _disposed = true;
- }
-
- private async Task EstablishConnectionAsync(CancellationToken token)
- {
- await StopConnectionLoopsAsync().ConfigureAwait(false);
-
- _connectionCts?.Dispose();
- _connectionCts = CancellationTokenSource.CreateLinkedTokenSource(token);
- CancellationToken connectionToken = _connectionCts.Token;
-
- _socket?.Dispose();
- _socket = new ClientWebSocket();
- _socket.Options.KeepAliveInterval = _socketKeepAliveInterval;
-
- try
- {
- await _socket.ConnectAsync(_endpointUri, connectionToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- McpLog.Error($"[WebSocket] Connection failed: {ex.Message}");
- return false;
- }
-
- StartBackgroundLoops(connectionToken);
-
- try
- {
- await SendRegisterAsync(connectionToken).ConfigureAwait(false);
- }
- catch (Exception ex)
- {
- McpLog.Error($"[WebSocket] Registration failed: {ex.Message}");
- return false;
- }
-
- return true;
- }
-
- ///
- /// Stops the connection loops and disposes of the connection CTS.
- /// Particularly useful when reconnecting, we want to ensure that background loops are cancelled correctly before starting new oens
- ///
- /// Whether to await the receive and keep alive tasks before disposing.
- private async Task StopConnectionLoopsAsync(bool awaitTasks = true)
- {
- if (_connectionCts != null && !_connectionCts.IsCancellationRequested)
- {
- try { _connectionCts.Cancel(); } catch { }
- }
-
- if (_receiveTask != null)
- {
- if (awaitTasks)
- {
- try { await _receiveTask.ConfigureAwait(false); } catch { }
- _receiveTask = null;
- }
- else if (_receiveTask.IsCompleted)
- {
- _receiveTask = null;
- }
- }
-
- if (_keepAliveTask != null)
- {
- if (awaitTasks)
- {
- try { await _keepAliveTask.ConfigureAwait(false); } catch { }
- _keepAliveTask = null;
- }
- else if (_keepAliveTask.IsCompleted)
- {
- _keepAliveTask = null;
- }
- }
-
- if (_connectionCts != null)
- {
- _connectionCts.Dispose();
- _connectionCts = null;
- }
- }
-
- private void StartBackgroundLoops(CancellationToken token)
- {
- if ((_receiveTask != null && !_receiveTask.IsCompleted) || (_keepAliveTask != null && !_keepAliveTask.IsCompleted))
- {
- return;
- }
-
- _receiveTask = Task.Run(() => ReceiveLoopAsync(token), CancellationToken.None);
- _keepAliveTask = Task.Run(() => KeepAliveLoopAsync(token), CancellationToken.None);
- }
-
- private async Task ReceiveLoopAsync(CancellationToken token)
- {
- while (!token.IsCancellationRequested)
- {
- try
- {
- string message = await ReceiveMessageAsync(token).ConfigureAwait(false);
- if (message == null)
- {
- continue;
- }
- await HandleMessageAsync(message, token).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- break;
- }
- catch (WebSocketException wse)
- {
- McpLog.Warn($"[WebSocket] Receive loop error: {wse.Message}");
- await HandleSocketClosureAsync(wse.Message).ConfigureAwait(false);
- break;
- }
- catch (Exception ex)
- {
- McpLog.Warn($"[WebSocket] Unexpected receive error: {ex.Message}");
- await HandleSocketClosureAsync(ex.Message).ConfigureAwait(false);
- break;
- }
- }
- }
-
- private async Task ReceiveMessageAsync(CancellationToken token)
- {
- if (_socket == null)
- {
- return null;
- }
-
- byte[] rentedBuffer = ArrayPool.Shared.Rent(8192);
- var buffer = new ArraySegment(rentedBuffer);
- using var ms = new MemoryStream(8192);
-
- try
- {
- while (!token.IsCancellationRequested)
- {
- WebSocketReceiveResult result = await _socket.ReceiveAsync(buffer, token).ConfigureAwait(false);
-
- if (result.MessageType == WebSocketMessageType.Close)
- {
- await HandleSocketClosureAsync(result.CloseStatusDescription ?? "Server closed connection").ConfigureAwait(false);
- return null;
- }
-
- if (result.Count > 0)
- {
- ms.Write(buffer.Array!, buffer.Offset, result.Count);
- }
-
- if (result.EndOfMessage)
- {
- break;
- }
- }
-
- if (ms.Length == 0)
- {
- return null;
- }
-
- return Encoding.UTF8.GetString(ms.ToArray());
- }
- finally
- {
- ArrayPool.Shared.Return(rentedBuffer);
- }
- }
-
- private async Task HandleMessageAsync(string message, CancellationToken token)
- {
- JObject payload;
- try
- {
- payload = JObject.Parse(message);
- }
- catch (Exception ex)
- {
- McpLog.Warn($"[WebSocket] Invalid JSON payload: {ex.Message}");
- return;
- }
-
- string messageType = payload.Value("type") ?? string.Empty;
-
- switch (messageType)
- {
- case "welcome":
- ApplyWelcome(payload);
- break;
- case "registered":
- await HandleRegisteredAsync(payload, token).ConfigureAwait(false);
- break;
- case "execute":
- await HandleExecuteAsync(payload, token).ConfigureAwait(false);
- break;
- case "ping":
- await SendPongAsync(token).ConfigureAwait(false);
- break;
- default:
- // No-op for unrecognised types (keep-alives, telemetry, etc.)
- break;
- }
- }
-
- private void ApplyWelcome(JObject payload)
- {
- int? keepAliveSeconds = payload.Value("keepAliveInterval");
- if (keepAliveSeconds.HasValue && keepAliveSeconds.Value > 0)
- {
- _keepAliveInterval = TimeSpan.FromSeconds(keepAliveSeconds.Value);
- _socketKeepAliveInterval = _keepAliveInterval;
- }
-
- int? serverTimeoutSeconds = payload.Value("serverTimeout");
- if (serverTimeoutSeconds.HasValue)
- {
- int sourceSeconds = keepAliveSeconds ?? serverTimeoutSeconds.Value;
- int safeSeconds = Math.Max(5, Math.Min(serverTimeoutSeconds.Value, sourceSeconds));
- _socketKeepAliveInterval = TimeSpan.FromSeconds(safeSeconds);
- }
- }
-
- private async Task HandleRegisteredAsync(JObject payload, CancellationToken token)
- {
- string newSessionId = payload.Value("session_id");
- if (!string.IsNullOrEmpty(newSessionId))
- {
- _sessionId = newSessionId;
- ProjectIdentityUtility.SetSessionId(_sessionId);
- _state = TransportState.Connected(TransportDisplayName, sessionId: _sessionId, details: _endpointUri.ToString());
- McpLog.Info($"[WebSocket] Registered with session ID: {_sessionId}");
-
- await SendRegisterToolsAsync(token).ConfigureAwait(false);
- }
- }
-
- private async Task SendRegisterToolsAsync(CancellationToken token)
- {
- if (_toolDiscoveryService == null) return;
-
- var tools = _toolDiscoveryService.DiscoverAllTools();
- var toolsArray = new JArray();
-
- foreach (var tool in tools)
- {
- var toolObj = new JObject
- {
- ["name"] = tool.Name,
- ["description"] = tool.Description,
- ["structured_output"] = tool.StructuredOutput,
- ["requires_polling"] = tool.RequiresPolling,
- ["poll_action"] = tool.PollAction
- };
-
- var paramsArray = new JArray();
- if (tool.Parameters != null)
- {
- foreach (var p in tool.Parameters)
- {
- paramsArray.Add(new JObject
- {
- ["name"] = p.Name,
- ["description"] = p.Description,
- ["type"] = p.Type,
- ["required"] = p.Required,
- ["default_value"] = p.DefaultValue
- });
- }
- }
- toolObj["parameters"] = paramsArray;
- toolsArray.Add(toolObj);
- }
-
- var payload = new JObject
- {
- ["type"] = "register_tools",
- ["tools"] = toolsArray
- };
-
- await SendJsonAsync(payload, token).ConfigureAwait(false);
- McpLog.Info($"[WebSocket] Sent {tools.Count} tools registration");
- }
-
- private async Task HandleExecuteAsync(JObject payload, CancellationToken token)
- {
- string commandId = payload.Value("id");
- string commandName = payload.Value("name");
- JObject parameters = payload.Value("params") ?? new JObject();
- int timeoutSeconds = payload.Value("timeout") ?? (int)DefaultCommandTimeout.TotalSeconds;
-
- if (string.IsNullOrEmpty(commandId) || string.IsNullOrEmpty(commandName))
- {
- McpLog.Warn("[WebSocket] Invalid execute payload (missing id or name)");
- return;
- }
-
- var commandEnvelope = new JObject
- {
- ["type"] = commandName,
- ["params"] = parameters
- };
-
- string responseJson;
- try
- {
- using var timeoutCts = CancellationTokenSource.CreateLinkedTokenSource(token);
- timeoutCts.CancelAfter(TimeSpan.FromSeconds(Math.Max(1, timeoutSeconds)));
- responseJson = await TransportCommandDispatcher.ExecuteCommandJsonAsync(commandEnvelope.ToString(Formatting.None), timeoutCts.Token).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- responseJson = JsonConvert.SerializeObject(new
- {
- status = "error",
- error = $"Command '{commandName}' timed out after {timeoutSeconds} seconds"
- });
- }
- catch (Exception ex)
- {
- responseJson = JsonConvert.SerializeObject(new
- {
- status = "error",
- error = ex.Message
- });
- }
-
- JToken resultToken;
- try
- {
- resultToken = JToken.Parse(responseJson);
- }
- catch
- {
- resultToken = new JObject
- {
- ["status"] = "error",
- ["error"] = "Invalid response payload"
- };
- }
-
- var responsePayload = new JObject
- {
- ["type"] = "command_result",
- ["id"] = commandId,
- ["result"] = resultToken
- };
-
- await SendJsonAsync(responsePayload, token).ConfigureAwait(false);
- }
-
- private async Task KeepAliveLoopAsync(CancellationToken token)
- {
- while (!token.IsCancellationRequested)
- {
- try
- {
- await Task.Delay(_keepAliveInterval, token).ConfigureAwait(false);
- if (_socket == null || _socket.State != WebSocketState.Open)
- {
- break;
- }
- await SendPongAsync(token).ConfigureAwait(false);
- }
- catch (OperationCanceledException)
- {
- break;
- }
- catch (Exception ex)
- {
- McpLog.Warn($"[WebSocket] Keep-alive failed: {ex.Message}");
- await HandleSocketClosureAsync(ex.Message).ConfigureAwait(false);
- break;
- }
- }
- }
-
- private async Task SendRegisterAsync(CancellationToken token)
- {
- var registerPayload = new JObject
- {
- ["type"] = "register",
- // session_id is now server-authoritative; omitted here or sent as null
- ["project_name"] = _projectName,
- ["project_hash"] = _projectHash,
- ["unity_version"] = _unityVersion
- };
-
- await SendJsonAsync(registerPayload, token).ConfigureAwait(false);
- }
-
- private Task SendPongAsync(CancellationToken token)
- {
- var payload = new JObject
- {
- ["type"] = "pong",
- };
- return SendJsonAsync(payload, token);
- }
-
- private async Task SendJsonAsync(JObject payload, CancellationToken token)
- {
- if (_socket == null)
- {
- throw new InvalidOperationException("WebSocket is not initialised");
- }
-
- string json = payload.ToString(Formatting.None);
- byte[] bytes = Encoding.UTF8.GetBytes(json);
- var buffer = new ArraySegment(bytes);
-
- await _sendLock.WaitAsync(token).ConfigureAwait(false);
- try
- {
- if (_socket.State != WebSocketState.Open)
- {
- throw new InvalidOperationException("WebSocket is not open");
- }
-
- await _socket.SendAsync(buffer, WebSocketMessageType.Text, true, token).ConfigureAwait(false);
- }
- finally
- {
- _sendLock.Release();
- }
- }
-
- private async Task HandleSocketClosureAsync(string reason)
- {
- if (_lifecycleCts == null || _lifecycleCts.IsCancellationRequested)
- {
- return;
- }
-
- if (Interlocked.CompareExchange(ref _isReconnectingFlag, 1, 0) != 0)
- {
- return;
- }
-
- _isConnected = false;
- _state = _state.WithError(reason ?? "Connection closed");
- McpLog.Warn($"[WebSocket] Connection closed: {reason}");
-
- await StopConnectionLoopsAsync(awaitTasks: false).ConfigureAwait(false);
-
- _ = Task.Run(() => AttemptReconnectAsync(_lifecycleCts.Token), CancellationToken.None);
- }
-
- private async Task AttemptReconnectAsync(CancellationToken token)
- {
- try
- {
- await StopConnectionLoopsAsync().ConfigureAwait(false);
-
- foreach (TimeSpan delay in ReconnectSchedule)
- {
- if (token.IsCancellationRequested)
- {
- return;
- }
-
- if (delay > TimeSpan.Zero)
- {
- try { await Task.Delay(delay, token).ConfigureAwait(false); }
- catch (OperationCanceledException) { return; }
- }
-
- if (await EstablishConnectionAsync(token).ConfigureAwait(false))
- {
- _state = TransportState.Connected(TransportDisplayName, sessionId: _sessionId, details: _endpointUri.ToString());
- _isConnected = true;
- McpLog.Info("[WebSocket] Reconnected to MCP server");
- return;
- }
- }
- }
- finally
- {
- Interlocked.Exchange(ref _isReconnectingFlag, 0);
- }
-
- _state = TransportState.Disconnected(TransportDisplayName, "Failed to reconnect");
- }
-
- private static Uri BuildWebSocketUri(string baseUrl)
- {
- if (string.IsNullOrWhiteSpace(baseUrl))
- {
- baseUrl = McpDistribution.Settings.defaultHttpBaseUrl;
- }
-
- if (!Uri.TryCreate(baseUrl, UriKind.Absolute, out var httpUri))
- {
- throw new InvalidOperationException($"Invalid MCP base URL: {baseUrl}");
- }
-
- string scheme = httpUri.Scheme.Equals("https", StringComparison.OrdinalIgnoreCase) ? "wss" : "ws";
- string builder = $"{scheme}://{httpUri.Authority}";
- if (!string.IsNullOrEmpty(httpUri.AbsolutePath) && httpUri.AbsolutePath != "/")
- {
- builder += httpUri.AbsolutePath.TrimEnd('/');
- }
-
- builder += "/hub/plugin";
-
- return new Uri(builder);
- }
- }
-}
diff --git a/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs.meta b/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs.meta
deleted file mode 100644
index 91b98e00..00000000
--- a/MCPForUnity/Editor/Services/Transport/Transports/WebSocketTransportClient.cs.meta
+++ /dev/null
@@ -1,11 +0,0 @@
-fileFormatVersion: 2
-guid: 044c8f7beb4af4a77a14d677190c21dc
-MonoImporter:
- externalObjects: {}
- serializedVersion: 2
- defaultReferences: []
- executionOrder: 0
- icon: {instanceID: 0}
- userData:
- assetBundleName:
- assetBundleVariant:
diff --git a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs
index 781d1c0e..010aa434 100644
--- a/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs
+++ b/MCPForUnity/Editor/Windows/Components/ClientConfig/McpClientConfigSection.cs
@@ -49,7 +49,9 @@ public class McpClientConfigSection
public McpClientConfigSection(VisualElement root)
{
Root = root;
- configurators = MCPServiceLocator.Client.GetAllClients().ToList();
+ // Client configuration service is no longer available
+ // This section should not be loaded, but handle gracefully if it is
+ configurators = new List();
CacheUIElements();
InitializeUI();
RegisterCallbacks();
@@ -184,51 +186,18 @@ private void UpdateClaudeCliPathVisibility()
private void OnConfigureAllClientsClicked()
{
- try
- {
- var summary = MCPServiceLocator.Client.ConfigureAllDetectedClients();
-
- string message = summary.GetSummaryMessage() + "\n\n";
- foreach (var msg in summary.Messages)
- {
- message += msg + "\n";
- }
-
- EditorUtility.DisplayDialog("Configure All Clients", message, "OK");
-
- if (selectedClientIndex >= 0 && selectedClientIndex < configurators.Count)
- {
- UpdateClientStatus();
- UpdateManualConfiguration();
- }
- }
- catch (Exception ex)
- {
- EditorUtility.DisplayDialog("Configuration Failed", ex.Message, "OK");
- }
+ EditorUtility.DisplayDialog("Client Configuration",
+ "Client configuration is no longer supported by this plugin.\n\n" +
+ "Please configure your MCP client (e.g., Cursor) directly using its configuration file (e.g., mcp.json).",
+ "OK");
}
private void OnConfigureClicked()
{
- if (selectedClientIndex < 0 || selectedClientIndex >= configurators.Count)
- return;
-
- var client = configurators[selectedClientIndex];
-
- try
- {
- MCPServiceLocator.Client.ConfigureClient(client);
- lastStatusChecks.Remove(client);
- RefreshClientStatus(client, forceImmediate: true);
- UpdateManualConfiguration();
- }
- catch (Exception ex)
- {
- clientStatusLabel.text = "Error";
- clientStatusLabel.style.color = Color.red;
- McpLog.Error($"Configuration failed: {ex.Message}");
- EditorUtility.DisplayDialog("Configuration Failed", ex.Message, "OK");
- }
+ EditorUtility.DisplayDialog("Client Configuration",
+ "Client configuration is no longer supported by this plugin.\n\n" +
+ "Please configure your MCP client (e.g., Cursor) directly using its configuration file (e.g., mcp.json).",
+ "OK");
}
private void OnBrowseClaudeClicked()
@@ -301,78 +270,24 @@ public void RefreshSelectedClient()
private void RefreshClientStatus(IMcpClientConfigurator client, bool forceImmediate = false)
{
- if (client is ClaudeCliMcpConfigurator)
- {
- RefreshClaudeCliStatus(client, forceImmediate);
- return;
- }
-
- if (forceImmediate || ShouldRefreshClient(client))
+ // Client configuration service is no longer available
+ // Just show not configured status
+ if (client is McpClientConfiguratorBase baseConfigurator)
{
- MCPServiceLocator.Client.CheckClientStatus(client);
- lastStatusChecks[client] = DateTime.UtcNow;
+ baseConfigurator.Client.SetStatus(McpStatus.NotConfigured, "Client configuration not supported");
}
-
ApplyStatusToUi(client);
}
private void RefreshClaudeCliStatus(IMcpClientConfigurator client, bool forceImmediate)
{
- if (forceImmediate)
- {
- MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false);
- lastStatusChecks[client] = DateTime.UtcNow;
- ApplyStatusToUi(client);
- return;
- }
-
- bool hasStatus = lastStatusChecks.ContainsKey(client);
- bool needsRefresh = !hasStatus || ShouldRefreshClient(client);
-
- if (!hasStatus)
- {
- ApplyStatusToUi(client, showChecking: true);
- }
- else
- {
- ApplyStatusToUi(client);
- }
-
- if (needsRefresh && !statusRefreshInFlight.Contains(client))
+ // Client configuration service is no longer available
+ // Just show not configured status
+ if (client is McpClientConfiguratorBase baseConfigurator)
{
- statusRefreshInFlight.Add(client);
- ApplyStatusToUi(client, showChecking: true);
-
- Task.Run(() =>
- {
- MCPServiceLocator.Client.CheckClientStatus(client, attemptAutoRewrite: false);
- }).ContinueWith(t =>
- {
- bool faulted = false;
- string errorMessage = null;
- if (t.IsFaulted && t.Exception != null)
- {
- var baseException = t.Exception.GetBaseException();
- errorMessage = baseException?.Message ?? "Status check failed";
- McpLog.Error($"Failed to refresh Claude CLI status: {errorMessage}");
- faulted = true;
- }
-
- EditorApplication.delayCall += () =>
- {
- statusRefreshInFlight.Remove(client);
- lastStatusChecks[client] = DateTime.UtcNow;
- if (faulted)
- {
- if (client is McpClientConfiguratorBase baseConfigurator)
- {
- baseConfigurator.Client.SetStatus(McpStatus.Error, errorMessage ?? "Status check failed");
- }
- }
- ApplyStatusToUi(client);
- };
- });
+ baseConfigurator.Client.SetStatus(McpStatus.NotConfigured, "Client configuration not supported");
}
+ ApplyStatusToUi(client);
}
private bool ShouldRefreshClient(IMcpClientConfigurator client)
diff --git a/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs b/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs
index 9b2cc933..bdc45377 100644
--- a/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs
+++ b/MCPForUnity/Editor/Windows/Components/Connection/McpConnectionSection.cs
@@ -1,49 +1,21 @@
-using System;
using System.Threading.Tasks;
-using MCPForUnity.Editor.Constants;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Services;
-using MCPForUnity.Editor.Services.Transport;
-using UnityEditor;
-using UnityEditor.UIElements;
-using UnityEngine;
using UnityEngine.UIElements;
namespace MCPForUnity.Editor.Windows.Components.Connection
{
///
/// Controller for the Connection section of the MCP For Unity editor window.
- /// Handles transport protocol, HTTP/stdio configuration, connection status, and health checks.
+ /// Handles stdio transport connection status and health checks.
///
public class McpConnectionSection
{
- // Transport protocol enum
- private enum TransportProtocol
- {
- HTTP,
- Stdio
- }
-
// UI Elements
- private EnumField transportDropdown;
- private VisualElement httpUrlRow;
- private VisualElement httpServerCommandSection;
- private TextField httpServerCommandField;
- private Button copyHttpServerCommandButton;
- private Label httpServerCommandHint;
- private TextField httpUrlField;
- private Button startHttpServerButton;
- private Button stopHttpServerButton;
- private VisualElement unitySocketPortRow;
- private TextField unityPortField;
- private VisualElement statusIndicator;
- private Label connectionStatusLabel;
- private Button connectionToggleButton;
private VisualElement healthIndicator;
private Label healthStatusLabel;
private Button testConnectionButton;
- private bool connectionToggleInProgress;
private Task verificationTask;
private string lastHealthStatus;
@@ -53,9 +25,6 @@ private enum TransportProtocol
private const string HealthStatusPingFailed = "Ping Failed";
private const string HealthStatusUnhealthy = "Unhealthy";
- // Events
- public event Action OnManualConfigUpdateRequested;
-
public VisualElement Root { get; private set; }
public McpConnectionSection(VisualElement root)
@@ -68,20 +37,6 @@ public McpConnectionSection(VisualElement root)
private void CacheUIElements()
{
- transportDropdown = Root.Q("transport-dropdown");
- httpUrlRow = Root.Q("http-url-row");
- httpServerCommandSection = Root.Q("http-server-command-section");
- httpServerCommandField = Root.Q("http-server-command");
- copyHttpServerCommandButton = Root.Q