For a new remote MCP server, start with Streamable HTTP. The older HTTP+SSE transport is still useful when a client only implements the 2024-11-05 protocol transport or when you must preserve an existing integration. In that legacy design, GET /sse opens a long-lived event stream and POST /messages receives JSON-RPC requests for the stream’s session.
This guide shows the official TypeScript compatibility pattern, explains host and request-size settings, and then outlines how to migrate to Streamable HTTP. “SSE” here means MCP’s older HTTP+SSE transport; it does not mean every modern MCP server must maintain a server-sent-events connection.
Choose the transport before writing code
| Concern | Legacy HTTP+SSE | Streamable HTTP |
|---|---|---|
| Protocol status | 2024-11-05 transport retained for backward compatibility | Recommended for new remote servers |
| Endpoints | Separate long-lived GET /sse and POST /messages |
One Streamable HTTP endpoint handling POST request/response; SSE can be enabled for notifications |
| Session model | Each SSE connection gets a session ID; POST messages must be routed to that session | Built-in session management and resumability options |
| Client compatibility | Required for clients that only speak the older transport | Preferred by current clients and new implementations |
| SDK status | Deprecated bridge; the v2 SDK does not serve this transport directly | Current implementation path |
The MCP TypeScript SDK v1 server guide states that “The older HTTP+SSE transport (protocol version 2024‑11‑05) is supported only for backwards compatibility.” Read the current guidance at the MCP TypeScript SDK server guide and the transport details in the MCP transport specification.
When legacy SSE is the right choice
- A production client explicitly documents support for HTTP+SSE but not Streamable HTTP.
- You are replacing an existing server without being able to upgrade all clients at once.
- You need a short-lived compatibility bridge while clients migrate.
For a greenfield v2 server, do not build new application logic around the legacy class. The v2 migration guide says SSEServerTransport was removed from v2; the frozen copy is a temporary bridge and is planned for removal in v3. Verify package exports and version instructions when you install the SDK.
#1 Best Overall
Build the compatibility server in TypeScript
1. Install the SDK and web framework
The example below follows the v2 legacy-client guide. It uses the frozen transport package, Express, and the MCP server class. Pin versions that are compatible with your application rather than copying a floating dependency range.
npm install @modelcontextprotocol/server @modelcontextprotocol/server-legacy express
npm install -D typescript tsx @types/express
2. Create a server factory
Create a fresh MCP server for every SSE connection. A transport represents one client session, so sharing one server instance across unrelated connections can leak state.
import { McpServer } from "@modelcontextprotocol/server";
import { z } from "zod";
export function createServer() {
const server = new McpServer({
name: "example-sse-server",
version: "1.0.0",
});
server.tool(
"add",
"Add two numbers",
{ a: z.number(), b: z.number() },
async ({ a, b }) => ({
content: [{ type: "text", text: String(a + b) }],
}),
);
return server;
}
Register your real tools, resources, and prompts in this factory. Keep tool input schemas explicit so malformed JSON-RPC arguments fail with a useful validation error.
3. Add the SSE and message routes
This is the important part: keep a map from session IDs to SSEServerTransport objects, create the transport on GET /sse, and dispatch each POST /messages request to the matching transport.
Recommended Free Tools
import express from "express";
import { SSEServerTransport } from "@modelcontextprotocol/server-legacy/sse";
import { createServer } from "./server.js";
const app = express();
const port = Number(process.env.PORT ?? 3000);
// The legacy SSE transport accepts messages up to 4 MB in the documented example.
app.use(express.json({ limit: "4mb" }));
const transports = new Map<string, SSEServerTransport>();
app.get("/sse", async (req, res) => {
const transport = new SSEServerTransport("/messages", res);
transports.set(transport.sessionId, transport);
res.on("close", () => {
transports.delete(transport.sessionId);
});
const server = createServer();
await server.connect(transport);
});
app.post("/messages", async (req, res) => {
const sessionId = req.query.sessionId;
if (typeof sessionId !== "string") {
res.status(400).json({ error: "sessionId must be a string" });
return;
}
const transport = transports.get(sessionId);
if (!transport) {
res.status(404).json({ error: "Unknown sessionId" });
return;
}
await transport.handlePostMessage(req, res);
});
app.listen(port, "127.0.0.1", () => {
console.log(`MCP SSE server listening on http://127.0.0.1:${port}`);
});
The transport sends an initial SSE endpoint event containing a URL such as /messages?sessionId=…. The client posts JSON-RPC traffic to that URL while reading responses from the open event stream.
Rank #2
4. Run and connect
npx tsx src/index.ts
Point an SSE-capable MCP client at http://127.0.0.1:3000/sse. Do not treat a browser tab opened on /sse as a complete test: the endpoint is an event stream, and a valid MCP client must parse the endpoint event and then post protocol messages using the supplied session ID.
Host validation and safe remote deployment
When the process listens only on localhost, the SDK’s local assumptions are usually sufficient. Once you bind to a non-loopback address, explicitly configure which host names the server accepts. The v2 example binds to 0.0.0.0 while allowing sse.example.com. Binding beyond localhost can drop default Host/Origin validation and expose DNS-rebinding risk if you do not list the hosts you actually serve.
app.listen(port, "0.0.0.0", () => {
console.log("Listening on all interfaces");
});
Use the host-allowlisting mechanism documented for your exact SDK version, terminate TLS at a trusted proxy or the application, and restrict access with authentication before exposing the endpoint to the public internet. Do not accept arbitrary Host or Origin values simply because a reverse proxy is in front of the process.
Request size, sessions, and lifecycle details
Raise the JSON limit deliberately
Express defaults to a 100 KB JSON body. The documented legacy transport example raises the limit to 4 MB because the transport accepts messages up to that size. Treat 4 MB as the example’s configuration, not a universal requirement: choose a smaller ceiling if your tools do not need large payloads, and enforce matching limits at your proxy.
Clean up disconnected clients
The close handler removes the session from the map. Without it, every abandoned browser, proxy timeout, or network interruption leaves a transport reference in memory. If your application allocates additional per-session resources, release them in the same handler.
Do not assume sessions survive restarts
The in-memory map is process-local. A restart invalidates every session, and multiple instances require sticky routing or a shared session design. Streamable HTTP is the better foundation when you need resumability and a modern session model.
Use Streamable HTTP for a new server
The v1 guide points new implementations to simpleStreamableHttp.ts. Start there, remove features you do not need, and register your own tools, resources, and prompts. Streamable HTTP uses POST request/response exchanges and can optionally return SSE for server-to-client notifications; it can also use JSON-only responses when streaming is unnecessary.
Crashes, No Sound, or Screen Glitches?
Random freezes, missing sound and display glitches usually trace back to one bad driver. Find and replace yours safely.Free scan · under a minuteWindows Errors? Fix Them Before They Spread
Repair common Windows errors and clear accumulated junk for a smoother, more stable PC - no reinstall needed.Free scan · no reinstallThis means “I need SSE” is not always a reason to select the legacy transport. If your requirement is notifications from server to client, enable the Streamable HTTP notification mode and retain the current protocol, session, and resumability behavior. Choose the frozen SSE bridge only when an actual client compatibility constraint requires it.
Supporting old and new clients during migration
- Implement the primary server with Streamable HTTP and test it with current clients.
- Expose the legacy
/sseand/messagesroutes using the frozen bridge package for clients that cannot migrate yet. - Keep tool behavior identical behind both transports; transport-specific code should be limited to connection and request handling.
- Log which transport each client uses, then contact owners of remaining legacy clients.
- Remove the bridge after those clients support Streamable HTTP and your compatibility window ends.
The bridge is deliberately temporary: the migration guide confirms that SSEServerTransport was removed from v2 and points developers toward Streamable HTTP.
Troubleshooting common failures
“Cannot find module @modelcontextprotocol/server-legacy/sse”
The frozen bridge package is not installed, or your SDK version does not export the path. Install the package named in the v2 legacy-client guide and verify its version and export map. Do not substitute a similarly named transport from an unrelated package.
The client receives an SSE stream but never sends requests
Check that the client parses the initial endpoint event and posts to /messages?sessionId=…. Posting to /messages without the query parameter cannot be routed to a session.
The Tool Desk
Outbyte PC Repair FREEClear out junk files and repair common Windows errorsFree Scan →Outbyte Driver Updater FREEFix the driver behind crashes, sound loss and screen glitchesFind Drivers →Rank #4
“Unknown sessionId” after reconnecting
The old connection was closed, the process restarted, or a load balancer sent the POST to another instance. Reconnect to /sse to obtain a new session and configure sticky routing or a shared design for multi-instance deployments.
413 Payload Too Large
Raise the Express JSON limit only as far as required and align the reverse proxy’s limit. The documented example uses 4 MB; larger values increase memory and denial-of-service risk.
Requests fail only through a proxy
Confirm that the proxy preserves long-lived streaming responses, disables buffering for the SSE route, forwards the query string, and allows idle connections to remain open. Also verify that the public host appears in your allowlist.
Nothing works when binding to 0.0.0.0
Binding changes the attack surface and can disable default host checks. Configure explicit allowed hosts and origins, use TLS, and test with the exact DNS name clients will send.
Quick wins for a faster PC:
Scan for outdated or missing drivers - takes under a minuteDriver Scan →Clear out junk files and repair common Windows errorsFree Scan →Or skip the browser setup
If your MCP tools need website screenshots rather than an MCP transport tutorial, ScreenshotNeo provides a screenshot API and MCP server. A single request returns PNG, JPEG, WebP, or PDF; it accepts cookie banners before capture and removes more than 60 known consent platforms, newsletter popups, and chat widgets. Bot checks, blank pages, timeouts, failed loads, and cache hits are not billed, and response headers identify the page verdict and billing status.
Use the API directly (the full option list is in the ScreenshotNeo documentation):
curl -G "https://api.screenshotneo.com/v1/shot" -d access_key=YOUR_API_KEY --data-urlencode url=https://example.com -o shot.webp
It also includes an MCP server with take_screenshot, get_page_info, and capture_pdf tools for Claude, Cursor, and other MCP clients. The Free plan includes 1,000 shots each month with no card; paid plans start at $5 for 3,000 shots. Create a free ScreenshotNeo account.
Best Value
Frequently Asked Questions
Does an MCP server have to use SSE?
No. Streamable HTTP is the recommended transport for new remote servers and can optionally use SSE for server-to-client notifications.
What’s actually slowing this PC down?
Pick the symptom - the matching free tool is one click away.
Which protocol version does legacy MCP HTTP+SSE implement?
It corresponds to the 2024-11-05 transport and is retained for backward compatibility.
Can I share one SSE transport between clients?
No. Each SSE connection is a session; keep a separate transport and session ID for each client.
What should I migrate to from SSEServerTransport?
Migrate to Streamable HTTP. The v2 migration guide describes the legacy package as a temporary frozen bridge.
Quick Recap
Product prices and availability are accurate as of the date/time indicated and are subject to change. Any price and availability information displayed on Amazon at the time of purchase will apply.

