skip to content

What must every MCP request carry in params._meta now that initialize is gone?

level: juniorimportance: must knowfreq 82%

answer

  1. Two keys, and one may be empty
  2. Namespaced under io.modelcontextprotocol/
  3. Stamped on every request, not once
  4. Missing key is invalid params, not a default
  5. protocolVersion plus clientCapabilities in _meta

basics

~10 s

Every MCP request must put io.modelcontextprotocol/protocolVersion and io.modelcontextprotocol/clientCapabilities inside params._meta. An empty capabilities object is legal, but the key itself is not optional. clientInfo is optional and SHOULD be sent.

solid answer

~40 s

Since revision 2026-07-28 MCP is stateless, so each request declares for itself what the old `initialize` handshake used to declare once. Inside `params._meta` a client MUST send `io.modelcontextprotocol/protocolVersion` (a dated revision string such as `"2026-07-28"`) and `io.modelcontextprotocol/clientCapabilities` (the capability object; `{}` is a legal declaration meaning "no optional client capabilities"). It SHOULD also send `io.modelcontextprotocol/clientInfo` with a name and version, which is self-reported and therefore untrusted. Optionally it may send `io.modelcontextprotocol/logLevel`. Omitting either required key is an invalid request: the server answers `-32602` invalid params, and over Streamable HTTP that comes back with HTTP 400. Servers echo their own identity the other way, putting `io.modelcontextprotocol/serverInfo` in the result's `_meta`.

code

json · 19 lines
json
{
  "jsonrpc": "2.0",
  "id": 7,
  "method": "tools/call",
  "params": {
    "name": "search_docs",
    "arguments": { "query": "retry policy" },
    "_meta": {
      "io.modelcontextprotocol/protocolVersion": "2026-07-28",
      "io.modelcontextprotocol/clientCapabilities": {
        "elicitation": { "form": {} }
      },
      "io.modelcontextprotocol/clientInfo": {
        "name": "acme-console",
        "version": "3.1.0"
      }
    }
  }
}

go deeper

for a junior

Be able to name the two required _meta keys — protocolVersion and clientCapabilities — and say that they ride on every request, not on a one-time setup call.

for a middle

Explain the required-but-may-be-empty rule for clientCapabilities, why a missing key is -32602 rather than a default, and which keys are only SHOULD-level.

for a senior

Show where you enforce this in a real implementation: one place in the transport that stamps _meta outgoing, one validation point before dispatch, and capability reads scoped to the request being served.

for a principal

Own the tradeoff: repeating metadata on every call costs bandwidth and payload size, and buys stateless routing, free reconnects and no per-connection server state. Be ready to argue when that trade is worth it.

