h3-mcp logoh3-mcp

Discovery

server/discover replaces the initialize handshake.

Modern clients do not negotiate. Every request carries what the server needs to know, and a single cacheable server/discover call tells the client what the server is.

#Per-Request _meta

Instead of handshake state, each request carries reserved keys in params._meta:

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "hello",
    "arguments": { "name": "world" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28", // required
      "io.modelcontextprotocol/clientInfo": { "name": "my-client", "version": "1.0.0" },
      "io.modelcontextprotocol/clientCapabilities": {}, // required
      "progressToken": "abc", // optional, opts into a response stream
      "io.modelcontextprotocol/logLevel": "info", // optional, same
    },
  },
}

The protocol version and client capabilities are required on every modern request; a request without them is rejected at the boundary. h3-mcp validates and surfaces them on the request context:

handler: (event) => {
  const mcp = event.context.mcp!;
  mcp.protocolVersion; // "2026-07-28"
  mcp.era; // "modern"
  mcp.clientInfo; // { name, version, ... } — client SHOULD send this
  mcp.clientCapabilities; // {} — what the client can do
  mcp.trace; // W3C traceparent / tracestate / baggage, if propagated
};

Requiring a capability is one call, and throws -32021 when the client did not declare it:

event.context.mcp!.requireClientCapability?.("sampling");
Read more in Request context reference.

#server/discover

The method is implemented from your handler options — there is nothing to wire up:

// ← result
{
  "resultType": "complete",
  "supportedVersions": ["2026-07-28", "2025-11-25", "2025-06-18", "2025-03-26"],
  "capabilities": { "tools": {}, "resources": {}, "prompts": {} },
  "instructions": "...",
  "ttlMs": 3600000,
  "cacheScope": "public",
  "_meta": {
    "io.modelcontextprotocol/serverInfo": { "name": "my-server", "version": "1.0.0" },
  },
}

supportedVersions reflects the configured era — a modern-only server lists only 2026-07-28. capabilities is derived from what you declared, and instructions, title, icons, and websiteUrl come from the handler options.

Because discovery output changes rarely, it is worth an explicit cache window:

defineMcpHandler({
  name: "my-server",
  version: "1.0.0",
  cache: { discover: { ttlMs: 3_600_000, cacheScope: "public" } },
});
Read more in Caching.

#Echoed Server Info

Every modern result carries _meta["io.modelcontextprotocol/serverInfo"], so a client that reconnected or lost its discovery cache still knows who answered. Opt out if you would rather not repeat it:

defineMcpHandler({ name: "my-server", version: "1.0.0", echoServerInfo: false });

#Result Type

Modern results also carry resultType, which is "complete" unless a handler asked the client for more input:

Read more in Multi round-trip requests.
h3-mcp logo

h3-mcp  MCP servers, built on H3.