agentscope-ai/agentscope · error · RuntimeError

gateway failed to list tools for MCP {self.name!r}: {_safe_d

Error message

gateway failed to list tools for MCP {self.name!r}: {_safe_detail(status, body)}

What it means

Raised by GatewayMCPClient.list_raw_tools() when GET /mcps/{name}/tools through the sandbox gateway returns HTTP >= 400. The gateway could not fetch the tool list from the upstream MCP server — usually because the MCP is not connected/registered, the upstream MCP server died, or the agent/session ids don't match. Note this method is also called lazily by get_tool() on cache miss.

Source

Thrown at src/agentscope/workspace/_gateway_client.py:330

        re-wrap them like a local :class:`MCPClient`. The unfiltered
        list is cached; the returned list has ``enable_tools`` /
        ``disable_tools`` applied.

        Raises:
            `RuntimeError`:
                Gateway returned non-2xx.
        """
        assert self._gateway is not None
        status, body = await self._gateway.exec_request(
            "GET",
            f"/mcps/{self.name}/tools",
            params={
                "agent_id": self._agent_id,
                "session_id": self._session_id,
            },
        )
        if status >= 400:
            raise RuntimeError(
                f"gateway failed to list tools for MCP {self.name!r}: "
                f"{_safe_detail(status, body)}",
            )
        data = json.loads(body)

        raw_tools = [mcp.types.Tool.model_validate(d) for d in data]
        self._cached_tools = raw_tools

        # Gateway returns the unfiltered upstream view; honour the same
        # enable/disable filtering ``MCPClient`` applies locally.
        if self.enable_tools is not None:
            raw_tools = [t for t in raw_tools if t.name in self.enable_tools]
        if self.disable_tools is not None:
            raw_tools = [
                t for t in raw_tools if t.name not in self.disable_tools
            ]
        return raw_tools

View on GitHub (pinned to e90f1c7592)

Solutions

  1. Ensure `await client.connect()` completed successfully before listing tools / calling get_tool
  2. Call `await gateway.health()` to confirm the gateway is alive, then `list_mcps()` to confirm the MCP is registered
  3. Check the status/body detail in the message: 404 means unregistered, 500 usually means the upstream MCP server died — inspect gateway logs (gateway_log_path)
  4. Re-create the client with correct agent_id/session_id if the gateway was restarted

Example fix

// before
client = gateway.make_client(spec)
tools = await client.list_raw_tools()  # RuntimeError
// after
client = gateway.make_client(spec)
await client.connect()
tools = await client.list_raw_tools()
Defensive patterns

Strategy: try-catch

Validate before calling

tools = await client.list_raw_tools()
names = {t.name for t in tools}
if want not in names: ...

Try / catch

try:
    tools = await client.list_raw_tools()
except RuntimeError as e:
    if "gateway failed to list tools" in str(e):
        # reconnect or re-register the MCP
        await client.connect()
    raise

Prevention

When it happens

Trigger: `await client.list_raw_tools()` or `await client.get_tool(name)` (cache miss triggers list) when the MCP was never connect()-ed on the gateway, the upstream MCP server process exited, or the gateway returned 404/500 for the tools listing.

Common situations: Forgetting `await client.connect()` before tool discovery; using a GatewayMCPClient built via make_client(connected=False) without connecting; upstream MCP server (stdio/sse) crashed inside the sandbox; stale agent_id/session_id after gateway restart.

Related errors


AI-assisted analysis of agentscope-ai/agentscope@e90f1c7592 (2026-08-28). Data as JSON: /api/errors/b983ac65bdafe68e. Report an issue: GitHub.