Skip to content

bug(mcp): server gets SIGINT'd 2-5s after handshake under Claude Code, never reconnects #648

Description

@ardelperal
  • I have searched existing issues and this is not a duplicate
  • I understand this issue needs status:approved before a PR can be opened

📝 Bug Description

The engram mcp stdio server fails its lifecycle handshake against Claude Code's MCP client. After initialize succeeds, Claude Code sends SIGINT to the server process 2-5 seconds later, clears its connection cache "for reconnection", and never reconnects for the rest of the session. The mem_* tools declared by the server never become reachable to the model.

This was originally reported against gentle-ai in Gentleman-Programming/gentle-ai#1019. The gentle-ai-side hardening landed in Gentleman-Programming/gentle-ai#1608 (tagged lifecycle regression test, gentle-ai doctor version gate with a dormant threshold). The actual lifecycle fix needs to live here, in the engram binary.

The investigation (see Gentleman-Programming/gentle-ai#1608 runbook and the upstream investigation in the PR diff at openspec/changes/debug-1019-engram-mcp-sigint/exploration.md) points to the engram MCP server failing to send the notifications/initialized notification after the initialize handshake. Per the MCP spec, the server must send this notification. Claude Code's stdio MCP client has a timeout (~2-5s) for this lifecycle step; when it never arrives, the client terminates the server process.

A reference MCP lifecycle implementation that DOES emit the notification lives in gentle-ai/internal/components/communitytool/pi_codegraph.go:550-574. For comparison, that reference sends the notification immediately after reading the initialize response and before any subsequent request.

🔄 Steps to Reproduce

Reproduced by the user in #1019 (Gentleman-Programming/gentle-ai):

  1. Have engram@engram enabled as a Claude Code plugin (enabledPlugins in ~/.claude/settings.json, installed via the gentle-ai/Engram marketplace, binary via Linuxbrew: /home/linuxbrew/.linuxbrew/bin/engram, engram v1.17.0).
  2. Start a new Claude Code session in any project (claude in a fresh terminal).
  3. Immediately after the SessionStart hook fires, inspect ~/.cache/claude-cli-nodejs/<project>/mcp-logs-plugin-engram-engram/<latest>.jsonl.
  4. Try calling any mem_* tool, or search for it via ToolSearch with query select:mem_save or mcp__plugin_engram_engram.

✅ Expected Behavior

The engram MCP connection stays alive for the duration of the session (as it does for other plugins/MCP servers, and as it used to for engram itself in older sessions — see Additional Context), and the mem_* tools declared by the server (mem_save, mem_search, mem_context, mem_session_summary, mem_get_observation, mem_save_prompt as always-loaded; the rest as ToolSearch-deferred) are actually reachable by the model.

❌ Actual Behavior

Every session start produces this exact pattern in mcp-logs-plugin-engram-engram/*.jsonl:

{"debug":"Starting connection with timeout of 30000ms", ...}
{"debug":"Successfully connected (transport: stdio) in ~190ms", ...}
{"debug":"Server instructions truncated from 2539 to 2048 chars", ...}
{"debug":"Connection established with capabilities: {\"hasTools\":true,...}", ...}
{"debug":"Sending SIGINT to MCP server process", ...}          ← 2-5s later, no tool ever called
{"debug":"UNKNOWN connection closed after Ns (cleanly)", ...}
{"debug":"Cleared connection cache for reconnection", ...}
{"debug":"MCP server process exited cleanly", ...}

No further connection attempt is logged for the rest of the session. ToolSearch with queries like select:mem_save or mcp__plugin_engram_engram returns "No matching deferred tools found".

Manual verification that the binary itself responds to raw JSON-RPC outside Claude Code (run by the user in #1019):

$ printf '{"jsonrpc":"2.0","id":1,"method":"initialize",...}\n{"jsonrpc":"2.0","id":2,"method":"tools/list","params":{}}\n' | engram mcp --tools=agent
{"jsonrpc":"2.0","id":1,"result":{...,"serverInfo":{"name":"engram","version":"0.1.0"},...}}
{"jsonrpc":"2.0","id":2,"result":{"tools":[... 18 valid tool definitions ...]}}

This proves the binary can complete the initialize + tools/list exchange. The manual test does NOT verify that the server emits notifications/initialized after initialize, which is the suspected gap.

🖥️ Environment

Operating System: Linux (Arch/Manjaro) — specifically CachyOS

Engram Version: v1.17.0 (per the reporter; verify with engram version — note: gentle-ai currently invokes engram version without double-dash per internal/components/engram/verify.go:31-37)

Agent / Client

Claude Code (CLI v2.1.201, up to date), registered via enabledPlugins["engram@engram"] in ~/.claude/settings.json

📋 Relevant Logs

Full mcp-logs-plugin-engram-engram/*.jsonl from the reporter:

{"debug":"Starting connection with timeout of 30000ms","timestamp":"2026-07-05T10:02:44.340Z","sessionId":"4f3f9430-...","cwd":"/home/ginopc/Desarrollo/Billetera"}
{"debug":"Successfully connected (transport: stdio) in 191ms","timestamp":"2026-07-05T10:02:44.528Z", ...}
{"debug":"Server instructions truncated from 2539 to 2048 chars","timestamp":"2026-07-05T10:02:44.529Z", ...}
{"debug":"Connection established with capabilities: {\"hasTools\":true,\"hasPrompts\":false,\"hasResources\":false,\"hasResourceSubscribe\":false,\"serverVersion\":{\"name\":\"engram\",\"version\":\"0.1.0\"}}","timestamp":"2026-07-05T10:02:44.529Z", ...}
{"debug":"Sending SIGINT to MCP server process","timestamp":"2026-07-05T10:02:48.884Z", ...}
{"debug":"UNKNOWN connection closed after 4s (cleanly)","timestamp":"2026-07-05T10:02:48.890Z", ...}
{"debug":"Cleared connection cache for reconnection","timestamp":"2026-07-05T10:02:48.896Z", ...}
{"debug":"MCP server process exited cleanly","timestamp":"2026-07-05T10:02:48.938Z", ...}

💡 Additional Context

Symptom onset. The reporter has healthy logs from before ~2026-07-03 showing the server connecting once and staying alive for the full session (hours, dozens of successful mem_save/mem_search/mem_update calls), with SIGINT only appearing once at genuine session end and no "Cleared connection cache for reconnection" line. That line only appears in the broken pattern. The reporter could not identify what changed on their side around that date.

Ruled out by the reporter. Outdated Claude Code CLI (already on latest 2.1.201), duplicate MCP registration (no engram entry exists in the project's .mcp.json, in ~/.claude.json mcpServers, or in ~/.claude/settings.json mcpServers — only via the plugin's own .mcp.json with enabledPlugins["engram@engram"] = true), and a corrupt/misbehaving binary (the manual JSON-RPC test above proves it responds correctly).

Reproduces across multiple projects on the same machine (confirmed in at least 2 separate project directories), and persists across a full process restart of Claude Code (not just /clear — closing the terminal entirely and relaunching claude in a new terminal reproduces the exact same failure).

gentle-ai-side companion work (for maintainer cross-reference; do not duplicate):

  • Gentleman-Programming/gentle-ai#1019 — original report (high priority, approved).
  • Gentleman-Programming/gentle-ai#1608 — gentle-ai-side hardening (tagged stdio lifecycle test behind //go:build engram_lifecycle, gentle-ai doctor version gate with dormant MinEngramVersionForHealthyLifecycle = "0.0.0", runbook at docs/engram-mcp-lifecycle.md). That PR documents this fix belongs in gentleman-programming/engram and includes the upstream investigation in openspec/changes/debug-1019-engram-mcp-sigint/exploration.md.

Suspected fix direction (not yet confirmed without binary-side instrumentation): add the notifications/initialized notification emit to the MCP server handshake path in cmd/engram/main.go (the mcp subcommand), mirroring the lifecycle in gentle-ai/internal/components/communitytool/pi_codegraph.go:550-574. Optionally also implement periodic ping response and a SIGHUP reload handler (see Gentleman-Programming/engram#206 for a related but distinct feature request about SIGHUP DB reload).

Open data requests for binary maintainers:

  1. Does the engram mcp server emit notifications/initialized after initialize? (Recipe: add a temporary stderr log line in cmd/engram/main.go immediately after writing the initialize response.)
  2. Does the server respond to ping requests from Claude Code's MCP client?
  3. Does the bug reproduce when the user deletes ~/.claude/mcp/engram.json and re-runs gentle-ai to force a fresh registration?
  4. Does Claude Code itself have a behavior change around 2026-07-03 that could be sending SIGINT prematurely even for correctly-behaving servers?

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions