-
Notifications
You must be signed in to change notification settings - Fork 3.2k
Open
Description
Initial Checks
- I confirm that I'm using the latest version of MCP Python SDK
- I confirm that I searched for my issue in https://github.com/modelcontextprotocol/python-sdk/issues before opening this issue
Description
Stdio transport: MCP server process survives parent death (stdin EOF not causing shutdown)
Description
When an MCP server using transport="stdio" has its parent process die (e.g. the MCP client exits, is killed, or crashes), the server process is orphaned and continues running indefinitely. It gets reparented to init/systemd and never exits.
Expected behavior
When the parent process dies and stdin reaches EOF, the server should detect this and shut down cleanly.
Analysis
Looking at the code flow in mcp/server/stdio.py:
stdin_reader()(line 60) iteratesasync for line in stdinwherestdinisanyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8"))- When stdin reaches EOF, the loop should end, closing
read_stream_writer - This should close the
read_stream, causingserver.run()'sasync for message in session.incoming_messagesloop to end run()returns →stdio_server()context exits → task group cancelsstdout_writer→ process exits
In practice, the process survives. The likely cause is that anyio.wrap_file runs the underlying readline() in a worker thread, and this thread may not wake up or propagate EOF correctly when the pipe is closed due to parent death.
Reproduction
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("test")
@mcp.tool()
async def hello() -> str:
return "hello"
mcp.run(transport="stdio")- Start the server as a subprocess from any parent process
- Kill the parent process (e.g.
kill -9 <parent_pid>) - Observe that the MCP server process is still running (
ps aux | grep python) - The process is now reparented to init/systemd and will never exit
Environment
mcpversion: 1.26.0- Python: 3.14
- OS: Linux (Fedora 43)
Fix
A proposed fix would be to monitor stdin via a selectors and when it becomes ready and returns "" shutdown the process.
Example Code
from mcp.server.fastmcp import FastMCP
mcp = FastMCP("test")
@mcp.tool()
async def hello() -> str:
return "hello"
mcp.run(transport="stdio")Python & MCP Python SDK
Environment
mcp version: 1.26.0
Python: 3.14
OS: Linux (Fedora 43)
Reactions are currently unavailable
Metadata
Metadata
Assignees
Labels
No labels