rohitg00/ai-engineering-from-scratch · error · ValueError
_meta.{CLIENT_CAPABILITIES_KEY} is required
Error message
_meta.{CLIENT_CAPABILITIES_KEY} is required What it means
The server requires `_meta["io.modelcontextprotocol/clientCapabilities"]` to be a dict. It may be empty, but it must exist and be an object — a stateless server re-negotiates capabilities on every request because it cannot rely on an initialize handshake. Missing key, null, arrays, or strings all trigger this error.
Source
Thrown at certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py:213
raise ValueError("notifications/cancelled reason must be a string")
metadata = params.get("_meta")
if metadata is not None and not isinstance(metadata, dict):
raise ValueError("notifications/cancelled _meta must be an object")
if set(params) - {"requestId", "reason", "_meta"}:
raise ValueError("notifications/cancelled contains unexpected fields")
def _validate_request_metadata(self, params: Any) -> dict[str, Any]:
if not isinstance(params, dict):
raise ValueError("params must be an object")
metadata = params.get("_meta")
if not isinstance(metadata, dict):
raise ValueError("_meta must be an object")
version = metadata.get(PROTOCOL_VERSION_KEY)
capabilities = metadata.get(CLIENT_CAPABILITIES_KEY)
if not isinstance(version, str):
raise ValueError(f"_meta.{PROTOCOL_VERSION_KEY} is required")
if not isinstance(capabilities, dict):
raise ValueError(f"_meta.{CLIENT_CAPABILITIES_KEY} is required")
client_info = metadata.get(CLIENT_INFO_KEY)
if client_info is not None and (
not isinstance(client_info, dict)
or not isinstance(client_info.get("name"), str)
or not isinstance(client_info.get("version"), str)
):
raise ValueError(f"_meta.{CLIENT_INFO_KEY} must include name and version")
if version != CURRENT_PROTOCOL_VERSION:
raise ProtocolError(
-32022,
"Unsupported protocol version",
{"supported": [CURRENT_PROTOCOL_VERSION], "requested": version},
)
return metadata
def _dispatch(
self, method: str, params: dict[str, Any], metadata: dict[str, Any]
) -> tuple[dict[str, Any], list[dict[str, Any]]]:View on GitHub (pinned to 39ea8a1c6d)
Solutions
- Add "io.modelcontextprotocol/clientCapabilities": {} (empty dict is valid) next to protocolVersion in _meta
- If you do advertise capabilities, keep it an object: {"roots": {"listChanged": true}}
- Validate the assembled _meta with a helper before calling exchange
Example fix
# before
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
}
# after
"_meta": {
"io.modelcontextprotocol/protocolVersion": "2026-07-28",
"io.modelcontextprotocol/clientCapabilities": {},
} Defensive patterns
Strategy: validation
Validate before calling
CAPS_KEY = "io.modelcontextprotocol/clientCapabilities"
def with_capabilities(meta: dict) -> dict:
if not isinstance(meta.get(CAPS_KEY), dict):
meta[CAPS_KEY] = {}
return meta Type guard
def has_client_capabilities(meta: dict) -> bool:
return isinstance(meta.get("io.modelcontextprotocol/clientCapabilities"), dict) Prevention
- Default clientCapabilities to {} in the shared request builder
- Treat _meta assembly as all-or-nothing: version, capabilities, and optional clientInfo in one function
- Test the minimal valid _meta against every method your client calls
When it happens
Trigger: _meta containing only protocolVersion; clientCapabilities set to [], "tools", null, or true; capabilities nested one level too deep (e.g. under clientInfo).
Common situations: Minimal hand-rolled clients that send only the version; clients modeled on the stateful initialize flow where capabilities were sent once and omitted afterward; merging bugs that drop the capabilities key during payload assembly.
Understand the failure class
Background: Schema validation failed / invalid input schema: payload rejected because its shape doesn't match the expected schema — this error's family across 28 libraries.
Related errors
- _meta.{PROTOCOL_VERSION_KEY} is required
- -32600
- -32603
- _meta must be an object
- _meta.{CLIENT_INFO_KEY} must include name and version
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/892b7c978ca4c28e.
Report an issue: GitHub.