Skip to content

fix(daemon): bound MCP proxy hello read so a wedged daemon socket never hangs the handshake#55

Merged
sunerpy merged 1 commit into
mainfrom
fix/mcp-proxy-timeout-and-skill-completion
Jun 25, 2026
Merged

fix(daemon): bound MCP proxy hello read so a wedged daemon socket never hangs the handshake#55
sunerpy merged 1 commit into
mainfrom
fix/mcp-proxy-timeout-and-skill-completion

Conversation

@sunerpy

@sunerpy sunerpy commented Jun 25, 2026

Copy link
Copy Markdown
Owner

Summary

Fixes an MCP initialize handshake hang reported by Kiro:
MCP server 'codegraph' connection timed out after 60000ms.

When a project has a .codegraph/ dir, serve --mcp runs in spawn-or-proxy
mode: it connects to the shared daemon over a Unix socket and proxies stdio.
The proxy's read_daemon_hello did a blocking read_line with no timeout.
A stale or wedged daemon.sock — a crashed daemon that left its socket behind,
or one that accepted the connection but stalled before writing its versioned
hello — made that read block forever, so the host never received an
initialize reply and timed out at 60s.

Fix

Bound the hello read with a recv timeout (DAEMON_HELLO_TIMEOUT_MS = 2000)
and clear it afterwards so steady-state JSON-RPC reads still block normally.
On timeout the read returns Err, which spawn_or_proxy already maps to a
clean fallback to direct serving — the same path that answers initialize in
<1s. Uses interprocess's native Stream::set_recv_timeout; no new dependency,
no stdout bytes, no extraction/golden changes.

Verification

  • New unit regression test read_daemon_hello_times_out_on_silent_socket:
    stands up a listener that accepts but never sends a hello and asserts the read
    returns Err near the 2s bound instead of hanging.
  • End-to-end: simulated a wedged daemon.sock (accept-but-never-hello) plus a
    live-looking pid file; serve --mcp now answers initialize in 2.1s
    (was an indefinite hang) by falling back to direct serving.
  • make ci green locally (fmt + clippy + full test suite + guardrail).

Note on the second reported issue (skill not in completion)

No code bug. The generated completion script already contains the skill
subcommand (clap_complete enumerates every subcommand). The complaint is a
stale installed completion file: skill is not in any released binary yet
(latest tag v0.12.1 predates it), and completions --install writes a static
snapshot. This release ships skill; after upgrading, re-run
codegraph completions zsh --install to refresh.

…er hangs the handshake

A stale or wedged .codegraph/daemon.sock (a crashed daemon that left its
socket, or one that accepted the connection but stalled before writing its
versioned hello) made the proxy's read_daemon_hello block forever on an
un-timed read_line. To an MCP host such as Kiro this surfaced as a 60s
"connection timed out" on the initialize handshake.

Apply a bounded recv timeout (DAEMON_HELLO_TIMEOUT_MS = 2000) around the
hello read and clear it afterwards so steady-state JSON-RPC reads still block
normally. On timeout the read returns Err, which spawn_or_proxy already maps
to a clean fallback to direct serving.
@sunerpy sunerpy merged commit 50580e3 into main Jun 25, 2026
4 checks passed
@sunerpy sunerpy deleted the fix/mcp-proxy-timeout-and-skill-completion branch June 25, 2026 09:47
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant