fix(skill-mcp-manager): remove process listeners on disconnect and guard connection races

H7: Process 'exit'/'SIGINT' listeners registered per-session were
never removed when all sessions disconnected, accumulating handlers.
- Add unregisterProcessCleanup() called in disconnectAll()

H8: Race condition where disconnectSession() during pending connection
left orphan clients in state.clients.
- Add disconnectedSessions Set to track mid-flight disconnects
- Check disconnect marker after connection resolves, close if stale
- Clear marker on reconnection for same session

Tests: 6 pass (3 disconnect + 3 race)
This commit is contained in:
YeonGyu-Kim
2026-03-11 20:10:34 +09:00
parent fed720dd11
commit d1fc6629c2
6 changed files with 301 additions and 2 deletions
+15 -2
View File
@@ -14,6 +14,7 @@ export async function getOrCreateClient(params: {
config: ClaudeCodeMcpServer
}): Promise<Client> {
const { state, clientKey, info, config } = params
state.disconnectedSessions.delete(info.sessionID)
const existing = state.clients.get(clientKey)
if (existing) {
@@ -28,14 +29,26 @@ export async function getOrCreateClient(params: {
}
const expandedConfig = expandEnvVarsInObject(config)
const connectionPromise = createClient({ state, clientKey, info, config: expandedConfig })
const connectionPromise = (async () => {
const client = await createClient({ state, clientKey, info, config: expandedConfig })
if (state.disconnectedSessions.has(info.sessionID)) {
await forceReconnect(state, clientKey)
throw new Error(`Session "${info.sessionID}" disconnected during MCP connection setup.`)
}
return client
})()
state.pendingConnections.set(clientKey, connectionPromise)
try {
const client = await connectionPromise
return client
} finally {
state.pendingConnections.delete(clientKey)
if (state.pendingConnections.get(clientKey) === connectionPromise) {
state.pendingConnections.delete(clientKey)
}
}
}