## What `_meta` is `_meta` is a reserved object that may appear inside a JSON-RPC request's `params`. In MCP revision 2026-07-28 it is the carrier for protocol-level metadata that is not part of the method's own arguments: which revision of the protocol this request speaks, what the client is able to do, who the client says it is, and a handful of cross-cutting concerns like progress tokens and tracing. The schema type is `RequestMetaObject`. Keys inside `_meta` are namespaced with a reverse-DNS-style prefix so that vendors and extensions cannot collide with the specification. The protocol's own keys use the `io.modelcontextprotocol/` prefix. ## The two required keys - `io.modelcontextprotocol/protocolVersion` — REQUIRED on every request. A dated revision string, for example `"2026-07-28"`. The server decides per request whether it can serve that revision; there is no handshake in which the two sides agree once and then stop talking about it. - `io.modelcontextprotocol/clientCapabilities` — REQUIRED on every request. The client capability object. An **empty object `{}` is legal and meaningful**: it says "I declare no optional client capabilities". What is not legal is leaving the key out. A server must not paper over the difference by treating an absent key as `{}` — an absent key is a malformed request. That distinction trips people up in interviews. "Required, but may be empty" is a deliberate design choice: it forces every client to state its position explicitly rather than letting silence mean anything. ## The SHOULD-level identity keys - `io.modelcontextprotocol/clientInfo` — optional, and a client SHOULD send it. It reports a client name and version for logging and operator-facing display. - `io.modelcontextprotocol/serverInfo` — the mirror image, which a server SHOULD put in the `_meta` of its *results*. Both are **self-reported and explicitly untrusted**. They are diagnostics, not credentials: no authorization, feature gating or consent decision may hang off them. ## Other keys that may appear - `io.modelcontextprotocol/logLevel` — optional; it sets the logging verbosity for this request. - Reserved names that the specification claims: `progressToken`, `io.modelcontextprotocol/subscriptionId`, and the W3C Trace Context exceptions `traceparent`, `tracestate` and `baggage` — the three tracing keys are the notable exception to the prefixing rule, kept unprefixed so standard tracing tooling works unchanged. ## Why per-request, and not once per connection The specification's own framing is blunt: "MCP is a stateless protocol: every request is self-contained and carries its own protocol version and capabilities." A server MUST NOT rely on prior requests over the same connection to establish context, and "an open connection, such as a STDIO process, is not a conversation or session." Repeating a few hundred bytes of metadata on every call is the price of that property, and it buys a lot: any node in a pool can serve any request, a reconnect costs nothing, and no server-side table maps connections to negotiated state. This is what replaced the `initialize` request and the `notifications/initialized` acknowledgement, both of which revision 2026-07-28 removed. Anything else the handshake used to deliver — the server's `instructions`, its `serverInfo`, the list of revisions it supports — now comes from the discovery call instead. ## What happens when a required field is missing A request whose `_meta` lacks `protocolVersion` or `clientCapabilities` is invalid params: the server responds with JSON-RPC error code `-32602`. On the Streamable HTTP transport that error is delivered with HTTP status 400. There is no defaulting, no grace period, and no "assume the latest revision". A separate error exists for the case where the metadata is well-formed but insufficient: `-32021` `MissingRequiredClientCapabilityError`, used when the server cannot serve the request without a client capability the request did not declare. ## Practical notes for implementers Build the `_meta` block once in your client's transport layer and stamp it onto every outgoing request rather than remembering it at call sites — the single most common bug in hand-rolled 2026-07-28 clients is a code path that forgets it. On the server side, validate `_meta` in one place before dispatch, and read capabilities from the request object you are currently serving rather than from any per-connection field. If you are writing a server that also answers older clients, remember that era is a property of the server, not of a single request: a modern-only server simply rejects handshake-era traffic.

  • What does a server return if a request omits io.modelcontextprotocol/protocolVersion?
    It is invalid params: JSON-RPC error `-32602`, delivered with HTTP 400 over Streamable HTTP. There is no defaulting to the newest revision and no tolerance window — both `protocolVersion` and `clientCapabilities` are required on every single request in revision 2026-07-28, so an absent key is a malformed request rather than an implicit declaration.
  • Is an empty clientCapabilities object legal, and what does it mean?
    Yes. `"io.modelcontextprotocol/clientCapabilities": {}` is a valid, explicit declaration that the client offers no optional client capabilities. The key being present is what matters; its contents may be empty. A server must not treat a *missing* key as equivalent to `{}` — missing is an error, empty is a statement.
  • Besides the protocol version and capabilities, what other names are reserved inside _meta?
    `progressToken`, `io.modelcontextprotocol/subscriptionId`, and the W3C Trace Context keys `traceparent`, `tracestate` and `baggage`. The tracing three are the deliberate exception to the reverse-DNS prefixing convention so that off-the-shelf distributed-tracing tooling interoperates without translation.

saying these in an interview costs you the question

  • Says the client sends capabilities once during initialize
  • Treats a missing clientCapabilities key as an empty object
  • Claims an empty capabilities object is invalid
  • Thinks clientInfo is required or trustworthy
  • Believes protocolVersion defaults to the newest revision

context