streamable-http MCP sessions never terminated on client shutdown — terminateSession() defined but never called

Resolved 💬 1 comment Opened May 5, 2026 by cosmicallycooked Closed Jun 3, 2026

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 initializetools/listresources/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.128 and the mirror at github.com/codeaashu/claude-code — zero call sites in both.
  • Codex CLI sends DELETE on shutdown — this is claude-code-specific.

View original on GitHub ↗

This issue has 1 comment on GitHub. Read the full discussion on GitHub ↗