Skip to content
Merged
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
21 changes: 2 additions & 19 deletions MCPForUnity/Editor/MenuItems/MCPForUnityMenu.cs
Original file line number Diff line number Diff line change
Expand Up @@ -5,42 +5,25 @@

namespace MCPForUnity.Editor.MenuItems
{
/// <summary>
/// Centralized menu items for MCP For Unity
/// </summary>
public static class MCPForUnityMenu
{
// ========================================
// Main Menu Items
// ========================================

/// <summary>
/// Show the Setup Window
/// </summary>
[MenuItem("Window/MCP For Unity/Setup Window", priority = 1)]
public static void ShowSetupWindow()
{
SetupWindowService.ShowSetupWindow();
}

/// <summary>
/// Toggle the main MCP For Unity window
/// </summary>
[MenuItem("Window/MCP For Unity/Toggle MCP Window %#m", priority = 2)]
public static void ToggleMCPWindow()
{
if (EditorWindow.HasOpenInstances<MCPForUnityEditorWindow>())
if (MCPForUnityEditorWindow.HasAnyOpenWindow())
{
foreach (var window in UnityEngine.Resources.FindObjectsOfTypeAll<MCPForUnityEditorWindow>())
{
window.Close();
}
MCPForUnityEditorWindow.CloseAllOpenWindows();
}
else
{
MCPForUnityEditorWindow.ShowWindow();
}
}

}
}
29 changes: 21 additions & 8 deletions MCPForUnity/Editor/Setup/SetupWindowService.cs
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,11 @@ public static class SetupWindowService
{
private const string SETUP_COMPLETED_KEY = EditorPrefKeys.SetupCompleted;
private const string SETUP_DISMISSED_KEY = EditorPrefKeys.SetupDismissed;
private static bool _hasCheckedThisSession = false;

// Use SessionState to persist "checked this editor session" across domain reloads.
// SessionState survives assembly reloads within the same Editor session, which prevents
// the setup window from reappearing after code reloads / playmode transitions.
private const string SessionCheckedKey = "MCPForUnity.SetupWindowCheckedThisEditorSession";

static SetupWindowService()
{
Expand All @@ -35,10 +39,12 @@ static SetupWindowService()
/// </summary>
private static void CheckSetupNeeded()
{
if (_hasCheckedThisSession)
// Ensure we only run once per Editor session (survives domain reloads).
// This avoids showing the setup dialog repeatedly when scripts recompile or Play mode toggles.
if (SessionState.GetBool(SessionCheckedKey, false))
return;

_hasCheckedThisSession = true;
SessionState.SetBool(SessionCheckedKey, true);

try
{
Expand All @@ -48,11 +54,16 @@ private static void CheckSetupNeeded()
bool userOverrodeHttpUrl = EditorPrefs.HasKey(EditorPrefKeys.HttpBaseUrl);

// In Asset Store builds with a remote default URL (and no user override), skip the local setup wizard.
if (!userOverrodeHttpUrl
if (
!userOverrodeHttpUrl
&& McpDistribution.Settings.skipSetupWindowWhenRemoteDefault
&& McpDistribution.Settings.IsRemoteDefault)
&& McpDistribution.Settings.IsRemoteDefault
)
{
McpLog.Info("Skipping Setup Window because this distribution ships with a hosted MCP URL. Open Window/MCP For Unity/Setup Window if you want to configure a local runtime.", always: false);
McpLog.Info(
"Skipping Setup Window because this distribution ships with a hosted MCP URL. Open Window/MCP For Unity/Setup Window if you want to configure a local runtime.",
always: false
);
return;
}

Expand All @@ -66,7 +77,10 @@ private static void CheckSetupNeeded()
}
else
{
McpLog.Info("Setup Window skipped - previously completed or dismissed", always: false);
McpLog.Info(
"Setup Window skipped - previously completed or dismissed",
always: false
);
}
}
catch (Exception ex)
Expand Down Expand Up @@ -108,6 +122,5 @@ public static void MarkSetupDismissed()
EditorPrefs.SetBool(SETUP_DISMISSED_KEY, true);
McpLog.Info("Setup marked as dismissed");
}

}
}
63 changes: 52 additions & 11 deletions MCPForUnity/Editor/Windows/MCPForUnityEditorWindow.cs
Original file line number Diff line number Diff line change
@@ -1,14 +1,14 @@
using System;
using System.Collections.Generic;
using System.Threading.Tasks;
using UnityEditor;
using UnityEngine;
using UnityEngine.UIElements;
using MCPForUnity.Editor.Helpers;
using MCPForUnity.Editor.Services;
using MCPForUnity.Editor.Windows.Components.Settings;
using MCPForUnity.Editor.Windows.Components.Connection;
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
{
Expand All @@ -20,14 +20,47 @@ public class MCPForUnityEditorWindow : EditorWindow
private McpClientConfigSection clientConfigSection;

private static readonly HashSet<MCPForUnityEditorWindow> OpenWindows = new();
private bool guiCreated = false;

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
Expand All @@ -37,7 +70,9 @@ public void CreateGUI()

if (visualTree == null)
{
McpLog.Error($"Failed to load UXML at: {basePath}/Editor/Windows/MCPForUnityEditorWindow.uxml");
McpLog.Error(
$"Failed to load UXML at: {basePath}/Editor/Windows/MCPForUnityEditorWindow.uxml"
);
return;
}

Expand Down Expand Up @@ -78,8 +113,10 @@ public void CreateGUI()
var settingsRoot = settingsTree.Instantiate();
sectionsContainer.Add(settingsRoot);
settingsSection = new McpSettingsSection(settingsRoot);
settingsSection.OnGitUrlChanged += () => clientConfigSection?.UpdateManualConfiguration();
settingsSection.OnHttpServerCommandUpdateRequested += () => connectionSection?.UpdateHttpServerCommandDisplay();
settingsSection.OnGitUrlChanged += () =>
clientConfigSection?.UpdateManualConfiguration();
settingsSection.OnHttpServerCommandUpdateRequested += () =>
connectionSection?.UpdateHttpServerCommandDisplay();
}

// Load and initialize Connection section
Expand All @@ -91,7 +128,8 @@ public void CreateGUI()
var connectionRoot = connectionTree.Instantiate();
sectionsContainer.Add(connectionRoot);
connectionSection = new McpConnectionSection(connectionRoot);
connectionSection.OnManualConfigUpdateRequested += () => clientConfigSection?.UpdateManualConfiguration();
connectionSection.OnManualConfigUpdateRequested += () =>
clientConfigSection?.UpdateManualConfiguration();
}

// Load and initialize Client Configuration section
Expand All @@ -105,6 +143,8 @@ public void CreateGUI()
clientConfigSection = new McpClientConfigSection(clientConfigRoot);
}

guiCreated = true;

// Initial updates
RefreshAllData();
}
Expand All @@ -119,6 +159,7 @@ private void OnDisable()
{
EditorApplication.update -= OnEditorUpdate;
OpenWindows.Remove(this);
guiCreated = false;
}

private void OnFocus()
Expand Down Expand Up @@ -163,11 +204,11 @@ private void ScheduleHealthCheck()
{
EditorApplication.delayCall += async () =>
{
if (this == null)
if (this == null || connectionSection == null)
{
return;
}
await connectionSection?.VerifyBridgeConnectionAsync();
await connectionSection.VerifyBridgeConnectionAsync();
};
}
}
Expand Down