Deploy Anywhere
An MCP endpoint is an H3 app — it runs wherever H3 runs.
h3-mcp adds no runtime requirements of its own: no filesystem, no timers beyond the per-stream keep-alive, no global state. Anything that can run H3 can serve MCP.
#Node, Deno, Bun
import { H3, serve } from "h3";
import { defineMcpHandler } from "h3-mcp";
const app = new H3().all("/mcp", defineMcpHandler({ name: "my-server", version: "1.0.0" }));
serve(app, { port: 3000 });node ./server.ts#Workers and Edge Runtimes
Export the fetch handler instead of listening:
import { H3 } from "h3";
import { defineMcpHandler } from "h3-mcp";
const app = new H3().all("/mcp", defineMcpHandler({ name: "my-server", version: "1.0.0" }));
export default { fetch: app.fetch };Two things to keep in mind on serverless platforms:
- Prefer stateless. Legacy sessions live in process memory, so they break as soon as more than one isolate serves the endpoint. Serve
era: "modern", or keep real state in your own store keyed by the session id. - Streams need a long-lived invocation.
subscriptions/listenand the legacyGETstream hold a connection open. Platforms that cap response duration will cut them; make sure clients reconnect, and keepkeepAliveMsbelow any idle timeout in front of you.
#Behind a Proxy
MCP is ordinary HTTP with SSE responses, so the usual rules apply: disable response buffering for text/event-stream, and allow an idle timeout longer than keepAliveMs. The keep-alive comments exist precisely so an idle stream still writes bytes.
If a gateway terminates auth for you, either leave auth off and trust the network, or keep it on with an internal token — but do not rely on a header the gateway does not strip from client input.
#Mounting Under a Path
An MCP handler is just a route, so it composes with the rest of your app:
const app = new H3()
.get("/health", () => "ok")
.all("/mcp", mcpHandler)
.all("/mcp/admin", adminMcpHandler);It also nests inside a larger app with H3's mount, which is convenient when MCP is one surface of a bigger service.
#Choosing an Entrypoint
For a deployment where bundle size matters — Workers, Lambda cold starts — import only the era you serve:
import { defineMcpHandler } from "h3-mcp/modern"; // ~28kb min / ~9kb gzip