Skip to main content
Glama
MCPForUnityEditorWindow.cs7.97 kB
using System; using System.Collections.Generic; using System.Threading.Tasks; using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Services; using MCPForUnity.Editor.Windows.Components.ClientConfig; using MCPForUnity.Editor.Windows.Components.Connection; using MCPForUnity.Editor.Windows.Components.Settings; using UnityEditor; using UnityEngine; using UnityEngine.UIElements; namespace MCPForUnity.Editor.Windows { public class MCPForUnityEditorWindow : EditorWindow { // Section controllers private McpSettingsSection settingsSection; private McpConnectionSection connectionSection; private McpClientConfigSection clientConfigSection; private static readonly HashSet<MCPForUnityEditorWindow> OpenWindows = new(); private bool guiCreated = false; private double lastRefreshTime = 0; private const double RefreshDebounceSeconds = 0.5; public static void ShowWindow() { var window = GetWindow<MCPForUnityEditorWindow>("MCP For Unity"); window.minSize = new Vector2(500, 600); } // Helper to check and manage open windows from other classes public static bool HasAnyOpenWindow() { return OpenWindows.Count > 0; } public static void CloseAllOpenWindows() { if (OpenWindows.Count == 0) return; // Copy to array to avoid modifying the collection while iterating var arr = new MCPForUnityEditorWindow[OpenWindows.Count]; OpenWindows.CopyTo(arr); foreach (var window in arr) { try { window?.Close(); } catch (Exception ex) { McpLog.Warn($"Error closing MCP window: {ex.Message}"); } } } public void CreateGUI() { // Guard against repeated CreateGUI calls (e.g., domain reloads) if (guiCreated) return; string basePath = AssetPathUtility.GetMcpPackageRootPath(); // Load main window UXML var visualTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>( $"{basePath}/Editor/Windows/MCPForUnityEditorWindow.uxml" ); if (visualTree == null) { McpLog.Error( $"Failed to load UXML at: {basePath}/Editor/Windows/MCPForUnityEditorWindow.uxml" ); return; } visualTree.CloneTree(rootVisualElement); // Load main window USS var mainStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>( $"{basePath}/Editor/Windows/MCPForUnityEditorWindow.uss" ); if (mainStyleSheet != null) { rootVisualElement.styleSheets.Add(mainStyleSheet); } // Load common USS var commonStyleSheet = AssetDatabase.LoadAssetAtPath<StyleSheet>( $"{basePath}/Editor/Windows/Components/Common.uss" ); if (commonStyleSheet != null) { rootVisualElement.styleSheets.Add(commonStyleSheet); } // Get sections container var sectionsContainer = rootVisualElement.Q<VisualElement>("sections-container"); if (sectionsContainer == null) { McpLog.Error("Failed to find sections-container in UXML"); return; } // Load and initialize Settings section var settingsTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>( $"{basePath}/Editor/Windows/Components/Settings/McpSettingsSection.uxml" ); if (settingsTree != null) { var settingsRoot = settingsTree.Instantiate(); sectionsContainer.Add(settingsRoot); settingsSection = new McpSettingsSection(settingsRoot); settingsSection.OnGitUrlChanged += () => clientConfigSection?.UpdateManualConfiguration(); settingsSection.OnHttpServerCommandUpdateRequested += () => connectionSection?.UpdateHttpServerCommandDisplay(); } // Load and initialize Connection section var connectionTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>( $"{basePath}/Editor/Windows/Components/Connection/McpConnectionSection.uxml" ); if (connectionTree != null) { var connectionRoot = connectionTree.Instantiate(); sectionsContainer.Add(connectionRoot); connectionSection = new McpConnectionSection(connectionRoot); connectionSection.OnManualConfigUpdateRequested += () => clientConfigSection?.UpdateManualConfiguration(); } // Load and initialize Client Configuration section var clientConfigTree = AssetDatabase.LoadAssetAtPath<VisualTreeAsset>( $"{basePath}/Editor/Windows/Components/ClientConfig/McpClientConfigSection.uxml" ); if (clientConfigTree != null) { var clientConfigRoot = clientConfigTree.Instantiate(); sectionsContainer.Add(clientConfigRoot); clientConfigSection = new McpClientConfigSection(clientConfigRoot); } guiCreated = true; // Initial updates RefreshAllData(); } private void OnEnable() { EditorApplication.update += OnEditorUpdate; OpenWindows.Add(this); } private void OnDisable() { EditorApplication.update -= OnEditorUpdate; OpenWindows.Remove(this); guiCreated = false; } private void OnFocus() { // Only refresh data if UI is built if (rootVisualElement == null || rootVisualElement.childCount == 0) return; RefreshAllData(); } private void OnEditorUpdate() { if (rootVisualElement == null || rootVisualElement.childCount == 0) return; connectionSection?.UpdateConnectionStatus(); } private void RefreshAllData() { // Debounce rapid successive calls (e.g., from OnFocus being called multiple times) double currentTime = EditorApplication.timeSinceStartup; if (currentTime - lastRefreshTime < RefreshDebounceSeconds) { return; } lastRefreshTime = currentTime; connectionSection?.UpdateConnectionStatus(); if (MCPServiceLocator.Bridge.IsRunning) { _ = connectionSection?.VerifyBridgeConnectionAsync(); } settingsSection?.UpdatePathOverrides(); clientConfigSection?.RefreshSelectedClient(); } internal static void RequestHealthVerification() { foreach (var window in OpenWindows) { window?.ScheduleHealthCheck(); } } private void ScheduleHealthCheck() { EditorApplication.delayCall += async () => { if (this == null || connectionSection == null) { return; } await connectionSection.VerifyBridgeConnectionAsync(); }; } } }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/CoplayDev/unity-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server