Skip to main content
RapidDev - Software Development Agency
mcp-tutorial

How to use the streamable HTTP transport for MCP

Streamable HTTP is MCP's newest transport protocol, replacing SSE for remote server communication. It uses a single /mcp endpoint, supports stateless and stateful sessions via the Mcp-Session-Id header, and integrates with Express or Hono middleware. Set up involves using createMcpExpressApp or createMcpHonoApp from the SDK, configuring Origin validation for security, and handling session lifecycle.

What you'll learn

  • How Streamable HTTP transport works under the hood
  • How to set up Streamable HTTP with Express and Hono
  • How to configure Mcp-Session-Id for stateful sessions
  • How to validate Origin headers for security
  • How to handle session lifecycle and cleanup
Book a free consultation
4.9Clutch rating
600+Happy partners
17+Countries served
190+Team members
Advanced8 min read25-35 minMCP SDK 1.0+, Express 4+, Hono 4+, Node.js 18+March 2026RapidDev Engineering Team
TL;DR

Streamable HTTP is MCP's newest transport protocol, replacing SSE for remote server communication. It uses a single /mcp endpoint, supports stateless and stateful sessions via the Mcp-Session-Id header, and integrates with Express or Hono middleware. Set up involves using createMcpExpressApp or createMcpHonoApp from the SDK, configuring Origin validation for security, and handling session lifecycle.

Streamable HTTP Transport for MCP Servers

Streamable HTTP is the recommended transport for remote MCP servers, replacing the older SSE approach. It communicates over a single HTTP endpoint, supports both request-response and server-sent event streaming, and works behind load balancers and CDNs. This tutorial covers the full setup using Express and Hono, including session management, Origin validation, and production hardening.

Prerequisites

  • A working MCP server built with @modelcontextprotocol/sdk
  • Node.js 18+ installed
  • Familiarity with Express.js or Hono web frameworks
  • Understanding of HTTP headers and CORS concepts
  • Basic knowledge of MCP architecture (hosts, clients, servers)

Step-by-step guide

1

Install the required dependencies

The MCP SDK includes built-in Express and Hono adapters for Streamable HTTP transport. Install the SDK along with your preferred web framework. Express is the most widely used and has the largest ecosystem, while Hono is lighter and faster — choose based on your preference. Both produce identical MCP behavior.

typescript
1# For Express
2npm install @modelcontextprotocol/sdk express zod
3npm install -D @types/express typescript
4
5# For Hono
6npm install @modelcontextprotocol/sdk hono zod
7npm install -D typescript

Expected result: All dependencies installed and ready for development.

2

Create a Streamable HTTP server with Express

The SDK provides createMcpExpressApp which takes your McpServer instance and returns a fully configured Express app. This app exposes a POST /mcp endpoint for client requests and supports response streaming via Server-Sent Events when the client sends an Accept: text/event-stream header. The helper handles all protocol details including JSON-RPC framing, session creation, and connection management.

typescript
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
3import { z } from "zod";
4
5const server = new McpServer({
6 name: "streamable-http-demo",
7 version: "1.0.0",
8});
9
10server.tool(
11 "get-weather",
12 "Get current weather for a city",
13 { city: z.string() },
14 async ({ city }) => ({
15 content: [{ type: "text", text: `Weather in ${city}: 22°C, sunny` }],
16 })
17);
18
19const app = createMcpExpressApp(server);
20
21app.listen(3000, () => {
22 console.error("Streamable HTTP MCP server on http://localhost:3000/mcp");
23});

Expected result: An Express server running at http://localhost:3000 with the MCP endpoint at /mcp.

3

Create a Streamable HTTP server with Hono

If you prefer Hono for its lightweight footprint and edge-runtime compatibility, the SDK also provides createMcpHonoApp. The API is nearly identical to the Express version. Hono is especially useful when deploying to edge platforms like Cloudflare Workers or Deno Deploy where Express is not available.

typescript
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2import { createMcpHonoApp } from "@modelcontextprotocol/sdk/server/hono.js";
3import { serve } from "@hono/node-server";
4import { z } from "zod";
5
6const server = new McpServer({
7 name: "hono-mcp-server",
8 version: "1.0.0",
9});
10
11server.tool(
12 "ping",
13 "Check if the server is alive",
14 {},
15 async () => ({
16 content: [{ type: "text", text: "pong" }],
17 })
18);
19
20const app = createMcpHonoApp(server);
21
22serve({ fetch: app.fetch, port: 3000 }, () => {
23 console.error("Hono MCP server running on port 3000");
24});

