Skip to content

Dispose the POST response in SseClientSessionTransport to stop leaking a connection per message - #1841

Open
yalcinfu22 wants to merge 1 commit into
modelcontextprotocol:mainfrom
yalcinfu22:fix/sse-post-response-leak
Open

Dispose the POST response in SseClientSessionTransport to stop leaking a connection per message#1841
yalcinfu22 wants to merge 1 commit into
modelcontextprotocol:mainfrom
yalcinfu22:fix/sse-post-response-leak

Conversation

@yalcinfu22

Copy link
Copy Markdown

Fixes #1840

What

One-word change in SseClientSessionTransport.SendMessageAsync: the HttpResponseMessage
returned for each POSTed JSON-RPC message is now wrapped in using.

Why

McpHttpClient.SendAsync sends every request with HttpCompletionOption.ResponseHeadersRead,
so the underlying connection stays checked out until the response is consumed or disposed.
The POST path never did either on the success path (a 202 Accepted whose body is unused),
so every sent message stranded one ESTABLISHED connection — never returned to the pool,
never reused, reclaimed only by GC or an idle timeout. Measurements and the full analysis
are in #1840 (per client: 1 live SSE connection + one stranded connection per POST —
initialize, notifications/initialized, tools/list).

The surrounding code already does this correctly: the SSE GET response is wrapped in
using var response in the same file, and the failure path reads the body before throwing.
Only the success path was missing the dispose.

Notes

  • The failure path is unaffected: the body is read and the exception is created before the
    using scope unwinds.
  • ModelContextProtocol.Core builds cleanly with the change.

🤖 Generated with Claude Code

SendMessageAsync sends every message with HttpCompletionOption.ResponseHeadersRead
(via McpHttpClient) but never disposed the returned HttpResponseMessage on the
success path, so the underlying connection was never returned to the pool. Each
JSON-RPC POST therefore opened and stranded a new connection, which was only
reclaimed by GC or an idle timeout.

Disposing the response returns the connection to the pool deterministically;
subsequent POSTs reuse a single connection.

Fixes modelcontextprotocol#1840

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

HTTP+SSE client: POST responses are never disposed, leaking one connection per sent message

1 participant