@@ -166,6 +166,70 @@ async def run_server():
166166 await server_run_returned .wait ()
167167
168168
169+ @pytest .mark .anyio
170+ async def test_server_bounds_drain_on_read_eof_when_handler_never_finishes ():
171+ handler_started = anyio .Event ()
172+ handler_cancelled = anyio .Event ()
173+ server_run_returned = anyio .Event ()
174+
175+ async def handle_call_tool (ctx : ServerRequestContext , params : CallToolRequestParams ) -> CallToolResult :
176+ handler_started .set ()
177+ try :
178+ await anyio .sleep_forever ()
179+ finally :
180+ handler_cancelled .set ()
181+ raise AssertionError # pragma: no cover
182+
183+ server = Server ("test" , on_call_tool = handle_call_tool )
184+
185+ to_server , server_read = anyio .create_memory_object_stream [SessionMessage | Exception ](10 )
186+ server_write , from_server = anyio .create_memory_object_stream [SessionMessage ](10 )
187+
188+ async def run_server ():
189+ await server .run (
190+ server_read ,
191+ server_write ,
192+ server .create_initialization_options (),
193+ drain_on_read_close = True ,
194+ read_eof_drain_timeout_seconds = 0.05 ,
195+ )
196+ server_run_returned .set ()
197+
198+ init_req = JSONRPCRequest (
199+ jsonrpc = "2.0" ,
200+ id = 1 ,
201+ method = "initialize" ,
202+ params = InitializeRequestParams (
203+ protocol_version = LATEST_PROTOCOL_VERSION ,
204+ capabilities = ClientCapabilities (),
205+ client_info = Implementation (name = "test" , version = "1.0" ),
206+ ).model_dump (by_alias = True , mode = "json" , exclude_none = True ),
207+ )
208+ initialized = JSONRPCNotification (jsonrpc = "2.0" , method = "notifications/initialized" )
209+ call_req = JSONRPCRequest (
210+ jsonrpc = "2.0" ,
211+ id = 2 ,
212+ method = "tools/call" ,
213+ params = CallToolRequestParams (name = "slow" , arguments = {}).model_dump (by_alias = True , mode = "json" ),
214+ )
215+
216+ with anyio .fail_after (2 ):
217+ async with anyio .create_task_group () as tg , to_server , server_read , server_write , from_server :
218+ tg .start_soon (run_server )
219+
220+ await to_server .send (SessionMessage (init_req ))
221+ await from_server .receive () # init response
222+ await to_server .send (SessionMessage (initialized ))
223+ await to_server .send (SessionMessage (call_req ))
224+
225+ await handler_started .wait ()
226+ await to_server .aclose ()
227+
228+ await server_run_returned .wait ()
229+
230+ assert handler_cancelled .is_set ()
231+
232+
169233@pytest .mark .anyio
170234async def test_server_reraises_handler_cancellation_when_server_is_cancelled ():
171235 """If the server task is cancelled (e.g. KeyboardInterrupt), in-flight
0 commit comments