h3-mcp logoh3-mcp

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

server.ts
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/listen and the legacy GET stream hold a connection open. Platforms that cap response duration will cut them; make sure clients reconnect, and keep keepAliveMs below 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
Read more in Entrypoints.
h3-mcp logo

h3-mcp  MCP servers, built on H3.