diff --git a/MCPForUnity/Editor/Services/McpEditorShutdownCleanup.cs b/MCPForUnity/Editor/Services/McpEditorShutdownCleanup.cs index 9a4b32269..e757b86ce 100644 --- a/MCPForUnity/Editor/Services/McpEditorShutdownCleanup.cs +++ b/MCPForUnity/Editor/Services/McpEditorShutdownCleanup.cs @@ -3,6 +3,7 @@ using MCPForUnity.Editor.Helpers; using MCPForUnity.Editor.Services.Transport; using UnityEditor; +using UnityEngine; namespace MCPForUnity.Editor.Services { @@ -22,8 +23,19 @@ static McpEditorShutdownCleanup() EditorApplication.quitting += OnEditorQuitting; } + // A -batchmode/CI instance resolves the interactive editor's server via the global + // pidfile+port handshake, so cleanup there would stop another user's server. Mirror the + // sibling guards (HttpAutoStartHandler, StdioBridgeHost): skip in batch unless opted in. + internal static bool ShouldRunCleanup() => + ShouldRunCleanup(Application.isBatchMode, Environment.GetEnvironmentVariable("UNITY_MCP_ALLOW_BATCH")); + + internal static bool ShouldRunCleanup(bool isBatchMode, string allowBatchEnv) => + !isBatchMode || !string.IsNullOrWhiteSpace(allowBatchEnv); + private static void OnEditorQuitting() { + if (!ShouldRunCleanup()) return; + // 1) Stop transports (best-effort, bounded wait). try { diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/McpEditorShutdownCleanupTests.cs b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/McpEditorShutdownCleanupTests.cs new file mode 100644 index 000000000..74bf851bd --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/McpEditorShutdownCleanupTests.cs @@ -0,0 +1,49 @@ +using System; +using NUnit.Framework; +using MCPForUnity.Editor.Services; +using UnityEngine; + +namespace MCPForUnityTests.Editor.Services +{ + [TestFixture] + public class McpEditorShutdownCleanupTests + { + [Test] + public void ShouldRunCleanup_InteractiveEditor_RunsCleanup() + { + Assert.IsTrue(McpEditorShutdownCleanup.ShouldRunCleanup(isBatchMode: false, allowBatchEnv: null)); + } + + [Test] + public void ShouldRunCleanup_BatchWithoutOverride_IsNoOp() + { + // Regression for #1196/#1010: a -batchmode/CI instance must not stop the + // interactive editor's server resolved via the global pidfile+port handshake. + Assert.IsFalse(McpEditorShutdownCleanup.ShouldRunCleanup(isBatchMode: true, allowBatchEnv: null)); + } + + [Test] + public void ShouldRunCleanup_BatchWithBlankOverride_IsNoOp() + { + // Whitespace is treated as unset, mirroring string.IsNullOrWhiteSpace in the sibling guards. + Assert.IsFalse(McpEditorShutdownCleanup.ShouldRunCleanup(isBatchMode: true, allowBatchEnv: "")); + Assert.IsFalse(McpEditorShutdownCleanup.ShouldRunCleanup(isBatchMode: true, allowBatchEnv: " ")); + } + + [Test] + public void ShouldRunCleanup_BatchWithOverride_RunsCleanup() + { + Assert.IsTrue(McpEditorShutdownCleanup.ShouldRunCleanup(isBatchMode: true, allowBatchEnv: "1")); + } + + [Test] + public void ShouldRunCleanup_Parameterless_MatchesEnvironment() + { + // Proves the wiring to Application.isBatchMode / UNITY_MCP_ALLOW_BATCH is correct + // without assuming how this test run was launched (GUI Test Runner vs -batchmode CI). + bool expected = !Application.isBatchMode + || !string.IsNullOrWhiteSpace(Environment.GetEnvironmentVariable("UNITY_MCP_ALLOW_BATCH")); + Assert.AreEqual(expected, McpEditorShutdownCleanup.ShouldRunCleanup()); + } + } +} diff --git a/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/McpEditorShutdownCleanupTests.cs.meta b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/McpEditorShutdownCleanupTests.cs.meta new file mode 100644 index 000000000..ee324b66b --- /dev/null +++ b/TestProjects/UnityMCPTests/Assets/Tests/EditMode/Services/McpEditorShutdownCleanupTests.cs.meta @@ -0,0 +1,11 @@ +fileFormatVersion: 2 +guid: 04b5977d361f459ea4726c6e176fa9d7 +MonoImporter: + externalObjects: {} + serializedVersion: 2 + defaultReferences: [] + executionOrder: 0 + icon: {instanceID: 0} + userData: + assetBundleName: + assetBundleVariant: