Skip to content

Commit 06f2719

Browse files
committed
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, VSCode
1 parent 2f387d3 commit 06f2719

File tree

3 files changed

+144
-64
lines changed

3 files changed

+144
-64
lines changed

UnityMcpBridge/Editor/Data/McpClients.cs

Lines changed: 37 additions & 32 deletions
Original file line numberDiff line numberDiff line change
@@ -9,24 +9,24 @@ public class McpClients
99
{
1010
public List<McpClient> clients = new()
1111
{
12+
// 1) Cursor
1213
new()
1314
{
14-
name = "Claude Desktop",
15+
name = "Cursor",
1516
windowsConfigPath = Path.Combine(
16-
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
17-
"Claude",
18-
"claude_desktop_config.json"
17+
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
18+
".cursor",
19+
"mcp.json"
1920
),
2021
linuxConfigPath = Path.Combine(
2122
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
22-
"Library",
23-
"Application Support",
24-
"Claude",
25-
"claude_desktop_config.json"
23+
".cursor",
24+
"mcp.json"
2625
),
27-
mcpType = McpTypes.ClaudeDesktop,
26+
mcpType = McpTypes.Cursor,
2827
configStatus = "Not Configured",
2928
},
29+
// 2) Claude Code
3030
new()
3131
{
3232
name = "Claude Code",
@@ -41,58 +41,63 @@ public class McpClients
4141
mcpType = McpTypes.ClaudeCode,
4242
configStatus = "Not Configured",
4343
},
44+
// 3) Windsurf
4445
new()
4546
{
46-
name = "Cursor",
47+
name = "Windsurf",
4748
windowsConfigPath = Path.Combine(
4849
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
49-
".cursor",
50-
"mcp.json"
50+
".codeium",
51+
"windsurf",
52+
"mcp_config.json"
5153
),
5254
linuxConfigPath = Path.Combine(
5355
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
54-
".cursor",
55-
"mcp.json"
56+
".codeium",
57+
"windsurf",
58+
"mcp_config.json"
5659
),
57-
mcpType = McpTypes.Cursor,
60+
mcpType = McpTypes.Windsurf,
5861
configStatus = "Not Configured",
5962
},
63+
// 4) Claude Desktop
6064
new()
6165
{
62-
name = "VSCode GitHub Copilot",
66+
name = "Claude Desktop",
6367
windowsConfigPath = Path.Combine(
6468
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
65-
"Code",
66-
"User",
67-
"settings.json"
69+
"Claude",
70+
"claude_desktop_config.json"
6871
),
6972
linuxConfigPath = Path.Combine(
7073
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
7174
"Library",
7275
"Application Support",
73-
"Code",
74-
"User",
75-
"settings.json"
76+
"Claude",
77+
"claude_desktop_config.json"
7678
),
77-
mcpType = McpTypes.VSCode,
79+
mcpType = McpTypes.ClaudeDesktop,
7880
configStatus = "Not Configured",
7981
},
82+
// 5) VSCode GitHub Copilot
8083
new()
8184
{
82-
name = "Windsurf",
85+
name = "VSCode GitHub Copilot",
8386
windowsConfigPath = Path.Combine(
84-
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
85-
".codeium",
86-
"windsurf",
87-
"mcp_config.json"
87+
Environment.GetFolderPath(Environment.SpecialFolder.ApplicationData),
88+
"Code",
89+
"User",
90+
"settings.json"
8891
),
8992
linuxConfigPath = Path.Combine(
9093
Environment.GetFolderPath(Environment.SpecialFolder.UserProfile),
91-
".codeium",
92-
"windsurf",
93-
"mcp_config.json"
94+
"Library",
95+
"Application Support",
96+
"Code",
97+
"User",
98+
"settings.json"
9499
),
95-
mcpType = McpTypes.Windsurf,
100+
mcpType = McpTypes.VSCode,
96101
configStatus = "Not Configured",
97102
},
98103
};

UnityMcpBridge/Editor/UnityMcpBridge.cs

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -45,17 +45,10 @@ public static void StartAutoConnect()
4545

4646
try
4747
{
48-
// Reuse stored project port when available to avoid port changes during setup
48+
// Prefer stored project port and start using the robust Start() path (with retries/options)
4949
currentUnityPort = PortManager.GetPortWithFallback();
50-
51-
listener = new TcpListener(IPAddress.Loopback, currentUnityPort);
52-
listener.Start();
53-
isRunning = true;
50+
Start();
5451
isAutoConnectMode = true;
55-
56-
Debug.Log($"UnityMcpBridge auto-connected on port {currentUnityPort}");
57-
Task.Run(ListenerLoop);
58-
EditorApplication.update += ProcessCommands;
5952
}
6053
catch (Exception ex)
6154
{
@@ -226,11 +219,12 @@ public static void Stop()
226219

227220
try
228221
{
222+
// Mark as stopping early to avoid accept logging during disposal
223+
isRunning = false;
229224
// Mark heartbeat one last time before stopping
230225
WriteHeartbeat(false);
231226
listener?.Stop();
232227
listener = null;
233-
isRunning = false;
234228
EditorApplication.update -= ProcessCommands;
235229
Debug.Log("UnityMcpBridge stopped.");
236230
}
@@ -261,6 +255,14 @@ private static async Task ListenerLoop()
261255
// Fire and forget each client connection
262256
_ = HandleClientAsync(client);
263257
}
258+
catch (ObjectDisposedException)
259+
{
260+
// Listener was disposed during stop/reload; exit quietly
261+
if (!isRunning)
262+
{
263+
break;
264+
}
265+
}
264266
catch (Exception ex)
265267
{
266268
if (isRunning)

UnityMcpBridge/Editor/Windows/UnityMcpEditorWindow.cs

Lines changed: 95 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -29,6 +29,7 @@ public class UnityMcpEditorWindow : EditorWindow
2929
private bool lastClientRegisteredOk;
3030
private bool lastBridgeVerifiedOk;
3131
private string pythonDirOverride = null;
32+
private bool debugLogsEnabled;
3233

3334
// Script validation settings
3435
private int validationLevelIndex = 1; // Default to Standard
@@ -56,6 +57,7 @@ private void OnEnable()
5657
// Refresh bridge status
5758
isUnityBridgeRunning = UnityMcpBridge.IsRunning;
5859
autoRegisterEnabled = EditorPrefs.GetBool("UnityMCP.AutoRegisterEnabled", true);
60+
debugLogsEnabled = EditorPrefs.GetBool("UnityMCP.DebugLogs", false);
5961
foreach (McpClient mcpClient in mcpClients.clients)
6062
{
6163
CheckMcpConfiguration(mcpClient);
@@ -148,14 +150,50 @@ private void OnGUI()
148150
// Header
149151
DrawHeader();
150152

151-
// Single-column streamlined layout
152-
DrawServerStatusSection();
153-
EditorGUILayout.Space(6);
154-
DrawBridgeSection();
155-
EditorGUILayout.Space(10);
156-
DrawUnifiedClientConfiguration();
153+
// Compute equal column widths for uniform layout
154+
float horizontalSpacing = 2f;
155+
float outerPadding = 20f; // approximate padding
156+
// Make columns a bit less wide for a tighter layout
157+
float computed = (position.width - outerPadding - horizontalSpacing) / 2f;
158+
float colWidth = Mathf.Clamp(computed, 220f, 340f);
159+
// Use fixed heights per row so paired panels match exactly
160+
float topPanelHeight = 190f;
161+
float bottomPanelHeight = 230f;
162+
163+
// Top row: Server Status (left) and Unity Bridge (right)
164+
EditorGUILayout.BeginHorizontal();
165+
{
166+
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(topPanelHeight));
167+
DrawServerStatusSection();
168+
EditorGUILayout.EndVertical();
169+
170+
EditorGUILayout.Space(horizontalSpacing);
171+
172+
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(topPanelHeight));
173+
DrawBridgeSection();
174+
EditorGUILayout.EndVertical();
175+
}
176+
EditorGUILayout.EndHorizontal();
177+
157178
EditorGUILayout.Space(10);
158-
DrawValidationSection();
179+
180+
// Second row: MCP Client Configuration (left) and Script Validation (right)
181+
EditorGUILayout.BeginHorizontal();
182+
{
183+
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(bottomPanelHeight));
184+
DrawUnifiedClientConfiguration();
185+
EditorGUILayout.EndVertical();
186+
187+
EditorGUILayout.Space(horizontalSpacing);
188+
189+
EditorGUILayout.BeginVertical(GUILayout.Width(colWidth), GUILayout.Height(bottomPanelHeight));
190+
DrawValidationSection();
191+
EditorGUILayout.EndVertical();
192+
}
193+
EditorGUILayout.EndHorizontal();
194+
195+
// Minimal bottom padding
196+
EditorGUILayout.Space(2);
159197

