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

  1. Call search_tools first and use an exact name from its results
  2. Check the tool exists and is visible to the current user/middleware
  3. 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

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


AI-assisted analysis of PrefectHQ/fastmcp@1f02114297 (2026-08-29). Data as JSON: /api/errors/d00abb964ac0e38c. Report an issue: GitHub.