Transports: Local stdio vs Remote Streamable HTTP

Duration
30 min
Last verified
2026-07-30
Source outline section
Module 1.2 — Transports: Local stdio vs Remote Streamable HTTP
Exit criterion
You can name which transport your agent is using with @runsnative/mcp-server, explain what changes when you switch to remote Streamable HTTP, and state the one consequence that remote transport introduces (which Module 1.3 resolves).

Module 1.1 traced a tool call all the way from config block to result and labeled every layer it crossed. One box stayed closed: the transport — “how the bytes actually move.” This module opens it. The answer will feel mechanical at first, but its consequence is structural: the choice of transport determines whether auth is required, who can connect, and what the deployment lifecycle looks like. Module 1.3 follows directly from this module because remote transport introduces a problem this module names but does not solve.

Local stdio — the default mode

The @runsnative/mcp-server you connected in the Pre-Course Setup runs over local stdio by default. The config block you pasted tells the host exactly what to do: launch an npx process, wire its stdin and stdout to the client, and speak MCP JSON-RPC over that pipe. On the server’s side this is two lines — a stdio MCP server’s entry point ends like this, and the shape is the same in every SDK implementation you will meet:

const transport = new StdioServerTransport()
await server.connect(transport)

What this means in practice:

This is not just the course’s configuration — it is the common production posture. In the deployment this course draws its examples from, every server registered with the desktop agent host is stdio. For the course artifact and for most locally-hosted capabilities, stdio is the default and the proven path.

Remote Streamable HTTP — the URL model

The other transport family is remote Streamable HTTP: instead of spawning a process, the host’s client connects to a URL. The server runs somewhere else — on a cloud service, behind a gateway, or on another machine — and the client speaks MCP JSON-RPC over HTTP POST.

The Mukadra portfolio has production examples of this today. A deployed Cloudflare Worker — the mukadra-mcp-gateway — accepts MCP JSON-RPC at /mcp over HTTPS. Claude.ai reaches two internal MCP backends through that Worker, not by spawning local processes: a project-management server and a knowledge-retrieval server, each speaking Streamable HTTP on the private side while the gateway brokers between them and the client.

Notice the shape, because it is the general pattern and not a quirk of this deployment: one public URL in front, several private backends behind it. The client sees a single MCP endpoint. The gateway decides which backend a call belongs to and holds the only credential that reaches them. This is how most remote MCP deployments end up structured once there is more than one capability to serve.

What this model changes:

The legacy transport: SSE

A third transport exists and you will encounter it in the wild: Server-Sent Events (SSE). It predates Streamable HTTP in the MCP ecosystem and some production servers still use it — including one still running inside the portfolio these examples come from, which is exactly why you should expect to meet it rather than treat it as historical. The distinction that matters for you as a consumer: if a remote server gives you an SSE endpoint rather than a streamable-HTTP one, your MCP client config uses a different transport type field. Recognize it; do not build new servers on it. Streamable HTTP supersedes SSE for new work.

The decision framework

The outline’s framing holds: local stdio = filesystem/process access, zero hosting cost, per-machine deployment; remote HTTP = no local install, centrally updated, multi-client, auth required. Choose based on what the server actually needs to do:

Notice what the framework does not say: it does not say “MCP over HTTP for modern clients, stdio for legacy.” Both transports are actively used for good reasons. The client-support landscape is a snapshot in time — as of 2026-06-12 (volatile, non-load-bearing): Claude Desktop and Claude Code support local stdio servers natively; claude.ai (web/mobile) reaches servers only through a remote gateway. Client support is volatile; this claim should be re-verified if you are making architecture decisions. The course teaches both transports regardless.

Lab: trace your stdio connection and sketch the HTTP alternative

This lab has two parts. Both run in your own agent session.

Part 1 — trace the transport you are using:

Ask your agent to call list_exercises on @runsnative/mcp-server. While it does, account for the transport layer:

  1. Where is the server process running? (On your machine, as a child of the host.)
  2. What carries the JSON-RPC messages? (stdin/stdout of that process.)
  3. What would need to change for this to be a URL instead of a process? (A running HTTP service at a URL the host can reach; auth credentials for that service.)

Part 2 — convert your own setup to the remote topology:

You have a working stdio server in front of you. This part asks you to redesign it as a remote one, on paper. No code, no deployment — the point is to find every consequence the transport change forces.

Take the connection you just traced and imagine the same capability served at a URL to a team of thirty people. Draw the path a single tool call takes, from the agent host to the code that answers it. Your sketch must place four things:

  1. The client — what the agent host now opens instead of a process.
  2. The public endpoint — the one URL the thirty people’s hosts are configured with.
  3. The backend — where the capability code actually executes.
  4. The auth boundary — the exact point where an unauthenticated request is rejected.

Then answer, from your sketch:

Check yourself: the auth boundary belongs between the client and the backend — at the public endpoint, not inside the backend. If you drew it anywhere else, re-read “Needs auth” above before continuing. The third question has no clean answer: a remote server genuinely cannot reach the user’s filesystem, which is the honest reason stdio still exists. If you concluded that, you have the module’s central point.

You know it worked when: you can say, for your current @runsnative/mcp-server connection, which transport it uses, why, and what single requirement would be added if it moved to a URL. If you can answer that question, you understand the structural difference between the two transport families and you are ready for Module 1.3.