`claude.exe -p` with native stdio MCP servers wedges silently for 180+ seconds on tool call (Windows headless mode)
Summary
On Windows, calling claude.exe -p headlessly with --mcp-config pointing at a working stdio MCP config wedges silently for 180+ seconds when the model tries to call a tool. The same invocation:
- works in interactive (TTY) mode on the same host (~6s),
- works headlessly on Linux on the same host (~10s, via Docker container),
- works headlessly on macOS on the same OAuth account (~10s).
So the wedge is specific to Windows + headless claude -p + native stdio MCP + tool dispatch. No stdout, no stderr, no error — just silent hang until the timeout fires.
Affects v2.1.101 (pre-native-binary), v2.1.126 (May 1), AND v2.1.137 (May 8 — most recent at time of filing). Reproducible on a clean ~/.claude reinstall. The fixes in v2.1.128–v2.1.137 (incl. v2.1.132's "headless -p mode retrying non-transient 4xx connection failures") do NOT address this — re-tested on May 8 with both the native Windows binary at .local/bin/claude.exe (still v2.1.126 because the native installer doesn't auto-upgrade) AND the npm-installed JS shim at AppData\Roaming\npm\claude.cmd (upgraded to v2.1.137 via npm install -g @anthropic-ai/claude-code@latest). Both wedge identically: 120s timeout, empty stdout, empty stderr.
Reproduction
On a Windows host running Claude Code v2.1.101+ with a working stdio MCP config:
C:\Users\derek\.local\bin\claude.exe -p "Call mcp__clausewell-db__check_circuit and report" `
--model claude-sonnet-4-6 `
--allowedTools "mcp__clausewell-db__check_circuit" `
--mcp-config C:\Users\derek\clausewell\.mcp-blue.json `
--output-format stream-json --verbose
.mcp-blue.json declares a normal stdio MCP server (Python SQLAlchemy server in our case, but the MCP server itself is not the problem — it never receives a request).
Expected: stream-json output in ~10s.
Actual: empty stdout, empty stderr, wedges for 180+ seconds, then times out.
Discriminators (what's confirmed and ruled out)
| Variant | Result |
|---|---|
| Interactive (TTY) claude on the same Windows host | Works (~6s) |
| Headless claude inside a Linux container on the same Windows host | Works (~10s) |
| Headless claude on macOS, same OAuth account | Works (~10s) |
| Headless claude.exe -p on Windows | Wedges 180s, empty stderr |
Ruled out:
- Defender: process exclusion didn't help;
Set-MpPreference -DisableTlsParsing $truedidn't help. - Network:
curl POST /v1/messagesreturns 401 in 136ms. - OAuth / account: Linux container with the same tokens works; Mac on the same account works.
- Hooks: blanked SessionStart + PostToolUse hooks, still wedges.
- Trust state: hand-seeded
hasTrustDialogAccepted: true, still wedges. - CLI version: tested v2.1.101 (pre-native-binary) AND v2.1.126 (native binary). Both wedge.
- Fresh
~/.claude: clean reinstall, still wedges. - Concurrency: clean slate, single subprocess, still wedges.
Onset: 2026-05-02 17:45 CT. v2.1.88 had been working all morning. Defender Security Intelligence updated at 17:30:55 CT (1.449.398.0 → 1.449.415.0), 14 minutes before wedge onset — but the Defender NIS / TLS-parsing tests above rule out Defender as the actual cause. Root cause unknown but localized to Windows + headless claude.exe + tool-use API call.
Sibling bugs
These are different but possibly related — the closest sibling is #53641, which is a Windows stdio MCP timeout-not-enforced bug. Our variant is the silent-wedge form (no timeout fires either).
- anthropics/claude-code#51021 (Linux Playwright stdio MCP)
- anthropics/claude-code#50289 (macOS HTTP MCP timeout)
- anthropics/claude-code#53641 (Windows stdio MCP timeout-not-enforced — closest sibling)
- anthropics/claude-code#50559 (Windows subprocess init never completes)
Workaround we shipped
Linux container sidecar. The daemon stays on Windows (NSSM service); only the headless claude -p invocation routes through docker exec into a Linux container running claude natively.
- Flag-gated via a
CLAUSEWELL_CLAUDE_RUNNERenv var so the rollback isunset CLAUSEWELL_CLAUDE_RUNNER. - Quick-tier timeout capped at 90s in the runner path so any wedge regression surfaces fast.
- Path translation rewrites
C:\Users\derek\clausewell\…to/app/…so config paths resolve inside the container. - The MCP servers themselves are container-local copies (separate config from the daemon's MCP), so blue and green get their own runner sidecar.
This works reliably (~10s response time, same as Mac/Linux). It's a workaround, not a fix — we'd love to drop the sidecar once the underlying wedge is resolved.
Test artifact
The 7 test scripts driving the discriminator matrix above are preserved at tests/manual/claude_*.py in our repo, with tests/manual/test_windows_wedge.py as the index. They include claude_devnull (stdin redirected), claude_empty_mcp (empty vs db-only vs full MCP config), claude_matrix (model x tool-invocation cross-product), claude_mcp_children (observe MCP children spawned by claude -p), claude_mcp_tool (minimal tool-call repro), claude_netstat (network-side observation during wedge), and claude_verbose (--verbose --output-format stream-json).
Happy to share full traces / strace-equivalent output / the exact MCP config if useful. We have a clean reproducer on a stable host that's been wedging for 6+ days now.
This issue has 3 comments on GitHub. Read the full discussion on GitHub ↗