Summary
DaemonConfig.idle_shutdown_seconds defaults to 30. The intent is "let idle daemons clean themselves up." But for MCP clients whose tool set is frozen at session-start (Claude Code is the example I hit; suspect Codex CLI and others), the default causes a perpetual disconnect loop:
- Claude Code session starts, probes engram MCP, finds daemon not running -> registers engram as "Failed to connect" for the session lifetime.
- A SessionStart hook or user action spawns the daemon AFTER session-start.
- Daemon comes up. No proxy connects, because Claude Code's MCP set is already frozen for this session.
- Daemon's
_connected_proxies == 0 condition holds, idle timer fires at 30s, daemon exits.
- Next session-start probe again sees "Failed to connect". Loop.
The loop is invisible to the user: each session correctly reports engram: ... Failed to connect and degrades silently to alternative persistent-memory MCPs. Detection only happens if the user explicitly checks claude mcp list or notices missing engram tool availability.
Reproduction
- Default config (no
daemon: block in vault config -> idle_shutdown_seconds=30).
- Engram registered in
~/.claude.json mcpServers.
- Start Claude Code session. Notice
claude mcp list shows engram: ... Failed to connect.
- Run
engram daemon start --vault <name> --detach from any shell.
- Verify daemon ready.
- Wait 35s, re-run
engram daemon status --vault <name>.
- Observe: daemon stopped (the idle timer fired before any proxy connected, because the MCP set was already frozen at step 3).
In my logs the pattern was textbook: every daemon process for 9+ consecutive invocations died with engram daemon stopped at exactly 30.0 +/- 0.1 seconds after engram daemon ready.
Proposed change
Two options, not mutually exclusive:
- (a) Change the default to 0 (always-on). Rationale: the 30s default optimizes for a "casual one-off CLI invocation" scenario that isn't actually engram's primary deployment shape. Local-first MCP servers are persistent by design; the daemon SHOULD stay up until explicitly stopped.
- (b) Document the gotcha prominently in
docs/DAEMON_MODE.md. Specifically: a section titled "Frozen-tool-set MCP clients (Claude Code, etc.)" explaining the loop and recommending idle_shutdown_seconds: 0 for these deployments.
(a) + (b) together would also work: change the default, document why, leave the knob configurable for the casual-CLI case (where the user might want auto-cleanup).
Workaround (current)
Set idle_shutdown_seconds: 0 in the per-vault engram.config.yaml:
daemon:
idle_shutdown_seconds: 0
Plus a SessionStart hook that calls engram daemon start --vault <name> --detach if the MCP shows Failed to connect. Both together close the loop. The hook is in my dotfiles; the config knob is in the vault repo.
Context
Surfaced 2026-05-16 during the same session that produced the three filed bug issues above. The 30s exact periodicity in the daemon log was the give-away — voluntary shutdowns, not crashes. Took longer than it should have to spot because "idle shutdown" intuitively reads as "the daemon was actually idle" (correct, by the daemon's definition) rather than "the daemon was waiting for a proxy that will never connect in this session" (the actual operational situation).
Summary
DaemonConfig.idle_shutdown_secondsdefaults to 30. The intent is "let idle daemons clean themselves up." But for MCP clients whose tool set is frozen at session-start (Claude Code is the example I hit; suspect Codex CLI and others), the default causes a perpetual disconnect loop:_connected_proxies == 0condition holds, idle timer fires at 30s, daemon exits.The loop is invisible to the user: each session correctly reports
engram: ... Failed to connectand degrades silently to alternative persistent-memory MCPs. Detection only happens if the user explicitly checksclaude mcp listor notices missing engram tool availability.Reproduction
daemon:block in vault config ->idle_shutdown_seconds=30).~/.claude.jsonmcpServers.claude mcp listshowsengram: ... Failed to connect.engram daemon start --vault <name> --detachfrom any shell.engram daemon status --vault <name>.In my logs the pattern was textbook: every daemon process for 9+ consecutive invocations died with
engram daemon stoppedat exactly 30.0 +/- 0.1 seconds afterengram daemon ready.Proposed change
Two options, not mutually exclusive:
docs/DAEMON_MODE.md. Specifically: a section titled "Frozen-tool-set MCP clients (Claude Code, etc.)" explaining the loop and recommendingidle_shutdown_seconds: 0for these deployments.(a) + (b) together would also work: change the default, document why, leave the knob configurable for the casual-CLI case (where the user might want auto-cleanup).
Workaround (current)
Set
idle_shutdown_seconds: 0in the per-vaultengram.config.yaml:Plus a SessionStart hook that calls
engram daemon start --vault <name> --detachif the MCP shows Failed to connect. Both together close the loop. The hook is in my dotfiles; the config knob is in the vault repo.Context
Surfaced 2026-05-16 during the same session that produced the three filed bug issues above. The 30s exact periodicity in the daemon log was the give-away — voluntary shutdowns, not crashes. Took longer than it should have to spot because "idle shutdown" intuitively reads as "the daemon was actually idle" (correct, by the daemon's definition) rather than "the daemon was waiting for a proxy that will never connect in this session" (the actual operational situation).