jd-opensource/joyagent-jdgenie · error · Exception

获取工具列表失败

Error message

{self.server_url} 获取工具列表失败: {str(e)}

What it means

`list_tools` wraps any exception from opening the SSE session or executing the MCP `list_tools` request into an Exception prefixed with the server URL. Like ping_server, it is a labeling wrapper over the underlying auth/network/protocol error.

Solutions

  1. Check the appended inner message / `__cause__` to identify the root cause (auth, network, or protocol)
  2. Confirm the server actually implements the MCP tools capability and a compatible protocol version
  3. Verify connectivity and credentials (same checks as ping_server)
  4. Capture and inspect server logs for the corresponding request

Example fix

// before
tools = await client.list_tools()  # no error context surfaced
// after
try:
    tools = await client.list_tools()
except Exception as e:
    logger.exception(e.__cause__)  # inspect the wrapped root cause
    raise
Defensive patterns

Strategy: try-catch

Validate before calling

try:
    await client.ping_server()  # connectivity/auth precheck
except Exception:
    raise RuntimeError("server unavailable; skip list_tools")
typeGuard = None

Try / catch

try:
    tools = await client.list_tools()
except Exception as e:
    logger.error(f"list_tools failed: {e.__cause__}")
    tools = []  # or re-raise depending on criticality

Prevention

When it happens

Trigger: Calling `await client.list_tools()` when the SSE connection fails (auth/network) or the server returns an invalid tools payload.

Common situations: Server doesn't implement the tools capability; MCP protocol version mismatch; same connectivity/auth misconfigurations as ping failures.

Related errors


AI-assisted analysis of jd-opensource/joyagent-jdgenie@2417e0b8b6 (2026-09-08). Data as JSON: /api/errors/a4edc8bcb8727c44. Report an issue: GitHub.

Appendix: source

Thrown at genie-client/app/client.py:294

        try:
            async with self._sse_connection() as session:
                logger.info(f"{self.server_url} 正在获取工具列表...")
                response = await session.list_tools()
                tools = response.tools if hasattr(response, 'tools') else []

                tool_count = len(tools)
                logger.info(f"成功获取 {tool_count} 个工具")

                # 记录工具名称(如果工具有name属性)
                if tools and hasattr(tools[0], 'name'):
                    tool_names = [tool.name for tool in tools if hasattr(tool, 'name')]
                    logger.debug(f"工具列表: {', '.join(tool_names)}")

                return tools
        except Exception as e:
            error_msg = f"{self.server_url} 获取工具列表失败: {str(e)}"
            logger.error(error_msg)
            raise Exception(error_msg) from e

    async def call_tool(self, name: str, arguments: Optional[Dict[str, Any]] = None) -> Any:
        """
        调用指定的工具

        Args:
            name: 工具名称
            arguments: 工具参数字典,默认为空字典

        Returns:
            Any: 工具执行结果

        Raises:
            ValueError: 当工具名称无效时抛出
            Exception: 当工具调用失败时抛出异常
        """
        # 参数验证
        if not name or not isinstance(name, str):

View on GitHub (pinned to 2417e0b8b6)