Skip to content
Ashish.
All posts
Diagram comparing MCP transport layers: stdio, HTTP, and Streamable HTTP.
6 min readDevelopmentBackend Developers, Platform EngineersFeatured#mcp#transport#stdio#http#streamable-http#sse#session-management#backend

MCP Transport Layers: stdio, HTTP, and Streamable HTTP

An examination of MCP transport protocols including stdio, HTTP, and Streamable HTTP, focusing on session management and SSE for backend developers.

By Ashish KumarPart 3 of MCP in Depth

The Model Context Protocol (MCP) defines a standardized way for Large Language Models (LLMs) to interact with external data sources and tools, but the mcp transport layer itself is agnostic to the underlying implementation. For backend developers, understanding the mechanics of how MCP moves bytes is critical because the transport layer dictates your system’s architecture, security boundary, and state management strategy.

Part 3 of the MCP in Depth series explores the three primary transport mechanisms defined in the MCP specification: stdio, HTTP, and Streamable HTTP. Each solves a different problem domain. Using the wrong one isn’t just a performance issue; it’s an architectural mismatch.

stdio: The Local Execution Boundary

stdio (standard input/output) is the simplest and most common transport for local development and single-process deployments. It treats the MCP server as a child process of the MCP client (the host application).

The Mechanism

Schematic diagram showing a client process spawning a child server process with stdin/stdout pipes for MCP stdio transport. Schematic diagram showing an HTTP POST request from a client to an MCP server endpoint, with a response arrow.

When you run an MCP server via stdio, the client spawns a process (e.g., python server.py). The client writes JSON-RPC messages to the child’s stdin and reads responses from its stdout.

The framing mechanism is simple: newline-delimited JSON. Each message is a complete JSON object terminated by a \n character. There is no length prefix, no WebSocket frames, and no TCP handshake.

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "initialize",
  "params": {
    "protocolVersion": "2024-11-05",
    "capabilities": {},
    "clientInfo": { "name": "my-client", "version": "1.0.0" }
  }
}

Why Use It?

  1. Process Isolation: If the MCP server crashes, it doesn’t take down the main application. It’s a separate OS process.
  2. No Network Stack: No ports, no firewalls, no TLS certificates required for local communication.
  3. Simplicity: Ideal for CLI tools or desktop applications where the server is bundled locally.

The Limitation

stdio is inherently local and synchronous. You cannot host an MCP server on a remote machine using stdio. The server must live on the same filesystem as the client. If you need cross-network communication, you must migrate to HTTP-based transports.

HTTP: The Stateless Request-Response Bridge

The basic HTTP transport treats MCP as a standard RESTful API. The client sends an HTTP POST request to the server, and the server responds with an HTTP response containing the JSON-RPC payload.

The Mechanism

  • Endpoint: Typically /mcp or /.
  • Method: POST.
  • Content-Type: application/json.
  • Payload: A single JSON-RPC message.
POST /mcp HTTP/1.1
Host: api.example.com
Content-Type: application/json
 
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/list",
  "params": {}
}

Why Use It?

  1. Firewall-Friendly: HTTP passes through almost every corporate firewall.
  2. Stateless: Each request is independent. This makes scaling trivial with load balancers.
  3. Familiarity: Backend engineers can use existing HTTP clients (curl, axios, httpx) without learning new protocols.

The Limitation

HTTP is request-response only. The server cannot initiate contact with the client. If the MCP server needs to notify the client about a background task completion or a real-time update, basic HTTP cannot do this natively. You would need to implement polling or fall back to WebSockets, which MCP does not define as a native transport.

Furthermore, without external correlation IDs (like sessionId in the body), maintaining context across multiple requests is up to the application layer, not the transport layer.

Streamable HTTP: The Persistent State Machine

Streamable HTTP is the most sophisticated transport, designed to bridge the gap between HTTP’s ubiquity and the need for persistent, bidirectional-like communication. It combines HTTP POST for requests with Server-Sent Events (SSE) for server-to-client notifications.

The Mechanism

Streamable HTTP uses two distinct channels over HTTP:

  1. Client-to-Server: Standard HTTP POST requests.
  2. Server-to-Client: An SSE stream that stays open, allowing the server to push multiple messages over time.

Session Management

Schematic diagram showing Streamable HTTP with a client sending POST requests and receiving an SSE stream from the server, including Session-Id header.

The key innovation is the Mcp-Session-Id header. When a client first connects, the server may not have a session. The client initiates a session by sending a POST request. The server responds with a 200 OK (or 202 Accepted) containing a Mcp-Session-Id header and begins an SSE stream. Subsequent requests use this ID to route messages to the existing stream.

HTTP/1.1 200 OK
Content-Type: text/event-stream
Mcp-Session-Id: abc-123-def-456
 
event: message
data: {"jsonrpc": "2.0", "result": {"version": "2024-11-05"}, "id": 1}

Subsequent requests from the client include the Mcp-Session-Id header to resume the conversation:

POST /mcp HTTP/1.1
Host: api.example.com
Mcp-Session-Id: abc-123-def-456
Content-Type: application/json
 
{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": { "name": "search", "arguments": { "query": "weather" } }
}

The server can then push multiple SSE events back to the client over the same session, enabling streaming responses (e.g., token-by-token output) without closing the connection.

Why Use It?

  1. Real-Time Updates: The server can push notifications (e.g., "tool execution started") without the client polling.
  2. Streaming Responses: Ideal for LLM interactions where you want to stream tokens to the UI.
  3. Stateful Context: The Mcp-Session-Id allows the server to maintain conversation history and tool state across multiple HTTP requests.

The Limitation

  1. Complexity: Implementing SSE correctly requires handling reconnection, event IDs, and error recovery.
  2. Proxy Compatibility: Some older proxies or load balancers may buffer SSE streams, breaking real-time delivery. You must ensure your infrastructure supports chunked transfer encoding and doesn’t interfere with SSE.
  3. Resource Usage: Keeping HTTP connections open consumes more server resources than stateless HTTP requests.

Comparison and Recommendation

FeaturestdioHTTPStreamable HTTP
DirectionBidirectional (pipe)Request-ResponseBidirectional (POST + SSE)
ScopeLocal onlyRemoteRemote
StateProcess memoryStatelessSession-based (Mcp-Session-Id)
Real-timeYesNoYes (SSE)
ComplexityLowLowHigh

Recommendation for Backend Developers

  • Use stdio when building local tools, CLI interfaces, or when the MCP server is tightly coupled with the client (e.g., a VS Code extension).
  • Use HTTP when you need simple, stateless integration with existing APIs, or when security policies forbid persistent connections.
  • Use Streamable HTTP for production-grade backend services where you need real-time updates, streaming LLM responses, or persistent conversational context.

Understanding these transport mechanisms allows you to choose the right architecture for your MCP deployment, ensuring robustness, scalability, and real-time responsiveness.

Conclusion

Choosing the correct transport layer for MCP is not merely a configuration choice but a fundamental architectural decision. stdio offers simplicity and isolation for local processes, while basic HTTP provides scalable, firewall-friendly statelessness. For complex, real-time interactions requiring persistent context, Streamable HTTP with SSE provides the necessary bidirectional capabilities over standard HTTP infrastructure. By aligning your transport choice with your system's state and connectivity requirements, you ensure a resilient and efficient integration.

Related posts