Transport & Lifecycle — stdio vs HTTP/SSE, Startup, Health, Reconnection
The transport choice is the single most consequential production decision in MCP server design — stdio servers die when the terminal closes; HTTP servers run until you tell them to stop.
The Decision That Changes Everything
There is one engineering decision in MCP server design that has more impact than any other: transport selection. Choose stdio, and your server's lifetime is bound to the process that spawned it. Choose HTTP/SSE, and your server runs until you tell it to stop.
Every other production concern — auth, observability, error handling — can be retrofitted. Transport is architectural. Changing from stdio to HTTP after a server is in production requires rewriting the connection layer and updating every entry in every ~/.claude.json that registered the server.
Side-by-Side: What Each Transport Actually Does
The transport lifecycle diagram tells the story. With stdio, the sequence ends badly for a 24/7 server: the client disconnects, the server exits, the next caller gets a connection error and must wait for a cold restart. With HTTP/SSE, the sequence is robust: the client connects, the client disconnects, the server keeps running, the next client connects immediately.
For Semantic Memory Layer, this matters concretely: Knox opens a Claude Code session on his laptop and queries memory_query("memory-service architecture"). The session closes. Six hours later, the ingest-corpus cron on the production server calls memory_remember() to index new lesson MDX. The server was running the entire time — it did not know or care that the laptop session had ended.
With stdio, that scenario requires the cron to spawn a new server process, wait for startup, call the tool, and clean up. Every cron run has a cold start. With HTTP, the cron just makes a POST request to an already-running server.
The Startup Sequence
The startup sequence has six phases, and none can be skipped. The most tempting skip is the index verification (phase 4). It feels redundant — if ChromaDB connected successfully, the index must be there. But connection success and index population are different checks.
A ChromaDB instance can connect successfully with zero chunks indexed. This happens when:
- The volume is new and ingest has never run
- The index was cleared for a maintenance reindex
- The volume was mounted incorrectly and the data is on the host, not the container
A server that passes DB connection (phase 3) but fails index verification (phase 4) should not start. It should log the failure clearly and exit — letting the supervisor (Docker or launchd) handle the restart with a backoff delay.
The /health endpoint (phase 5) must not respond until all six phases complete. A supervisor that polls /health before all phases complete gets a false positive — it thinks the server is ready when it cannot serve accurate tool responses.
Reconnection Strategy for HTTP/SSE
When Claude Code uses SSE (Server-Sent Events) streaming transport, the connection is a long-lived HTTP stream. If the network drops or the client closes the session, the stream closes. The server does not exit. The next client to connect gets a new stream.
The reconnection from the client's perspective is automatic and transparent: Claude Code connects to the MCP server at session start. If the session was previously running and the server is still up, reconnection is instantaneous. The server has no state tied to the previous stream — each stream is stateless from the server's perspective.
This is the production design pattern: server state lives in the database (ChromaDB), not in the stream. The stream is just a transport layer. Client disconnects are not server failures.
Now turn that pattern into code: take the stdio skeleton and make it the long-lived HTTP/SSE service this lesson describes — one where /health stays closed until the index verifies and a dropped stream never takes the process down.
Launchd vs Docker for Supervision
On the production server, Knox uses Docker to supervise Semantic Memory Layer (restart: always) rather than a launchd plist. The reason is the “Observability & Failure Modes” lesson's central lesson: when both a Docker container and a launchd plist bind the same port, SO_REUSEPORT (a socket option that lets two processes bind the same port — explained fully in that lesson) allows both to succeed silently. The OS load-balances requests between them, each serving divergent state.
The lesson from the incident: choose one supervisor and disable the other. Docker is the canonical supervisor for Semantic Memory Layer. The launchd plist was disabled. Every production MCP server should have exactly one supervisor. If you have both Docker and launchd configured for the same server, you have created the conditions for a split-brain incident.
Health Endpoint Design
The /health endpoint has three levels of completeness. Level 1 (process alive) returns 200. Level 2 (DB connected) returns { status: ok, db_connected: true }. Level 3 (pipeline healthy) returns the full status including chunk count and last_indexed_at.
For a supervisor that only checks process liveness, Level 1 is adequate. For any production MCP server where the downstream pipeline matters — which is every knowledge-OS server — Level 3 is required. The chunk count threshold (chunks_indexed > 2500) is the check that catches ingest-corpus stopping while the server continues appearing healthy.
Transport Note: Streamable HTTP vs SSE
The examples in this lesson use the SSE (Server-Sent Events) transport — the two-endpoint pattern (/sse + /messages) originally supported by Claude Code. Streamable HTTP (StreamableHTTPServerTransport) is the modern successor: it uses a single /mcp POST endpoint and is what current Claude Code uses for HTTP-transport servers. SSE transport remains a valid compatibility option, but new servers should implement Streamable HTTP. The ~/.claude.json registration ("url": "https://host/mcp") targets the Streamable HTTP endpoint.
What's Next
The next lesson covers auth and secrets — bearer tokens, scoped access, the five ways secrets accidentally leak through MCP servers, and the correct secret injection flow from Doppler to the server process to the validation layer.