Remote MCP Servers with Streamable HTTP
Stdio servers run on the user's machine. A remote MCP server runs on your infrastructure and is reached over HTTP, which is how SaaS products expose their tools to AI apps. The current HTTP transport is Streamable HTTP: one endpoint that accepts JSON-RPC over POST and can stream responses.
A stateless server with Express
import express from 'express';
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import { z } from 'zod';
function buildServer() {
const server = new McpServer({ name: 'status-page', version: '1.0.0' });
server.registerTool(
'get_service_status',
{ description: 'Current status of a service', inputSchema: { service: z.enum(['api', 'web', 'db']) } },
async ({ service }) => ({ content: [{ type: 'text', text: await statusOf(service) }] })
);
return server;
}
const app = express();
app.use(express.json());
app.post('/mcp', async (req, res) => {
// Stateless mode: a fresh server and transport per request
const server = buildServer();
const transport = new StreamableHTTPServerTransport({ sessionIdGenerator: undefined });
res.on('close', () => {
transport.close();
server.close();
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(3333, () => console.log('MCP server on http://localhost:3333/mcp'));
Stateless vs stateful
- Stateless (above) scales horizontally behind a load balancer and suits simple tool servers.
- Stateful sessions use an
Mcp-Session-Idheader and are needed for features such as server-initiated notifications. Pass asessionIdGeneratorand keep transports in a map keyed by session id.
Before you deploy
- Authentication - the MCP spec uses OAuth 2.1 for remote servers. At minimum require a bearer token.
- HTTPS only in production.
- Validate the
Originheader and bind local development servers to 127.0.0.1 to prevent DNS-rebinding attacks. - Per-user authorisation in every tool: the token identifies the user, and the tool checks what they may access.
- Rate limits and logging like any public API.
Was this article helpful?
Comments
No comments yet. Have a question or a tip? Be the first to share it.