What must every MCP request carry in params._meta now that initialize is gone?
answer
- Two keys, and one may be empty
- Namespaced under io.modelcontextprotocol/
- Stamped on every request, not once
- Missing key is invalid params, not a default
- protocolVersion plus clientCapabilities in _meta
basics
~10 sEvery 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 sSince 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{
"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
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.
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.
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.
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