# Stateless MCP — one shared local daemon instead of ~50 resident plugin processes

outcome: plugin MCP servers (quietcontext first) become ONE supervised localhost HTTP service all sessions share; sessions connect lazily on first tool use (Claude Code v2.1.221+ discovery cache), eliminating ~50× resident child processes and the session-start spawn burst.
status: PROPOSED — findings reviewed and presented 2026-08-15; awaiting owner pick to start the prototype lane.
source request: owner 2026-08-15 — "the new stateless MCP protocol may be the solution... /ask-codex gpt-5.6-sol/low to web research this topic... you review it, and present the findings"; follow-up: "you dont need anything LAZY with stateless MCP. it just does not work this way anymore."

## Findings (research log: ~/.local/state/overdeck/systray/runtime/logs/cdx/20260815-130833-2825.log)

- Streamable HTTP is spec-standard (2025-03-26 through 2026-07-28 revisions; 2026 removes protocol sessions entirely). One HTTP process serves many clients.
- Claude Code ships `"type": "http"` plugin/servers config, headers/OAuth/headersHelper, reconnect with backoff, and (v2.1.221+) cached discovery: "connects on first use" — documented for remote HTTP/SSE, NOT stdio. No commitment exists to ever lazy-spawn stdio servers.
- Migration: host one daemon, one route per logical server (`/quietcontext/mcp`, ...), bind 127.0.0.1 only, bearer token; make tools carry working-root explicitly (no per-session env). Avoid stdio-to-HTTP bridges that spawn a child per request.

## Prototype slice (on owner go)

quietcontext as native HTTP under systemd; point plugin config at the URL; measure: total RSS before/after, session-start latency, daemon RSS at 50 cached-but-unused sessions, 50 concurrent calls. Failure modes named in research: shared mutable state, crash concentration (supervisor + health checks), discovery-cache staleness after tool changes.

Next executable action: owner go → claim lane, build quietcontext-over-HTTP prototype with the measurement set above.


## Follow-up slice — compaction resets the burst-hint counter (owner-designed 2026-08-15, queued behind daemon-hardening merge)

Detection already exists: `compact-context-restore.mjs` fires on SessionStart source=compact (and `auto-handoff.mjs` covers PreCompact). Delivery is the only missing piece:
- Hook side: on source=compact, ONE authenticated HTTP request to the daemon (127.0.0.1:48619, bearer token from ~/.local/state/quietcontext/daemon.token): "root X compacted — reset its hint counter." One line in the compact-restore path or a small dispatcher module.
- The notification CARRIES THE SESSION ID (hooks know it; plugin MCP header config cannot — `${CLAUDE_SESSION_ID}` does not expand there, empirically tested 2026-08-15, which is why the daemon keys by root). This gives the daemon its first per-session mark: v1 uses it only for logging/ledger attribution; per-session hint state becomes possible later without another transport change.
- Daemon side: tiny endpoint (or parameter on the existing surface) zeroing the per-root hint counter.
- Documented behavior, not a surprise: hint state is per working ROOT, not per session — two sessions sharing a root (mainly the main-checkout root) both get reset by either one's compaction. Corner case, accepted.
- Until this lands, the idle-timeout approximation (>10min idle resets) ships with daemon-hardening and is the interim behavior.
