@@ -98,14 +98,69 @@ async def test_sse_response_disconnect_before_any_event_id_fails_request() -> No
9898
9999 async with read_stream_writer , read_stream :
100100 await transport ._handle_sse_response (response , ctx )
101- message = await read_stream .receive ()
101+ with anyio .fail_after (5 ):
102+ message = await read_stream .receive ()
102103
103104 assert isinstance (message , SessionMessage )
104105 assert isinstance (message .message , JSONRPCError )
105106 assert message .message .id == 1
106107 assert message .message .error .code == CONNECTION_CLOSED
107108
108109
110+ @pytest .mark .anyio
111+ async def test_reconnection_empty_streams_count_toward_max_attempts (monkeypatch : pytest .MonkeyPatch ) -> None :
112+ class PrimingOnlyEventSource :
113+ def __init__ (self ) -> None :
114+ self .response = httpx .Response (200 )
115+
116+ async def __aenter__ (self ) -> "PrimingOnlyEventSource" :
117+ nonlocal reconnect_attempts
118+ reconnect_attempts += 1
119+ return self
120+
121+ async def __aexit__ (self , * args : object ) -> None :
122+ return None
123+
124+ async def aiter_sse (self ) -> object :
125+ yield type (
126+ "SSE" ,
127+ (),
128+ {"event" : "message" , "data" : "" , "id" : f"event-{ reconnect_attempts } " , "retry" : 0 },
129+ )()
130+
131+ def connect_sse (* args : object , ** kwargs : object ) -> PrimingOnlyEventSource :
132+ return PrimingOnlyEventSource ()
133+
134+ reconnect_attempts = 0
135+ monkeypatch .setattr (
136+ "mcp.client.streamable_http.aconnect_sse" ,
137+ connect_sse ,
138+ )
139+
140+ transport = StreamableHTTPTransport ("http://example.com/mcp" )
141+ async with httpx .AsyncClient () as client :
142+ read_stream_writer , read_stream = create_context_streams [SessionMessage | Exception ](1 )
143+ request = JSONRPCRequest (jsonrpc = "2.0" , id = 1 , method = "tools/call" , params = {"name" : "noop" , "arguments" : {}})
144+ ctx = RequestContext (
145+ client = client ,
146+ session_id = None ,
147+ session_message = SessionMessage (request ),
148+ metadata = None ,
149+ read_stream_writer = read_stream_writer ,
150+ )
151+
152+ async with read_stream_writer , read_stream :
153+ with anyio .fail_after (5 ):
154+ await transport ._handle_reconnection (ctx , "event-1" , retry_interval_ms = 0 )
155+ message = await read_stream .receive ()
156+
157+ assert reconnect_attempts == 2
158+ assert isinstance (message , SessionMessage )
159+ assert isinstance (message .message , JSONRPCError )
160+ assert message .message .id == 1
161+ assert message .message .error .code == CONNECTION_CLOSED
162+
163+
109164@pytest .mark .anyio
110165async def test_sse_response_disconnect_ignores_closed_read_stream () -> None :
111166 transport = StreamableHTTPTransport ("http://example.com/mcp" )
0 commit comments