This is page 2 of 2. Use http://codebase.md/quazaai/unitymcpintegration?lines=true&page={x} to view the full context.
# Directory Structure
```
├── .gitignore
├── CHANGELOG.md
├── CHANGELOG.md.meta
├── CODE_OF_CONDUCT.md
├── CODE_OF_CONDUCT.md.meta
├── Editor
│ ├── GamePilot.UnityMCP.asmdef
│ ├── GamePilot.UnityMCP.asmdef.meta
│ ├── MCPCodeExecutor.cs
│ ├── MCPCodeExecutor.cs.meta
│ ├── MCPConnectionManager.cs
│ ├── MCPConnectionManager.cs.meta
│ ├── MCPDataCollector.cs
│ ├── MCPDataCollector.cs.meta
│ ├── MCPLogger.cs
│ ├── MCPLogger.cs.meta
│ ├── MCPManager.cs
│ ├── MCPManager.cs.meta
│ ├── MCPMessageHandler.cs
│ ├── MCPMessageHandler.cs.meta
│ ├── MCPMessageSender.cs
│ ├── MCPMessageSender.cs.meta
│ ├── Models
│ │ ├── MCPEditorState.cs
│ │ ├── MCPEditorState.cs.meta
│ │ ├── MCPSceneInfo.cs
│ │ └── MCPSceneInfo.cs.meta
│ ├── Models.meta
│ ├── UI
│ │ ├── MCPDebugSettings.json
│ │ ├── MCPDebugSettings.json.meta
│ │ ├── MCPDebugWindow.cs
│ │ ├── MCPDebugWindow.cs.meta
│ │ ├── MCPDebugWindow.uss
│ │ ├── MCPDebugWindow.uss.meta
│ │ ├── MCPDebugWindow.uxml
│ │ └── MCPDebugWindow.uxml.meta
│ └── UI.meta
├── Editor.meta
├── LICENSE
├── LICENSE.meta
├── mcpInspector.png
├── mcpInspector.png.meta
├── mcpServer
│ ├── .dockerignore
│ ├── .env.example
│ ├── build
│ │ ├── filesystemTools.js
│ │ ├── filesystemTools.js.meta
│ │ ├── index.js
│ │ ├── index.js.meta
│ │ ├── toolDefinitions.js
│ │ ├── toolDefinitions.js.meta
│ │ ├── types.js
│ │ ├── types.js.meta
│ │ ├── websocketHandler.js
│ │ └── websocketHandler.js.meta
│ ├── build.meta
│ ├── docker-compose.yml
│ ├── docker-compose.yml.meta
│ ├── Dockerfile
│ ├── Dockerfile.meta
│ ├── MCPSummary.md
│ ├── MCPSummary.md.meta
│ ├── node_modules.meta
│ ├── package-lock.json
│ ├── package-lock.json.meta
│ ├── package.json
│ ├── package.json.meta
│ ├── smithery.yaml
│ ├── src
│ │ ├── filesystemTools.ts
│ │ ├── filesystemTools.ts.meta
│ │ ├── index.ts
│ │ ├── index.ts.meta
│ │ ├── toolDefinitions.ts
│ │ ├── toolDefinitions.ts.meta
│ │ ├── types.ts
│ │ ├── types.ts.meta
│ │ ├── websocketHandler.ts
│ │ └── websocketHandler.ts.meta
│ ├── src.meta
│ ├── tsconfig.json
│ └── tsconfig.json.meta
├── mcpServer.meta
├── package.json
├── package.json.meta
├── README.md
└── README.md.meta
```
# Files
--------------------------------------------------------------------------------
/Editor/UI/MCPDebugWindow.cs:
--------------------------------------------------------------------------------
```csharp
1 | using UnityEditor;
2 | using UnityEditor.UIElements;
3 | using UnityEngine;
4 | using UnityEngine.UIElements;
5 | using System;
6 | using System.Collections.Generic;
7 | using System.IO;
8 | using Newtonsoft.Json;
9 |
10 | namespace Plugins.GamePilot.Editor.MCP
11 | {
12 | [Serializable]
13 | public class MCPDebugSettings
14 | {
15 | public int port = 5010;
16 | public bool autoReconnect = false;
17 | public bool globalLoggingEnabled = false;
18 | public Dictionary<string, bool> componentLoggingEnabled = new Dictionary<string, bool>();
19 | }
20 |
21 | public class MCPDebugWindow : EditorWindow
22 | {
23 | [SerializeField]
24 | private VisualTreeAsset uxml;
25 | [SerializeField]
26 | private StyleSheet uss;
27 |
28 | [SerializeField]
29 | private VisualTreeAsset m_VisualTreeAsset = default;
30 |
31 | private Label connectionStatusLabel;
32 | private Button connectButton;
33 | private Button disconnectButton;
34 | private Toggle autoReconnectToggle;
35 | private TextField serverPortField;
36 |
37 | // Component logging toggles
38 | private Dictionary<string, Toggle> logToggles = new Dictionary<string, Toggle>();
39 |
40 | // Connection info labels
41 | private Label lastErrorLabel;
42 | private Label connectionTimeLabel;
43 |
44 | // Statistics elements
45 | private Label messagesSentLabel;
46 | private Label messagesReceivedLabel;
47 | private Label reconnectAttemptsLabel;
48 |
49 | // Statistics counters
50 | private int messagesSent = 0;
51 | private int messagesReceived = 0;
52 | private int reconnectAttempts = 0;
53 | private DateTime? connectionStartTime = null;
54 |
55 | // Settings
56 | private MCPDebugSettings settings;
57 | private string settingsPath;
58 |
59 | [MenuItem("Window/MCP Debug")]
60 | public static void ShowWindow()
61 | {
62 | MCPDebugWindow wnd = GetWindow<MCPDebugWindow>();
63 | wnd.titleContent = new GUIContent("MCP Debug");
64 | wnd.minSize = new Vector2(400, 500);
65 | }
66 |
67 | private void OnEnable()
68 | {
69 | // Get the path to save settings
70 | settingsPath = GetSettingsPath();
71 |
72 | // Load or create settings
73 | LoadSettings();
74 | }
75 |
76 | private string GetSettingsPath()
77 | {
78 | // Get the script location
79 | var script = MonoScript.FromScriptableObject(this);
80 | var scriptPath = AssetDatabase.GetAssetPath(script);
81 | var directoryPath = Path.GetDirectoryName(scriptPath);
82 |
83 | // Create settings path in the same directory
84 | return Path.Combine(directoryPath, "MCPDebugSettings.json");
85 | }
86 |
87 | private void LoadSettings()
88 | {
89 | settings = new MCPDebugSettings();
90 |
91 | try
92 | {
93 | // Check if settings file exists
94 | if (File.Exists(settingsPath))
95 | {
96 | string json = File.ReadAllText(settingsPath);
97 | settings = JsonConvert.DeserializeObject<MCPDebugSettings>(json);
98 |
99 | Debug.Log($"[MCP] [MCPDebugWindow] Loaded settings from {settingsPath}");
100 | }
101 | else
102 | {
103 | // Create default settings
104 | settings = new MCPDebugSettings();
105 | SaveSettings();
106 | Debug.Log($"[MCP] [MCPDebugWindow] Created default settings at {settingsPath}");
107 | }
108 | }
109 | catch (Exception ex)
110 | {
111 | Debug.LogError($"[MCP] [MCPDebugWindow] Error loading settings: {ex.Message}");
112 | settings = new MCPDebugSettings();
113 | }
114 |
115 | // Apply settings to MCPLogger
116 | MCPLogger.GlobalLoggingEnabled = settings.globalLoggingEnabled;
117 |
118 | // Apply component logging settings
119 | foreach (var pair in settings.componentLoggingEnabled)
120 | {
121 | MCPLogger.SetComponentLoggingEnabled(pair.Key, pair.Value);
122 | }
123 | }
124 |
125 | private void SaveSettings()
126 | {
127 | try
128 | {
129 | // Save settings using Newtonsoft.Json which supports dictionaries directly
130 | string json = JsonConvert.SerializeObject(settings, Formatting.Indented);
131 | File.WriteAllText(settingsPath, json);
132 |
133 | Debug.Log($"[MCP] [MCPDebugWindow] Saved settings to {settingsPath}");
134 | }
135 | catch (Exception ex)
136 | {
137 | Debug.LogError($"[MCP] [MCPDebugWindow] Error saving settings: {ex.Message}");
138 | }
139 | }
140 |
141 | public void CreateGUI()
142 | {
143 | VisualElement root = rootVisualElement;
144 |
145 | if (uxml != null)
146 | {
147 | uxml.CloneTree(root);
148 | }
149 | else
150 | {
151 | Debug.LogError("VisualTreeAsset not found. Please check the path.");
152 | }
153 |
154 | if (uss != null)
155 | {
156 | root.styleSheets.Add(uss);
157 | } else
158 | {
159 | Debug.LogError("StyleSheet not found. Please check the path.");
160 | }
161 |
162 | // Get UI elements
163 | connectionStatusLabel = root.Q<Label>("connection-status");
164 | connectButton = root.Q<Button>("connect-button");
165 | disconnectButton = root.Q<Button>("disconnect-button");
166 | autoReconnectToggle = root.Q<Toggle>("auto-reconnect-toggle");
167 | serverPortField = root.Q<TextField>("server-port-field");
168 |
169 | lastErrorLabel = root.Q<Label>("last-error-value");
170 | connectionTimeLabel = root.Q<Label>("connection-time-value");
171 |
172 | messagesSentLabel = root.Q<Label>("messages-sent-value");
173 | messagesReceivedLabel = root.Q<Label>("messages-received-value");
174 | reconnectAttemptsLabel = root.Q<Label>("reconnect-attempts-value");
175 |
176 | // Apply settings to UI
177 | serverPortField.value = settings.port.ToString();
178 | autoReconnectToggle.value = settings.autoReconnect;
179 |
180 | // Setup UI events
181 | connectButton.clicked += OnConnectClicked;
182 | disconnectButton.clicked += OnDisconnectClicked;
183 | autoReconnectToggle.RegisterValueChangedCallback(OnAutoReconnectChanged);
184 | serverPortField.RegisterValueChangedCallback(OnPortChanged);
185 |
186 | // Setup component logging toggles
187 | SetupComponentLoggingToggles(root);
188 |
189 | // Initialize UI with current state
190 | UpdateUIFromState();
191 |
192 | // Register for updates
193 | EditorApplication.update += OnEditorUpdate;
194 | }
195 |
196 | private void OnPortChanged(ChangeEvent<string> evt)
197 | {
198 | if (int.TryParse(evt.newValue, out int port) && port >= 1 && port <= 65535)
199 | {
200 | settings.port = port;
201 | SaveSettings();
202 | }
203 | }
204 |
205 | private void CreateFallbackUI(VisualElement root)
206 | {
207 | // Create a simple fallback UI if UXML fails to load
208 | root.Add(new Label("MCP Debug Window - UXML not found") { style = { fontSize = 16, marginBottom = 10 } });
209 |
210 | // Removed serverUrlField - only using port field as requested
211 |
212 | serverPortField = new TextField("Port (Default: 5010)") { value = "5010" };
213 | root.Add(serverPortField);
214 |
215 | var connectButton = new Button(OnConnectClicked) { text = "Connect" };
216 | root.Add(connectButton);
217 |
218 | var disconnectButton = new Button(OnDisconnectClicked) { text = "Disconnect" };
219 | root.Add(disconnectButton);
220 |
221 | var autoReconnectToggle = new Toggle("Auto Reconnect");
222 | autoReconnectToggle.RegisterValueChangedCallback(OnAutoReconnectChanged);
223 | root.Add(autoReconnectToggle);
224 |
225 | connectionStatusLabel = new Label("Status: Not Connected");
226 | root.Add(connectionStatusLabel);
227 | }
228 |
229 | private void SetupComponentLoggingToggles(VisualElement root)
230 | {
231 | var loggingContainer = root.Q<VisualElement>("logging-container");
232 |
233 | // Register MCPDebugWindow as a component for logging
234 | MCPLogger.InitializeComponent("MCPDebugWindow", settings.componentLoggingEnabled.ContainsKey("MCPDebugWindow") ?
235 | settings.componentLoggingEnabled["MCPDebugWindow"] : false);
236 |
237 | // Global logging toggle
238 | var globalToggle = new Toggle("Enable All Logging");
239 | globalToggle.value = settings.globalLoggingEnabled;
240 | globalToggle.RegisterValueChangedCallback(evt => {
241 | settings.globalLoggingEnabled = evt.newValue;
242 | MCPLogger.GlobalLoggingEnabled = evt.newValue;
243 | SaveSettings();
244 |
245 | // First make sure all components are properly initialized before updating UI
246 | EnsureComponentsInitialized();
247 |
248 | // Update all component toggles to show they're enabled/disabled
249 | foreach (var componentName in MCPLogger.GetRegisteredComponents())
250 | {
251 | if (logToggles.TryGetValue(componentName, out var toggle))
252 | {
253 | // Don't disable the toggle UI, just update its interactable state
254 | toggle.SetEnabled(true);
255 | }
256 | }
257 | });
258 | loggingContainer.Add(globalToggle);
259 |
260 | // Add a separator
261 | var separator = new VisualElement();
262 | separator.style.height = 1;
263 | separator.style.marginTop = 5;
264 | separator.style.marginBottom = 5;
265 | separator.style.backgroundColor = new Color(0.3f, 0.3f, 0.3f);
266 | loggingContainer.Add(separator);
267 |
268 | // Ensure all components are initialized
269 | EnsureComponentsInitialized();
270 |
271 | // Create toggles for standard components
272 | string[] standardComponents = {
273 | "MCPManager",
274 | "MCPConnectionManager",
275 | "MCPDataCollector",
276 | "MCPMessageHandler",
277 | "MCPCodeExecutor",
278 | "MCPMessageSender",
279 | "MCPDebugWindow" // Add the debug window itself
280 | };
281 |
282 | foreach (string componentName in standardComponents)
283 | {
284 | bool isEnabled = settings.componentLoggingEnabled.ContainsKey(componentName) ?
285 | settings.componentLoggingEnabled[componentName] : false;
286 |
287 | CreateLoggingToggle(loggingContainer, componentName, $"Enable {componentName} logging", isEnabled);
288 | }
289 |
290 | // Add any additional registered components not in our standard list
291 | foreach (var componentName in MCPLogger.GetRegisteredComponents())
292 | {
293 | if (!logToggles.ContainsKey(componentName))
294 | {
295 | bool isEnabled = settings.componentLoggingEnabled.ContainsKey(componentName) ?
296 | settings.componentLoggingEnabled[componentName] : false;
297 |
298 | CreateLoggingToggle(loggingContainer, componentName, $"Enable {componentName} logging", isEnabled);
299 | }
300 | }
301 | }
302 |
303 | // Make sure all components are initialized in the logger
304 | private void EnsureComponentsInitialized()
305 | {
306 | string[] standardComponents = {
307 | "MCPManager",
308 | "MCPConnectionManager",
309 | "MCPDataCollector",
310 | "MCPMessageHandler",
311 | "MCPCodeExecutor",
312 | "MCPMessageSender",
313 | "MCPDebugWindow"
314 | };
315 |
316 | foreach (string componentName in standardComponents)
317 | {
318 | MCPLogger.InitializeComponent(componentName, false);
319 | }
320 | }
321 |
322 | private void CreateLoggingToggle(VisualElement container, string componentName, string label, bool initialValue)
323 | {
324 | var toggle = new Toggle(label);
325 | toggle.value = initialValue;
326 |
327 | // Make all toggles interactive, they'll work based on global enabled state
328 | toggle.SetEnabled(true);
329 |
330 | toggle.RegisterValueChangedCallback(evt => OnLoggingToggleChanged(componentName, evt.newValue));
331 | container.Add(toggle);
332 | logToggles[componentName] = toggle;
333 | }
334 |
335 | private void OnLoggingToggleChanged(string componentName, bool enabled)
336 | {
337 | MCPLogger.SetComponentLoggingEnabled(componentName, enabled);
338 | settings.componentLoggingEnabled[componentName] = enabled;
339 | SaveSettings();
340 | }
341 |
342 | private void OnConnectClicked()
343 | {
344 | // Always use localhost for the WebSocket URL
345 | string serverUrl = "ws://localhost";
346 |
347 | // Get the server port from the text field
348 | string portText = serverPortField.value;
349 |
350 | // If port is empty, default to 5010
351 | if (string.IsNullOrWhiteSpace(portText))
352 | {
353 | portText = "5010";
354 | serverPortField.value = portText;
355 | }
356 |
357 | // Validate port format
358 | if (!int.TryParse(portText, out int port) || port < 1 || port > 65535)
359 | {
360 | EditorUtility.DisplayDialog("Invalid Port",
361 | "Please enter a valid port number between 1 and 65535.", "OK");
362 | return;
363 | }
364 |
365 | // Save the port setting
366 | settings.port = port;
367 | SaveSettings();
368 |
369 | try {
370 | // Create the WebSocket URL with the specified port
371 | Uri uri = new Uri($"{serverUrl}:{port}");
372 |
373 | // If we have access to the ConnectionManager, try to update its server URI
374 | var connectionManager = GetConnectionManager();
375 | if (connectionManager != null)
376 | {
377 | // Use reflection to set the serverUri field if it exists
378 | var serverUriField = typeof(MCPConnectionManager).GetField("serverUri",
379 | System.Reflection.BindingFlags.NonPublic |
380 | System.Reflection.BindingFlags.Instance);
381 |
382 | if (serverUriField != null)
383 | {
384 | serverUriField.SetValue(connectionManager, uri);
385 | }
386 | }
387 |
388 | // Initiate manual connection
389 | if (MCPManager.IsInitialized)
390 | {
391 | MCPManager.RetryConnection();
392 | connectionStartTime = DateTime.Now;
393 | UpdateUIFromState();
394 | }
395 | else
396 | {
397 | MCPManager.Initialize();
398 | connectionStartTime = DateTime.Now;
399 | UpdateUIFromState();
400 | }
401 | }
402 | catch (UriFormatException)
403 | {
404 | EditorUtility.DisplayDialog("Invalid URL",
405 | "The URL format is invalid.", "OK");
406 | }
407 | catch (Exception ex)
408 | {
409 | EditorUtility.DisplayDialog("Connection Error",
410 | $"Error connecting to server: {ex.Message}", "OK");
411 | }
412 | }
413 |
414 | private void OnDisconnectClicked()
415 | {
416 | if (MCPManager.IsInitialized)
417 | {
418 | MCPManager.Shutdown();
419 | connectionStartTime = null;
420 | UpdateUIFromState();
421 | }
422 | }
423 |
424 | private void OnAutoReconnectChanged(ChangeEvent<bool> evt)
425 | {
426 | settings.autoReconnect = evt.newValue;
427 | SaveSettings();
428 |
429 | if (MCPManager.IsInitialized)
430 | {
431 | MCPManager.EnableAutoReconnect(evt.newValue);
432 | }
433 | }
434 |
435 | private void OnEditorUpdate()
436 | {
437 | // Update connection status and statistics
438 | UpdateUIFromState();
439 | }
440 |
441 | private void UpdateUIFromState()
442 | {
443 | bool isInitialized = MCPManager.IsInitialized;
444 | bool isConnected = MCPManager.IsConnected;
445 |
446 | // Only log status if logging is enabled
447 | if (MCPLogger.IsLoggingEnabled("MCPDebugWindow"))
448 | {
449 | Debug.Log($"[MCP] [MCPDebugWindow] Status check: IsInitialized={isInitialized}, IsConnected={isConnected}");
450 | }
451 |
452 | // Update status label
453 | if (!isInitialized)
454 | {
455 | connectionStatusLabel.text = "Not Initialized";
456 | connectionStatusLabel.RemoveFromClassList("status-connected");
457 | connectionStatusLabel.RemoveFromClassList("status-connecting");
458 | connectionStatusLabel.AddToClassList("status-disconnected");
459 | }
460 | else if (isConnected)
461 | {
462 | connectionStatusLabel.text = "Connected";
463 | connectionStatusLabel.RemoveFromClassList("status-disconnected");
464 | connectionStatusLabel.RemoveFromClassList("status-connecting");
465 | connectionStatusLabel.AddToClassList("status-connected");
466 |
467 | // If we're in the connected state, make sure connectionStartTime is set
468 | // This ensures the timer works properly
469 | if (!connectionStartTime.HasValue)
470 | {
471 | connectionStartTime = DateTime.Now;
472 | }
473 | }
474 | else
475 | {
476 | connectionStatusLabel.text = "Disconnected";
477 | connectionStatusLabel.RemoveFromClassList("status-connected");
478 | connectionStatusLabel.RemoveFromClassList("status-connecting");
479 | connectionStatusLabel.AddToClassList("status-disconnected");
480 |
481 | // Reset connection time when disconnected
482 | connectionStartTime = null;
483 | }
484 |
485 | // Update button states
486 | connectButton.SetEnabled(!isConnected);
487 | disconnectButton.SetEnabled(isInitialized);
488 | serverPortField.SetEnabled(!isConnected); // Only allow port changes when disconnected
489 |
490 | // Update connection time if connected
491 | if (connectionStartTime.HasValue && isConnected)
492 | {
493 | TimeSpan duration = DateTime.Now - connectionStartTime.Value;
494 | connectionTimeLabel.text = $"{duration.Hours:00}:{duration.Minutes:00}:{duration.Seconds:00}";
495 | }
496 | else
497 | {
498 | connectionTimeLabel.text = "00:00:00";
499 | }
500 |
501 | // Update statistics if available
502 | if (isInitialized)
503 | {
504 | // Get connection statistics
505 | var connectionManager = GetConnectionManager();
506 | if (connectionManager != null)
507 | {
508 | messagesSentLabel.text = connectionManager.MessagesSent.ToString();
509 | messagesReceivedLabel.text = connectionManager.MessagesReceived.ToString();
510 | reconnectAttemptsLabel.text = connectionManager.ReconnectAttempts.ToString();
511 | lastErrorLabel.text = !string.IsNullOrEmpty(connectionManager.LastErrorMessage)
512 | ? connectionManager.LastErrorMessage : "None";
513 | }
514 | else
515 | {
516 | messagesSentLabel.text = "0";
517 | messagesReceivedLabel.text = "0";
518 | reconnectAttemptsLabel.text = "0";
519 | lastErrorLabel.text = "None";
520 | }
521 | }
522 | }
523 |
524 | // Helper to access connection manager through reflection if needed
525 | private MCPConnectionManager GetConnectionManager()
526 | {
527 | if (!MCPManager.IsInitialized)
528 | return null;
529 |
530 | // Try to access the connection manager using reflection
531 | try
532 | {
533 | var managerType = typeof(MCPManager);
534 | var field = managerType.GetField("connectionManager",
535 | System.Reflection.BindingFlags.NonPublic |
536 | System.Reflection.BindingFlags.Static);
537 |
538 | if (field != null)
539 | {
540 | return field.GetValue(null) as MCPConnectionManager;
541 | }
542 | }
543 | catch (Exception ex)
544 | {
545 | Debug.LogError($"Error accessing connection manager: {ex.Message}");
546 | }
547 |
548 | return null;
549 | }
550 |
551 | private void OnDisable()
552 | {
553 | // Unregister from editor updates
554 | EditorApplication.update -= OnEditorUpdate;
555 |
556 | // Save settings one last time when window is closed
557 | SaveSettings();
558 | }
559 | }
560 | }
561 |
```