rohitg00/ai-engineering-from-scratch · error · ProtocolError
-32021
-32021
Error message
Server requires roots, sampling, and elicitation capabilities for this request
What it means
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.
Source
Thrown at certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py:381
"properties": {"goal": {"type": "string"}},
"required": ["goal"],
},
},
},
}
capabilities = metadata[CLIENT_CAPABILITIES_KEY]
missing: dict[str, Any] = {}
if not isinstance(capabilities.get("roots"), dict):
missing["roots"] = {}
if not isinstance(capabilities.get("sampling"), dict):
missing["sampling"] = {}
elicitation = capabilities.get("elicitation")
if not isinstance(elicitation, dict) or (
elicitation and not isinstance(elicitation.get("form"), dict)
):
missing["elicitation"] = {"form": {}}
if missing:
raise ProtocolError(
-32021,
"Server requires roots, sampling, and elicitation capabilities for this request",
{"requiredCapabilities": missing},
)
state = params.get("requestState")
responses = params.get("inputResponses")
if state is None:
return self._input_required(
inputRequests=input_requests,
requestState=self.state_signer.issue("tools/call", "prepare_review", arguments),
)
self.state_signer.verify(state, "tools/call", "prepare_review", arguments)
if not isinstance(responses, dict):
return self._input_required(inputRequests=input_requests, requestState=state)
missing_responses = {
key: request for key, request in input_requests.items() if key not in responses
}View on GitHub (pinned to 39ea8a1c6d)
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
Example fix
# before
meta = {"clientCapabilities": {"roots": {}}}
# after
meta = {"clientCapabilities": {"roots": {}, "sampling": {}, "elicitation": {"form": {}}}} Defensive patterns
Strategy: validation
Validate before calling
caps = meta["clientCapabilities"]
required = {"roots": {}, "sampling": {}, "elicitation": {"form": {}}}
missing = {k: v for k, v in required.items() if not isinstance(caps.get(k), dict)}
if missing:
raise ValueError(f"client missing capabilities: {sorted(missing)}")
server.exchange("tools/call", {"name": "prepare_review", ...}, metadata=meta) Type guard
def has_required_capabilities(caps: object) -> bool:
if not isinstance(caps, dict):
return False
if not all(isinstance(caps.get(k), dict) for k in ("roots", "sampling")):
return False
el = caps.get("elicitation")
return isinstance(el, dict) and isinstance(el.get("form"), dict) Try / catch
try:
result = server.exchange("tools/call", params, metadata=meta)
except ProtocolError as exc:
if exc.code == -32021:
upgrade_client_capabilities(exc.data["requiredCapabilities"])
raise Prevention
- 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
When it happens
Trigger: 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).
Common situations: 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.
Related errors
- _meta.{CLIENT_CAPABILITIES_KEY} is required
- -32022
- inputResponses entries must be objects
- review_goal.content must be an object
- review_goal must be accepted with a string goal
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/1799e5d9762285ca.
Report an issue: GitHub.