MCP defines what a message means — a tool call, a result, a notification — but it doesn’t care how the bytes actually move between client and server. That job belongs to the transport, and MCP has used three of them over its life: stdio for a local process, an early HTTP+SSE pairing for remote servers, and Streamable HTTP, the one remote transport the spec recommends today. Here is what each one actually does, and why the middle one didn’t last.

What a transport is responsible for

A transport’s job is narrow: get a JSON-RPC 2.0 message from one side to the other, intact, and know when a reply belongs to which request. It has no opinion on tools/call versus resources/read, no opinion on what the handshake negotiates, and no opinion on authentication beyond carrying whatever headers or environment the layer above hands it. Swap the transport under a client and server that already speak MCP to each other, and nothing about the protocol itself changes — only how far apart the two sides can be, and what it takes to connect them.

stdio: the local transport

stdio is what a client uses to run a server as a child process on the same machine. The client launches the server’s executable, writes JSON-RPC messages to its standard input, and reads messages back from its standard output — one message per line, nothing fancier than that. There is no network hop, no port, and no listener to configure; the server only exists for as long as the client keeps the process alive.

That simplicity is exactly why stdio is still the right choice for anything that only makes sense running where you are — a filesystem tool, a local database client, a wrapper around a CLI you already have installed. It is also why a stdio server can see whatever your user account can see, which is the risk side of the same coin covered in MCP server supply chain risk. stdio has no remote equivalent of its own; once a server needs to run somewhere else, or serve more than one caller, it needs one of the HTTP-based transports instead — the tradeoff laid out in remote vs. local MCP servers.

HTTP+SSE: the transport MCP already moved past

The original remote transport, defined in MCP’s first public spec, split the connection into two separate HTTP mechanisms. The client opened a long-lived Server-Sent Events stream with a GET request to receive messages from the server, and sent its own messages separately as individual POST requests to an endpoint the server handed back at the start of that SSE stream. Two connections, two directions, tied together by an endpoint URL instead of a single session.

It worked, but it carried real friction. A client had to hold that SSE connection open for the entire session just to receive anything, which is awkward behind load balancers and proxies that don’t expect a response stream to stay open indefinitely, and every message leg had its own failure mode to handle. MCP’s March 2025 spec revision replaced it with Streamable HTTP, and the June 2025 revision removed the old transport from the spec text entirely — it is legacy at this point, and a server still advertising it is signaling an old SDK, not a current implementation choice.

Streamable HTTP: the current standard

Streamable HTTP collapses the split back into one endpoint. A client sends a JSON-RPC request as a normal POST to that single URL. For a request that only needs one reply, the server can just answer with a normal HTTP response body — done, no stream involved. For a request where the server wants to push more than one message back — progress updates, a sampling request mid-call, several results over time — it can instead respond with Content-Type: text/event-stream and keep that one response open as an SSE stream tied to that specific request, closing it once it’s done. The client can also open a bare GET against the same endpoint if it wants a channel for server-initiated messages that aren’t replying to anything in particular.

The practical gain is that a plain request/response exchange looks like ordinary HTTP — cacheable, proxyable, no connection held open longer than the call itself needs — and streaming only happens when a specific exchange actually calls for it. That also lines up with MCP going stateless by default, covered in what the 2026-07-28 update actually changes: a server no longer has to assume one long session per client, because the transport itself stopped requiring one.

Where this actually shows up

Day to day, a transport is invisible — you add a server by URL or by local command, and your client picks the matching transport without asking. It surfaces in two places. First, in setup: a local-only tool needs a command your client can launch, a remote one needs Streamable HTTP support and usually an OAuth grant, and the two aren’t interchangeable just because both speak MCP. Second, in debugging: opening raw traffic in the MCP Inspector is the fastest way to tell a transport-level failure (a connection that never opens, a stream that drops mid-response) from a protocol-level one (a request that opens fine and gets a JSON-RPC error back) — the same JSON-RPC message shapes ride on top of either kind of transport, so a server returning a well-formed error is a server that connected.

Where gate fits: gate is a gateway for remote servers, which in practice means Streamable HTTP on the wire for every connection it manages — one URL in front of however many servers you’ve connected, each one scanned before it goes live and logged in plain language afterward. Local stdio servers run entirely on your own machine and sit outside what a hosted gateway touches by design; browse the remote servers gate already supports on the server directory, or see how the gateway itself works if you’re deciding whether you need one.

The bottom line

Three transports, one still local and current (stdio), one remote and current (Streamable HTTP), and one remote and retired (HTTP+SSE). None of them change what a tool call means or how a result is shaped — they only decide how far the message has to travel and what keeps the connection open while it does. If you’re building a server from an existing API with something like gate’s MCP server builder, the transport is handled by the SDK underneath; the thing actually worth checking by hand is whether a server you’re connecting to still speaks the deprecated pairing, which is usually a sign its implementation hasn’t been touched in a while.