Migrate Your Claude Connector from SSE to Streamable HTTP
Migrate your Claude Connector from SSE to Streamable HTTP transport.
TL;DR: The MCP spec deprecated HTTP+SSE in March 2025 and replaced it with Streamable HTTP. Claude still supports SSE today, but Anthropic says that will end soon. The Connectors Directory already requires Streamable HTTP. Migration means replacing SSEServerTransport with StreamableHTTPServerTransport and consolidating two endpoints into one. Here’s exactly how to do it.
If your Claude Connector uses SSE transport, you need to migrate. The MCP specification version 2025-03-26 made Streamable HTTP the standard remote transport and deprecated HTTP+SSE. Claude still accepts SSE connections for now, but Anthropic has signaled that support will be removed. And if you want to submit to the Connectors Directory, Streamable HTTP is already required.
This post walks through the migration for both the TypeScript and Python MCP SDKs, covers the architectural differences you need to understand, and flags the gotchas that trip people up.
What Changed and Why
The old HTTP+SSE transport worked like this:
- The client opened a long-lived SSE connection via
GET /sse - The server sent an
endpointevent with a URL for client messages - The client sent JSON-RPC messages via
POSTto that endpoint - The server streamed responses back over the SSE connection
This had problems. Persistent SSE connections don’t work on serverless platforms like Cloudflare Workers or Vercel Edge Functions, which kill long-lived connections. Load balancers needed sticky sessions to route requests back to the same server holding the SSE connection. And there was no built-in session management or resumability.
Streamable HTTP fixes all of this:
- One endpoint (like
/mcp) handles everything - The client sends JSON-RPC messages via
POSTto that endpoint - The server responds with either
application/json(single response) ortext/event-stream(SSE stream for multiple messages) - The client can optionally
GETthe endpoint for server-initiated messages - Sessions are managed via the
Mcp-Session-Idheader - Resumability is built in via SSE event IDs
The SSE stream is still there as a response format when the server needs to stream multiple messages back. The difference is that it’s a response to a POST request, not a persistent connection. This means serverless platforms work out of the box.
Migration: TypeScript SDK
The TypeScript SDK (@modelcontextprotocol/sdk) added Streamable HTTP support in v1.10.0. If you’re on an older version, update first:
pnpm add @modelcontextprotocol/sdk@latest
Before (SSE)
A typical SSE server used SSEServerTransport with two Express routes:
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
const app = express();
const server = new McpServer({ name: "my-connector", version: "1.0.0" });
// Register tools
server.tool("get-tickets", { status: z.string() }, async ({ status }) => {
// ... handler
});
let transport: SSEServerTransport;
app.get("/sse", async (req, res) => {
transport = new SSEServerTransport("/message", res);
await server.connect(transport);
});
app.post("/message", async (req, res) => {
await transport.handlePostMessage(req, res);
});
app.listen(8000);
Two endpoints. A persistent SSE connection. No session management.
After (Streamable HTTP)
Replace SSEServerTransport with StreamableHTTPServerTransport and consolidate to one endpoint:
import express from "express";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
const app = express();
app.use(express.json());
const server = new McpServer({ name: "my-connector", version: "1.0.0" });
// Register tools (same as before)
server.tool("get-tickets", { status: z.string() }, async ({ status }) => {
// ... handler
});
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless mode
});
await server.connect(transport);
// All three methods on one endpoint
app.post("/mcp", async (req, res) => {
await transport.handleRequest(req, res);
});
app.get("/mcp", async (req, res) => {
await transport.handleRequest(req, res);
});
app.delete("/mcp", async (req, res) => {
await transport.handleRequest(req, res);
});
app.listen(8000);
One endpoint. No persistent connections. Works on serverless.
The Express/Hono Shortcut
The SDK also provides helper functions that set up the endpoint with DNS rebinding protection included:
import { createMcpExpressApp } from "@modelcontextprotocol/sdk/server/express.js";
const { app } = createMcpExpressApp(server);
app.listen(8000);
This creates the /mcp endpoint with POST, GET, and DELETE handlers, Origin header validation, and session management. If you’re starting fresh or doing a full rewrite, this is the simplest path.
Migration: Python SDK
The Python MCP SDK (mcp) supports Streamable HTTP in current versions.
Before (SSE)
from mcp.server import Server
from mcp.server.sse import SseServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
server = Server("my-connector")
sse = SseServerTransport("/message")
# Register tools with @server.tool() decorator
app = Starlette(routes=[
Route("/sse", endpoint=sse.handle_sse_connection),
Route("/message", endpoint=sse.handle_post_message, methods=["POST"]),
])
After (Streamable HTTP)
from mcp.server import Server
from mcp.server.streamable_http import StreamableHTTPServerTransport
from starlette.applications import Starlette
from starlette.routing import Route
server = Server("my-connector")
transport = StreamableHTTPServerTransport(
stateless_http=True,
json_response=True, # JSON instead of SSE streams
)
# Register tools with @server.tool() decorator
async def handle_mcp(request):
await transport.handle_request(request.scope, request.receive, request.send)
app = Starlette(routes=[
Route("/mcp", endpoint=handle_mcp, methods=["POST", "GET", "DELETE"]),
])
Set stateless_http=True for serverless deployments. Set json_response=True if you don’t need streaming responses (simpler for most connectors).
Stateful vs Stateless Mode
Streamable HTTP gives you a choice the old transport didn’t.
Stateless mode (sessionIdGenerator: undefined in TypeScript, stateless_http=True in Python) means each request is independent. No session tracking, no server-side state. This is ideal for serverless platforms and horizontal scaling because any server instance can handle any request.
Stateful mode (sessionIdGenerator: () => randomUUID()) creates a session ID on the first request and returns it in the Mcp-Session-Id header. The client includes this header on all subsequent requests. If the session expires or the server restarts, the server returns HTTP 404 and the client re-initializes.
Most Claude Connectors work fine in stateless mode. Use stateful mode if your connector needs to track multi-step interactions across requests, like a wizard flow or a paginated data fetch where page tokens are stored server-side.
Supporting Both Transports During Migration
If you have existing clients on SSE and can’t cut them over instantly, run both transports side by side:
import { SSEServerTransport } from "@modelcontextprotocol/sdk/server/sse.js";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
// Streamable HTTP on /mcp
const streamableTransport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
});
await server.connect(streamableTransport);
app.post("/mcp", async (req, res) => {
await streamableTransport.handleRequest(req, res);
});
app.get("/mcp", async (req, res) => {
await streamableTransport.handleRequest(req, res);
});
app.delete("/mcp", async (req, res) => {
await streamableTransport.handleRequest(req, res);
});
// Legacy SSE on /sse + /message
app.get("/sse", async (req, res) => {
const sseTransport = new SSEServerTransport("/message", res);
await server.connect(sseTransport);
});
app.post("/message", async (req, res) => {
// handle legacy SSE messages
});
This way, clients that POST to /mcp get Streamable HTTP, and clients that GET /sse still work. Remove the legacy endpoints once all clients have migrated.
Gotchas
A few things that catch people during migration.
DNS Rebinding Protection
The MCP spec now requires servers to validate the Origin header on all incoming requests. Local servers should bind to 127.0.0.1, not 0.0.0.0. The SDK’s createMcpExpressApp() and createMcpHonoApp() helpers handle this automatically. If you’re wiring up routes manually, add Origin validation yourself.
CORS Configuration
Your old SSE setup had CORS configured for GET /sse and POST /message. The new single endpoint needs CORS for POST, GET, and DELETE on /mcp. If you use a reverse proxy like nginx, update the allowed methods.
Session Expiry Handling
If you use stateful mode, your server must return HTTP 404 when a client sends an expired or unknown Mcp-Session-Id. The client interprets this as “re-initialize.” Make sure your error handling doesn’t swallow this and return a generic 500 instead.
Endpoint Path
Claude expects the /mcp path. If your old SSE server used /sse and /message, make sure your new Streamable HTTP endpoint is at /mcp. This is the path you’ll enter in Claude Settings > Connectors and the path the Connectors Directory expects.
Serverless Cold Starts
Streamable HTTP works on serverless, but cold starts still matter. If your Cloudflare Worker or Vercel Edge Function takes too long to initialize, Claude’s tool call may time out. Keep your server startup fast by lazy-loading heavy dependencies.
Testing the Migration
Before connecting to Claude, verify your migrated server works locally. sunpeak’s inspector runs a replica of the Claude runtime on localhost, so you can test tools, resources, and display modes without a Claude account.
sunpeak dev
Open http://localhost:3000, select Claude from the Host dropdown, and trigger your tools. If everything renders and responds correctly, your server is ready.
For a production test, deploy your server and add the URL (with /mcp path) in Claude Settings > Connectors. Run a conversation that triggers your tools and verify the responses come back correctly.
When to Migrate
If you’re building a new Claude Connector, use Streamable HTTP from the start. The SDKs default to it and the Connectors Directory requires it.
If you have an existing SSE connector, migrate soon. Claude still accepts SSE connections today, but Anthropic has said that will change. The migration is straightforward for most connectors: swap the transport class, consolidate endpoints, and test. The dual-transport approach lets you do it without a hard cutover.
Get Started
pnpm add -g sunpeak && sunpeak new
Further Reading
- Claude Connectors tutorial - build a connector from scratch
- Deploying Claude Connectors - hosting options and production setup
- Debugging Claude Connectors - fix connection failures and tool errors
- Claude Connector Directory submission - requirements for getting listed
- What are Claude Connectors - overview, data access, and auth
- Claude Connector Framework - sunpeak overview
- MCP Spec - Transports
- sunpeak documentation
Frequently Asked Questions
Is SSE deprecated for Claude Connectors?
Yes. The MCP specification version 2025-03-26 replaced HTTP+SSE with Streamable HTTP as the standard remote transport. Anthropic says SSE support for Claude Connectors "may be deprecated in the coming months." The Connectors Directory already requires Streamable HTTP for new submissions. Existing SSE connectors still work today, but you should migrate.
What is the difference between SSE and Streamable HTTP in MCP?
The old HTTP+SSE transport used two endpoints: a GET /sse endpoint for a long-lived SSE stream and a separate POST endpoint for client messages. Streamable HTTP uses a single endpoint (like /mcp) that accepts POST requests and optionally GET for server-initiated messages. The server can respond with either JSON or SSE streams. Streamable HTTP also adds session management via Mcp-Session-Id headers and optional resumability.
How do I migrate my MCP server from SSE to Streamable HTTP?
Replace SSEServerTransport with StreamableHTTPServerTransport in your server code. Change your endpoint from two routes (GET /sse + POST /message) to a single route that handles POST, GET, and DELETE on one path like /mcp. Update your session management to use the Mcp-Session-Id header. The TypeScript SDK provides StreamableHTTPServerTransport and helpers like createMcpExpressApp() that handle most of this.
Can I support both SSE and Streamable HTTP during migration?
Yes. The MCP spec includes a backwards compatibility approach: keep your old GET /sse and POST /message endpoints alongside the new Streamable HTTP endpoint. Clients that send a POST with an Initialize request use Streamable HTTP. Clients that send a GET to /sse use the legacy transport. This lets you migrate without breaking existing integrations.
Does the Anthropic Connectors Directory require Streamable HTTP?
Yes. The Connectors Directory requires Streamable HTTP transport for all submissions. If your connector uses SSE, you must migrate before submitting. Existing listed connectors that use SSE should migrate before Anthropic removes SSE support.
What are the benefits of Streamable HTTP over SSE for Claude Connectors?
Streamable HTTP works on serverless platforms like Cloudflare Workers and Vercel Edge Functions because it does not require persistent connections. It adds session management and resumability. It uses a single endpoint instead of two, which simplifies deployment and proxy configuration. Stateless mode allows horizontal scaling without sticky sessions.
How do I test my Claude Connector after migrating to Streamable HTTP?
Use sunpeak to run a local Claude inspector with sunpeak dev. The inspector connects to your MCP server and lets you test tools, UI resources, and display modes locally. You can verify the Streamable HTTP transport works correctly before deploying or connecting to Claude. For production testing, add your deployed URL in Claude Settings > Connectors.
What breaks when migrating from SSE to Streamable HTTP?
The endpoint architecture changes from two routes to one. Reverse proxy and CORS configurations may need updating. Session management via Mcp-Session-Id is new and servers must handle 404 responses for expired sessions. DNS rebinding protection (Origin header validation) is now required. If you use load balancers with sticky sessions for the old SSE connection, you can switch to stateless mode and remove that requirement.