PrefectHQ/fastmcp · error · NotFoundError
Unknown tool: {name!r}
Error message
Unknown tool: {name!r} What it means
The call_tool proxy created by the search transform only reaches tools the model was allowed to discover; if the requested name is not present in the current tool catalog, it raises NotFoundError('Unknown tool: {name!r}') rather than forwarding to the server. This prevents the proxy from being an unmediated second entry point.
Source
Thrown at fastmcp_slim/fastmcp/server/transforms/search/base.py:250
dict[str, Any] | None, "Arguments to pass to the tool"
] = None,
ctx: Context = None, # type: ignore[assignment] # ty:ignore[invalid-parameter-default]
) -> ToolResult:
"""Call a tool by name with the given arguments.
Use this to execute tools discovered via search_tools.
"""
if name in {transform._call_tool_name, transform._search_tool_name}:
raise ValueError(
f"'{name}' is a synthetic search tool and cannot be called via the call_tool proxy"
)
# The name comes from the model, so this proxy is a second way
# into the server that no host mediates. It may reach only what
# the model was allowed to discover.
if not any(
tool.name == name for tool in await transform.get_tool_catalog(ctx)
):
raise NotFoundError(f"Unknown tool: {name!r}")
return await ctx.fastmcp.call_tool(name, arguments)
return Tool.from_function(fn=call_tool, name=self._call_tool_name)
# ------------------------------------------------------------------
# Serialization
# ------------------------------------------------------------------
async def _render_results(self, tools: Sequence[Tool]) -> Any:
return await _invoke_serializer(self._search_result_serializer, tools)
# ------------------------------------------------------------------
# Catalog access
# ------------------------------------------------------------------
async def _get_visible_tools(self, ctx: Context) -> Sequence[Tool]:
"""Get the auth-filtered tool catalog, excluding pinned tools."""
tools = await self.get_tool_catalog(ctx)View on GitHub (pinned to 1f02114297)
Solutions
- Call search_tools first and use an exact name from its results
- Check the tool exists and is visible to the current user/middleware
- Fix the tool name typo or register the missing tool on the server
Example fix
// before
await call_tool("summarize_doc", {...}) # never discovered
// after
catalog = await search_tools("summarize documents")
await call_tool(catalog[0].name, {...}) Defensive patterns
Strategy: try-catch
Validate before calling
catalog = await transform.get_tool_catalog(ctx)
if not any(t.name == name for t in catalog):
raise LookupError(f"{name!r} not discoverable; call search_tools first") Try / catch
try:
result = await call_tool(name, args)
except NotFoundError:
results = await search_tools(name.replace('_', ' '))
# retry with a discovered exact name Prevention
- Always discover tools via search_tools before proxy calls
- Use exact catalog names, not memory or LLM guesses
- Verify the tool isn't hidden by visibility/auth middleware
- Handle NotFoundError by re-running discovery
When it happens
Trigger: Calling the proxy with a tool name that doesn't exist, was renamed, is filtered out by visibility/auth, or wasn't returned by search_tools' catalog.
Common situations: Stale tool name from a previous session/catalog; hallucinated tool name from an LLM; tool hidden by middleware visibility rules.
Related errors
- '{name}' is a synthetic search tool and cannot be called via
- To decorate a classmethod, use @classmethod above @tool. See
- The function '{fn_name}' has '{params[0]}' as its first para
- Cannot specify both a name as first argument and as keyword
- First argument to @tool must be a function, string, or None,
AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29).
Data as JSON: /api/errors/d00abb964ac0e38c.
Report an issue: GitHub.