Skip to content
Closed
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
4 changes: 2 additions & 2 deletions MCPForUnity/Editor/Helpers/AssetPathUtility.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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";
}

/// <summary>
Expand Down
7 changes: 0 additions & 7 deletions MCPForUnity/Editor/MenuItems/MCPForUnityMenu.cs
Original file line number Diff line number Diff line change
@@ -1,4 +1,3 @@
using MCPForUnity.Editor.Setup;
using MCPForUnity.Editor.Windows;
using UnityEditor;
using UnityEngine;
Expand All @@ -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()
{
Expand Down
13 changes: 3 additions & 10 deletions MCPForUnity/Editor/Migrations/LegacyServerSrcMigration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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)
{
Expand All @@ -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)
{
Expand Down
42 changes: 3 additions & 39 deletions MCPForUnity/Editor/Migrations/StdIoVersionMigration.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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<McpClientConfiguratorBase>().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)
Expand Down
2 changes: 1 addition & 1 deletion MCPForUnity/Editor/Resources/Editor/Selection.cs
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
65 changes: 21 additions & 44 deletions MCPForUnity/Editor/Services/BridgeControlService.cs
Original file line number Diff line number Diff line change
@@ -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
{
/// <summary>
/// Bridges the editor UI to the active transport (HTTP with WebSocket push, or stdio).
/// Bridges the editor UI to the stdio transport.
/// </summary>
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
Expand All @@ -53,45 +43,40 @@ public bool IsRunning
{
get
{
var mode = ResolvePreferredMode();
return _transportManager.IsRunning(mode);
return _transportManager.IsRunning();
}
}

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<bool> 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;
}
}
Expand All @@ -100,8 +85,7 @@ public async Task StopAsync()
{
try
{
var mode = ResolvePreferredMode();
await _transportManager.StopAsync(mode);
await _transportManager.StopAsync();
}
catch (Exception ex)
{
Expand All @@ -111,28 +95,21 @@ public async Task StopAsync()

public async Task<BridgeVerificationResult> 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);
}
Comment on lines 103 to 113
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Potential deadlock risk with synchronous blocking on async method.

Verify(int port) calls .GetAwaiter().GetResult() on an async method. In Unity's editor context with a SynchronizationContext, this pattern can cause deadlocks if the async continuation tries to marshal back to the main thread.

Consider either:

  1. Making Verify async and renaming the existing one
  2. Using Task.Run to avoid capturing the synchronization context
         public BridgeVerificationResult Verify(int port)
         {
-            bool pingSucceeded = _transportManager.VerifyAsync().GetAwaiter().GetResult();
+            // Avoid deadlock by running on thread pool without capturing sync context
+            bool pingSucceeded = Task.Run(() => _transportManager.VerifyAsync()).GetAwaiter().GetResult();
             var state = _transportManager.GetState();

             bool handshakeValid = state.IsConnected && port == CurrentPort;

Committable suggestion skipped: line range outside the PR's diff.


}
Expand Down
Loading