Graceful SSE drain on session manager shutdown#2239
Open
wiggzz wants to merge 1 commit intomodelcontextprotocol:mainfrom
Open
Graceful SSE drain on session manager shutdown#2239wiggzz wants to merge 1 commit intomodelcontextprotocol:mainfrom
wiggzz wants to merge 1 commit intomodelcontextprotocol:mainfrom
Conversation
wiggzz
added a commit
to dbt-labs/mcp-python-sdk
that referenced
this pull request
Mar 6, 2026
Terminate all active transports before cancelling the task group during shutdown. This closes in-memory anyio streams cleanly, allowing EventSourceResponse to send a final `more_body=False` chunk — a clean HTTP close instead of a connection reset that triggers "upstream prematurely closed connection" errors at reverse proxies. Changes: - Track in-flight stateless transports in _stateless_transports set - In run() finally block, call terminate() on all transports (both stateful and stateless) before tg.cancel_scope.cancel() - Add E2E tests for graceful shutdown in both stateless and stateful modes using httpx.ASGITransport Upstream PR: modelcontextprotocol#2239
0ec94b4 to
d73a757
Compare
Terminate all active transports before cancelling the task group during StreamableHTTPSessionManager shutdown. This closes their in-memory streams, allowing EventSourceResponse to send a final `more_body=False` chunk — a clean HTTP close instead of a connection reset. Without this, reverse proxies like nginx see "upstream prematurely closed connection" and return 502 to clients during rolling deploys. Changes: - Track in-flight stateless transports in `_stateless_transports` set - In `run()` finally block, call `terminate()` on all stateful and stateless transports before `tg.cancel_scope.cancel()` - Add E2E tests for both stateless and stateful modes that verify the SSE stream closes cleanly when the manager shuts down while a tool call is in-flight
d73a757 to
b4c5fd6
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Summary
When
StreamableHTTPSessionManager.run()exits (e.g., during server shutdown), active SSE streams are abruptly cancelled by the task group cancellation. This meansEventSourceResponsenever sends its finalmore_body=Falsechunk, causing the TCP connection to reset. Reverse proxies like nginx interpret this as "upstream prematurely closed connection" and return 502 to clients.This PR fixes the issue by terminating all active transports before cancelling the task group during shutdown.
transport.terminate()closes the in-memory anyio streams, which causessse_writerto exit cleanly →sse_stream_writercloses →EventSourceResponse._stream_responseiterator ends →more_body=Falseis sent → clean HTTP close.Changes
streamable_http_manager.py: Track in-flight stateless transports in_stateless_transportsset. Inrun()'s finally block, callterminate()on all stateful and stateless transports beforetg.cancel_scope.cancel().test_streamable_http_manager.py: Add E2E tests for both stateless and stateful modes that verify the SSE stream closes cleanly when the manager shuts down while a tool call is in-flight.Motivation
In production, every rolling deploy triggers connection errors at our gateway since the streams are not terminated gracefully.
Breaking changes
None. This is an internal implementation change that only affects shutdown behavior.