@@ -69,48 +69,47 @@ async def mock_github_endpoint(request: Request) -> Response:
6969
7070
7171@pytest .mark .anyio
72- async def test_405_get_stream_does_not_hang (caplog : pytest .LogCaptureFixture ):
72+ async def test_405_get_stream_does_not_hang (caplog : pytest .LogCaptureFixture ) -> None :
7373 """Test that client handles 405 on GET gracefully and doesn't hang."""
7474 app = Starlette (routes = [Route ("/mcp" , mock_github_endpoint , methods = ["GET" , "POST" ])])
7575
76+ expected_log = "Server does not support GET for SSE events (405 Method Not Allowed)"
77+ got_405 = anyio .Event ()
78+
79+ class _405LogHandler (logging .Handler ):
80+ def emit (self , record : logging .LogRecord ) -> None :
81+ if expected_log in record .getMessage ():
82+ got_405 .set ()
83+
84+ mcp_logger = logging .getLogger ("mcp.client.streamable_http" )
85+ handler = _405LogHandler ()
86+ mcp_logger .addHandler (handler )
7687 with caplog .at_level (logging .INFO ):
7788 async with httpx .AsyncClient (
7889 transport = httpx .ASGITransport (app = app ), base_url = "http://testserver" , timeout = 5.0
7990 ) as http_client :
8091 transport_cm = streamable_http_client ("http://testserver/mcp" , http_client = http_client )
81- async with transport_cm as transport_streams :
92+ async with transport_cm as transport_streams : # pragma: no branch
8293 read_stream , write_stream = transport_streams
83- async with ClientSession (read_stream , write_stream ) as session :
84- # Initialize sends the initialized notification internally
94+ async with ClientSession (read_stream , write_stream ) as session : # pragma: no branch
8595 with anyio .fail_after (5.0 ):
8696 init_result = await session .initialize ()
8797 assert isinstance (init_result , InitializeResult )
8898
89- # Wait until the GET stream task fails with 405 and logs the expected message
90- expected_log = "Server does not support GET for SSE events (405 Method Not Allowed)"
9199 with anyio .fail_after (5.0 ):
92- while not any (expected_log in record .getMessage () for record in caplog .records ):
93- await anyio .sleep (0.05 )
100+ await got_405 .wait ()
94101
95- # This should not hang and will now complete successfully
96102 with anyio .fail_after (5.0 ):
97103 tools_result = await session .list_tools ()
98104 assert len (tools_result .tools ) == 1
99105 assert tools_result .tools [0 ].name == "test_tool"
100106
101- # Verify the 405 was logged and no retries occurred
102107 log_messages = [record .getMessage () for record in caplog .records ]
103- assert any (
104- "Server does not support GET for SSE events (405 Method Not Allowed)" in msg
105- for msg in log_messages
106- ), ( # pragma: no branch
107- f"Expected 405 log message not found in: { log_messages } "
108- )
108+ assert any (expected_log in msg for msg in log_messages ) # pragma: no branch
109109
110110 reconnect_messages = [msg for msg in log_messages if "reconnecting" in msg .lower ()]
111- assert len (reconnect_messages ) == 0 , ( # pragma: no branch
112- f"Should not retry on 405, but found: { reconnect_messages } "
113- )
111+ assert len (reconnect_messages ) == 0 # pragma: no branch
112+ mcp_logger .removeHandler (handler )
114113
115114
116115@pytest .mark .anyio
0 commit comments