160198
EditorGUILayout.EndScrollView();
161199
}
@@ -205,21 +243,12 @@ private void DrawServerStatusSection()
205243

206244
EditorGUILayout.Space(5);
207245

208-
// Connection mode and Setup controls
246+
// Connection mode
209247
EditorGUILayout.BeginHorizontal();
210-
211248
bool isAutoMode = UnityMcpBridge.IsAutoConnectMode();
212249
GUIStyle modeStyle = new GUIStyle(EditorStyles.miniLabel) { fontSize = 11 };
213250
EditorGUILayout.LabelField($"Mode: {(isAutoMode ? "Auto" : "Standard")}", modeStyle);
214-
215251
GUILayout.FlexibleSpace();
216-
217-
// Bind to Clients button
218-
if (GUILayout.Button("Bind to Clients", GUILayout.Width(140), GUILayout.Height(24)))
219-
{
220-
RunSetupNow();
221-
}
222-
223252
EditorGUILayout.EndHorizontal();
224253

225254
// Current ports display
@@ -231,6 +260,27 @@ private void DrawServerStatusSection()
231260
EditorGUILayout.LabelField($"Ports: Unity {currentUnityPort}, MCP {mcpPort}", portStyle);
232261
EditorGUILayout.Space(5);
233262

263+
// Auto-Setup button below ports
264+
string setupButtonText = (lastClientRegisteredOk && lastBridgeVerifiedOk) ? "Connected ✓" : "Auto-Setup";
265+
if (GUILayout.Button(setupButtonText, GUILayout.Height(24)))
266+
{
267+
RunSetupNow();
268+
}
269+
EditorGUILayout.Space(4);
270+
271+
// Debug logs toggle inside Server Status
272+
using (new EditorGUILayout.HorizontalScope())
273+
{
274+
GUILayout.FlexibleSpace();
275+
bool newDebug = EditorGUILayout.ToggleLeft("Show Debug Logs", debugLogsEnabled, GUILayout.Width(150));
276+
if (newDebug != debugLogsEnabled)
277+
{
278+
debugLogsEnabled = newDebug;
279+
EditorPrefs.SetBool("UnityMCP.DebugLogs", debugLogsEnabled);
280+
}
281+
}
282+
EditorGUILayout.Space(2);
283+
234284
// Removed redundant inline badges to streamline UI
235285

