From 9f12a633bc56d1f26d91975388eec7d64eb29e26 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E6=9C=B1=E6=BD=AE?= Date: Fri, 17 Apr 2026 11:05:16 +0800 Subject: [PATCH] =?UTF-8?q?=E4=BC=98=E5=8C=96mcp=E9=85=8D=E7=BD=AE?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .features/skill/MEMORY.md | 5 +-- README.md | 5 +-- agent/deep_assistant.py | 10 +---- agent/plugin_hook_loader.py | 45 ++++++++++++++++++- agent/prompt_loader.py | 30 +------------ mcp/mcp_settings.json | 5 --- .../.claude-plugin/plugin.json | 2 +- .../rag-retrieve/.claude-plugin/plugin.json | 2 +- 8 files changed, 51 insertions(+), 53 deletions(-) delete mode 100644 mcp/mcp_settings.json diff --git a/.features/skill/MEMORY.md b/.features/skill/MEMORY.md index 7d6fc1a..e8f40d5 100644 --- a/.features/skill/MEMORY.md +++ b/.features/skill/MEMORY.md @@ -88,9 +88,8 @@ skill-name/ ## Skill 加载优先级 -1. Skill MCP 配置(最高) -2. 默认 MCP 配置 (`mcp/mcp_settings.json`) -3. 用户传入参数(覆盖所有) +1. Skill MCP 配置 +2. 用户传入参数(覆盖已有同名配置) ## 安全措施 diff --git a/README.md b/README.md index 980f892..6c5e3c0 100644 --- a/README.md +++ b/README.md @@ -396,7 +396,6 @@ dataset_name/ │ ├── document.txt # 原始文本内容 │ ├── serialization.txt # 结构化数据 │ └── schema.json # 字段定义和元数据 -├── mcp_settings.json # MCP 工具配置 └── system_prompt.md # 系统提示词(可选) ``` @@ -405,7 +404,6 @@ dataset_name/ - **document.txt**: 原始 Markdown 文本,提供完整上下文 - **serialization.txt**: 格式化结构数据,每行 `字段1:值1;字段2:值2` - **schema.json**: 字段定义、枚举值映射和文件关联关系 -- **mcp_settings.json**: MCP 工具配置,定义可用的数据处理工具 --- @@ -565,8 +563,7 @@ qwen-agent/ │ ├── multi_keyword_search_server.py # 多关键词搜索服务 │ ├── excel_csv_operator_server.py # Excel/CSV 操作服务 │ ├── json_reader_server.py # JSON 读取服务 -│ ├── mcp_settings.json # MCP 配置文件 -│ └── tools/ # 工具定义文件 +│ └── tools/ # 工具定义文件 ├── models/ # 模型文件 ├── projects/ # 项目目录 │ └── queue_data/ # 队列数据 diff --git a/agent/deep_assistant.py b/agent/deep_assistant.py index 126a842..ce827de 100644 --- a/agent/deep_assistant.py +++ b/agent/deep_assistant.py @@ -117,13 +117,6 @@ def read_system_prompt(): return f.read().strip() -def read_mcp_settings(): - """读取MCP工具配置""" - with open("./mcp/mcp_settings.json", "r") as f: - mcp_settings_json = json.load(f) - return mcp_settings_json - - async def get_tools_from_mcp(mcp): """从MCP配置中提取工具(带缓存)""" start_time = time.time() @@ -195,8 +188,7 @@ async def init_agent(config: AgentConfig): final_system_prompt = await load_system_prompt_async(config) final_mcp_settings = await load_mcp_settings_async(config) - # 如果没有提供mcp,使用config中的mcp_settings - mcp_settings = final_mcp_settings if final_mcp_settings else read_mcp_settings() + mcp_settings = final_mcp_settings if final_mcp_settings else [] system_prompt = final_system_prompt if final_system_prompt else read_system_prompt() config.system_prompt = mcp_settings diff --git a/agent/plugin_hook_loader.py b/agent/plugin_hook_loader.py index 90b9059..5796e59 100644 --- a/agent/plugin_hook_loader.py +++ b/agent/plugin_hook_loader.py @@ -5,6 +5,7 @@ Claude Plugins 模式的 Hook 加载器 """ import os import json +import copy import logging import asyncio import subprocess @@ -116,7 +117,8 @@ async def merge_skill_mcp_configs(bot_id: str) -> List[Dict]: plugin_config = json.load(f) servers = plugin_config.get('mcpServers', {}) if servers: - merged_servers.update(servers) + normalized_servers = _normalize_skill_mcp_servers(servers, skill_path) + merged_servers.update(normalized_servers) logger.info(f"Loaded MCP config from skill: {skill_name}") except Exception as e: logger.error(f"Failed to load mcpServers from {skill_name}: {e}") @@ -127,6 +129,47 @@ async def merge_skill_mcp_configs(bot_id: str) -> List[Dict]: return [] +def _normalize_skill_mcp_servers(servers: Dict[str, Any], skill_path: str) -> Dict[str, Any]: + """将 skill plugin 中 stdio MCP server 的相对路径归一化为基于 skill 目录的绝对路径。""" + normalized_servers = copy.deepcopy(servers) + + for server_name, server_config in normalized_servers.items(): + if not isinstance(server_config, dict): + continue + + transport = server_config.get('transport') + if not transport: + transport = 'http' if 'url' in server_config else 'stdio' + if transport != 'stdio': + continue + + command = server_config.get('command') + if isinstance(command, str): + server_config['command'] = _resolve_skill_relative_path(command, skill_path) + + args = server_config.get('args') + if isinstance(args, list): + server_config['args'] = [ + _resolve_skill_relative_path(arg, skill_path) if isinstance(arg, str) else arg + for arg in args + ] + + return normalized_servers + + +def _resolve_skill_relative_path(value: str, skill_path: str) -> str: + """将 ./ 或 ../ 开头且不含占位符的路径转为基于 skill 目录的绝对路径。""" + if '{' in value or '}' in value: + return value + + if not value.startswith(('./', '../')): + return value + + normalized_path = os.path.abspath(os.path.join(skill_path, value)) + logger.debug(f"Resolved skill MCP path: {value} -> {normalized_path}") + return normalized_path + + def _load_plugin_config(plugin_json_path: str) -> Dict: """加载 plugin.json 配置""" try: diff --git a/agent/prompt_loader.py b/agent/prompt_loader.py index 1642d21..5e5407d 100644 --- a/agent/prompt_loader.py +++ b/agent/prompt_loader.py @@ -203,10 +203,9 @@ async def load_mcp_settings_async(config) -> List[Dict]: List[Dict]: 合并后的MCP设置列表 Note: - 支持在 mcp_settings.json 的 args 中使用 {dataset_dir} 占位符, + 支持在传入或合并后的 mcp_settings 的 args 中使用 {dataset_dir} 占位符, 会在 init_modified_agent_service_with_files 中被替换为实际的路径。 """ - from agent.config_cache import config_cache # 从config中获取参数 project_dir = getattr(config, 'project_dir', None) @@ -222,33 +221,6 @@ async def load_mcp_settings_async(config) -> List[Dict]: skill_mcp_servers = skill_mcp_settings[0].get('mcpServers', {}) logger.info(f"Loaded {len(skill_mcp_servers)} MCP servers from skills") # =========================================================================================== - - # 2. 读取默认MCP设置(使用缓存) - default_mcp_settings = [] - try: - default_mcp_file = os.path.join("mcp", f"mcp_settings.json") - default_mcp_settings = await config_cache.get_json_file(default_mcp_file) or [] - if default_mcp_settings: - logger.info(f"Using cached default mcp_settings from mcp folder") - except Exception as e: - logger.error(f"Failed to load default mcp_settings: {str(e)}") - default_mcp_settings = [] - - # 3. 合并默认设置到merged_settings(默认设置被skill覆盖) - if default_mcp_settings and len(default_mcp_settings) > 0: - default_mcp_servers = default_mcp_settings[0].get('mcpServers', {}) - if merged_settings and len(merged_settings) > 0: - # skill配置已存在,将默认配置合并进去(skill优先) - skill_mcp_servers = merged_settings[0].get('mcpServers', {}) - # 默认配置中不存在的才添加 - for server_name, server_config in default_mcp_servers.items(): - if server_name not in skill_mcp_servers: - skill_mcp_servers[server_name] = server_config - else: - # 没有skill配置,直接使用默认配置 - merged_settings = default_mcp_settings.copy() - - # 遍历mcpServers工具,给每个工具增加env参数 if merged_settings and len(merged_settings) > 0: mcp_servers = merged_settings[0].get('mcpServers', {}) for server_name, server_config in mcp_servers.items(): diff --git a/mcp/mcp_settings.json b/mcp/mcp_settings.json deleted file mode 100644 index 3aa61ae..0000000 --- a/mcp/mcp_settings.json +++ /dev/null @@ -1,5 +0,0 @@ -[ - { - "mcpServers": {} - } -] diff --git a/skills/rag-retrieve-only/.claude-plugin/plugin.json b/skills/rag-retrieve-only/.claude-plugin/plugin.json index 704ff52..a1a38c5 100644 --- a/skills/rag-retrieve-only/.claude-plugin/plugin.json +++ b/skills/rag-retrieve-only/.claude-plugin/plugin.json @@ -14,7 +14,7 @@ "transport": "stdio", "command": "python", "args": [ - "./skills/rag-retrieve-only/rag_retrieve_server.py", + "./rag_retrieve_server.py", "{bot_id}" ] } diff --git a/skills_autoload/rag-retrieve/.claude-plugin/plugin.json b/skills_autoload/rag-retrieve/.claude-plugin/plugin.json index 925751c..a929aa6 100644 --- a/skills_autoload/rag-retrieve/.claude-plugin/plugin.json +++ b/skills_autoload/rag-retrieve/.claude-plugin/plugin.json @@ -14,7 +14,7 @@ "transport": "stdio", "command": "python", "args": [ - "./skills_autoload/rag-retrieve/rag_retrieve_server.py", + "./rag_retrieve_server.py", "{bot_id}" ] }