Summary
The initialize handshake timeout for stdio upstream servers is hardcoded to
30 seconds (src/protocol/stdio.rs:237, unchanged in v0.6.0) and cannot be
overridden per server or globally. Any stdio server whose first response takes
longer than 30s at gateway startup fails the handshake and enters the respawn
loop, never connecting — and the gateway does not bind its listen port until the
initial connection phase settles, so a couple of slow servers delay startup by
minutes.
Where
src/protocol/stdio.rs:237
let result = tokio::time::timeout(Duration::from_secs(30), rx)
There is a configurable circuit breaker (circuit_breaker_max_crashes,
circuit_breaker_window_secs) and an HTTP request_timeout_secs, but no knob for
the stdio handshake/initialize timeout.
Repro
Configure a stdio server launched via npx with a cold npm/npx cache:
[[servers]]
name = "desktop-commander"
command = "npx"
args = ["@wonderwhy-er/[email protected]"]
On first boot (cold cache) npx resolves + downloads the package over the network
before the MCP server even starts. Under contention from several servers spawning
in parallel this regularly exceeds 30s. Log:
WARN Respawn attempt N/5 for 'desktop-commander' failed: MCP initialize handshake
failed: Request to 'desktop-commander' timed out after 30s (method=initialize, id=10)
INFO [desktop-commander] stdout closed (server process ended)
WARN Stdio server 'desktop-commander' exited: exit status: 0
The same server, once its cache is warm, handshakes in <1s — so the process is
healthy; the 30s ceiling is simply too tight for a cold-start.
Impact
- Slow-but-healthy stdio servers (npx cold start, heavy model/embedding init,
first-run compilation) can never connect, with no config recourse.
- Gateway startup is delayed while these servers burn through respawn attempts.
Proposed fix
Add a configurable handshake timeout, ideally per-server with a global default:
[[servers]]
name = "desktop-commander"
command = "npx"
args = ["@wonderwhy-er/[email protected]"]
handshake_timeout_secs = 90 # default 30, 0 = no timeout
Fall back to a global default_handshake_timeout_secs when unset, keeping the
current 30s as the default so behavior is unchanged unless opted in.
Workaround (for reference)
Install the package globally and point the server at the resolved binary so there
is no per-boot download:
command = "/opt/homebrew/bin/desktop-commander"
args = []
or front a warm HTTP server instead of stdio. Both avoid the cold-start entirely,
but a configurable timeout is the general fix for genuinely slow-initializing
servers.
Environment
- MCPlex v0.6.0, macOS (arm64), 5 stdio/http servers, semantic router.
Summary
The
initializehandshake timeout for stdio upstream servers is hardcoded to30 seconds (
src/protocol/stdio.rs:237, unchanged in v0.6.0) and cannot beoverridden per server or globally. Any stdio server whose first response takes
longer than 30s at gateway startup fails the handshake and enters the respawn
loop, never connecting — and the gateway does not bind its listen port until the
initial connection phase settles, so a couple of slow servers delay startup by
minutes.
Where
There is a configurable circuit breaker (
circuit_breaker_max_crashes,circuit_breaker_window_secs) and an HTTPrequest_timeout_secs, but no knob forthe stdio handshake/initialize timeout.
Repro
Configure a stdio server launched via
npxwith a cold npm/npx cache:On first boot (cold cache) npx resolves + downloads the package over the network
before the MCP server even starts. Under contention from several servers spawning
in parallel this regularly exceeds 30s. Log:
The same server, once its cache is warm, handshakes in <1s — so the process is
healthy; the 30s ceiling is simply too tight for a cold-start.
Impact
first-run compilation) can never connect, with no config recourse.
Proposed fix
Add a configurable handshake timeout, ideally per-server with a global default:
Fall back to a global
default_handshake_timeout_secswhen unset, keeping thecurrent 30s as the default so behavior is unchanged unless opted in.
Workaround (for reference)
Install the package globally and point the server at the resolved binary so there
is no per-boot download:
or front a warm HTTP server instead of stdio. Both avoid the cold-start entirely,
but a configurable timeout is the general fix for genuinely slow-initializing
servers.
Environment