PrefectHQ/fastmcp · error · NotFoundError

Unknown tool: {name!r}

Error message

Unknown tool: {name!r}

What it means

When a tool is resolved via hashed backend-name dispatch (<hash>_<local_name>), FastMCP still runs the tool's auth checks on the bypass path. If run_auth_checks denies the request, or the tool's auth raises AuthorizationError, the server raises NotFoundError('Unknown tool: ...') — deliberately indistinguishable from a missing tool so existence is not leaked to unauthorized callers.

Source

Thrown at fastmcp_slim/fastmcp/server/server.py:1482

                tool: Tool | None = await self.get_tool(name, version=version)

                # If that fails, try hashed-name dispatch. This walks
                # the provider tree recursively (same pattern as the old
                # get_app_tool) looking for a tool whose stored hash
                # matches the parsed prefix.
                if tool is None:
                    hashed = parse_hashed_backend_name(name)
                    if hashed is not None:
                        digest, local_name = hashed
                        tool = await self.get_tool_by_hash(digest, local_name)
                        if tool is not None:
                            # Auth still applies on the bypass path.
                            skip_auth, token = _get_auth_context()
                            if not skip_auth and tool.auth is not None:
                                try:
                                    auth_ctx = AuthContext(token=token, component=tool)
                                    if not await run_auth_checks(tool.auth, auth_ctx):
                                        raise NotFoundError(f"Unknown tool: {name!r}")
                                except AuthorizationError:
                                    raise NotFoundError(
                                        f"Unknown tool: {name!r}"
                                    ) from None

                if tool is None:
                    raise NotFoundError(f"Unknown tool: {name!r}")
                span.set_attributes(tool.get_span_attributes())
                try:
                    return await tool._run(arguments or {})
                except ValidationError as e:
                    # Argument-validation failure (a bad call). FunctionTool
                    # converts pydantic's call-validation error into fastmcp's
                    # ValidationError (see #4128) so it can be filtered as a
                    # client error. Log the underlying detail without a URL or
                    # traceback, matching the previous pydantic-error logging.
                    cause = e.__cause__
                    detail = (

View on GitHub (pinned to 1f02114297)

Solutions

  1. Refresh the client's credentials/token so run_auth_checks passes
  2. Check the tool's auth configuration (tool.auth) and confirm the caller meets its requirements
  3. If you don't need hashed-name dispatch, call the tool by its display name through the normal get_tool path
  4. Verify _get_auth_context is supplying the intended token (skip_auth/token wiring)

Example fix

// before: calling with stale/missing credentials
await client.call_tool('a1b2c3_my_tool', {...})
// after: re-authenticate the client first
async with Client(server, auth=BearerAuth(fresh_token)) as client:
    await client.call_tool('a1b2c3_my_tool', {...})
Defensive patterns

Strategy: try-catch

Validate before calling

token_is_fresh = auth.expires_at > time.time() and 'tool:invoke' in auth.scopes
assert token_is_fresh, 'refresh credentials before calling protected tools'

Type guard

def is_auth_error(e: BaseException) -> bool:
    return isinstance(e, NotFoundError) and 'Unknown tool' in str(e)

Try / catch

try:
    result = await client.call_tool(hashed_name, args)
except NotFoundError as e:
    if 'Unknown tool' in str(e):
        await reauthenticate(client)  # refresh token, then retry once
    else:
        raise

Prevention

When it happens

Trigger: Calling a hashed-name tool (parsed by parse_hashed_backend_name) whose auth checks fail: an invalid/expired bearer token, a token lacking the required scope, or tool.auth rules that reject the caller. Also raised when AuthorizationError escapes the auth check.

Common situations: Expired or rotated API tokens; per-tool auth configured after deployment and clients still using old credentials; calling an app-callable backend tool directly by hashed name with insufficient permissions.

Related errors


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