
# Handler Options

> Every field of `McpHandlerOptions`.

`defineMcpHandler` takes this object, or a function returning it:

```ts
defineMcpHandler(options: McpHandlerOptions | ((event: H3Event) => McpHandlerOptions));
```

## Identity

| Option         | Type        | Default | Notes                                             |
| -------------- | ----------- | ------- | ------------------------------------------------- |
| `name`         | `string`    | —       | **Required.** Machine name of the server          |
| `version`      | `string`    | —       | **Required.** Server version                      |
| `title`        | `string`    | —       | Human-friendly display name                       |
| `description`  | `string`    | —       | Short description                                 |
| `icons`        | `McpIcon[]` | —       | `{ src, mimeType?, sizes?, theme? }`              |
| `websiteUrl`   | `string`    | —       | Server homepage                                   |
| `instructions` | `string`    | —       | Guidance passed to the model — worth writing well |

## Definitions

| Option              | Type                                         | Notes                                                   |
| ------------------- | -------------------------------------------- | ------------------------------------------------------- |
| `tools`             | `MaybeLazy<McpToolDefinition>[]`             | [Tools](/guide/basics/tools)                            |
| `resources`         | `MaybeLazy<McpResourceDefinition>[]`         | [Resources](/guide/basics/resources)                    |
| `resourceTemplates` | `MaybeLazy<McpResourceTemplateDefinition>[]` | [Templates](/guide/basics/resources#resource-templates) |
| `prompts`           | `MaybeLazy<McpPromptDefinition>[]`           | [Prompts](/guide/basics/prompts)                        |

`MaybeLazy<T>` is `T | (() => T | Promise<T>)`, so any entry may be a function resolved once on first use.

:read-more{to="/guide/basics/handler#lazy-definitions" title="Lazy definitions"}

## Era

| Option | Type                             | Default  | Notes                                                          |
| ------ | -------------------------------- | -------- | -------------------------------------------------------------- |
| `era`  | `"dual" \| "modern" \| "legacy"` | `"dual"` | Ignored on the `h3-mcp/modern` and `h3-mcp/legacy` entrypoints |

:read-more{to="/guide/eras" title="Protocol eras"}

## Modern Era

| Option           | Type                            | Default                               | Notes                                                                                             |
| ---------------- | ------------------------------- | ------------------------------------- | ------------------------------------------------------------------------------------------------- |
| `cache`          | `McpCacheOptions`               | `{ ttlMs: 0, cacheScope: "private" }` | Per-method keys: `discover`, `tools`, `prompts`, `resources`, `resourceTemplates`, `resourceRead` |
| `extensions`     | `Record<string, object>`        | —                                     | Advertised in `capabilities.extensions`                                                           |
| `logging`        | `boolean`                       | `false`                               | Advertise `logging` on `server/discover`                                                          |
| `echoServerInfo` | `boolean`                       | `true`                                | Include `_meta` server info on modern results                                                     |
| `subscriptions`  | `{ max?, keepAliveMs? }`        | `{ max: 100, keepAliveMs: 15000 }`    | `subscriptions/listen` limits                                                                     |
| `onListen`       | `(subscription, event) => void` | —                                     | Called when a listen stream opens                                                                 |

:read-more{to="/guide/modern/caching" title="Caching"}

## Legacy Era

| Option          | Type                            | Default | Notes                                     |
| --------------- | ------------------------------- | ------- | ----------------------------------------- |
| `session`       | `boolean \| McpSessionOptions`  | `false` | `{ enabled?, generateId?, maxSessions? }` |
| `onStream`      | `(stream, event) => void`       | —       | Enables the `GET`/SSE stream              |
| `onSubscribe`   | `(uri, event) => void`          | —       | `resources/subscribe`                     |
| `onUnsubscribe` | `(uri, event) => void`          | —       | `resources/unsubscribe`                   |
| `onLogLevelSet` | `(level, event) => void`        | —       | `logging/setLevel`                        |
| `onCancelled`   | `(notification, event) => void` | —       | `notifications/cancelled`                 |
| `onProgress`    | `(notification, event) => void` | —       | `notifications/progress`                  |

All of these are `@deprecated` in the sense that `2026-07-28` removed the feature — they remain fully functional on the legacy path.

:read-more{to="/guide/legacy/handshake" title="Legacy era"}

## Security

| Option   | Type                        | Default                                                     | Notes                                                 |
| -------- | --------------------------- | ----------------------------------------------------------- | ----------------------------------------------------- |
| `auth`   | `false \| McpAuthOptions`   | `false` (disabled)                                          | `{ enabled?, schemes?, tokens?, header?, validate? }` |
| `origin` | `false \| McpOriginOptions` | enabled: `{ allow: [], allowMissing: true }`                | `{ allow?, allowMissing?, validate? }`                |
| `limits` | `false \| McpLimitsOptions` | `{ maxBodySize: 4 MiB, contentType: ["application/json"] }` | Applied to POST before parsing                        |

Auth defaults, once enabled: `schemes: ["bearer", "api-key"]`, `header: "x-api-key"`. Enabling auth requires at least one of `tokens` or `validate`.

:read-more{to="/security" title="Security"}

## Dynamic Resolution

Every option above can be computed per request:

```ts
defineMcpHandler((event) => ({
  name: "my-server",
  version: "1.0.0",
  tools: getToolsForUser(event),
  auth: { tokens: tokensForTenant(event) },
}));
```

The function runs at most once per event and the result is cached in a `WeakMap`.

:read-more{to="/guide/basics/handler#dynamic-options" title="Dynamic options"}
