Skip to content

Commit ca4e7ed

Browse files
Implement MCP spec-compliant stdio shutdown sequence
Following the MCP spec recommendations, the shutdown sequence now: 1. Closes stdin first to allow graceful server exit 2. Waits 2 seconds for the server to exit on its own 3. Only escalates to SIGTERM if the server doesn't exit 4. Finally uses SIGKILL as a last resort This unified approach works consistently across all platforms and gives well-behaved servers a chance to exit cleanly without signals. Co-Authored-By: davenpi <[email protected]>
1 parent 1d495ca commit ca4e7ed

File tree

1 file changed

+25
-4
lines changed

1 file changed

+25
-4
lines changed

src/mcp/client/stdio/__init__.py

Lines changed: 25 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -177,17 +177,38 @@ async def stdin_writer():
177177
try:
178178
yield read_stream, write_stream
179179
finally:
180-
# Clean up process to prevent any dangling orphaned processes
180+
# MCP spec: stdio shutdown sequence
181+
# 1. Close input stream to server
182+
# 2. Wait for server to exit, or send SIGTERM if it doesn't exit in time
183+
# 3. Send SIGKILL if still not exited
184+
if process.stdin:
185+
try:
186+
await process.stdin.aclose()
187+
except Exception:
188+
# stdin might already be closed, which is fine
189+
pass
190+
181191
try:
182-
process.terminate()
192+
# Give the process time to exit gracefully after stdin closes
183193
with anyio.fail_after(2.0):
184194
await process.wait()
185195
except TimeoutError:
186-
# If process doesn't terminate in time, force kill it
187-
process.kill()
196+
# Process didn't exit from stdin closure, escalate to SIGTERM
197+
try:
198+
process.terminate()
199+
with anyio.fail_after(2.0):
200+
await process.wait()
201+
except TimeoutError:
202+
# Process didn't respond to SIGTERM, force kill it
203+
process.kill()
204+
await process.wait()
205+
except ProcessLookupError:
206+
# Process already exited, which is fine
207+
pass
188208
except ProcessLookupError:
189209
# Process already exited, which is fine
190210
pass
211+
191212
await read_stream.aclose()
192213
await write_stream.aclose()
193214
await read_stream_writer.aclose()

0 commit comments

Comments
 (0)