aget_tool_with_error in client_tool_manager

This commit is contained in:
2026-03-05 19:31:21 +08:00
parent f8364bea68
commit ddfda10700

View File

@@ -252,16 +252,10 @@ class ClientToolManager:
with open(self.config.mcp_config_f, "r") as f: with open(self.config.mcp_config_f, "r") as f:
self.mcp_configs:dict = commentjson.load(f) self.mcp_configs:dict = commentjson.load(f)
async def aget_tools(self): def _get_to_load_configs(self) -> dict:
"""
Get tools from all configured MCP servers.
Handles connection failures gracefully by logging warnings and continuing.
"""
def get_to_load_configs() -> dict:
if self.config.tool_keys is None: if self.config.tool_keys is None:
to_load_config = self.mcp_configs return self.mcp_configs
else:
if len(self.config.tool_keys) == 0: if len(self.config.tool_keys) == 0:
logger.info("no tools will be loaded") logger.info("no tools will be loaded")
return {} return {}
@@ -273,10 +267,14 @@ class ClientToolManager:
logger.warning(f"{key} is not in mcp tools") logger.warning(f"{key} is not in mcp tools")
else: else:
to_load_config[key] = val to_load_config[key] = val
return to_load_config return to_load_config
to_load_config = get_to_load_configs() async def aget_tools(self):
"""
Get tools from all configured MCP servers.
Handles connection failures gracefully by logging warnings and continuing.
"""
to_load_config = self._get_to_load_configs()
all_tools = [] all_tools = []
for server_name, server_config in to_load_config.items(): for server_name, server_config in to_load_config.items():
try: try:
@@ -298,6 +296,78 @@ class ClientToolManager:
return all_tools return all_tools
async def aget_tools_with_errors(self):
"""
Get tools and collect human-readable per-server errors.
Returns:
(all_tools, errors)
"""
to_load_config = self._get_to_load_configs()
all_tools = []
errors = []
for server_name, server_config in to_load_config.items():
try:
single_server_config = {server_name: server_config}
client = MultiServerMCPClient(single_server_config)
tools = await client.get_tools()
all_tools.extend(tools)
logger.info(
f"Successfully connected to MCP server '{server_name}', retrieved {len(tools)} tools"
)
except Exception as e:
url = (
server_config.get("url", "unknown URL")
if isinstance(server_config, dict)
else "unknown URL"
)
err_msg = (
f"{server_name} ({url}): {type(e).__name__}: {e}"
)
errors.append(err_msg)
logger.exception(
f"Failed to connect to MCP server '{server_name}' at {url}"
)
if hasattr(e, "exceptions"):
for nested_exc in e.exceptions:
errors.append(
f"{server_name} nested: {type(nested_exc).__name__}: {nested_exc}"
)
continue
return all_tools, errors
async def aget_tools_by_server(self) -> dict:
"""
Get MCP tools grouped by server name, including per-server error (if any).
Returns:
{
"server_name": {
"tools": ["tool_a", "tool_b"],
"error": "ExceptionType: message" | None,
},
...
}
"""
to_load_config = self._get_to_load_configs()
grouped = {}
for server_name, server_config in to_load_config.items():
grouped[server_name] = {"tools": [], "error": None}
try:
single_server_config = {server_name: server_config}
client = MultiServerMCPClient(single_server_config)
tools = await client.get_tools()
tool_names = sorted(
{
str(getattr(tool, "name", "")).strip()
for tool in tools
if str(getattr(tool, "name", "")).strip()
}
)
grouped[server_name] = {"tools": tool_names, "error": None}
except Exception as e:
grouped[server_name]["error"] = f"{type(e).__name__}: {e}"
logger.exception(f"Failed to connect to MCP server '{server_name}'")
return grouped
def get_tools(self): def get_tools(self):
try: try:
loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()