@@ -181,29 +181,7 @@ async def stdin_writer():
181181 try :
182182 yield read_stream , write_stream
183183 finally :
184- # MCP spec: stdio shutdown sequence
185- # 1. Close input stream to server
186- # 2. Wait for server to exit, or send SIGTERM if it doesn't exit in time
187- # 3. Send SIGKILL if still not exited (forcibly kill on Windows)
188- if process .stdin :
189- try :
190- await process .stdin .aclose ()
191- except Exception :
192- # stdin might already be closed, which is fine
193- pass
194-
195- try :
196- # Give the process time to exit gracefully after stdin closes
197- with anyio .fail_after (2.0 ):
198- await process .wait ()
199- except TimeoutError :
200- # Process didn't exit from stdin closure, use our termination function
201- # that handles child processes properly
202- await _terminate_process_with_children (process )
203- except ProcessLookupError :
204- # Process already exited, which is fine
205- pass
206-
184+ await _shutdown_process (process )
207185 await read_stream .aclose ()
208186 await write_stream .aclose ()
209187 await read_stream_writer .aclose ()
@@ -251,6 +229,35 @@ async def _create_platform_compatible_process(
251229 return process
252230
253231
232+ async def _shutdown_process (process : Process | FallbackProcess ) -> None :
233+ """
234+ MCP spec: stdio shutdown sequence
235+ 1. Close input stream to server
236+ 2. Wait for server to exit, or send SIGTERM if it doesn't exit in time
237+ 3. Send SIGKILL if still not exited (forcibly kill on Windows)
238+ """
239+
240+ # Close input stream to server
241+ if process .stdin :
242+ try :
243+ await process .stdin .aclose ()
244+ except Exception :
245+ # stdin might already be closed, which is fine
246+ pass
247+
248+ try :
249+ # Wait for server to exit gracefully after stdin closes
250+ with anyio .fail_after (2.0 ):
251+ await process .wait ()
252+ except TimeoutError :
253+ # 2. send SIGTERM if it doesn't exit in time
254+ # 3. Send SIGKILL if still not exited (forcibly kill on Windows)
255+ await _terminate_process_with_children (process )
256+ except ProcessLookupError :
257+ # Process already exited, which is fine
258+ pass
259+
260+
254261async def _terminate_process_with_children (process : Process | FallbackProcess , timeout : float = 2.0 ) -> None :
255262 """
256263 Terminate a process and all its children using psutil.
0 commit comments