rohitg00/ai-engineering-from-scratch · error · ValueError
unknown tool
Error message
unknown tool
What it means
The name is a valid non-empty string but self.tools (fixed at server construction) contains no tool with that exact key. The registry is static; the server does not create tools on demand. This is the lookup counterpart of the type check in error 111.
Source
Thrown at certifications/claude/lessons/11-mcp-server-design-and-integration/code/main.py:314
return self._complete(
messages=[
{
"role": "user",
"content": {"type": "text", "text": self.prompts[name]},
}
]
), []
raise LookupError(f"Method not found: {method}")
def _call_tool(
self, params: dict[str, Any], metadata: dict[str, Any]
) -> tuple[dict[str, Any], list[dict[str, Any]]]:
name = params["name"]
if not isinstance(name, str) or not name:
raise ValueError("name must be a non-empty string")
tool = self.tools.get(name)
if tool is None:
raise ValueError("unknown tool")
arguments = tool.validate_arguments(params.get("arguments", {}))
if name == "prepare_review":
return self._prepare_review(params, metadata, arguments), []
token = metadata.get("progressToken")
notifications: list[dict[str, Any]] = []
if token is not None:
if not isinstance(token, (str, int)) or isinstance(token, bool):
raise ValueError("progressToken must be a string or integer")
notifications = [
self._progress(token, 0, 1, "starting"),
self._progress(token, 1, 1, "complete"),
]
value = tool.handler(arguments)
return self._complete(
content=[{"type": "text", "text": json.dumps(value)}], isError=False
), notifications
View on GitHub (pinned to 39ea8a1c6d)
Solutions
- Call tools/list first and use a name from the returned list verbatim
- Re-fetch tools/list after any server upgrade instead of caching tool names
- Check exact casing, underscores, and hyphens in the name
Example fix
# before
params = {"name": "PrepareReview"}
# after
names = {t["name"] for t in server.exchange("tools/list", {})["tools"]}
assert "prepare_review" in names
params = {"name": "prepare_review"} Defensive patterns
Strategy: validation
Validate before calling
tools = server.exchange("tools/list", {})["tools"]
known = {t["name"] for t in tools}
if name not in known:
raise ValueError(f"tool {name!r} not advertised by this server")
server.exchange("tools/call", {"name": name, "arguments": args}) Type guard
def is_known_tool(server, name: str) -> bool:
tools = server.exchange("tools/list", {})["tools"]
return name in {t["name"] for t in tools} Try / catch
try:
result = server.exchange("tools/call", params)
except ValueError as exc:
if "unknown tool" in str(exc):
refresh_tool_cache()
raise Prevention
- Always resolve tool names from a fresh tools/list result
- Refresh the tool cache after server upgrades or reconnects
- Treat tool names as opaque exact-match identifiers
When it happens
Trigger: tools/call with a name that is not registered: a stale name from an older server version, wrong casing ('Prepare_Review'), or a tool belonging to a different MCP server.
Common situations: Server upgraded and a tool renamed or removed; client caches the tools/list result across restarts; copy-pasting a tool name from documentation for another server.
Related errors
- unknown resource
- unknown prompt
- name must be a non-empty string
- policy must be prefix-on-collision or reject
- canonical collision: {canonical_name}
AI-assisted analysis of rohitg00/ai-engineering-from-scratch@39ea8a1c6d (2026-08-26).
Data as JSON: /api/errors/50331245312cdc7c.
Report an issue: GitHub.