Expected result: A Hono server running with the same MCP capabilities as the Express version.

4

Understand and configure Mcp-Session-Id

Streamable HTTP uses the Mcp-Session-Id header to maintain session state between requests. When a client sends its first request (the initialize method), the server generates a unique session ID and returns it in the response header. The client includes this ID in all subsequent requests. This enables stateful conversations while keeping individual requests stateless — perfect for load-balanced deployments. The SDK manages sessions automatically, but you should understand the flow for debugging.

typescript
1// Session lifecycle:
2// 1. Client sends POST /mcp with {"method": "initialize"}
3// 2. Server responds with Mcp-Session-Id: abc123 header
4// 3. Client includes Mcp-Session-Id: abc123 in all future requests
5// 4. Client sends DELETE /mcp with Mcp-Session-Id to end session
6
7// The SDK handles all of this automatically.
8// To inspect sessions for debugging, you can add middleware:
9
10import express from "express";
11const debugApp = express();
12
13debugApp.use((req, _res, next) => {
14 console.error(`[${req.method}] ${req.path}`);
15 console.error(` Session: ${req.headers["mcp-session-id"] || "new"}`);
16 next();
17});

Expected result: You understand the Mcp-Session-Id lifecycle and can inspect session headers for debugging.

5

Add Origin validation for security

When exposing an MCP server over HTTP, you should validate the Origin header to prevent unauthorized browser-based access. This is especially important for servers deployed on the public internet. Add middleware that checks the Origin header against an allowlist before processing requests. The SDK does not enforce Origin validation by default, so this is your responsibility.

typescript
1import express from "express";
2
3const ALLOWED_ORIGINS = [
4 "https://claude.ai",
5 "https://your-app.com",
6];
7
8function originValidation(
9 req: express.Request,
10 res: express.Response,
11 next: express.NextFunction
12) {
13 const origin = req.headers.origin;
14 if (origin && !ALLOWED_ORIGINS.includes(origin)) {
15 res.status(403).json({ error: "Origin not allowed" });
16 return;
17 }
18 next();
19}
20
21// Apply before MCP routes
22app.use("/mcp", originValidation);

Expected result: Requests from unauthorized origins are rejected with a 403 status before reaching your MCP server logic.

6

Test with MCP Inspector and connect your host

Before connecting a production host, test your Streamable HTTP server with the MCP Inspector tool. Run your server, then launch the Inspector pointing to your HTTP endpoint. Verify that tools are listed, execute a test call, and check the response. Once confirmed working, update your Claude Desktop or Cursor configuration. For teams building complex MCP architectures with multiple servers and transports, the RapidDev team offers architecture consulting to help design reliable setups.

typescript
1# Start your server
2node dist/http.js
3
4# In another terminal, run MCP Inspector
5npx -y @modelcontextprotocol/inspector --transport http --url http://localhost:3000/mcp
6
7# Then configure your host:
8# .cursor/mcp.json
9{
10 "mcpServers": {
11 "my-http-server": {
12 "url": "http://localhost:3000/mcp"
13 }
14 }
15}

Expected result: MCP Inspector shows your tools and resources, and your host application connects successfully.

Complete working example

src/streamable-http-server.ts
1import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
3import express from "express";
4import { z } from "zod";
5
6// --- Origin validation middleware ---
7const ALLOWED_ORIGINS = process.env.ALLOWED_ORIGINS?.split(",") || [];
8
9function validateOrigin(
10 req: express.Request,
11 res: express.Response,
12 next: express.NextFunction
13) {
14 const origin = req.headers.origin;
15 if (origin && ALLOWED_ORIGINS.length > 0 && !ALLOWED_ORIGINS.includes(origin)) {
16 res.status(403).json({ error: "Origin not allowed" });
17 return;
18 }
19 next();
20}
21
22// --- MCP server setup ---
23const server = new McpServer({
24 name: "streamable-http-production",
25 version: "1.0.0",
26});
27
28server.tool(
29 "search",
30 "Search for items",
31 { query: z.string(), limit: z.number().optional().default(10) },
32 async ({ query, limit }) => {
33 const results = Array.from({ length: limit }, (_, i) => ({
34 id: i + 1,
35 title: `Result ${i + 1} for "${query}"`,
36 }));
37 return {
38 content: [{ type: "text", text: JSON.stringify(results, null, 2) }],
39 };
40 }
41);
42
43// --- Express app with Streamable HTTP transport ---
44const app = createMcpExpressApp(server);
45
46app.use("/mcp", validateOrigin);
47
48app.get("/", (_req, res) => {
49 res.json({ status: "ok", transport: "Streamable HTTP" });
50});
51
52const PORT = parseInt(process.env.PORT || "3000", 10);
53app.listen(PORT, () => {
54 console.error(`Streamable HTTP server on port ${PORT}`);
55});

