Windows: stdio MCP server spawning ssh.exe never connects (same server works via macOS client, Codex CLI, and manual pipe)
Environment
- Claude Code 2.1.178, 2.1.209, and 2.1.215 (native Windows installer), reproduced identically on two separate Windows 11 machines
- ssh client:
C:\Windows\System32\OpenSSH\ssh.exe(OpenSSH_for_Windows_9.5p2) - Server: Python FastMCP stdio server (mcp 1.28.1) on a remote macOS host, launched over SSH via a wrapper script
Summary
A user-scope stdio MCP server whose command is ssh.exe relaying to a remote launcher never connects on Windows. claude mcp get <name> reports Failed to connect, and in real claude -p sessions the server's tools are absent. The identical configuration works everywhere else I can test it.
Evidence matrix
| Test | Result |
|---|---|
| Exact registered command run manually from cmd in the same user session, piping a JSON-RPC initialize | Handshake completes, response in ~7 s |
| Same server registered in Claude Code on macOS with equivalent ssh args | Connected |
| OpenAI Codex CLI on the same Windows machine, identical ssh.exe command and args (startup_timeout_sec = 120) | Connects, completes tool calls |
| Other stdio servers on the same Windows Claude install (local op run -- node ..., a local .exe server) | Connected |
| Remote HTTP MCP servers on the same install | Connected |
| The ssh-hop stdio server, any variant below | Failed to connect |
Variants tried (all fail identically)
- Plain:
C:\Windows\System32\OpenSSH\ssh.exe -o BatchMode=yes -o ConnectTimeout=10 user@host /path/launcher.sh - With
-o IdentityAgent=none -o IdentitiesOnly=yes - Wrapped:
C:\Windows\System32\cmd.exe /c ssh.exe ... - Wrapped with stderr discarded:
cmd.exe /c "ssh.exe ... 2>nul" MCP_TIMEOUT=120000set as user env (verified present withecho %MCP_TIMEOUT%in the session running the health check)
Server cold start is ~6-10 s (remote secret resolution plus Python startup), far under 120 s. Auth is BatchMode key auth with the host key pre-trusted; the transport is proven by the manual handshake above.
Expected / actual
Expected: the health check and sessions complete the handshake, as the same command does when run manually and as the same server does from the macOS client.
Actual: Failed to connect health check on Windows; tools unavailable in -p sessions; identical across three versions and two machines.
Additional notes
- On these native Windows installs,
%LOCALAPPDATA%\claude-cli-nodejsdoes not exist and there is no%USERPROFILE%\.claude\logs, so I could not locate client-side MCP logs to attach.claude --debug mcp listproduced no per-server error lines for this server. Happy to run any debug flag and attach output if you can point me at where MCP spawn logs land on the native Windows build. - Guess at the fault class, for what it's worth: something in how the Windows build spawns or pipes stdio to an
ssh.exechild (console/handle inheritance or pipe draining), since the same bytes flow fine when cmd owns the child, and non-ssh stdio children work.