Deploy a remote MCP server agents can reach by URL.
From stdio process to Streamable HTTP endpoint: the stateless server pattern, where to host it, and the production checklist that makes the endpoint listable — and chargeable.
A local MCP server reaches exactly the people willing to install it. A remote one reaches anyone with the URL — no runtime, no package manager, no config beyond a single entry. For a server you want widely used, remote deployment is the difference between a tool and a product.
It's also the precondition for getting paid: only an endpoint you operate can return HTTP 402 and require USDC payment before a tool runs. This guide covers the transport, the code shape, and what production-ready means for an MCP endpoint.
Streamable HTTP, stateless by default
Modern remote MCP runs on the Streamable HTTP transport: a single route — conventionally /mcp — accepting POSTed JSON-RPC, with streamed responses when a call needs them. It replaced the older two-endpoint SSE arrangement and is what current clients expect.
The pattern with the fewest operational sharp edges is stateless: handle each request with a fresh transport, hold no session affinity, and let any instance serve any request. You give up server-initiated extras, but you gain free horizontal scaling and zero sticky-session configuration — the right trade for a public tool server.
The handler
With the TypeScript SDK and Express, the whole transport layer fits in a screen. Your tool definitions don't change at all from a stdio version — only the wiring does.
import express from "express";
import { StreamableHTTPServerTransport } from "@modelcontextprotocol/sdk/server/streamableHttp.js";
import { buildServer } from "./server.js"; // your tools, unchanged
const app = express();
app.use(express.json());
app.post("/mcp", async (req, res) => {
const server = buildServer();
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined, // stateless
});
res.on("close", () => transport.close());
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
});
app.listen(process.env.PORT ?? 3000);Where to run it
An MCP endpoint is an ordinary HTTP service, so anything that hosts one works: edge platforms like Cloudflare Workers suit lightweight tools and ship MCP-specific scaffolding; container hosts like Fly.io, Railway, and Render suit servers with dependencies or longer-running calls; your existing cloud account works fine if you already operate there.
Two selection criteria actually matter for MCP specifically: request timeout ceilings (a slow tool call shouldn't be killed by a 10-second platform limit) and streaming support, since Streamable HTTP responses may be SSE streams. Check both before committing; everything else is standard web-service judgment.
The production checklist
HTTPS is non-negotiable — clients won't connect without it. Add a plain health route for your host's checks, keep it separate from /mcp. Log every tool invocation with duration and outcome; this is your only window into how agents actually use the server. Rate-limit by IP or key, because agents retry enthusiastically and a runaway loop looks like a small DDoS.
Decide the access model before announcing the URL. Open is fine for cheap, harmless tools. OAuth fits user-scoped data. And x402 fits metered value: the endpoint quotes a price per call, the agent pays in USDC, settlement lands on Base in roughly two seconds, and the handler executes only after payment is secured — which makes the payment gate double as your abuse control.
After deploy: verify, register, list
Point the MCP Inspector at the live URL and run every tool once — handshake bugs and broken env vars surface here, not in a user's chat session. Then add the remote URL to your server's entry in the official MCP registry so clients and indexes know the endpoint exists.
From there, Loomal can carry it as a listing with a live tool list, and if you attach pricing — minimum $0.01 per call, repriceable in one console field — the endpoint becomes discoverable by agents that pay per call. The fee on settled transactions is 5%, currently waived.
FAQ
Do I have to rewrite my stdio server to make it remote?
No — tool definitions carry over untouched. You swap the stdio transport for a Streamable HTTP transport behind an HTTP route and deploy it like any web service. Keeping both entrypoints in one codebase is common: stdio for local use, HTTP for the hosted endpoint.
Stateless or session-based — which should I deploy?
Stateless unless you have a concrete need for sessions. Stateless instances scale horizontally with no sticky-session setup and survive restarts invisibly. Session mode buys server-initiated messages and per-session state at the cost of real operational complexity.
How should I secure a public MCP endpoint?
Match the mechanism to the risk: open access for cheap harmless tools, OAuth when tools touch user-scoped data, x402 when calls cost you money to serve. Payment is underrated as security — a per-call price from $0.01 ends free-rider scraping without maintaining a key system.
How do agents discover the endpoint once it's live?
Register the remote URL in your official MCP registry entry, then claim the resulting Loomal listing. A claimed listing publishes the server's live tool list, and a priced endpoint becomes machine-discoverable by agents that can pay and call it in one flow.
Deployed and verified?
See how live remote servers present themselves on the Loomal Index.