{"record":{"id":"1799e5d9762285ca","repo":"rohitg00/ai-engineering-from-scratch","slug":"32021","errorCode":"-32021","errorMessage":"Server requires roots, sampling, and elicitation capabilities for this request","messagePattern":"Server requires roots, sampling, and elicitation capabilities for this request","errorType":"error_code","errorClass":"ProtocolError","httpStatus":null,"severity":"error","filePath":"certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py","lineNumber":381,"sourceCode":"                        \"properties\": {\"goal\": {\"type\": \"string\"}},\n                        \"required\": [\"goal\"],\n                    },\n                },\n            },\n        }\n        capabilities = metadata[CLIENT_CAPABILITIES_KEY]\n        missing: dict[str, Any] = {}\n        if not isinstance(capabilities.get(\"roots\"), dict):\n            missing[\"roots\"] = {}\n        if not isinstance(capabilities.get(\"sampling\"), dict):\n            missing[\"sampling\"] = {}\n        elicitation = capabilities.get(\"elicitation\")\n        if not isinstance(elicitation, dict) or (\n            elicitation and not isinstance(elicitation.get(\"form\"), dict)\n        ):\n            missing[\"elicitation\"] = {\"form\": {}}\n        if missing:\n            raise ProtocolError(\n                -32021,\n                \"Server requires roots, sampling, and elicitation capabilities for this request\",\n                {\"requiredCapabilities\": missing},\n            )\n\n        state = params.get(\"requestState\")\n        responses = params.get(\"inputResponses\")\n        if state is None:\n            return self._input_required(\n                inputRequests=input_requests,\n                requestState=self.state_signer.issue(\"tools/call\", \"prepare_review\", arguments),\n            )\n        self.state_signer.verify(state, \"tools/call\", \"prepare_review\", arguments)\n        if not isinstance(responses, dict):\n            return self._input_required(inputRequests=input_requests, requestState=state)\n        missing_responses = {\n            key: request for key, request in input_requests.items() if key not in responses\n        }","sourceCodeStart":363,"sourceCodeEnd":399,"githubUrl":"https://github.com/rohitg00/ai-engineering-from-scratch/blob/39ea8a1c6d0b61f071226eff7ede4d4105fed820/certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py#L363-L399","documentation":"The prepare_review tool needs the client to gather workspace roots (roots/list), a model sample (sampling/createMessage), and a form elicitation (elicitation/create) on its behalf. Before starting that flow the server checks the client capabilities in metadata; if roots, sampling, or elicitation.form is missing it raises ProtocolError -32021 with a requiredCapabilities payload naming exactly what is absent.","triggerScenarios":"Calling tools/call prepare_review without declaring roots/sampling/elicitation capabilities under the client-capabilities metadata key the server reads (CLIENT_CAPABILITIES_KEY from initialize).","commonSituations":"A minimal MCP client that skips capability negotiation; a client that supports roots but not elicitation forms; test harnesses reusing metadata from a different lesson or an older server revision.","solutions":["Read the error's data.requiredCapabilities — it lists the exact capability objects to add","Declare roots {}, sampling {}, and elicitation {\"form\": {}} in the client capabilities metadata before calling prepare_review","Gate the prepare_review call on negotiated capabilities exactly as the server does"],"exampleFix":"# before\nmeta = {\"clientCapabilities\": {\"roots\": {}}}\n\n# after\nmeta = {\"clientCapabilities\": {\"roots\": {}, \"sampling\": {}, \"elicitation\": {\"form\": {}}}}","handlingStrategy":"validation","validationCode":"caps = meta[\"clientCapabilities\"]\nrequired = {\"roots\": {}, \"sampling\": {}, \"elicitation\": {\"form\": {}}}\nmissing = {k: v for k, v in required.items() if not isinstance(caps.get(k), dict)}\nif missing:\n    raise ValueError(f\"client missing capabilities: {sorted(missing)}\")\nserver.exchange(\"tools/call\", {\"name\": \"prepare_review\", ...}, metadata=meta)","typeGuard":"def has_required_capabilities(caps: object) -> bool:\n    if not isinstance(caps, dict):\n        return False\n    if not all(isinstance(caps.get(k), dict) for k in (\"roots\", \"sampling\")):\n        return False\n    el = caps.get(\"elicitation\")\n    return isinstance(el, dict) and isinstance(el.get(\"form\"), dict)","tryCatchPattern":"try:\n    result = server.exchange(\"tools/call\", params, metadata=meta)\nexcept ProtocolError as exc:\n    if exc.code == -32021:\n        upgrade_client_capabilities(exc.data[\"requiredCapabilities\"])\n    raise","preventionTips":["Negotiate capabilities at initialize and store them","Check the server's tool descriptions for capability requirements before calling","Treat -32021 data.requiredCapabilities as the authoritative fix list"],"tags":["mcp","capabilities","negotiation","elicitation"],"backgroundTag":"capability-negotiation-failed","analyzedSha":"39ea8a1c6d0b61f071226eff7ede4d4105fed820","analyzedAt":"2026-08-26T03:13:46.626Z","schemaVersion":2},"datasetVersion":"2026-08-26T07:17:17.940Z"}