diff --git a/main/xiaozhi-server/core/mcp/MCPClient.py b/main/xiaozhi-server/core/mcp/MCPClient.py index 103ac645..7996ff67 100644 --- a/main/xiaozhi-server/core/mcp/MCPClient.py +++ b/main/xiaozhi-server/core/mcp/MCPClient.py @@ -4,6 +4,7 @@ from contextlib import AsyncExitStack import os, shutil from mcp import ClientSession, StdioServerParameters from mcp.client.stdio import stdio_client +from mcp.client.sse import sse_client from config.logger import setup_logging @@ -21,29 +22,36 @@ class MCPClient: async def initialize(self): args = self.config.get("args", []) - command = ( - shutil.which("npx") - if self.config["command"] == "npx" - else self.config["command"] - ) - - env={**os.environ} - if self.config.get("env"): - env.update(self.config["env"]) - - server_params = StdioServerParameters( - command=command, - args=args, - env=env - ) - - stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params)) - self.stdio, self.write = stdio_transport - time_out_delta = timedelta(seconds=15) - self.session = await self.exit_stack.enter_async_context(ClientSession(read_stream=self.stdio, write_stream=self.write, read_timeout_seconds=time_out_delta)) - + if "command" in self.config: + command = ( + shutil.which("npx") + if self.config["command"] == "npx" + else self.config["command"] + ) + env = {**os.environ} + if self.config.get("env"): + env.update(self.config["env"]) + server_params = StdioServerParameters( + command=command, + args=args, + env=env + ) + stdio_transport = await self.exit_stack.enter_async_context(stdio_client(server_params)) + self.stdio, self.write = stdio_transport + time_out_delta = timedelta(seconds=15) + self.session = await self.exit_stack.enter_async_context( + ClientSession(read_stream=self.stdio, write_stream=self.write, read_timeout_seconds=time_out_delta) + ) + elif "url" in self.config: + sse_transport = await self.exit_stack.enter_async_context(sse_client(self.config["url"])) + self.sse_read, self.sse_write = sse_transport + self.session = await self.exit_stack.enter_async_context( + ClientSession(read_stream=self.sse_read, write_stream=self.sse_write) + ) + else: + raise ValueError("MCPClient config must have 'command' or 'url'.") + await self.session.initialize() - # List available tools response = await self.session.list_tools() tools = response.tools diff --git a/main/xiaozhi-server/core/mcp/manager.py b/main/xiaozhi-server/core/mcp/manager.py index f212b67a..a0e310fa 100644 --- a/main/xiaozhi-server/core/mcp/manager.py +++ b/main/xiaozhi-server/core/mcp/manager.py @@ -50,9 +50,9 @@ class MCPManager: """初始化所有MCP服务""" config = self.load_config() for name, srv_config in config.items(): - if not srv_config.get("command"): + if not srv_config.get("command") and not srv_config.get("url"): self.logger.bind(tag=TAG).warning( - f"Skipping server {name}: command not specified" + f"Skipping server {name}: neither command nor url specified" ) continue diff --git a/main/xiaozhi-server/mcp_server_settings.json b/main/xiaozhi-server/mcp_server_settings.json index a38bbdfb..dc173f37 100644 --- a/main/xiaozhi-server/mcp_server_settings.json +++ b/main/xiaozhi-server/mcp_server_settings.json @@ -3,7 +3,8 @@ "在data目录下创建.mcp_server_settings.json文件,可以选择下面的MCP服务,也可以自行添加新的MCP服务。", "后面不断测试补充好用的mcp服务,欢迎大家一起补充。", "记得删除注释行,des属性仅为说明,不会被解析。", - "des和link属性,仅为说明安装方式,方便大家查看原始链接,不是必须项。" + "des和link属性,仅为说明安装方式,方便大家查看原始链接,不是必须项。", + "当前支持stdio/sse两种模式。" ], "mcpServers": { "filesystem": { diff --git a/main/xiaozhi-server/requirements.txt b/main/xiaozhi-server/requirements.txt index e26fec18..02fbfcdb 100755 --- a/main/xiaozhi-server/requirements.txt +++ b/main/xiaozhi-server/requirements.txt @@ -22,7 +22,7 @@ mem0ai==0.1.62 bs4==0.0.2 modelscope==1.23.2 sherpa_onnx==1.11.0 -mcp==1.4.1 +mcp==1.7.1 cnlunar==0.2.0 PySocks==1.7.1 dashscope==1.23.1