feat(editor): 2x2 layout (Server/Bridge | Clients/Validation), Auto-Setup with Connected ✓ state; add Debug Logs toggle and gate verbose logs
fix(bridge): reuse stored port in StartAutoConnect; guard listener stop to avoid ObjectDisposedException chore(clients): reorder dropdown to Cursor, Claude Code, Windsurf, Claude Desktop, VSCodemain
parent
2f387d3417
commit
06f271926b
|
|
@ -9,6 +9,58 @@ namespace UnityMcpBridge.Editor.Data
|
|||
{
|
||||
public List<McpClient> clients = new()
|
||||
{
|
||||
// 1) Cursor
|
||||
new()
|
||||
{
|
||||
name = "Cursor",
|
||||
windowsConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".cursor",
|
||||
"mcp.json"
|
||||
),
|
||||
linuxConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".cursor",
|
||||
"mcp.json"
|
||||
),
|
||||
mcpType = McpTypes.Cursor,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
// 2) Claude Code
|
||||
new()
|
||||
{
|
||||
name = "Claude Code",
|
||||
windowsConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".claude.json"
|
||||
),
|
||||
linuxConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".claude.json"
|
||||
),
|
||||
mcpType = McpTypes.ClaudeCode,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
// 3) Windsurf
|
||||
new()
|
||||
{
|
||||
name = "Windsurf",
|
||||
windowsConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".codeium",
|
||||
"windsurf",
|
||||
"mcp_config.json"
|
||||
),
|
||||
linuxConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".codeium",
|
||||
"windsurf",
|
||||
"mcp_config.json"
|
||||
),
|
||||
mcpType = McpTypes.Windsurf,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
// 4) Claude Desktop
|
||||
new()
|
||||
{
|
||||
name = "Claude Desktop",
|
||||
|
|
@ -27,36 +79,7 @@ namespace UnityMcpBridge.Editor.Data
|
|||
mcpType = McpTypes.ClaudeDesktop,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "Claude Code",
|
||||
windowsConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".claude.json"
|
||||
),
|
||||
linuxConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".claude.json"
|
||||
),
|
||||
mcpType = McpTypes.ClaudeCode,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "Cursor",
|
||||
windowsConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".cursor",
|
||||
"mcp.json"
|
||||
),
|
||||
linuxConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".cursor",
|
||||
"mcp.json"
|
||||
),
|
||||
mcpType = McpTypes.Cursor,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
// 5) VSCode GitHub Copilot
|
||||
new()
|
||||
{
|
||||
name = "VSCode GitHub Copilot",
|
||||
|
|
@ -77,24 +100,6 @@ namespace UnityMcpBridge.Editor.Data
|
|||
mcpType = McpTypes.VSCode,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
new()
|
||||
{
|
||||
name = "Windsurf",
|
||||
windowsConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".codeium",
|
||||
"windsurf",
|
||||
"mcp_config.json"
|
||||
),
|
||||
linuxConfigPath = Path.Combine(
|
||||
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
|
||||
".codeium",
|
||||
"windsurf",
|
||||
"mcp_config.json"
|
||||
),
|
||||
mcpType = McpTypes.Windsurf,
|
||||
configStatus = "Not Configured",
|
||||
},
|
||||
};
|
||||
|
||||
// Initialize status enums after construction
|
||||
|
|
|
|||
|
|
@ -45,17 +45,10 @@ namespace UnityMcpBridge.Editor
|
|||
|
||||
try
|
||||
{
|
||||
// Reuse stored project port when available to avoid port changes during setup
|
||||
// Prefer stored project port and start using the robust Start() path (with retries/options)
|
||||
currentUnityPort = PortManager.GetPortWithFallback();
|
||||
|
||||
listener = new TcpListener(IPAddress.Loopback, currentUnityPort);
|
||||
listener.Start();
|
||||
isRunning = true;
|
||||
Start();
|
||||
isAutoConnectMode = true;
|
||||
|
||||
Debug.Log($"UnityMcpBridge auto-connected on port {currentUnityPort}");
|
||||
Task.Run(ListenerLoop);
|
||||
EditorApplication.update += ProcessCommands;
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
|
|
@ -226,11 +219,12 @@ namespace UnityMcpBridge.Editor
|
|||
|
||||
try
|
||||
{
|
||||
// Mark as stopping early to avoid accept logging during disposal
|
||||
isRunning = false;
|
||||
// Mark heartbeat one last time before stopping
|
||||
WriteHeartbeat(false);
|
||||
listener?.Stop();
|
||||
listener = null;
|
||||
isRunning = false;
|
||||
EditorApplication.update -= ProcessCommands;
|
||||
Debug.Log("UnityMcpBridge stopped.");
|
||||
}
|
||||
|
|
@ -261,6 +255,14 @@ namespace UnityMcpBridge.Editor
|
|||
// Fire and forget each client connection
|
||||
_ = HandleClientAsync(client);
|
||||
}
|
||||
catch (ObjectDisposedException)
|
||||
{
|
||||
// Listener was disposed during stop/reload; exit quietly
|
||||
if (!isRunning)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
catch (Exception ex)
|
||||
{
|
||||
if (isRunning)
|
||||
|
|
|
|||
|
|
@ -29,6 +29,7 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
private bool lastClientRegisteredOk;
|
||||
private bool lastBridgeVerifiedOk;
|
||||
private string pythonDirOverride = null;
|
||||
private bool debugLogsEnabled;
|
||||
|
||||
// Script validation settings
|
||||
private int validationLevelIndex = 1; // Default to Standard
|
||||
|
|
@ -56,6 +57,7 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
// Refresh bridge status
|
||||
isUnityBridgeRunning = UnityMcpBridge.IsRunning;
|
||||
autoRegisterEnabled = EditorPrefs.GetBool("UnityMCP.AutoRegisterEnabled", true);
|
||||
debugLogsEnabled = EditorPrefs.GetBool("UnityMCP.DebugLogs", false);
|
||||
foreach (McpClient mcpClient in mcpClients.clients)
|
||||
{
|
||||
CheckMcpConfiguration(mcpClient);
|
||||
|
|
@ -148,14 +150,50 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
// Header
|
||||
DrawHeader();
|
||||
|
||||
// Single-column streamlined layout
|
||||
// Compute equal column widths for uniform layout
|
||||
float horizontalSpacing = 2f;
|
||||
float outerPadding = 20f; // approximate padding
|
||||
// Make columns a bit less wide for a tighter layout
|
||||
float computed = (position.width - outerPadding - horizontalSpacing) / 2f;
|
||||
float colWidth = Mathf.Clamp(computed, 220f, 340f);
|
||||
// Use fixed heights per row so paired panels match exactly
|
||||
float topPanelHeight = 190f;
|
||||
float bottomPanelHeight = 230f;
|
||||
|
||||
// Top row: Server Status (left) and Unity Bridge (right)
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
{
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(topPanelHeight));
|
||||
DrawServerStatusSection();
|
||||
EditorGUILayout.Space(6);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(horizontalSpacing);
|
||||
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(topPanelHeight));
|
||||
DrawBridgeSection();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
EditorGUILayout.Space(10);
|
||||
|
||||
// Second row: MCP Client Configuration (left) and Script Validation (right)
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
{
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(bottomPanelHeight));
|
||||
DrawUnifiedClientConfiguration();
|
||||
EditorGUILayout.Space(10);
|
||||
EditorGUILayout.EndVertical();
|
||||
|
||||
EditorGUILayout.Space(horizontalSpacing);
|
||||
|
||||
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(bottomPanelHeight));
|
||||
DrawValidationSection();
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// Minimal bottom padding
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
EditorGUILayout.EndScrollView();
|
||||
}
|
||||
|
|
@ -205,21 +243,12 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Connection mode and Setup controls
|
||||
// Connection mode
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
|
||||
bool isAutoMode = UnityMcpBridge.IsAutoConnectMode();
|
||||
GUIStyle modeStyle = new GUIStyle(EditorStyles.miniLabel) { fontSize = 11 };
|
||||
EditorGUILayout.LabelField($"Mode: {(isAutoMode ? "Auto" : "Standard")}", modeStyle);
|
||||
|
||||
GUILayout.FlexibleSpace();
|
||||
|
||||
// Bind to Clients button
|
||||
if (GUILayout.Button("Bind to Clients", GUILayout.Width(140), GUILayout.Height(24)))
|
||||
{
|
||||
RunSetupNow();
|
||||
}
|
||||
|
||||
EditorGUILayout.EndHorizontal();
|
||||
|
||||
// Current ports display
|
||||
|
|
@ -231,6 +260,27 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
EditorGUILayout.LabelField($"Ports: Unity {currentUnityPort}, MCP {mcpPort}", portStyle);
|
||||
EditorGUILayout.Space(5);
|
||||
|
||||
// Auto-Setup button below ports
|
||||
string setupButtonText = (lastClientRegisteredOk && lastBridgeVerifiedOk) ? "Connected ✓" : "Auto-Setup";
|
||||
if (GUILayout.Button(setupButtonText, GUILayout.Height(24)))
|
||||
{
|
||||
RunSetupNow();
|
||||
}
|
||||
EditorGUILayout.Space(4);
|
||||
|
||||
// Debug logs toggle inside Server Status
|
||||
using (new EditorGUILayout.HorizontalScope())
|
||||
{
|
||||
GUILayout.FlexibleSpace();
|
||||
bool newDebug = EditorGUILayout.ToggleLeft("Show Debug Logs", debugLogsEnabled, GUILayout.Width(150));
|
||||
if (newDebug != debugLogsEnabled)
|
||||
{
|
||||
debugLogsEnabled = newDebug;
|
||||
EditorPrefs.SetBool("UnityMCP.DebugLogs", debugLogsEnabled);
|
||||
}
|
||||
}
|
||||
EditorGUILayout.Space(2);
|
||||
|
||||
// Removed redundant inline badges to streamline UI
|
||||
|
||||
// Troubleshooting helpers
|
||||
|
|
@ -318,7 +368,18 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
EditorGUILayout.Space(8);
|
||||
string description = GetValidationLevelDescription(validationLevelIndex);
|
||||
EditorGUILayout.HelpBox(description, MessageType.Info);
|
||||
EditorGUILayout.Space(5);
|
||||
EditorGUILayout.Space(4);
|
||||
// Inline debug logs toggle under Script Validation
|
||||
EditorGUILayout.BeginHorizontal();
|
||||
GUILayout.FlexibleSpace();
|
||||
bool newDebug = EditorGUILayout.ToggleLeft("Show Debug Logs", debugLogsEnabled, GUILayout.Width(150));
|
||||
if (newDebug != debugLogsEnabled)
|
||||
{
|
||||
debugLogsEnabled = newDebug;
|
||||
EditorPrefs.SetBool("UnityMCP.DebugLogs", debugLogsEnabled);
|
||||
}
|
||||
EditorGUILayout.EndHorizontal();
|
||||
EditorGUILayout.Space(2);
|
||||
EditorGUILayout.EndVertical();
|
||||
}
|
||||
|
||||
|
|
@ -869,8 +930,11 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
foreach (string devPath in devPaths)
|
||||
{
|
||||
if (Directory.Exists(devPath) && File.Exists(Path.Combine(devPath, "server.py")))
|
||||
{
|
||||
if (debugLogsEnabled)
|
||||
{
|
||||
UnityEngine.Debug.Log($"Currently in development mode. Package: {devPath}");
|
||||
}
|
||||
return devPath;
|
||||
}
|
||||
}
|
||||
|
|
@ -931,8 +995,11 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
}
|
||||
|
||||
// If still not found, return the placeholder path
|
||||
if (debugLogsEnabled)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning("Could not find Python directory, using placeholder path");
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Error finding package path: {e.Message}");
|
||||
|
|
@ -1318,10 +1385,13 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
|
||||
}
|
||||
else if (!string.IsNullOrEmpty(errors))
|
||||
{
|
||||
if (debugLogsEnabled)
|
||||
{
|
||||
UnityEngine.Debug.LogWarning($"Claude MCP errors: {errors}");
|
||||
}
|
||||
}
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
UnityEngine.Debug.LogError($"Claude CLI registration failed: {e.Message}");
|
||||
|
|
@ -1806,7 +1876,10 @@ namespace UnityMcpBridge.Editor.Windows
|
|||
? mcpClient.windowsConfigPath
|
||||
: mcpClient.linuxConfigPath;
|
||||
|
||||
if (debugLogsEnabled)
|
||||
{
|
||||
UnityEngine.Debug.Log($"Checking Claude config at: {configPath}");
|
||||
}
|
||||
|
||||
if (!File.Exists(configPath))
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue