4444StreamWriter = ContextSendStream [SessionMessageOrError ]
4545StreamReader = ContextReceiveStream [SessionMessage ]
4646
47+
48+ async def _send_or_ignore_closed (read_stream_writer : StreamWriter , message : SessionMessageOrError ) -> bool :
49+ try :
50+ await read_stream_writer .send (message )
51+ except (anyio .BrokenResourceError , anyio .ClosedResourceError ):
52+ logger .debug ("Read stream closed before Streamable HTTP message could be delivered" , exc_info = True )
53+ return False
54+ return True
55+
56+
4757MCP_SESSION_ID = "mcp-session-id"
4858LAST_EVENT_ID = "last-event-id"
4959
@@ -157,17 +167,17 @@ async def _handle_sse_event(
157167 # Otherwise, return False to continue listening
158168 return isinstance (message , JSONRPCResponse | JSONRPCError )
159169
160- # Forwarding to a closed read stream lands here when the caller cancels mid-SSE
161- # (BrokenResourceError, not a parse failure); coverage is timing-dependent in the
162- # streaming story's modern HTTP cancellation leg.
170+ except ( anyio . BrokenResourceError , anyio . ClosedResourceError ):
171+ logger . debug ( "Read stream closed while forwarding SSE message" , exc_info = True )
172+ return True
163173 except Exception as exc : # pragma: lax no cover
164174 logger .exception ("Error parsing SSE message" )
165175 if original_request_id is not None :
166176 error_data = ErrorData (code = PARSE_ERROR , message = f"Failed to parse SSE message: { exc } " )
167177 error_msg = SessionMessage (JSONRPCError (jsonrpc = "2.0" , id = original_request_id , error = error_data ))
168- await read_stream_writer . send ( error_msg )
178+ await _send_or_ignore_closed ( read_stream_writer , error_msg )
169179 return True
170- await read_stream_writer . send ( exc )
180+ await _send_or_ignore_closed ( read_stream_writer , exc )
171181 return False
172182 else : # pragma: no cover
173183 logger .warning (f"Unknown SSE event: { sse .event } " )
@@ -383,7 +393,7 @@ async def _handle_sse_response(
383393 if last_event_id is None :
384394 error_data = ErrorData (code = CONNECTION_CLOSED , message = "SSE stream disconnected before response completed" )
385395 error_msg = SessionMessage (JSONRPCError (jsonrpc = "2.0" , id = original_request_id , error = error_data ))
386- await ctx .read_stream_writer . send ( error_msg )
396+ await _send_or_ignore_closed ( ctx .read_stream_writer , error_msg )
387397 return
388398
389399 logger .info ("SSE stream disconnected, reconnecting..." )
@@ -407,7 +417,7 @@ async def _handle_reconnection(
407417 data = {"last_event_id" : last_event_id },
408418 )
409419 error_msg = SessionMessage (JSONRPCError (jsonrpc = "2.0" , id = original_request_id , error = error_data ))
410- await ctx .read_stream_writer . send ( error_msg )
420+ await _send_or_ignore_closed ( ctx .read_stream_writer , error_msg )
411421 logger .debug (f"Max reconnection attempts ({ MAX_RECONNECTION_ATTEMPTS } ) exceeded" )
412422 return
413423
0 commit comments