Overview
MCP defines two standard transports: stdio and HTTP with Server-Sent Events. The choice is not cosmetic. stdio is the default for local servers and eliminates network configuration entirely. HTTP/SSE is required for remote servers, shared services, and multi-tenant deployments. Each transport has distinct lifecycle semantics, reconnection behavior, and security posture. Picking the wrong one adds operational overhead without benefit. See mcp-protocol for how JSON-RPC messages ride on top of these transports.
Use stdio for local servers; it is the simplest deployment
In the stdio transport, the client launches the server as a child process and communicates over stdin and stdout. Each JSON-RPC message is a newline-delimited JSON string. There are no ports, no TLS certificates, and no authentication tokens beyond what the process inherits from its environment.
{
"mcpServers": {
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
}
}
}Use stdio when the server and client run on the same machine, when the user is the sole consumer, and when the server does not need to persist state across client sessions. Filesystem servers, local database servers, and dev-tool servers belong here.
Use HTTP/SSE for remote and shared servers
The HTTP with SSE transport runs the server as an HTTP service. The client opens a long-lived SSE connection to receive server-initiated messages (notifications, progress). Tool calls and resource fetches go over standard HTTP POST requests.
Use HTTP/SSE when the server is a SaaS integration (GitHub, Notion, Slack), when multiple clients share the same server, or when the server must be deployed independently of the client machines. Remote servers require auth at the transport layer: a bearer token or mTLS on every request.
HTTP/SSE is also the correct choice for servers behind a CDN or reverse proxy. Cloudflare Workers, for example, can host a lightweight MCP proxy layer. See cloudflare for deployment patterns.
Handle reconnection explicitly in HTTP/SSE clients
SSE connections drop. Networks partition, servers restart, and load balancers time out idle connections. A production HTTP/SSE MCP client must implement reconnection with exponential backoff and re-send the initialize handshake on every new connection.
async def connect_with_retry(url: str, max_attempts: int = 5):
for attempt in range(max_attempts):
try:
async with sse_client(url) as client:
await client.initialize()
return client
except ConnectionError:
await asyncio.sleep(2 ** attempt)
raise RuntimeError("MCP server unreachable after retries")Reconnection is invisible in stdio because the client controls the server process lifecycle. In HTTP/SSE it is the client’s responsibility. Build it before shipping.
Manage server lifecycle: startup, shutdown, and health checks
For stdio servers, lifecycle is tied to the client process. The server starts when the client starts, receives SIGTERM or EOF when the client exits, and should flush logs and close connections in its shutdown handler.
For HTTP servers, treat startup and shutdown as first-class concerns. Provide a /health endpoint that returns 200 when the server is ready to accept connections. Wire a graceful shutdown handler that completes in-flight requests before stopping. Emit a structured startup log that records the server version, transport, and declared capabilities.
A server that starts slowly or crashes silently during development is invisible to the client until tool calls begin failing. Health checks surface problems earlier. Log the startup event with a timestamp so latency tracking can measure cold-start overhead.
Apply per-session isolation in multi-tenant HTTP deployments
When multiple agents share a single HTTP/SSE MCP server, each session must be isolated. Rate limits, credential scoping, and audit logs must be keyed to the session, not the server instance.
Assign each client a session token at initialize time. Validate it on every subsequent request. Store session state (rate-limit counters, audit log entries) under the session token key. Never allow one session to read another session’s state.
Multi-tenant deployments also require care with secrets. A server that serves multiple organizations must scope credentials per organization at the session level, not at the server level. Mixing credentials across sessions is a data-isolation violation.