From e051955c057098ef8d8b9aac2e09bc4789284bbf Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=98=8E=E5=A4=A9=E5=A5=BD=E5=83=8F=E6=B2=A1=E4=BB=80?= =?UTF-8?q?=E4=B9=88?= Date: Fri, 31 Oct 2025 21:32:06 +0800 Subject: [PATCH 1/2] =?UTF-8?q?=E8=B0=83=E6=95=B4=E4=B8=80=E4=B8=8B?= =?UTF-8?q?=E7=9B=AE=E5=BD=95=E7=BB=93=E6=9E=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- depends-data/mcp.schema.json | 144 +++++++++++++++++++ src/plugin_system/core/mcp_client_manager.py | 16 ++- template/mcp.json | 88 ++++++++++++ 3 files changed, 246 insertions(+), 2 deletions(-) create mode 100644 depends-data/mcp.schema.json create mode 100644 template/mcp.json diff --git a/depends-data/mcp.schema.json b/depends-data/mcp.schema.json new file mode 100644 index 000000000..38ce78ba7 --- /dev/null +++ b/depends-data/mcp.schema.json @@ -0,0 +1,144 @@ +{ + "$schema": "http://json-schema.org/draft-07/schema#", + "title": "MoFox Bot MCP Configuration", + "description": "Configuration for Model Context Protocol (MCP) servers", + "type": "object", + "properties": { + "mcpServers": { + "type": "object", + "description": "Dictionary of MCP server configurations", + "patternProperties": { + "^[a-zA-Z0-9_-]+$": { + "type": "object", + "properties": { + "description": { + "type": "string", + "description": "Human-readable description of this MCP server" + }, + "enabled": { + "type": "boolean", + "description": "Whether this server is enabled", + "default": true + }, + "transport": { + "type": "object", + "description": "Transport configuration", + "properties": { + "type": { + "type": "string", + "enum": ["streamable-http", "stdio"], + "description": "Transport type" + }, + "url": { + "type": "string", + "description": "URL for HTTP-based transports", + "format": "uri" + }, + "command": { + "type": "string", + "description": "Command for stdio transport" + }, + "args": { + "type": "array", + "items": { + "type": "string" + }, + "description": "Arguments for stdio transport" + } + }, + "required": ["type"], + "oneOf": [ + { + "properties": { + "type": { + "enum": ["streamable-http"] + } + }, + "required": ["url"] + }, + { + "properties": { + "type": { + "enum": ["stdio"] + } + }, + "required": ["command"] + } + ] + }, + "auth": { + "oneOf": [ + { + "type": "null" + }, + { + "type": "object", + "properties": { + "type": { + "type": "string", + "enum": ["bearer", "oauth"] + }, + "token": { + "type": "string", + "description": "Bearer token" + } + }, + "required": ["type"] + } + ], + "description": "Authentication configuration" + }, + "timeout": { + "type": "number", + "description": "Request timeout in seconds", + "minimum": 1, + "default": 30 + }, + "retry": { + "type": "object", + "properties": { + "max_retries": { + "type": "integer", + "minimum": 0, + "default": 3 + }, + "retry_delay": { + "type": "number", + "minimum": 0, + "default": 1 + } + } + } + }, + "required": ["transport"] + } + } + }, + "global": { + "type": "object", + "description": "Global MCP configuration", + "properties": { + "default_timeout": { + "type": "number", + "minimum": 1, + "default": 30 + }, + "max_concurrent_connections": { + "type": "integer", + "minimum": 1, + "default": 5 + }, + "auto_reconnect": { + "type": "boolean", + "default": true + }, + "log_level": { + "type": "string", + "enum": ["DEBUG", "INFO", "WARNING", "ERROR"], + "default": "INFO" + } + } + } + }, + "required": ["mcpServers"] +} diff --git a/src/plugin_system/core/mcp_client_manager.py b/src/plugin_system/core/mcp_client_manager.py index de19383fe..bf7713ac7 100644 --- a/src/plugin_system/core/mcp_client_manager.py +++ b/src/plugin_system/core/mcp_client_manager.py @@ -6,6 +6,7 @@ MCP Client Manager import asyncio import json +import shutil from pathlib import Path from typing import Any @@ -72,8 +73,19 @@ class MCPClientManager: Dict[str, MCPServerConfig]: 服务器名称 -> 配置对象 """ if not self.config_path.exists(): - logger.warning(f"MCP 配置文件不存在: {self.config_path}") - return {} + # 尝试从模板创建配置文件 + template_path = Path("template/mcp.json") + if template_path.exists(): + try: + self.config_path.parent.mkdir(parents=True, exist_ok=True) + shutil.copy(template_path, self.config_path) + logger.info(f"已从模板创建 MCP 配置文件: {self.config_path}") + except Exception as e: + logger.error(f"从模板创建配置文件失败: {e}") + return {} + else: + logger.warning(f"MCP 配置文件和模板都不存在: {self.config_path}, {template_path}") + return {} try: with open(self.config_path, encoding="utf-8") as f: diff --git a/template/mcp.json b/template/mcp.json new file mode 100644 index 000000000..9d8a228e3 --- /dev/null +++ b/template/mcp.json @@ -0,0 +1,88 @@ +{ + "$schema": "../depends-data/mcp.schema.json", + "mcpServers": { + "demo_server": { + "description": "演示用的 MCP 服务器(无认证)", + "enabled": true, + "transport": { + "type": "streamable-http", + "url": "http://localhost:8000/mcp" + }, + "auth": null, + "timeout": 30, + "retry": { + "max_retries": 3, + "retry_delay": 1 + } + }, + "bearer_auth_server": { + "description": "使用 Bearer Token 认证的服务器", + "enabled": false, + "transport": { + "type": "streamable-http", + "url": "https://api.example.com/mcp" + }, + "auth": { + "type": "bearer", + "token": "your-secret-bearer-token-here" + }, + "timeout": 30, + "retry": { + "max_retries": 3, + "retry_delay": 1 + } + }, + "oauth_server": { + "description": "使用 OAuth 认证的服务器", + "enabled": false, + "transport": { + "type": "streamable-http", + "url": "https://api.example.com/mcp" + }, + "auth": { + "type": "oauth", + "client_id": "your-client-id", + "client_secret": "your-client-secret", + "token_url": "https://auth.example.com/oauth/token" + }, + "timeout": 60, + "retry": { + "max_retries": 5, + "retry_delay": 2 + } + }, + "local_stdio_server": { + "description": "本地进程通过 stdio 通信的 MCP 服务器", + "enabled": false, + "transport": { + "type": "stdio", + "command": "python", + "args": [ + "-m", + "my_mcp_server", + "--config", + "server_config.json" + ] + }, + "timeout": 30 + }, + "node_stdio_server": { + "description": "Node.js MCP 服务器示例", + "enabled": false, + "transport": { + "type": "stdio", + "command": "node", + "args": [ + "path/to/mcp-server.js" + ] + }, + "timeout": 30 + } + }, + "global": { + "default_timeout": 30, + "max_concurrent_connections": 5, + "auto_reconnect": true, + "log_level": "INFO" + } +} From 0d99b4e6cb0ddf1f018830a79465a20d2ef14410 Mon Sep 17 00:00:00 2001 From: minecraft1024a Date: Fri, 31 Oct 2025 21:39:50 +0800 Subject: [PATCH 2/2] =?UTF-8?q?fix(chatter):=20=E4=BF=AE=E5=A4=8D=E5=9B=A0?= =?UTF-8?q?=E6=B6=88=E6=81=AF=E6=97=A0ID=E5=AF=BC=E8=87=B4=E7=9A=84?= =?UTF-8?q?=E5=A4=84=E7=90=86=E9=94=99=E8=AF=AF?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在处理消息映射时,增加了一项检查。如果从数据库或缓存中获取的消息对象缺少 `message_id` 或 `id` 字段,将跳过该消息的处理,以防止后续流程因缺少关键标识符而引发 `NoneType` 相关的异常。(实现了plan_filiter.py的basic模式下的类型错误清零) --- src/plugins/built_in/affinity_flow_chatter/plan_filter.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/plugins/built_in/affinity_flow_chatter/plan_filter.py b/src/plugins/built_in/affinity_flow_chatter/plan_filter.py index 4c7c690d6..c15f5d244 100644 --- a/src/plugins/built_in/affinity_flow_chatter/plan_filter.py +++ b/src/plugins/built_in/affinity_flow_chatter/plan_filter.py @@ -402,6 +402,9 @@ class ChatterPlanFilter: mapped = message_id_list[idx] synthetic_id = mapped.get("id") real_msg_id = msg.get("message_id") or msg.get("id") + if not real_msg_id: + continue # 如果消息没有ID,则跳过 + msg_time = time.strftime("%H:%M:%S", time.localtime(msg.get("time", time.time()))) user_nickname = msg.get("user_nickname", "未知用户") msg_content = msg.get("processed_plain_text", "") @@ -568,7 +571,7 @@ class ChatterPlanFilter: # 确保字典中有 message_id 字段 if "message_id" not in target_message_obj and "id" in target_message_obj: target_message_obj["message_id"] = target_message_obj["id"] - + try: # 使用 ** 解包字典传入构造函数 action_message_obj = DatabaseMessages(**target_message_obj)