From 43ce1df0ed16026a9481f1b615d82ad655960a7d Mon Sep 17 00:00:00 2001 From: myifeng Date: Fri, 30 May 2025 11:35:12 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=20has=5Ftool=E9=94=99?= =?UTF-8?q?=E8=AF=AF=EF=BC=9B=E4=BC=98=E5=8C=96=E6=A3=80=E7=B4=A2=E5=A4=8D?= =?UTF-8?q?=E6=9D=82=E5=BA=A6?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- main/xiaozhi-server/core/connection.py | 18 ++++++--- main/xiaozhi-server/core/handle/mcpHandle.py | 40 +++++++++++--------- 2 files changed, 34 insertions(+), 24 deletions(-) diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index da0e6b46..a268b71c 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -704,17 +704,23 @@ class ConnectionHandler: "arguments": function_arguments, } - # 处理MCP工具调用 + # 处理Server端MCP工具调用 if self.mcp_manager.is_mcp_tool(function_name): result = self._handle_mcp_tool_call(function_call_data) elif hasattr(self, "mcp_client") and self.mcp_client.has_tool(function_name): - # 如果是MCP工具调用 + # 如果是小智端MCP工具调用 self.logger.bind(tag=TAG).debug( - f"调用MCP工具: {function_name}, 参数: {function_arguments}" + f"调用小智端MCP工具: {function_name}, 参数: {function_arguments}" ) - result = asyncio.run_coroutine_threadsafe(call_mcp_tool(self, self.mcp_client, function_name, function_arguments), self.loop).result() - self.logger.bind(tag=TAG).debug(f"MCP工具调用结果: {result}") - result = ActionResponse(action=Action.REQLLM, result=result, response="") + try: + result = asyncio.run_coroutine_threadsafe(call_mcp_tool(self, self.mcp_client, function_name, function_arguments), self.loop).result() + self.logger.bind(tag=TAG).debug(f"MCP工具调用结果: {result}") + result = ActionResponse(action=Action.REQLLM, result=result, response="") + except Exception as e: + self.logger.bind(tag=TAG).error(f"MCP工具调用失败: {e}") + result = ActionResponse( + action=Action.REQLLM, result="MCP工具调用失败", response="" + ) else: # 处理系统函数 result = self.func_handler.handle_llm_function_call( diff --git a/main/xiaozhi-server/core/handle/mcpHandle.py b/main/xiaozhi-server/core/handle/mcpHandle.py index e03fe72f..4b0e2884 100644 --- a/main/xiaozhi-server/core/handle/mcpHandle.py +++ b/main/xiaozhi-server/core/handle/mcpHandle.py @@ -7,33 +7,36 @@ TAG = __name__ class MCPClient: """MCPClient,用于管理MCP状态和工具""" def __init__(self): - self.tools = [] + self.tools = {} # Dictionary for O(1) lookup self.ready = False self.call_results = {} # To store Futures for tool call responses self.next_id = 1 self.lock = asyncio.Lock() + self._cached_available_tools = None # Cache for get_available_tools - async def has_tool(self, name: str) -> bool: - async with self.lock: - for tool in self.tools: - if tool["name"] == name: - return True - return False + def has_tool(self, name: str) -> bool: + return name in self.tools def get_available_tools(self) -> list: - # async with self.lock: + # Check if the cache is valid + if self._cached_available_tools is not None: + return self._cached_available_tools + + # If cache is not valid, regenerate the list result = [] - for tool in self.tools: + for tool_name, tool_data in self.tools.items(): function_def = { - "name": tool["name"], - "description": tool["description"], + "name": tool_data["name"], + "description": tool_data["description"], "parameters": { - "type": tool["inputSchema"].get("type", "object"), - "properties": tool["inputSchema"].get("properties", {}), - "required": tool["inputSchema"].get("required", []) + "type": tool_data["inputSchema"].get("type", "object"), + "properties": tool_data["inputSchema"].get("properties", {}), + "required": tool_data["inputSchema"].get("required", []) } } result.append({"type": "function", "function": function_def}) + + self._cached_available_tools = result # Store the generated list in cache return result async def is_ready(self) -> bool: @@ -46,8 +49,9 @@ class MCPClient: async def add_tool(self, tool_data: dict): async with self.lock: - self.tools.append(tool_data) - + self.tools[tool_data["name"]] = tool_data + self._cached_available_tools = None # Invalidate the cache when a tool is added + async def get_next_id(self) -> int: async with self.lock: current_id = self.next_id @@ -232,7 +236,7 @@ async def call_mcp_tool(conn, mcp_client: MCPClient, tool_name: str, args: str = if not await mcp_client.is_ready(): raise RuntimeError("MCP客户端尚未准备就绪") - if not await mcp_client.has_tool(tool_name): + if not mcp_client.has_tool(tool_name): raise ValueError(f"工具 {tool_name} 不存在") tool_call_id = await mcp_client.get_next_id() @@ -272,4 +276,4 @@ async def call_mcp_tool(conn, mcp_client: MCPClient, tool_name: str, args: str = raise TimeoutError("工具调用请求超时") except Exception as e: await mcp_client.cleanup_call_result(tool_call_id) - raise e + raise e \ No newline at end of file