
# 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](https://h3.dev) can serve MCP.

## Node, Deno, Bun

```ts [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 });
```

::code-group

```bash [node]
node ./server.ts
```

```bash [deno]
deno run -A ./server.ts
```

```bash [bun]
bun run server.ts
```

::

## Workers and Edge Runtimes

Export the fetch handler instead of listening:

```ts
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](/guide/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`](/guide/modern/subscriptions) 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`](/security/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:

```ts
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`](https://h3.dev/guide/api/h3#h3mount), 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:

```ts
import { defineMcpHandler } from "h3-mcp/modern"; // ~28kb min / ~9kb gzip
```

:read-more{to="/guide/eras#entrypoints" title="Entrypoints"}
