Model Context Protocol (MCP): Connect AI to Your Tools and DataPrimitives, client features and transports · Lesson 5 of 18

Transports: stdio, Streamable HTTP and running at scale

Article · 14 min · 9 min lecture

Video lecture

Transports: stdio, Streamable HTTP and running at scale

15 chapters · about 9 min · full transcript

Coming soon

Chapter 1 of 15

Transports

  • stdio
  • Streamable HTTP
  • 2026 changes
  • Local server security

The narrated lecture is in production

Every chapter is scripted and ready. Browse the chapters and read the full transcript now — the video will appear here when it’s published.

Chapters

Two standard transports

stdio: the client launches the server as a subprocess and exchanges newline-delimited JSON-RPC messages over stdin/stdout. The server may log to stderr; it must never write anything to stdout that is not a valid MCP message. Best for local tools: file access, local databases, CLIs, developer utilities. Credentials come from the environment, not OAuth.

Streamable HTTP: the server is an HTTP service with a single MCP endpoint (commonly /mcp). The client POSTs each JSON-RPC message; the server replies with a JSON body or, when it needs to send notifications such as progress first, an SSE stream for that response. Best for remote, shared and multi-user servers. Authorization uses OAuth (module 5).

The older HTTP+SSE transport (two endpoints) was superseded in 2025-03-26 and is formally deprecated; do not build new servers on it, though some SDKs still offer it for legacy clients.

What changed for HTTP in 2026-07-28

  • No sessions: no Mcp-Session-Id; each request is independent and self-describing.
  • Required headers: Mcp-Method and Mcp-Name on POSTs, plus the protocol version header, so infrastructure can route and authorize on headers. Tool parameters can be exposed as custom headers with x-mcp-header when you need routing on an argument (never for sensitive values).
  • No GET stream: server-to-client change notifications move to an opt-in subscriptions/listen request whose response is a long-lived stream.
  • No resumability: SSE event IDs and Last-Event-ID redelivery were removed; if a stream breaks, the client re-issues the request with a new ID. Design tools to be idempotent (lesson 8).
  • Cacheable lists: list and read results carry ttlMs and cacheScope hints.

Result: an MCP server can now scale like any stateless web API behind a round-robin load balancer.

Security basics for local HTTP servers

A local HTTP server bound to localhost is reachable by any web page the user visits via DNS rebinding unless you validate the Host and Origin headers. Official SDK helpers (for example createMcpExpressApp in TypeScript) enable this validation by default for localhost binds. Rules:

  • Bind to 127.0.0.1, not 0.0.0.0, for local servers.
  • Validate Host and Origin.
  • Require auth for anything non-trivial.

For stdio servers, remember the host executes your command with the user's privileges: a malicious "server" is just malware. Only install servers from trusted sources and review their launch commands.

Choosing a transport

SituationTransport
Personal tool on a laptop, touches local filesstdio
Team or company-wide service, many usersStreamable HTTP + OAuth
SaaS vendor offering an integration to customersStreamable HTTP + OAuth, listed in registries
Desktop app bundling a helperstdio (or a local HTTP server with Host/Origin validation)
Serverless / edge deploymentStreamable HTTP (stateless fits well)

Worked example: from laptop to team service

A data analyst in Lahore builds a stdio server exposing read-only SQL over a local copy of marketing data. Colleagues want it. Moving to Streamable HTTP:

  1. Same server code; change the run call to Streamable HTTP.
  2. Deploy behind the company's API gateway with TLS.
  3. Add OAuth via the company identity provider; tools check scopes (analytics:read).
  4. Gateway rate-limits on Mcp-Name per user; logs include method and tool name without parsing bodies.
  5. Horizontal scaling works because there is no session state.

Hands-on: the same server over both transports

Python (SDK v2):

from mcp.server import MCPServer

mcp = MCPServer("metrics")

@mcp.tool()
def weekly_signups(market: str) -> dict:
    """Signups for the last 7 days in a market (UAE, KSA, PK, UK, US)."""
    return {"market": market, "signups": 412}   # replace with a real query

if __name__ == "__main__":
    import sys
    if "--http" in sys.argv:
        mcp.run(transport="streamable-http", port=3001)   # serves http://127.0.0.1:3001/mcp by default
    else:
        mcp.run()                                           # stdio

TypeScript (SDK v2) with Express and DNS-rebinding protection:

import { createMcpExpressApp } from '@modelcontextprotocol/express';
import { toNodeHandler } from '@modelcontextprotocol/node';
import { createMcpHandler, McpServer } from '@modelcontextprotocol/server';
import * as z from 'zod/v4';

const handler = createMcpHandler(() => {
  const server = new McpServer({ name: 'metrics', version: '1.0.0' });
  server.registerTool(
    'weekly_signups',
    { description: 'Signups for the last 7 days in a market', inputSchema: z.object({ market: z.string() }) },
    async ({ market }) => ({ content: [{ type: 'text', text: JSON.stringify({ market, signups: 412 }) }] })
  );
  return server;
});

const app = createMcpExpressApp();            // Host/Origin validation on localhost binds
const node = toNodeHandler(handler);
app.all('/mcp', (req, res) => void node(req, res, req.body));
app.listen(3001, '127.0.0.1');

The TypeScript v2 handler builds a fresh server per request (a factory), which is why it scales statelessly: create connection pools at module scope, not inside the factory.

Pitfalls

  • Printing debug output to stdout in a stdio server (corrupts the protocol).
  • Binding local HTTP servers to all interfaces without Host/Origin checks.
  • Relying on removed session or resumability features after upgrading.
  • Building new servers on the deprecated HTTP+SSE transport.

Measuring success

For remote servers: p95 latency per method, error rates by Mcp-Name, instance count versus load, and zero auth bypasses in security tests.

Key takeaways

  • stdio suits local subprocess servers; Streamable HTTP suits remote, shared servers.
  • Never write non-protocol output to stdout in a stdio server; log to stderr.
  • 2026-07-28 HTTP is stateless: no sessions, required Mcp-Method/Mcp-Name headers, no resumability.
  • Protect local HTTP servers against DNS rebinding with Host and Origin validation and a loopback bind.
  • HTTP+SSE is deprecated; don't build new servers on it.

Check your understanding

Quick questions to lock in the lesson. They don’t count towards your certificate.

  1. A stdio server keeps disconnecting right after start-up. What is a common cause?
  2. What mitigates DNS-rebinding attacks on a local HTTP MCP server?
  3. Why can 2026-07-28 servers sit behind a plain round-robin load balancer?

Put it into practice

Run the lesson's server over stdio and Streamable HTTP, connect the Inspector to each, and write down which transport your first production server should use and why.

Enrol for free to save your progress

Reading is always free. Enrol to keep your place, take the final assessment and earn a verifiable certificate.