streamable-http MCP sessions never terminated on client shutdown — terminateSession() defined but never called
Summary
claude-code never sends DELETE /mcp to terminate streamable-http MCP sessions. Each claude invocation leaks one session, and /exit leaks two. Server-side session maps grow unbounded.
StreamableHTTPClientTransport.terminateSession() exists in the bundled SDK and is the method that sends DELETE. grep -rn "terminateSession" src/ returns zero call sites.
Reproducer
Configure any streamable-http server in ~/.claude/settings.json, then claude → /exit. Server logs:
22:13:48 session opened <A> claude-code/2.1.128 (cli)
22:14:00 /exit pressed
22:14:00 session opened <B> claude-code/2.1.128 (cli)
Neither sends DELETE. <B> does a full initialize → tools/list → resources/list handshake and then the process exits.
Root cause
src/services/mcp/client.ts ~L1566:
await client.close() // closes local transport only — no DELETE sent
client.close() aborts local streams; per the SDK contract it does not terminate server-side state. That requires transport.terminateSession() — which the SDK exposes but claude-code never calls.
Fix
Before client.close() in the cleanup function:
const transport = (client as any)._transport
if (transport?.terminateSession) {
try { await transport.terminateSession() }
catch (e) { logMCPDebug(name, `terminateSession failed: ${e}`) }
}
(The _transport access is ugly — you'll have a cleaner internal accessor.)
Notes
- The second connect on
/exit(<B>above) is observed but not traced; same cleanup path, same fix handles both. - Verified against
@anthropic-ai/claude-code@2.1.128and the mirror atgithub.com/codeaashu/claude-code— zero call sites in both. - Codex CLI sends DELETE on shutdown — this is claude-code-specific.
This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