diff --git a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java index e56cb381..34192dd8 100644 --- a/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java +++ b/main/manager-api/src/main/java/xiaozhi/modules/config/service/impl/ConfigServiceImpl.java @@ -1,6 +1,10 @@ package xiaozhi.modules.config.service.impl; -import java.util.*; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Objects; import org.apache.commons.lang3.StringUtils; import org.springframework.stereotype.Service; @@ -15,6 +19,7 @@ import xiaozhi.common.utils.JsonUtils; import xiaozhi.modules.agent.entity.AgentEntity; import xiaozhi.modules.agent.entity.AgentPluginMapping; import xiaozhi.modules.agent.entity.AgentTemplateEntity; +import xiaozhi.modules.agent.service.AgentMcpAccessPointService; import xiaozhi.modules.agent.service.AgentPluginMappingService; import xiaozhi.modules.agent.service.AgentService; import xiaozhi.modules.agent.service.AgentTemplateService; @@ -39,6 +44,7 @@ public class ConfigServiceImpl implements ConfigService { private final RedisUtils redisUtils; private final TimbreService timbreService; private final AgentPluginMappingService agentPluginMappingService; + private final AgentMcpAccessPointService agentMcpAccessPointService; @Override public Object getConfig(Boolean isCache) { @@ -144,6 +150,12 @@ public class ConfigServiceImpl implements ConfigService { result.put("plugins", pluginParams); } } + // 获取mcp接入点地址 + String mcpEndpoint = agentMcpAccessPointService.getAgentMcpAccessAddress(agent.getId()); + if (StringUtils.isNotBlank(mcpEndpoint) && mcpEndpoint.startsWith("ws")) { + mcpEndpoint = mcpEndpoint.replace("/mcp/", "/call/"); + result.put("mcp_endpoint", mcpEndpoint); + } // 构建模块配置 buildModuleConfig( diff --git a/main/xiaozhi-server/core/connection.py b/main/xiaozhi-server/core/connection.py index 7826a1e9..b99d862c 100644 --- a/main/xiaozhi-server/core/connection.py +++ b/main/xiaozhi-server/core/connection.py @@ -473,6 +473,8 @@ class ConnectionHandler: self.max_output_size = int(private_config["device_max_output_size"]) if private_config.get("chat_history_conf", None) is not None: self.chat_history_conf = int(private_config["chat_history_conf"]) + if private_config.get("mcp_endpoint", None) is not None: + self.config["mcp_endpoint"] = private_config["mcp_endpoint"] try: modules = initialize_modules( self.logger, diff --git a/main/xiaozhi-server/core/providers/tools/device_iot/iot_handler.py b/main/xiaozhi-server/core/providers/tools/device_iot/iot_handler.py index ffea57a4..63992f27 100644 --- a/main/xiaozhi-server/core/providers/tools/device_iot/iot_handler.py +++ b/main/xiaozhi-server/core/providers/tools/device_iot/iot_handler.py @@ -58,10 +58,7 @@ async def handleIotDescriptors(conn, descriptors): # 注册IoT工具到统一工具处理器 await conn.func_handler.register_iot_tools(descriptors) - func_names = conn.func_handler.current_support_functions() - logger.bind(tag=TAG).info( - f"更新function描述列表完成,当前支持的函数: {func_names}" - ) + conn.func_handler.current_support_functions() async def handleIotStatus(conn, states): diff --git a/main/xiaozhi-server/core/providers/tools/device_mcp/mcp_handler.py b/main/xiaozhi-server/core/providers/tools/device_mcp/mcp_handler.py index d5f42833..c1b4d6c8 100644 --- a/main/xiaozhi-server/core/providers/tools/device_mcp/mcp_handler.py +++ b/main/xiaozhi-server/core/providers/tools/device_mcp/mcp_handler.py @@ -200,6 +200,11 @@ async def handle_mcp_message(conn, mcp_client: MCPClient, payload: dict): else: await mcp_client.set_ready(True) logger.bind(tag=TAG).info("所有工具已获取,MCP客户端准备就绪") + + # 刷新工具缓存,确保MCP工具被包含在函数列表中 + if hasattr(conn, "func_handler") and conn.func_handler: + conn.func_handler.tool_manager.refresh_tools() + conn.func_handler.current_support_functions() return # Handle method calls (requests from the client) diff --git a/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_client.py b/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_client.py index da8241d5..4e9e5303 100644 --- a/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_client.py +++ b/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_client.py @@ -12,7 +12,8 @@ logger = setup_logging() class MCPEndpointClient: """MCP接入点客户端,用于管理MCP接入点状态和工具""" - def __init__(self): + def __init__(self, conn=None): + self.conn = conn self.tools = {} # sanitized_name -> tool_data self.name_mapping = {} self.ready = False diff --git a/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_handler.py b/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_handler.py index 298f59b7..c6afb07b 100644 --- a/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_handler.py +++ b/main/xiaozhi-server/core/providers/tools/mcp_endpoint/mcp_endpoint_handler.py @@ -4,8 +4,6 @@ import json import asyncio import re import websockets -from concurrent.futures import Future -from core.utils.util import sanitize_tool_name from config.logger import setup_logging from .mcp_endpoint_client import MCPEndpointClient @@ -13,16 +11,15 @@ TAG = __name__ logger = setup_logging() -async def connect_mcp_endpoint(mcp_endpoint_url: str) -> MCPEndpointClient: +async def connect_mcp_endpoint(mcp_endpoint_url: str, conn=None) -> MCPEndpointClient: """连接到MCP接入点""" if not mcp_endpoint_url or "你的" in mcp_endpoint_url or mcp_endpoint_url == "null": return None try: - logger.bind(tag=TAG).info(f"正在连接到MCP接入点: {mcp_endpoint_url}") websocket = await websockets.connect(mcp_endpoint_url) - mcp_client = MCPEndpointClient() + mcp_client = MCPEndpointClient(conn) mcp_client.set_websocket(websocket) # 启动消息监听器 @@ -85,18 +82,27 @@ async def handle_mcp_endpoint_message(mcp_client: MCPEndpointClient, message: st if msg_id == 1: # mcpInitializeID logger.bind(tag=TAG).debug("收到MCP接入点初始化响应") - server_info = result.get("serverInfo") - if isinstance(server_info, dict): - name = server_info.get("name") - version = server_info.get("version") - logger.bind(tag=TAG).info( - f"MCP接入点服务器信息: name={name}, version={version}" + if result is not None and isinstance(result, dict): + server_info = result.get("serverInfo") + if isinstance(server_info, dict): + name = server_info.get("name") + version = server_info.get("version") + logger.bind(tag=TAG).info( + f"MCP接入点服务器信息: name={name}, version={version}" + ) + else: + logger.bind(tag=TAG).warning( + "MCP接入点初始化响应结果为空或格式错误" ) return elif msg_id == 2: # mcpToolsListID logger.bind(tag=TAG).debug("收到MCP接入点工具列表响应") - if isinstance(result, dict) and "tools" in result: + if ( + result is not None + and isinstance(result, dict) + and "tools" in result + ): tools_data = result["tools"] if not isinstance(tools_data, list): logger.bind(tag=TAG).error("工具列表格式错误") @@ -152,7 +158,9 @@ async def handle_mcp_endpoint_message(mcp_client: MCPEndpointClient, message: st ) tool_data["description"] = description - next_cursor = result.get("nextCursor", "") + next_cursor = ( + result.get("nextCursor", "") if result is not None else "" + ) if next_cursor: logger.bind(tag=TAG).info( f"有更多工具,nextCursor: {next_cursor}" @@ -165,6 +173,24 @@ async def handle_mcp_endpoint_message(mcp_client: MCPEndpointClient, message: st logger.bind(tag=TAG).info( "所有MCP接入点工具已获取,客户端准备就绪" ) + + # 刷新工具缓存,确保MCP接入点工具被包含在函数列表中 + if ( + hasattr(mcp_client, "conn") + and mcp_client.conn + and hasattr(mcp_client.conn, "func_handler") + and mcp_client.conn.func_handler + ): + mcp_client.conn.func_handler.tool_manager.refresh_tools() + mcp_client.conn.func_handler.current_support_functions() + + logger.bind(tag=TAG).info( + f"MCP接入点工具获取完成,共 {len(mcp_client.tools)} 个工具" + ) + else: + logger.bind(tag=TAG).warning( + "MCP接入点工具列表响应结果为空或格式错误" + ) return # Handle method calls (requests from the endpoint) diff --git a/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py b/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py index 6589c302..52ab2b79 100644 --- a/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py +++ b/main/xiaozhi-server/core/providers/tools/server_mcp/mcp_manager.py @@ -66,6 +66,10 @@ class ServerMCPManager: f"Failed to initialize MCP server {name}: {e}" ) + # 输出当前支持的服务端MCP工具列表 + if hasattr(self.conn, "func_handler") and self.conn.func_handler: + self.conn.func_handler.current_support_functions() + def get_all_tools(self) -> List[Dict[str, Any]]: """获取所有服务的工具function定义""" return self.tools diff --git a/main/xiaozhi-server/core/providers/tools/unified_tool_handler.py b/main/xiaozhi-server/core/providers/tools/unified_tool_handler.py index 999594de..b81029ac 100644 --- a/main/xiaozhi-server/core/providers/tools/unified_tool_handler.py +++ b/main/xiaozhi-server/core/providers/tools/unified_tool_handler.py @@ -71,6 +71,9 @@ class UnifiedToolHandler: self.finish_init = True self.logger.info("统一工具处理器初始化完成") + # 输出当前支持的所有工具列表 + self.current_support_functions() + except Exception as e: self.logger.error(f"统一工具处理器初始化失败: {e}") @@ -88,7 +91,9 @@ class UnifiedToolHandler: and mcp_endpoint_url != "null" ): self.logger.info(f"正在初始化MCP接入点: {mcp_endpoint_url}") - mcp_endpoint_client = await connect_mcp_endpoint(mcp_endpoint_url) + mcp_endpoint_client = await connect_mcp_endpoint( + mcp_endpoint_url, self.conn + ) if mcp_endpoint_client: # 将MCP接入点客户端保存到连接对象中