@@ -188,9 +188,11 @@ def __init__(
188188 ] = {}
189189 self ._sse_stream_writers : dict [RequestId , MemoryObjectSendStream [SSEEvent ]] = {}
190190 self ._terminated = False
191- # CancelScope for the active GET SSE response, so terminate() can
192- # force-cancel a hung EventSourceResponse on Windows (python-sdk#2653).
193- self ._active_get_response_scope : anyio .CancelScope | None = None
191+ # CancelScopes for active GET SSE responses, so terminate() can
192+ # force-cancel all hung EventSourceResponses on Windows (python-sdk#2653).
193+ # A set is used instead of a single scope because concurrent GET-start
194+ # races can create multiple in-flight SSE responses.
195+ self ._active_get_response_scopes : set [anyio .CancelScope ] = set ()
194196 # Idle timeout cancel scope; managed by the session manager.
195197 self .idle_scope : anyio .CancelScope | None = None
196198
@@ -745,19 +747,22 @@ async def standalone_sse_writer():
745747 headers = headers ,
746748 )
747749
750+ scope : anyio .CancelScope | None = None
748751 try :
749752 # Guard the SSE response with a cancel scope so terminate()
750753 # can force-cancel a hung EventSourceResponse on Windows
751754 # where sse-starlette's TaskGroup children may not respond
752755 # to cancellation promptly (python-sdk#2653).
753- self ._active_get_response_scope = anyio .CancelScope ()
754- with self ._active_get_response_scope :
756+ scope = anyio .CancelScope ()
757+ self ._active_get_response_scopes .add (scope )
758+ with scope :
755759 await response (request .scope , request .receive , send )
756760 except Exception : # pragma: lax no cover
757761 logger .exception ("Error in standalone SSE response" )
758762 await self ._clean_up_memory_streams (GET_STREAM_KEY )
759763 finally :
760- self ._active_get_response_scope = None
764+ if scope is not None :
765+ self ._active_get_response_scopes .discard (scope )
761766 await sse_stream_writer .aclose ()
762767 await sse_stream_reader .aclose ()
763768
@@ -797,10 +802,11 @@ async def terminate(self) -> None:
797802 self ._terminated = True
798803 logger .info (f"Terminating session: { self .mcp_session_id } " )
799804
800- # Cancel any in-flight GET SSE response to prevent
805+ # Cancel all in-flight GET SSE responses to prevent
801806 # sse-starlette TaskGroup deadlock on Windows (python-sdk#2653).
802- if self ._active_get_response_scope is not None : # pragma: no cover
803- self ._active_get_response_scope .cancel ()
807+ for scope in self ._active_get_response_scopes :
808+ scope .cancel ()
809+ self ._active_get_response_scopes .clear ()
804810
805811 # We need a copy of the keys to avoid modification during iteration
806812 request_stream_keys = list (self ._request_streams .keys ())
0 commit comments