
# Security

> What is protected by default, and what is yours to do.

An MCP endpoint is an API whose callers are language models acting on instructions you did not write. `h3-mcp` therefore ships its protections **on**, and validates every client-supplied value at the transport boundary rather than inside your handlers.

## On by Default

| Protection                               | Default                                                                                          |
| ---------------------------------------- | ------------------------------------------------------------------------------------------------ |
| [Origin validation](/security/origin)    | Requests with an `Origin` header are denied unless allowlisted; requests without one are allowed |
| [Body size limit](/security/limits)      | 4 MiB, enforced before parsing                                                                   |
| [Content-Type check](/security/limits)   | `application/json` only                                                                          |
| [Input validation](/security/validation) | Every method's params type-checked at the boundary                                               |
| Error opacity                            | Handler error messages are never forwarded on the modern era                                     |
| Cache scope                              | `private`, `ttlMs: 0` — results are never shared                                                 |
| Header/body agreement                    | Mirrored modern headers must match the body (`-32020`)                                           |

## Off by Default

| Protection                         | Why it is opt-in                                                 |
| ---------------------------------- | ---------------------------------------------------------------- |
| [Transport auth](/security/auth)   | Many MCP servers run behind a gateway that already authenticates |
| [Sessions](/guide/legacy/sessions) | Legacy only, and stateful — most servers don't need them         |
| `maxSessions` cap                  | Only meaningful once sessions are on — set it when they are      |

## Your Responsibilities

The library cannot know your threat model. These stay with you:

- **Authorize each operation.** Auth answers "is this a valid caller"; only your handler knows whether _this_ caller may read _that_ record. Tool arguments are model-generated; treat them as untrusted input from the internet.
- **Keep results scoped.** A per-user result must never be `cacheScope: "public"` — see [caching](/guide/modern/caching).
- **Integrity-protect `requestState`.** It round-trips through the client. Sign it if it influences authorization — see [MRTR](/guide/modern/mrtr).
- **Don't mirror secrets into headers.** `x-mcp-header` annotations are visible to intermediaries — see [request headers](/guide/modern/headers).
- **Bound what you keep.** Your own maps of sessions, subscriptions, and replay buffers grow until you cap them.
- **Watch what you say.** Tool results, log messages, and completion values all reach the client, and often the model. No credentials, internal hostnames, or stack traces.

## Deployment Checklist

```ts
defineMcpHandler({
  name: "my-server",
  version: "1.0.0",

  // 1. Who may call at all
  auth: { tokens: [process.env.MCP_TOKEN!] },

  // 2. Which browsers may call from a page
  origin: { allow: ["https://app.example.com"] },

  // 3. How much you accept per request
  limits: { maxBodySize: 512 * 1024 },

  // 4. What may be cached, and by whom
  cache: { ttlMs: 0, cacheScope: "private", discover: { ttlMs: 3_600_000, cacheScope: "public" } },

  tools: [/* validate inside every handler too */],
});
```

Then confirm, against the deployed endpoint, that:

- An unauthenticated request is `401`, and a wrong-`Origin` request is `403`.
- A 10 MiB body is `413` and `text/plain` is `415`.
- A tool called with wrong argument types fails without reaching your logic.
- A tool handler that throws returns `-32603 "Internal error"` — with no path, query, or upstream detail in the message.

::card-group

::card
---

title: Transport Auth
icon: ph:key-duotone
to: /security/auth
---

Bearer tokens, API keys, custom validators.
::

::card
---

title: Origin Validation
icon: ph:shield-check-duotone
to: /security/origin
---

DNS rebinding protection for local servers.
::

::card
---

title: Request Limits
icon: ph:ruler-duotone
to: /security/limits
---

Body size and `Content-Type` enforcement.
::

::card
---

title: Input Validation
icon: ph:check-square-duotone
to: /security/validation
---

What is validated for you, and where.
::

::