Common mistakes when using the streamable HTTP transport for MCP

Why it's a problem: Not handling the Mcp-Session-Id header in custom middleware

How to avoid: If you add custom middleware before the MCP routes, make sure it passes through the Mcp-Session-Id header. The SDK needs it to route requests to the correct session.

Why it's a problem: Blocking streaming responses with buffering middleware

How to avoid: Remove response compression or buffering middleware from the /mcp route, as it can prevent SSE streaming from working correctly.

Why it's a problem: Forgetting to validate Origin in production

How to avoid: Add Origin validation middleware for any MCP server exposed on the public internet. Without it, any website could potentially interact with your server.

Why it's a problem: Using GET instead of POST for MCP requests

How to avoid: Streamable HTTP uses POST for all client-to-server messages and GET only for server-to-client event streams. The SDK handles this automatically, but custom clients must use POST.

Best practices

  • Use createMcpExpressApp or createMcpHonoApp instead of building transport handling manually
  • Always validate the Origin header in production deployments
  • Add a health check endpoint separate from the /mcp route for load balancer probes
  • Log session creation and destruction events for debugging connection issues
  • Set appropriate CORS headers if your server will be accessed from browser-based clients
  • Use environment variables for the allowed origins list so you can update without redeploying
  • Test with MCP Inspector before connecting production hosts
  • Monitor session counts to detect connection leaks

Still stuck?

Copy one of these prompts to get a personalized, step-by-step explanation.

ChatGPT Prompt

Help me set up an MCP server with Streamable HTTP transport using Express. I need Origin validation, session logging for debugging, and a health check endpoint. The server should expose tools for [describe your tools]. Show me the complete code.

MCP Prompt

Create an MCP server using Streamable HTTP transport with @modelcontextprotocol/sdk and Express. Add Origin validation middleware that reads allowed origins from an ALLOWED_ORIGINS environment variable. Include a health check endpoint and session debug logging.

Frequently asked questions

What is the difference between Streamable HTTP and SSE transport?

Streamable HTTP uses a single endpoint (/mcp) for all communication, while SSE required two separate endpoints (one for sending, one for receiving). Streamable HTTP also supports stateless operation, making it compatible with serverless platforms and load balancers.

Can I use Streamable HTTP with Cloudflare Workers?

Yes, use the Hono adapter since Hono is natively compatible with Cloudflare Workers. The createMcpHonoApp function returns a Hono app that works directly in edge runtimes.

Do I need to manage sessions manually?

No, the SDK handles session creation, tracking via Mcp-Session-Id headers, and cleanup automatically. You only need to interact with sessions if you want to add custom logging or enforce session limits.

Is Streamable HTTP backward compatible with SSE clients?

No, they are different protocols. A client built for SSE transport cannot connect to a Streamable HTTP server. However, you can run both transports on the same server by mounting them on different routes.

How do I handle CORS for browser-based MCP clients?

Add CORS middleware to your Express or Hono app that allows the necessary origins, methods (POST, GET, DELETE), and headers (Content-Type, Mcp-Session-Id). The SDK does not add CORS headers automatically.

What happens when the client disconnects unexpectedly?

The SDK detects closed connections and cleans up the session automatically. For long-running operations, the server can continue processing and the client can reconnect using the same Mcp-Session-Id to receive pending results.

RapidDev

Talk to an Expert

Our team has built 600+ apps. Get personalized help with your project.

Book a free consultation

Need help with your project?

Our experts have built 600+ apps and can accelerate your development. Book a free consultation — no strings attached.

Book a free consultation

We put the rapid in RapidDev

Need a dedicated strategic tech and growth partner? Discover what RapidDev can do for your business! Book a call with our team to schedule a free, no-obligation consultation. We'll discuss your project and provide a custom quote at no cost.