h3-mcp logoh3-mcp

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

ProtectionDefault
Origin validationRequests with an Origin header are denied unless allowlisted; requests without one are allowed
Body size limit4 MiB, enforced before parsing
Content-Type checkapplication/json only
Input validationEvery method's params type-checked at the boundary
Error opacityHandler error messages are never forwarded on the modern era
Cache scopeprivate, ttlMs: 0 — results are never shared
Header/body agreementMirrored modern headers must match the body (-32020)

#Off by Default

ProtectionWhy it is opt-in
Transport authMany MCP servers run behind a gateway that already authenticates
SessionsLegacy only, and stateful — most servers don't need them
maxSessions capOnly 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.
  • Integrity-protect requestState. It round-trips through the client. Sign it if it influences authorization — see MRTR.
  • Don't mirror secrets into headers. x-mcp-header annotations are visible to intermediaries — see request 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

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.
h3-mcp logo

h3-mcp  MCP servers, built on H3.