unclecode/crawl4ai · error · HTTPException

tool not found

Error message

tool not found

What it means

The MCP bridge's call_tool handler raises HTTPException(404, 'tool not found') when the requested tool name is not in the tools registry built at attach_mcp time. The registry is fixed when the bridge is attached, so names are exactly those advertised by tools/list.

Source

Thrown at deploy/docker/mcp_bridge.py:149

        return None

    # MCP handlers
    @mcp.list_tools()
    async def _list_tools() -> List[t.Tool]:
        out = []
        for k, (proxy, orig_fn) in tools.items():
            desc   = getattr(orig_fn, "__mcp_description__", None) or inspect.getdoc(orig_fn) or ""
            schema = getattr(orig_fn, "__mcp_schema__", None) or _schema(_body_model(orig_fn))
            out.append(
                t.Tool(name=k, description=desc, inputSchema=schema)
            )
        return out
             

    @mcp.call_tool()
    async def _call_tool(name: str, arguments: Dict | None) -> List[t.TextContent]:
        if name not in tools:
            raise HTTPException(404, "tool not found")
        
        proxy, _ = tools[name]
        try:
            res = await proxy(**(arguments or {}))
        except HTTPException as exc:
            # map server‑side errors into MCP "text/error" payloads
            err = {"error": exc.status_code, "detail": exc.detail}
            return [t.TextContent(type = "text", text=json.dumps(err, ensure_ascii=False))]
        return [t.TextContent(type = "text", text=json.dumps(res, default=str, ensure_ascii=False))]

    @mcp.list_resources()
    async def _list_resources() -> List[t.Resource]:
        return [
            t.Resource(name=k, description=inspect.getdoc(f) or "", mime_type="application/json")
            for k, f in resources.items()
        ]

    @mcp.read_resource()

View on GitHub (pinned to 7e80152142)

Solutions

  1. Call tools/list first and use an exact name from the response.
  2. Refresh the client's tool cache after the server restarts or routes change.
  3. Ensure custom tools/routes are registered on the FastAPI app before attach_mcp() is called.

Example fix

# before
tools/call {"name": "Crawl", "arguments": {...}}

# after
tools/list  ->  ["crawl", ...]
tools/call {"name": "crawl", "arguments": {...}}
Defensive patterns

Strategy: validation

Validate before calling

names = [t.name for t in (await session.list_tools()).tools]
if tool_name not in names:
    raise ValueError(f"tool {tool_name!r} not in {names}")

Type guard

def is_known_tool(name, advertised) -> bool:
    return isinstance(name, str) and name in advertised

Try / catch

result = await session.call_tool(name, args)
if is_error_payload(result):
    err = json.loads(result.content[0].text)
    if err.get("error") == 404 and "tool not found" in err.get("detail", ""):
        advertised = await session.list_tools()
        raise ToolNameError(f"{name!r} not in {[t.name for t in advertised.tools]}")

Prevention

When it happens

Trigger: Calling tools/call with a name that has a typo, different casing, or refers to a route/tool registered after attach_mcp ran; using a stale tool list from an earlier process.

Common situations: Client caches the tool list across a server restart where routes changed; renamed endpoints; assuming case-insensitive matching.

Related errors


AI-assisted analysis of unclecode/crawl4ai@7e80152142 (2026-08-14). Data as JSON: /api/errors/e51ca93fef4c3f4d. Report an issue: GitHub.