TL;DR: The MCP specification defines two standard transports: a locally launched subprocess over
stdio, and a remote endpoint using Streamable HTTP. Claude Code also documents compatibility paths such as legacy SSE and WebSocket. Those client-specific options should not be mistaken for the protocol's portable baseline. The transport moves bytes; it does not decide which tools, resources, or prompts the server exposes.
The previous article ended just before the handshake: Claude Code knows a server exists, but it still needs a way to speak to it.
MCP calls that way of speaking a transport. A transport determines how messages travel between client and server. It is separate from the question of what those messages describe—tools, resources, prompts, and the other capabilities a server may advertise. The next article will examine how a declared server tool enters Claude Code's tool list.
The protocol standardizes two transports
The Model Context Protocol standard defines two standard client-server transports:
stdio- Streamable HTTP
Both carry the same JSON-RPC messages. That separation is deliberate: once the protocol layer abstracts the communication mechanism, the application-level interaction should look the same regardless of the transport.
stdio: launch a local subprocess
With stdio, the client starts the MCP server as a child process. The server reads JSON-RPC messages from standard input and writes JSON-RPC messages to standard output. Its standard error stream remains available for logging, but standard output must contain valid MCP messages only.
This is the natural shape for a local integration. If an issue-tracker helper is an executable installed on your machine, Claude Code can launch it directly rather than putting a network hop in the middle. Claude Code's MCP documentation describes local stdio servers as a fit for custom scripts and tools that need direct access to the system.
A stdio transport is not “MCP over a terminal” in the casual sense. The process boundary matters. Claude Code owns the child process lifecycle, while the server owns the logic behind the protocol. If the server exits, the connection is gone because the program that supplied it is gone.
Streamable HTTP: connect to a remote endpoint
Streamable HTTP is the standard remote option. The client sends JSON-RPC messages with HTTP requests, and the server may return either a normal JSON response or an SSE stream. A server can also use an HTTP GET request to open an SSE stream for server-to-client messages.
This replaced the older HTTP-plus-SSE transport from the 2024-11-05 protocol revision. The current specification keeps a backwards-compatibility path, but new servers should implement Streamable HTTP rather than build on the retired design.
For a remote server, this distinction matters operationally as well as architecturally. An HTTP endpoint may be separately deployed, authenticated, monitored, and shared by many clients. The transport boundary is now a network boundary.
The same message shape travels through either path
A transport is a delivery mechanism, not a capability model.
Whether the server is a subprocess or an HTTPS endpoint, the client and server exchange JSON-RPC protocol messages. The server implementation should not need one set of tool semantics for local use and a different set for remote use. It can expose a tools/list response, accept tools/call, and participate in initialization with the same MCP-level meaning on either transport.
That is the practical payoff of the abstraction:
- the connection layer chooses how messages get there;
- the protocol layer gives those messages a common shape; and
- later layers decide how discovered capabilities become available to the agent.
For the code that discovers a tool or calls it, “local process” and “remote service” should not produce two unrelated APIs. They are two implementations of the same connection contract.
Claude Code supports more than the portable baseline
The protocol's two standard transports are the portable baseline. A particular MCP client can additionally support compatibility or product-specific connection paths, and Claude Code does.
Its current public documentation describes four externally configurable server types:
- local
stdioservers; - remote HTTP servers, with
streamable-httpaccepted as an alias in JSON configuration; - legacy remote SSE servers; and
- remote WebSocket servers.
The latter two are useful examples of the difference between a specification and a client implementation. SSE remains available because some services still expose an older endpoint, but Claude Code marks it deprecated and recommends HTTP when possible. WebSocket is a persistent bidirectional connection that is useful for servers that push events without waiting for a request; Claude Code's own docs recommend HTTP when a server only needs request-response behavior.
MCP itself also permits pluggable custom transports. That permission does not make every client-specific transport universally available. When you build a third-party server intended to interoperate broadly, stdio and Streamable HTTP are the dependable choices because they are the two mechanisms the specification standardizes.
The distinction is easy to blur when reading a client codebase. A client may contain adapters for legacy protocols, SDK-hosted integrations, browser or IDE components, or internal wiring that reuse MCP's message handling. Reuse of the message format and lifecycle code does not turn each internal pathway into a general-purpose transport that an outside server author should choose.
An in-process integration can remove the channel entirely
The most extreme client-specific case is an in-process integration: no child process and no network request. Both sides are already in the same process, so they can exchange messages directly while presenting a common MCP-shaped interface to code above them.
Why preserve the abstraction when there is no real transport to manage? Because the rest of the client benefits from a uniform contract. The capability-discovery layer does not need separate logic for “a remote server,” “a local executable,” and “a built-in integration.” It can ask for tools, resources, and prompts through one conceptual interface.
This illustrates what the transport layer is buying: upper layers should not need to care how the bytes moved—or, in an in-process case, whether bytes crossed a boundary at all. The next stage can focus on naming, discovery, permissions, and invocation instead.
Local and remote failures have different recovery paths
A transport choice also changes what a disconnection means.
For a local stdio server, an exit or failed startup points to a process-level problem: a missing executable, invalid arguments, a bad environment, or a crash. Claude Code's current guidance does not automatically retry a failed first stdio connection. In an interactive session, the server is marked failed and can be reconnected from /mcp; in a non-interactive run, a later run or tool call provides the next opportunity to start it again.
A remote connection has a different failure model. A server may be healthy while a network request times out, a proxy returns a transient error, or a connection drops mid-session. Claude Code therefore retries transient remote failures. For a dropped remote server, it first makes up to five reconnection attempts with exponential backoff beginning at one second. For an initial HTTP or SSE connection that fails transiently, it retries up to three times.
These rules are not a claim that local failures are never transient or that network failures always are. They encode a useful operational default:
- a local process failure often needs configuration or environment diagnosis;
- a remote interruption is more likely to benefit from a bounded retry; and
- authentication and not-found errors should normally be fixed rather than blindly retried.
There are details by runtime and server type. In particular, the documented initial-connection retries apply to HTTP and SSE, not stdio or WebSocket. Treat the transport as part of the diagnosis, not just a field in configuration.
The boundary to carry forward
The useful mental model is simple:
-
The MCP specification provides the interoperable baseline:
stdioand Streamable HTTP. - Claude Code can support additional compatibility and product-specific paths, including legacy SSE and WebSocket in its public configuration.
- Higher layers should work with the capabilities discovered through the connection, rather than hard-coding assumptions about the channel underneath.
Once the server is connected, initialized, and queried, it may declare a tool such as create_issue. How does that server-side name become an entry the model can call? How are collisions avoided, and how does the tool reach the model's tool list?
That is the subject of the next article: from tools/list to one entry in the Tool list.
Top comments (0)