You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
{{ message }}
Repository navigation
Make stdio MCP handshake timeout configurable (currently hardcoded 30s in stdio.rs:237) #20
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/desktop-commander@0.2.38"]
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/desktop-commander@0.2.38"]
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:
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.
The handshake (initialize) and steady-state tool-call timeouts are now fully independent:
security.default_handshake_timeout_secs — global default for the initial stdio handshake (30s, matches previous hardcoded behavior)
servers[].handshake_timeout_secs — per-server override, e.g. for a slow-starting npx server:
[[servers]]
name = "slow-npx-server"command = "npx"args = ["-y", "@some/mcp-server"]
handshake_timeout_secs = 90# default 30, 0 = no timeout
security.request_timeout_secs continues to apply only to steady-state tools/call/etc. requests once connected — it's no longer shared with the handshake.
Also implemented 0 = no timeout for both, which was documented but not actually honored anywhere before this change.
Activity
ModernOps888 commentedon Jul 21, 2026
Fixed in #21.
The handshake (
initialize) and steady-state tool-call timeouts are now fully independent:security.default_handshake_timeout_secs— global default for the initial stdio handshake (30s, matches previous hardcoded behavior)servers[].handshake_timeout_secs— per-server override, e.g. for a slow-startingnpxserver:security.request_timeout_secscontinues to apply only to steady-statetools/call/etc. requests once connected — it's no longer shared with the handshake.Also implemented
0 = no timeoutfor both, which was documented but not actually honored anywhere before this change.Will close once #21 merges.
Merge pull request #21 from ModernOps888/security/rbac-and-hardening-…
Fix RBAC self-assertion bypass, harden dashboard/cache/config, make s…