Skip to content

Windows stdio server: subprocess spawned inside a tool handler hangs forever when it inherits the server stdin (stdin=DEVNULL fixes) #3788

Open

Description

@dadawalissonsilva

What happens

A subprocess spawned from INSIDE a FastMCP tool handler (stdio server, Windows) hangs forever when the child inherits the server stdin — which is the default in subprocess.run/create_subprocess_exec if stdin is not set.

Repro (Windows, minimal)

FastMCP server (stdio) with a tool that spawns a trivial child (e.g. python -c "print(1)"):

  • subprocess.run([...]) with inherited stdin → child never completes; tool call hangs (abort only via MCP idle-timeout — 30 min by default in Claude Code)
  • same call with stdin=subprocess.DEVNULL → completes in ~0.6 s

A/B tested with FastMCP pure (zero user imports) to rule out user code; also reproduced via asyncio.create_subprocess_exec on the event loop. The moment the child stops sharing the server jsonrpc stdin, everything works.

Suggested guard

Document/force stdin=DEVNULL for subprocesses spawned in handlers, or detach the child stdin by default in any subprocess helper the SDK provides — inheriting the protocol pipe is never what a tool wants.

Environment: Windows 11 (26200), Python 3.14, mcp/fastmcp stdio transport, Claude Code 2.1.310 host.

Activity

  1. added
    bugSomething isn't working
    v1Affects the v1.x maintenance line
    on Oct 30, 2026
  2. Ben-Home commented on Oct 30, 2026

    @Ben-Home
  3. prafulla2121 commented on Oct 31, 2026

    @prafulla2121

    Confirmed on Windows 11 (26200), Python 3.12.10, mcp 1.30.0: the inherit tool hangs, the stdin=DEVNULL one returns in ~0.4s.

    One correction on the cause: it isn't a second reader on the pipe. Popen returns immediately, and hostname.exe / cmd /c echo 1 children complete fine. Only a Python child freezes, and it stays alive at ~4 MB, stuck in interpreter startup. That is CPython gh-78961 (synchronous pipe I/O is serialized on Windows, and the server always has a blocking read pending on stdin), so this looks like a duplicate of #671.

    It's already fixed on main by #3117: the same repro against 2.3.1.dev17 returns in ~0.7s. v1.x doesn't have it (no stdin diversion in src/mcp/server/stdio.py). On v1 the workaround is stdin=subprocess.DEVNULL in the handler.

    If a backport to v1.x is wanted, I'd be glad to do it. Otherwise this can be closed in favour of #671.

    Disclosure: I investigated this with the help of an AI coding assistant (Claude Code). I ran every repro above myself on my machine.

  4. techaurah commented on Nov 1, 2026

    @techaurah

    Hi, we are TechAura. We would like to work on this if a maintainer is willing to assign it to us. Our read of the problem: in a stdio server, a subprocess spawned inside a tool handler inherits the server stdin and blocks waiting on it, while passing stdin=DEVNULL returns immediately. The fix we have in mind is to stop tool-handler-spawned subprocesses from inheriting server stdin, defaulting to DEVNULL, with a regression test covering the hang case. Happy to adjust the approach if the maintainers prefer a different shape.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    bugSomething isn't workingv1Affects the v1.x maintenance line

    Type

    No type

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions