Skip to content

Stdio transport: MCP server process survives parent death (stdin EOF not causing shutdown) #2231

@arcivanov

Description

@arcivanov

Initial Checks

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:

  1. stdin_reader() (line 60) iterates async for line in stdin where stdin is anyio.wrap_file(TextIOWrapper(sys.stdin.buffer, encoding="utf-8"))
  2. When stdin reaches EOF, the loop should end, closing read_stream_writer
  3. This should close the read_stream, causing server.run()'s async for message in session.incoming_messages loop to end
  4. run() returns → stdio_server() context exits → task group cancels stdout_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")
  1. Start the server as a subprocess from any parent process
  2. Kill the parent process (e.g. kill -9 <parent_pid>)
  3. Observe that the MCP server process is still running (ps aux | grep python)
  4. The process is now reparented to init/systemd and will never exit

Environment

  • mcp version: 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)

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions