@@ -130,6 +130,8 @@ def __init__(
130130 self ._endpoint = endpoint
131131 self ._read_stream_writers = {}
132132 self ._session_owners = {}
133+ # SSE body writers; closed on shutdown so EventSourceResponse can finish.
134+ self ._sse_stream_writers : dict [UUID , Any ] = {}
133135 self ._security = TransportSecurityMiddleware (security_settings )
134136 self ._post_message_app = RequestBodyLimitMiddleware (self ._handle_post_message , max_request_body_size )
135137 logger .debug (f"SseServerTransport initialized with endpoint: { endpoint } " )
@@ -175,6 +177,7 @@ async def connect_sse(self, scope: Scope, receive: Receive, send: Send):
175177 client_post_uri_data = f"{ quote (full_message_path_for_client )} ?session_id={ session_id .hex } "
176178
177179 sse_stream_writer , sse_stream_reader = anyio .create_memory_object_stream [dict [str , Any ]](0 )
180+ self ._sse_stream_writers [session_id ] = sse_stream_writer
178181
179182 async def sse_writer ():
180183 logger .debug ("Starting SSE writer" )
@@ -214,8 +217,26 @@ async def response_wrapper(scope: Scope, receive: Receive, send: Send):
214217 yield (read_stream , write_stream )
215218 finally :
216219 self ._read_stream_writers .pop (session_id , None )
220+ self ._sse_stream_writers .pop (session_id , None )
217221 self ._session_owners .pop (session_id , None )
218222
223+ async def close (self ) -> None :
224+ """Close all active SSE sessions so the ASGI server can shut down.
225+
226+ Uvicorn waits for outstanding streaming responses on SIGINT. Closing the
227+ per-session SSE and read streams unblocks EventSourceResponse and the
228+ MCP session task so the process can exit.
229+ """
230+ session_ids = set (self ._read_stream_writers ) | set (self ._sse_stream_writers )
231+ for session_id in session_ids :
232+ read_writer = self ._read_stream_writers .pop (session_id , None )
233+ sse_writer = self ._sse_stream_writers .pop (session_id , None )
234+ self ._session_owners .pop (session_id , None )
235+ if read_writer is not None :
236+ await read_writer .aclose ()
237+ if sse_writer is not None :
238+ await sse_writer .aclose ()
239+
219240 async def handle_post_message (self , scope : Scope , receive : Receive , send : Send ) -> None :
220241 """ASGI application for the message endpoint.
221242
0 commit comments