@@ -656,6 +656,46 @@ async def send(message: Message) -> None:
656656 return next (message ["status" ] for message in sent if message ["type" ] == "http.response.start" )
657657
658658
659+ @pytest .mark .anyio
660+ async def test_streamable_http_post_client_disconnect_is_not_reported_as_500 (
661+ caplog : pytest .LogCaptureFixture ,
662+ ) -> None :
663+ caplog .set_level (logging .ERROR , logger = "mcp.server.streamable_http" )
664+ transport = StreamableHTTPServerTransport (mcp_session_id = "valid-id" )
665+ assert transport .mcp_session_id is not None
666+ scope : Scope = {
667+ "type" : "http" ,
668+ "method" : "POST" ,
669+ "path" : "/mcp" ,
670+ "query_string" : b"" ,
671+ "headers" : [
672+ (b"content-type" , b"application/json" ),
673+ (b"accept" , b"application/json, text/event-stream" ),
674+ (MCP_SESSION_ID_HEADER .encode (), transport .mcp_session_id .encode ()),
675+ ],
676+ }
677+ sent : list [Message ] = []
678+
679+ async def receive () -> Message :
680+ return {"type" : "http.disconnect" }
681+
682+ async def send (message : Message ) -> None :
683+ sent .append (message )
684+
685+ async with transport .connect ():
686+ with anyio .move_on_after (1 ) as cancel_scope :
687+ await transport .handle_request (scope , receive , send )
688+
689+ assert not cancel_scope .cancel_called
690+ assert not sent
691+ assert [
692+ record .getMessage ()
693+ for record in caplog .records
694+ if record .name == "mcp.server.streamable_http" and record .levelno >= logging .ERROR
695+ ] == []
696+ await transport .terminate ()
697+
698+
659699@pytest .mark .anyio
660700async def test_transport_whose_idle_period_ran_out_answers_as_terminated () -> None :
661701 """Once the idle scope has fired, a request that still reaches the transport is answered 404 and the
0 commit comments