From aa1aad3bb1039652e14bdcd8c46f668133d5bbdf Mon Sep 17 00:00:00 2001 From: YeonGyu-Kim Date: Thu, 12 Mar 2026 00:51:02 +0900 Subject: [PATCH] fix: add disposed guard to MCP manager and guard unregister on pending connections --- src/features/skill-mcp-manager/cleanup.ts | 5 +++-- src/features/skill-mcp-manager/connection-race.test.ts | 1 + src/features/skill-mcp-manager/connection.ts | 4 ++++ src/features/skill-mcp-manager/disconnect-cleanup.test.ts | 1 + src/features/skill-mcp-manager/manager.ts | 1 + src/features/skill-mcp-manager/types.ts | 1 + 6 files changed, 11 insertions(+), 2 deletions(-) diff --git a/src/features/skill-mcp-manager/cleanup.ts b/src/features/skill-mcp-manager/cleanup.ts index c217826f6..71ef2c345 100644 --- a/src/features/skill-mcp-manager/cleanup.ts +++ b/src/features/skill-mcp-manager/cleanup.ts @@ -81,7 +81,7 @@ async function cleanupIdleClients(state: SkillMcpManagerState): Promise { } } - if (state.clients.size === 0) { + if (state.clients.size === 0 && state.pendingConnections.size === 0) { stopCleanupTimer(state) unregisterProcessCleanup(state) } @@ -119,7 +119,7 @@ export async function disconnectSession(state: SkillMcpManagerState, sessionID: state.pendingConnections.delete(key) } - if (state.clients.size === 0) { + if (state.clients.size === 0 && state.pendingConnections.size === 0) { stopCleanupTimer(state) unregisterProcessCleanup(state) } @@ -127,6 +127,7 @@ export async function disconnectSession(state: SkillMcpManagerState, sessionID: export async function disconnectAll(state: SkillMcpManagerState): Promise { state.shutdownGeneration++ + state.disposed = true stopCleanupTimer(state) unregisterProcessCleanup(state) diff --git a/src/features/skill-mcp-manager/connection-race.test.ts b/src/features/skill-mcp-manager/connection-race.test.ts index d56db4033..582c81b72 100644 --- a/src/features/skill-mcp-manager/connection-race.test.ts +++ b/src/features/skill-mcp-manager/connection-race.test.ts @@ -81,6 +81,7 @@ function createState(): SkillMcpManagerState { idleTimeoutMs: 5 * 60 * 1000, shutdownGeneration: 0, inFlightConnections: new Map(), + disposed: false, } trackedStates.push(state) diff --git a/src/features/skill-mcp-manager/connection.ts b/src/features/skill-mcp-manager/connection.ts index c4af1b705..fabc8924f 100644 --- a/src/features/skill-mcp-manager/connection.ts +++ b/src/features/skill-mcp-manager/connection.ts @@ -15,6 +15,10 @@ export async function getOrCreateClient(params: { }): Promise { const { state, clientKey, info, config } = params + if (state.disposed) { + throw new Error(`MCP manager for "${info.sessionID}" has been shut down, cannot create new connections.`) + } + const existing = state.clients.get(clientKey) if (existing) { existing.lastUsedAt = Date.now() diff --git a/src/features/skill-mcp-manager/disconnect-cleanup.test.ts b/src/features/skill-mcp-manager/disconnect-cleanup.test.ts index bd293940b..654c4a2ff 100644 --- a/src/features/skill-mcp-manager/disconnect-cleanup.test.ts +++ b/src/features/skill-mcp-manager/disconnect-cleanup.test.ts @@ -26,6 +26,7 @@ function createState(): SkillMcpManagerState { idleTimeoutMs: 5 * 60 * 1000, shutdownGeneration: 0, inFlightConnections: new Map(), + disposed: false, } trackedStates.push(state) diff --git a/src/features/skill-mcp-manager/manager.ts b/src/features/skill-mcp-manager/manager.ts index 52f141553..00980e987 100644 --- a/src/features/skill-mcp-manager/manager.ts +++ b/src/features/skill-mcp-manager/manager.ts @@ -18,6 +18,7 @@ export class SkillMcpManager { idleTimeoutMs: 5 * 60 * 1000, shutdownGeneration: 0, inFlightConnections: new Map(), + disposed: false, } private getClientKey(info: SkillMcpClientInfo): string { diff --git a/src/features/skill-mcp-manager/types.ts b/src/features/skill-mcp-manager/types.ts index 17c867799..1fb704a69 100644 --- a/src/features/skill-mcp-manager/types.ts +++ b/src/features/skill-mcp-manager/types.ts @@ -59,6 +59,7 @@ export interface SkillMcpManagerState { idleTimeoutMs: number shutdownGeneration: number inFlightConnections: Map + disposed: boolean } export interface SkillMcpClientConnectionParams {