{"record":{"id":"e3125eb01522bde7","repo":"rohitg00/ai-engineering-from-scratch","slug":"meta-protocol-version-key-is-required","errorCode":null,"errorMessage":"_meta.{PROTOCOL_VERSION_KEY} is required","messagePattern":"_meta\\.(.+?) is required","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py","lineNumber":211,"sourceCode":"        reason = params.get(\"reason\")\n        if reason is not None and not isinstance(reason, str):\n            raise ValueError(\"notifications/cancelled reason must be a string\")\n        metadata = params.get(\"_meta\")\n        if metadata is not None and not isinstance(metadata, dict):\n            raise ValueError(\"notifications/cancelled _meta must be an object\")\n        if set(params) - {\"requestId\", \"reason\", \"_meta\"}:\n            raise ValueError(\"notifications/cancelled contains unexpected fields\")\n\n    def _validate_request_metadata(self, params: Any) -> dict[str, Any]:\n        if not isinstance(params, dict):\n            raise ValueError(\"params must be an object\")\n        metadata = params.get(\"_meta\")\n        if not isinstance(metadata, dict):\n            raise ValueError(\"_meta must be an object\")\n        version = metadata.get(PROTOCOL_VERSION_KEY)\n        capabilities = metadata.get(CLIENT_CAPABILITIES_KEY)\n        if not isinstance(version, str):\n            raise ValueError(f\"_meta.{PROTOCOL_VERSION_KEY} is required\")\n        if not isinstance(capabilities, dict):\n            raise ValueError(f\"_meta.{CLIENT_CAPABILITIES_KEY} is required\")\n        client_info = metadata.get(CLIENT_INFO_KEY)\n        if client_info is not None and (\n            not isinstance(client_info, dict)\n            or not isinstance(client_info.get(\"name\"), str)\n            or not isinstance(client_info.get(\"version\"), str)\n        ):\n            raise ValueError(f\"_meta.{CLIENT_INFO_KEY} must include name and version\")\n        if version != CURRENT_PROTOCOL_VERSION:\n            raise ProtocolError(\n                -32022,\n                \"Unsupported protocol version\",\n                {\"supported\": [CURRENT_PROTOCOL_VERSION], \"requested\": version},\n            )\n        return metadata\n\n    def _dispatch(","sourceCodeStart":193,"sourceCodeEnd":229,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py#L193-L229","documentation":"After confirming `_meta` is an object, the server looks up the namespaced key `io.modelcontextprotocol/protocolVersion` and requires it to be a string. Missing key, null, or a non-string value (e.g. a number) raises this error. Version negotiation is mandatory per request in this stateless design, so there is no session-cached version to fall back on.","triggerScenarios":"_meta that includes clientCapabilities but omits protocolVersion; protocolVersion set to null, 2026 (int), or a list; typo'd key like \"protocolVersion\" without the io.modelcontextprotocol/ namespace prefix.","commonSituations":"Copy-paste from older MCP examples that used a bare \"protocolVersion\" key inside InitializeParams rather than the namespaced _meta key; schema drift between client and server after a spec version bump; YAML/JSON config where the date string got coerced.","solutions":["Set _meta[\"io.modelcontextprotocol/protocolVersion\"] = \"2026-07-28\" (CURRENT_PROTOCOL_VERSION, a string) in every request","Check for the exact namespaced key — a bare \"protocolVersion\" key is silently ignored","If the value comes from config, assert isinstance(value, str) before sending"],"exampleFix":"# before\n\"_meta\": {\"io.modelcontextprotocol/clientCapabilities\": {}}\n# after\n\"_meta\": {\n  \"io.modelcontextprotocol/protocolVersion\": \"2026-07-28\",\n  \"io.modelcontextprotocol/clientCapabilities\": {},\n}","handlingStrategy":"validation","validationCode":"VERSION_KEY = \"io.modelcontextprotocol/protocolVersion\"\n\ndef with_version(meta: dict) -> dict:\n    meta[VERSION_KEY] = str(meta.get(VERSION_KEY, \"2026-07-28\"))\n    return meta","typeGuard":"def has_protocol_version(meta: dict) -> bool:\n    return isinstance(meta.get(\"io.modelcontextprotocol/protocolVersion\"), str)","tryCatchPattern":"try:\n    server.exchange(method, params)\nexcept ValueError as e:\n    if \"protocolVersion\" in str(e):\n        params[\"_meta\"][VERSION_KEY] = \"2026-07-28\"\n        server.exchange(method, params)","preventionTips":["Use the namespaced key verbatim — define it as a module constant, never retype it","Stringify any version value coming from config or packaging metadata","Fail fast client-side with an assert instead of a server round trip"],"tags":["mcp","protocol-version","metadata","validation"],"backgroundTag":"schema-validation-failed","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}