What happened?
Auggie's Streamable HTTP MCP client never sends DELETE to terminate an MCP session it has stopped using. Each new session mints a fresh Mcp-Session-Id via initialize, and the previous one is abandoned rather than terminated: its TCP connection and standalone SSE GET keepalive stream stay open indefinitely, so the server keeps the session (and everything it pins) alive forever.
Over a normal working day a single auggie process accumulates well over a hundred simultaneously-open MCP sessions against one server.
Measured against a local MCP server (Go, modelcontextprotocol/go-sdk v1.4.0, Streamable HTTP on 127.0.0.1:5757/mcp), with server-side request logging:
| Metric (single server, ~29-minute log window) |
Value |
Distinct Mcp-Session-Ids seen on SSE GET |
287 |
Distinct Mcp-Session-Ids that ever sent a POST (i.e. actually in use) |
14 |
DELETE requests received from any client, ever |
0 |
Established TCP connections from the busiest single auggie pid |
146 |
So ~95% of the live sessions are pure keepalive ghosts: they were initialized, used briefly or not at all, then abandoned while still holding an open SSE stream.
lsof -nP -iTCP:5757 -sTCP:ESTABLISHED, grouped by client pid:
146 node 50364
60 node 61552
34 node 90807
19 node 54851
11 node 67619
Each ghost session costs the server a goroutine set, a file descriptor and an open SSE stream. On our side ~89% of the server process's goroutines were attributable to these abandoned sessions. It is also self-harming for Auggie: 146 sockets to one server saturates the client's own connection pool, and we correlated this with Auggie's own MCP initialization timed out after Ns gate firing (budgets escalating 11s → 634s) while the server's handler was answering in under 1ms throughout.
The MCP spec (2025-03-26, §Session Management) is explicit here:
Clients that no longer need a particular session ... SHOULD send an HTTP DELETE to the MCP endpoint with the Mcp-Session-Id header, to explicitly terminate the session.
What did you expect to happen?
When Auggie retires an MCP transport — the ACP/agent session it belonged to ended, the client reconnected, the server list was reloaded, or the process is shutting down — it should send DELETE <endpoint> with the Mcp-Session-Id header (and close the SSE GET) before dropping the transport, so the server can release the session immediately.
Steady state should be roughly one live MCP session per active agent session, not an ever-growing pile.
Steps to reproduce
- Register any Streamable HTTP MCP server globally (
~/.augment/settings.json), e.g. http://127.0.0.1:5757/mcp.
- Enable request logging on that server (method, path,
Mcp-Session-Id).
- Run Auggie normally for an hour, starting/ending several agent sessions (or driving it via ACP so sessions are created and retired repeatedly).
- Observe on the server:
- the number of distinct
Mcp-Session-Ids climbs monotonically (~8 new sessions/hour/process in our case);
- the great majority of them only ever issue the standalone
GET keepalive, never a POST;
- not a single
DELETE is ever received;
lsof -nP -iTCP:<port> -sTCP:ESTABLISHED shows the connection count from the auggie pid growing without bound.
Server-side termination is well tolerated, which confirms the sessions really are abandoned. We deleted two sessions by hand with curl -X DELETE -H 'Mcp-Session-Id: <id>' <endpoint> (both returned 204):
- an idle ghost (SSE only, zero
POSTs ever): the client retried the GET twice, got 404 both times, then tore the transport down cleanly — no error storm, sibling sessions unaffected;
- a live session that was actively carrying
tools/call traffic: subsequent tool calls continued with zero user-visible disruption, transparently failing over to another pooled session the same process already held.
Auggie version
0.34.0 (commit 81042879), installed via npm/Homebrew (@augmentcode/auggie)
Request ID
n/a — this is a client-side transport-lifecycle issue observed from the MCP server side, not a model request failure.
Environment details
Environment
- OS: macOS (Apple Silicon)
- Shell: zsh
- Tool/CLI version: auggie 0.34.0 (commit 81042879)
- MCP server under test: Go,
github.com/modelcontextprotocol/go-sdk v1.4.0, Streamable HTTP transport, 127.0.0.1:5757/mcp
- Transport: Streamable HTTP (MCP spec 2025-03-26),
JSONResponse: true
Anything else we need to know?
Relationship to #149. These are two halves of the same lifecycle gap and a fix for one without the other is incomplete:
Concretely: a server operator's only defence against the pile-up is a SessionTimeout, and turning that on is exactly what exposes #149. Fixing both — DELETE on retire here, and 404 → re-initialize in #149 — closes the loop.
Workaround for server authors (what we did): set an idle-session timeout on the Streamable HTTP handler. With the Go SDK, mcp.StreamableHTTPOptions{SessionTimeout: 30 * time.Minute}. Note the SDK's idle timer keys off POST activity only — the periodic SSE GET recycle does not refresh it — which is what makes this effective against exactly the ghost population described above. Server-side reaping is a mitigation, not a fix: it cannot reclaim a session any earlier than the timeout, and it depends on clients handling the resulting 404 correctly (#149).
Happy to supply the raw server logs or re-run the measurement against a build with a fix.
What happened?
Auggie's Streamable HTTP MCP client never sends
DELETEto terminate an MCP session it has stopped using. Each new session mints a freshMcp-Session-Idviainitialize, and the previous one is abandoned rather than terminated: its TCP connection and standalone SSEGETkeepalive stream stay open indefinitely, so the server keeps the session (and everything it pins) alive forever.Over a normal working day a single
auggieprocess accumulates well over a hundred simultaneously-open MCP sessions against one server.Measured against a local MCP server (Go,
modelcontextprotocol/go-sdkv1.4.0, Streamable HTTP on127.0.0.1:5757/mcp), with server-side request logging:Mcp-Session-Ids seen on SSEGETMcp-Session-Ids that ever sent aPOST(i.e. actually in use)DELETErequests received from any client, everauggiepidSo ~95% of the live sessions are pure keepalive ghosts: they were
initialized, used briefly or not at all, then abandoned while still holding an open SSE stream.lsof -nP -iTCP:5757 -sTCP:ESTABLISHED, grouped by client pid:Each ghost session costs the server a goroutine set, a file descriptor and an open SSE stream. On our side ~89% of the server process's goroutines were attributable to these abandoned sessions. It is also self-harming for Auggie: 146 sockets to one server saturates the client's own connection pool, and we correlated this with Auggie's own
MCP initialization timed out after Nsgate firing (budgets escalating 11s → 634s) while the server's handler was answering in under 1ms throughout.The MCP spec (2025-03-26, §Session Management) is explicit here:
What did you expect to happen?
When Auggie retires an MCP transport — the ACP/agent session it belonged to ended, the client reconnected, the server list was reloaded, or the process is shutting down — it should send
DELETE <endpoint>with theMcp-Session-Idheader (and close the SSEGET) before dropping the transport, so the server can release the session immediately.Steady state should be roughly one live MCP session per active agent session, not an ever-growing pile.
Steps to reproduce
~/.augment/settings.json), e.g.http://127.0.0.1:5757/mcp.Mcp-Session-Id).Mcp-Session-Ids climbs monotonically (~8 new sessions/hour/process in our case);GETkeepalive, never aPOST;DELETEis ever received;lsof -nP -iTCP:<port> -sTCP:ESTABLISHEDshows the connection count from theauggiepid growing without bound.Server-side termination is well tolerated, which confirms the sessions really are abandoned. We deleted two sessions by hand with
curl -X DELETE -H 'Mcp-Session-Id: <id>' <endpoint>(both returned204):POSTs ever): the client retried theGETtwice, got404both times, then tore the transport down cleanly — no error storm, sibling sessions unaffected;tools/calltraffic: subsequent tool calls continued with zero user-visible disruption, transparently failing over to another pooled session the same process already held.Auggie version
0.34.0 (commit 81042879), installed via npm/Homebrew (
@augmentcode/auggie)Request ID
n/a — this is a client-side transport-lifecycle issue observed from the MCP server side, not a model request failure.
Environment details
Environment
github.com/modelcontextprotocol/go-sdkv1.4.0, Streamable HTTP transport,127.0.0.1:5757/mcpJSONResponse: trueAnything else we need to know?
Relationship to #149. These are two halves of the same lifecycle gap and a fix for one without the other is incomplete:
404and wedges on the dead session ID.Concretely: a server operator's only defence against the pile-up is a
SessionTimeout, and turning that on is exactly what exposes #149. Fixing both —DELETEon retire here, and404→ re-initialize in #149 — closes the loop.Workaround for server authors (what we did): set an idle-session timeout on the Streamable HTTP handler. With the Go SDK,
mcp.StreamableHTTPOptions{SessionTimeout: 30 * time.Minute}. Note the SDK's idle timer keys offPOSTactivity only — the periodic SSEGETrecycle does not refresh it — which is what makes this effective against exactly the ghost population described above. Server-side reaping is a mitigation, not a fix: it cannot reclaim a session any earlier than the timeout, and it depends on clients handling the resulting404correctly (#149).Happy to supply the raw server logs or re-run the measurement against a build with a fix.