From latestaiagents
Choose between MCP transports — stdio for local processes, Streamable HTTP for remote servers, SSE for legacy — and implement each correctly with reconnection, backpressure, and session handling. Use this skill when deciding transport for a new MCP server, migrating SSE to Streamable HTTP, or debugging transport-level issues (connection drops, buffering, session loss). Activate when: MCP transport, stdio vs HTTP, Streamable HTTP, MCP SSE, MCP reconnection, MCP session.
npx claudepluginhub latestaiagents/agent-skills --plugin skills-authoringThis skill uses the workspace's default tool permissions.
**Pick the right transport and the rest of your server design follows.**
Provides patterns, architecture diagrams, and decision trees for building, testing, and deploying Model Context Protocol (MCP) servers in Python and TypeScript with tools, resources, prompts, and transports like stdio, SSE, streamable HTTP.
Builds MCP servers with Node/TypeScript SDK: register tools/resources/prompts, Zod validation, stdio/Streamable HTTP transports, debugging.
Builds MCP servers using Node/TypeScript SDK: register tools/resources/prompts, Zod schemas, stdio vs Streamable HTTP transports. For new implementations, upgrades, debugging.
Share bugs, ideas, or general feedback.
Pick the right transport and the rest of your server design follows.
| Transport | Use when | Pros | Cons |
|---|---|---|---|
| stdio | Local tools, CLI integrations, single-user | Zero network, trusted process, simplest | Only local; one client per server process |
| Streamable HTTP | Remote multi-user servers | Bidirectional, resumable, load-balanceable | More complex; requires session layer |
| SSE (legacy) | Existing deployments | — | Deprecated; migrate to Streamable HTTP |
Server reads from stdin, writes JSON-RPC to stdout, logs to stderr. Client spawns the process.
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
const transport = new StdioServerTransport();
await server.connect(transport);
Critical rule: never write non-JSON to stdout. console.log breaks the protocol. Use console.error for logs.
// BAD
console.log("Server started"); // corrupts protocol stream
// GOOD
console.error("Server started");
Single HTTP endpoint handles all methods. Server pushes messages via SSE on long-lived GET, receives messages via POST, supports resumable sessions via Mcp-Session-Id header.
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import express from "express";
const app = express();
app.use(express.json());
const transports = new Map<string, StreamableHTTPServerTransport>();
app.all("/mcp", async (req, res) => {
const sessionId = req.headers["mcp-session-id"] as string | undefined;
let transport = sessionId ? transports.get(sessionId) : undefined;
if (!transport) {
transport = new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
onsessioninitialized: (id) => transports.set(id, transport!),
});
await server.connect(transport);
}
await transport.handleRequest(req, res, req.body);
});
app.listen(3000);
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const transport = new StreamableHTTPClientTransport(new URL("https://mcp.example.com/mcp"));
await client.connect(transport);
initialize without session headerMcp-Session-Id response headerMcp-Session-Id on every subsequent requestDELETE /mcp + session headerLong-lived SSE stream may drop (proxy timeouts, NAT rebinding). Client reconnects with Last-Event-ID and the server replays missed messages from a per-session buffer:
new StreamableHTTPServerTransport({
sessionIdGenerator: () => crypto.randomUUID(),
enableJsonResponse: false,
eventStore: new RedisEventStore({ ttlSeconds: 3600 }), // custom
});
If you skip the event store, reconnects lose in-flight tool results. Fine for dev, bad for production.
Multi-instance deployments must share session state. Options:
transports map and event storeMcp-Session-Id to a pod)Never round-robin with in-memory session state — clients will randomly land on the wrong pod and see "session not found".
Streamable HTTP lets the server push many messages to the client. If the client is slow:
progress/cancelled notification and drop the requestOld SSE used two endpoints (/sse for events, /messages for POST). The new transport unifies them at one endpoint:
POST /mcp → request
GET /mcp → open SSE stream for server→client
DELETE /mcp → end session
Migration: run both in parallel for a release, redirect SSE clients to the new endpoint, then remove old routes. The MCP SDK's StreamableHTTPClientTransport auto-negotiates.
console.log, print, debug libsres.flush() or use X-Accel-Buffering: noproxy_read_timeout ≥ 60s) for SSE