236286
// Troubleshooting helpers
@@ -318,7 +368,18 @@ private void DrawValidationSection()
318368
EditorGUILayout.Space(8);
319369
string description = GetValidationLevelDescription(validationLevelIndex);
320370
EditorGUILayout.HelpBox(description, MessageType.Info);
321-
EditorGUILayout.Space(5);
371+
EditorGUILayout.Space(4);
372+
// Inline debug logs toggle under Script Validation
373+
EditorGUILayout.BeginHorizontal();
374+
GUILayout.FlexibleSpace();
375+
bool newDebug = EditorGUILayout.ToggleLeft("Show Debug Logs", debugLogsEnabled, GUILayout.Width(150));
376+
if (newDebug != debugLogsEnabled)
377+
{
378+
debugLogsEnabled = newDebug;
379+
EditorPrefs.SetBool("UnityMCP.DebugLogs", debugLogsEnabled);
380+
}
381+
EditorGUILayout.EndHorizontal();
382+
EditorGUILayout.Space(2);
322383
EditorGUILayout.EndVertical();
323384
}
324385

@@ -870,7 +931,10 @@ private string FindPackagePythonDirectory()
870931
{
871932
if (Directory.Exists(devPath) && File.Exists(Path.Combine(devPath, "server.py")))
872933
{
873-
UnityEngine.Debug.Log($"Currently in development mode. Package: {devPath}");
934+
if (debugLogsEnabled)
935+
{
936+
UnityEngine.Debug.Log($"Currently in development mode. Package: {devPath}");
937+
}
874938
return devPath;
875939
}
876940
}
@@ -931,7 +995,10 @@ private string FindPackagePythonDirectory()
931995
}
932996

933997
// If still not found, return the placeholder path
934-
UnityEngine.Debug.LogWarning("Could not find Python directory, using placeholder path");
998+
if (debugLogsEnabled)
999+
{
1000+
UnityEngine.Debug.LogWarning("Could not find Python directory, using placeholder path");
1001+
}
9351002
}
9361003
catch (Exception e)
9371004
{
@@ -1319,7 +1386,10 @@ private void RegisterWithClaudeCode(string pythonDir)
13191386
}
13201387
else if (!string.IsNullOrEmpty(errors))
13211388
{
1322-
UnityEngine.Debug.LogWarning($"Claude MCP errors: {errors}");
1389+
if (debugLogsEnabled)
1390+
{
1391+
UnityEngine.Debug.LogWarning($"Claude MCP errors: {errors}");
1392+
}
13231393
}
13241394
}
13251395
catch (Exception e)
@@ -1806,7 +1876,10 @@ private void CheckClaudeCodeConfiguration(McpClient mcpClient)
18061876
? mcpClient.windowsConfigPath
18071877
: mcpClient.linuxConfigPath;
18081878

1809-
UnityEngine.Debug.Log($"Checking Claude config at: {configPath}");
1879+
if (debugLogsEnabled)
1880+
{
1881+
UnityEngine.Debug.Log($"Checking Claude config at: {configPath}");
1882+
}
18101883

18111884
if (!File.Exists(configPath))
18121885
{

0 commit comments

Comments
 (0)