{"record":{"id":"38428b7cc4d3df38","repo":"rohitg00/ai-engineering-from-scratch","slug":"meta-client-info-key-must-include-name-and-vers","errorCode":null,"errorMessage":"_meta.{CLIENT_INFO_KEY} must include name and version","messagePattern":"_meta\\.(.+?) must include name and version","errorType":"validation","errorClass":"ValueError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py","lineNumber":220,"sourceCode":"    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(\n        self, method: str, params: dict[str, Any], metadata: dict[str, Any]\n    ) -> tuple[dict[str, Any], list[dict[str, Any]]]:\n        if method == \"server/discover\":\n            extra = set(params) - {\"_meta\"}\n            if extra:\n                raise ValueError(\"server/discover accepts no params beyond _meta\")\n            return self._complete(\n                supportedVersions=[CURRENT_PROTOCOL_VERSION],\n                capabilities={\"prompts\": {}, \"resources\": {}, \"tools\": {}},","sourceCodeStart":202,"sourceCodeEnd":238,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py#L202-L238","documentation":"Unlike version and capabilities, `_meta[\"io.modelcontextprotocol/clientInfo\"]` is optional — but if present it must be an object with string `name` and string `version` fields, matching the MCP ImplementationInfo shape. This error fires only when the key exists and is malformed: not a dict, missing name/version, or either field is not a string.","triggerScenarios":"clientInfo set to \"my-client\" (bare string), {\"name\": \"x\"} (no version), {\"name\": \"x\", \"version\": 1.2} (numeric version), or null under the key.","commonSituations":"Injecting a version parsed from pyproject/package.json that arrives as a tuple or float; optional-field handling where None is sent instead of omitting the key entirely; clientInfo confused with serverInfo in the payload.","solutions":["Either omit the clientInfo key entirely (it is optional) or send {\"name\": \"<string>\", \"version\": \"<string>\"}","Stringify version numbers: str(__version__) or version from importlib.metadata as str","When clientInfo is unknown, delete the key from the payload rather than setting it to None"],"exampleFix":"# before\n\"_meta\": {\n  \"io.modelcontextprotocol/protocolVersion\": \"2026-07-28\",\n  \"io.modelcontextprotocol/clientCapabilities\": {},\n  \"io.modelcontextprotocol/clientInfo\": None,\n}\n# after\n\"_meta\": {\n  \"io.modelcontextprotocol/protocolVersion\": \"2026-07-28\",\n  \"io.modelcontextprotocol/clientCapabilities\": {},\n  \"io.modelcontextprotocol/clientInfo\": {\"name\": \"my-client\", \"version\": \"1.0.0\"},\n}","handlingStrategy":"type-guard","validationCode":"INFO_KEY = \"io.modelcontextprotocol/clientInfo\"\n\ndef clean_client_info(meta: dict) -> dict:\n    info = meta.get(INFO_KEY)\n    if info is None:\n        meta.pop(INFO_KEY, None)\n    else:\n        meta[INFO_KEY] = {\n            \"name\": str(info[\"name\"]),\n            \"version\": str(info[\"version\"]),\n        }\n    return meta","typeGuard":"def valid_client_info(meta: dict) -> bool:\n    info = meta.get(\"io.modelcontextprotocol/clientInfo\")\n    return info is None or (\n        isinstance(info, dict)\n        and isinstance(info.get(\"name\"), str)\n        and isinstance(info.get(\"version\"), str)\n    )","tryCatchPattern":null,"preventionTips":["Omit clientInfo when unknown rather than sending None or partial dicts","Coerce name/version with str() at the boundary","Centralize clientInfo in one config so partial copies cannot leak into requests"],"tags":["mcp","client-info","metadata","optional-fields"],"backgroundTag":"schema-validation-failed","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}