PrefectHQ/fastmcp · error · NotFoundError
Unknown tool: {tool_name}
Error message
Unknown tool: {tool_name} What it means
Inside the CodeMode sandbox, call_tool() resolves the requested tool against the backend catalog via _find_tool(). If no tool matches the given name, NotFoundError('Unknown tool: <name>') is raised, and the sandboxed code sees this as a failed tool call.
Source
Thrown at fastmcp_slim/fastmcp/experimental/transforms/code_mode.py:621
) -> Any:
"""Execute tool calls using Python code."""
call_count = 0
async def call_tool(tool_name: str, params: dict[str, Any]) -> Any:
nonlocal call_count
if max_tool_calls is not None:
call_count += 1
if call_count > max_tool_calls:
raise ToolError(
f"Tool call limit exceeded: at most {max_tool_calls} "
"call_tool() invocations are allowed per execute()."
)
backend_tools = await transform.get_tool_catalog(ctx)
tool = transform._find_tool(tool_name, backend_tools)
if tool is None:
raise NotFoundError(f"Unknown tool: {tool_name}")
result = await ctx.fastmcp.call_tool(tool.name, params)
return _unwrap_tool_result(result)
return await transform.sandbox_provider.run(
code,
external_functions={"call_tool": call_tool},
)
return Tool.from_function(
fn=execute,
name=self.execute_tool_name,
description=self._build_execute_description(),
)
__all__ = [
"CodeMode",View on GitHub (pinned to 1f02114297)
Solutions
- Fix the tool name in the generated code to match one in the catalog exactly
- Have sandboxed code call the discovery/catalog tool first to list valid names before invoking
- Verify the tool exists on the underlying FastMCP server and isn't filtered out by the transform's tool set
- Catch NotFoundError inside generated code and fall back to listing tools
Example fix
// before
result = await call_tool("get_wether", {"city": "Paris"})
// after
catalog = await get_tool_catalog()
result = await call_tool("get_weather", {"city": "Paris"}) Defensive patterns
Strategy: try-catch
Validate before calling
catalog = await transform.get_tool_catalog(ctx)
available = {t.name for t in catalog}
assert "get_weather" in available, f"Tool missing; available: {sorted(available)}" Type guard
def tool_exists(catalog_names: set[str], tool_name: str) -> bool:
return tool_name in catalog_names Try / catch
try:
result = await call_tool("get_weather", params)
except NotFoundError:
catalog = await get_tool_catalog()
raise RuntimeError(f"Unknown tool 'get_weather'; valid: {[t.name for t in catalog]}") Prevention
- Always consult the discovery/catalog tool to list valid names before calling
- Instruct the code-generating model to use only names from the catalog, verbatim
- Remember discovery names may be transformed/prefixed versus raw server tool names
When it happens
Trigger: Generated code calls call_tool() with a name not present in the transform's tool catalog — a hallucinated/mistyped tool name, a name only available after prefixing/renaming by the transform, or a tool removed from the underlying server between catalog fetches.
Common situations: LLM-written code guessing tool names instead of using the discovery tool's catalog; stale catalogs after server tool changes; name mismatches because discovery lists transformed names while code uses original server names.
Related errors
- Tool call limit exceeded: at most {max_tool_calls} call_tool
- CodeMode requires pydantic-monty for the Monty sandbox provi
- Discovery tool name '{self.execute_tool_name}' collides with
- Discovery tools must have unique names.
- mcp_tool() got unexpected keyword argument(s): {sorted(unkno
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/91afcc320ed20ded.
Report an issue: GitHub.