@@ -181,29 +181,7 @@ async def stdin_writer():
181
181
try :
182
182
yield read_stream , write_stream
183
183
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 )
207
185
await read_stream .aclose ()
208
186
await write_stream .aclose ()
209
187
await read_stream_writer .aclose ()
@@ -251,6 +229,35 @@ async def _create_platform_compatible_process(
251
229
return process
252
230
253
231
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
+
254
261
async def _terminate_process_with_children (process : Process | FallbackProcess , timeout : float = 2.0 ) -> None :
255
262
"""
256
263
Terminate a process and all its children using psutil.
0 commit comments