ARTICLE · INTELLIGENCE

战地情报 · 详情页

来自尧图项目组的一线实战观察与深度解析

MCP工具零配置自动同步:本地多Agent协同新范式

MCP工具零配置自动同步:本地多Agent协同新范式

1. 项目概述:当本地 Agent 不再需要手写 MCP 配置文件

你有没有在深夜调试一个本地 AI 编程助手时,反复打开mcp-tools.json、skills.yaml、agent-config.toml这类配置文件,只为给新写的 Python 工具加一行id: file_reader和三行参数定义?我试过——在某次为 Hermes Agent 接入自定义数据库查询能力时,光是校验 JSON Schema 格式、确认 tool_call 的 input_schema 字段嵌套层级、检查 MCP Server 端口是否与本地 agent 冲突,就花了 47 分钟。而真正写工具逻辑本身,只用了 19 分钟。

这根本不是开发效率问题,而是配置范式正在拖垮本地智能体的演进节奏。Claude Code、Codex(指开源代码智能体框架,非 OpenAI 原始 Codex)、Cursor 的插件生态、Hermes Agent 的技能注册机制,本质上都在复刻同一个底层协议:MCP(Model Context Protocol)。但当前几乎所有实现,都要求开发者手动声明工具元信息、硬编码调用路径、维护独立的技能注册表、甚至为每个 agent 单独配置 MCP server 地址和认证 token。这种“配置即代码”的模式,在单 agent 小规模验证阶段尚可忍受;一旦进入多 agent 协同、热插拔技能、跨进程工具共享场景,就成了不可逾越的运维高墙。

本项目标题里那句“0 配置,本机全部 agent 同步 MCP 工具和技能”,不是营销话术,而是通过一套轻量级运行时发现与自动注册机制实现的确定性结果。它不依赖任何中心化 MCP registry 服务,不修改任何 agent 主体代码,不引入额外网络跳转层,仅靠本地文件系统监听 + 进程间共享内存 + 标准化工具描述规范,就让 Claude Code、Codex 框架实例、Cursor 插件环境、Hermes Agent 实例——这四类原本互不感知的本地 agent,能实时看到彼此暴露的 MCP 工具列表,并直接发起符合 MCP v0.3 规范的 tool_call 请求。核心关键词“MCP”“技能”“同步”“0 配置”在此处有明确技术指向:MCP 是协议标准,技能是符合该协议的可调用函数单元,同步是跨进程状态一致性保障,0 配置是指开发者无需编写任何声明式配置文件,仅需按约定方式导出函数即可。

适合谁参考?如果你正在做以下任何一件事,这篇内容就是为你写的:

  • 正在基于 Hermes Agent 搭建私有代码助手,却被register_skill()手动调用链缠住手脚;
  • 用 Cursor 自定义插件时,每次新增一个 API 调用工具都要改manifest.json和index.ts两处;
  • 在本地跑多个 Codex 实例做 A/B 测试,却要为每个实例单独维护一份tools/目录和对应的加载逻辑;
  • 或者,你只是个想快速验证某个 Claude Code 插件能否调用本地 Python 数据处理脚本的普通开发者——不需要懂 MCP 协议细节,只要把脚本放对位置,它就会自动出现在所有支持 MCP 的 agent 工具面板里。

这不是一个新 agent 框架,而是一套“协议胶水层”。它解决的不是“如何让 agent 更聪明”,而是“如何让已有的、分散的、异构的本地 agent,像同一台机器的器官一样协同工作”。

2. 核心设计思路:为什么放弃配置文件,转向运行时自动发现

2.1 配置文件模式的三大结构性缺陷

我们先直面现实:为什么“手写 MCP 配置文件”这条路走不通了?这不是工程洁癖,而是由本地智能体运行环境的本质决定的。

第一,配置与实现严重割裂。以一个读取 Excel 文件的工具为例,你在tools/excel_reader.py里写了def read_excel(path: str) -> dict:,却必须在mcp-tools.json里重复声明:

{ "id": "excel_reader", "name": "Read Excel File", "description": "Parse .xlsx file and return sheet data as JSON", "input_schema": { "type": "object", "properties": { "path": { "type": "string" } } } }

这里存在三重冗余:函数签名(path: str)与input_schema重复;函数 docstring 与description重复;函数名read_excel与id字段重复。更致命的是,一旦你修改了函数参数(比如增加sheet_name: str = "Sheet1"),就必须同步修改 JSON,否则 runtime 会因 schema 校验失败而静默拒绝调用。我见过至少三个团队因此在生产环境中出现“工具明明存在却无法被调用”的诡异故障,排查耗时从 2 小时到 3 天不等。

第二,配置作用域天然封闭。MCP 协议设计初衷是让模型能跨服务调用工具,但当前主流实现(包括官方示例)都将工具注册绑定在单个 agent 进程内。Hermes Agent 的SkillRegistry是单例,Codex 的ToolManager是实例属性,Cursor 插件的工具集在PluginContext初始化时固化。这意味着:你无法让 Hermes Agent 调用 Codex 实例里刚写好的数据库查询工具,除非你手动把那个工具的描述复制一份,再通过 HTTP POST 到 Codex 的 MCP server 端点——这已经不是“集成”,而是“手工缝合”。

第三,配置更新无法热生效。所有配置文件都是 agent 启动时一次性加载的。你想在 Claude Code 运行中动态启用一个新工具?不行。想临时禁用某个高风险的 shell 执行工具?得重启整个进程。在真实开发流中,这相当于要求你每次写完一个函数就要 kill -9 当前 agent 再 reload —— 完全违背“快速迭代”的基本前提。

提示:这些缺陷不是某个框架的 bug,而是将“声明式配置”强加于“运行时动态环境”时必然产生的摩擦。就像试图用静态链接库的方式管理微服务,方向错了,再优化也难逃瓶颈。

2.2 运行时自动发现的核心原理:文件系统即注册中心

既然配置文件这条路走不通,我们就换一个锚点:本地文件系统。它天然具备四个关键属性:

  • 全局可见性:所有进程(只要权限允许)都能访问同一组路径;
  • 事件可监听:inotify(Linux/macOS)或 ReadDirectoryChangesW(Windows)可捕获文件增删改;
  • 结构可约定:通过固定目录结构(如~/.mcp/tools/)和命名规范(如tool_<name>.py),就能建立确定性映射;
  • 无状态性:不依赖进程内存或网络连接,重启后自动恢复一致视图。

我们的方案正是基于此构建:

  1. 定义统一工具存放目录~/.mcp/tools/(用户可配置,但默认如此);
  2. 规定工具文件必须以tool_开头、.py结尾(如tool_git_status.py);
  3. 工具文件内必须导出一个符合 MCP 规范的函数,且函数需带特定装饰器@mcp_tool;
  4. 启动一个轻量级守护进程mcp-discoveryd,持续监听~/.mcp/tools/下的文件变化;
  5. 当检测到新文件创建或修改,mcp-discoveryd动态导入该模块,解析@mcp_tool装饰的函数,生成标准 MCP Tool Description 对象;
  6. 将该对象写入本地共享内存段(POSIX shared memory on Linux/macOS, MemoryMappedFile on Windows),并广播变更事件;
  7. 所有接入本机制的 agent(Claude Code、Codex 等),只需在启动时映射同一块共享内存,并订阅事件,即可实时获取最新工具列表。

这个设计绕开了所有配置文件,因为“工具存在”这件事本身,就由文件系统的物理存在性保证;“工具元信息”则由装饰器在运行时动态提取,与函数实现零耦合;“工具可用性”由共享内存的原子写入和事件广播保障,毫秒级同步。

2.3 为何选择共享内存而非 HTTP 或 IPC?

你可能会问:为什么不直接起一个本地 MCP registry HTTP 服务,让各 agent 轮询/tools接口?或者用 Unix Domain Socket 做 IPC?这是我们在原型阶段认真对比过的三条技术路径,最终选择共享内存是基于三个硬性约束:

  • 零依赖:HTTP 服务需要额外端口、可能冲突、需处理 CORS/鉴权;IPC 需要进程间握手、错误恢复复杂。而共享内存是操作系统原生支持,无需安装任何 runtime,mmap系统调用在所有现代 OS 上都稳定存在。

  • 亚毫秒级延迟:实测在 macOS M2 上,共享内存段读取一次工具列表耗时 8–12 微秒;HTTP 轮询(即使本地 loopback)平均 3.2 毫秒,且存在 polling interval 延迟;IPC 消息传递约 150–300 微秒。对于高频 tool_call 场景(如代码补全每秒触发多次工具查询),这个差距直接决定 UX 是否卡顿。

  • 崩溃隔离性:mcp-discoveryd进程崩溃,共享内存段不会消失,agent 仍能读取最后已知状态,最多丢失增量更新;而 HTTP 服务崩溃,所有 agent 立即失去工具发现能力;IPC 崩溃则需复杂的 reconnect 逻辑。

我们做过压力测试:在~/.mcp/tools/下同时创建 200 个工具文件,mcp-discoveryd平均响应时间为 47 毫秒,内存占用峰值 12MB,CPU 占用率低于 3%。这意味着它完全可以作为系统级服务常驻,而不影响开发者主机性能。

3. 核心细节解析:从一个 Python 函数到全 agent 可见的 MCP 工具

3.1 工具文件的最小可行结构:三行代码完成注册

现在我们来看最核心的部分:如何写一个函数,让它自动成为所有 agent 都能调用的 MCP 工具?答案是——只需要三行符合规范的 Python 代码,且无需任何配置文件。

以一个最简单的“获取当前时间戳”工具为例,创建文件~/.mcp/tools/tool_timestamp.py:

from mcp_sdk import mcp_tool @mcp_tool( id="timestamp_now", name="Get Current Timestamp", description="Returns the current Unix timestamp as integer" ) def get_timestamp() -> int: """Return current Unix timestamp.""" import time return int(time.time())

就这么简单。没有 JSON,没有 YAML,没有 TOML,没有register_tool()调用,没有server.start()。只要你把这段代码保存到~/.mcp/tools/目录下,mcp-discoveryd进程会在 100 毫秒内发现它、导入模块、解析装饰器参数、生成 MCP Tool Description,并推送到共享内存。

这里的关键在于@mcp_tool装饰器的设计。它不是一个空壳,而是一个运行时元信息提取器。当你调用get_timestamp()时,装饰器会:

  • 检查函数签名,自动生成input_schema(此处无参数,故为{ "type": "object", "properties": {} });
  • 解析函数返回类型注解-> int,生成output_schema({ "type": "integer" });
  • 读取函数 docstring 作为description的 fallback(若装饰器未显式传入);
  • 验证id是否符合 MCP 规范(仅含字母、数字、下划线、短横线,长度 1–64)。

注意:id字段是 MCP 协议中 tool_call 的唯一标识符,必须全局唯一。mcp-discoveryd会在写入共享内存前校验重复 ID,若发现冲突(如两个工具都设id="file_reader"),会拒绝注册并记录警告日志,避免静默覆盖。

3.2 处理复杂参数:从类型注解到完整 MCP Schema

真实工具远不止无参函数。当你的工具需要接收结构化输入时,@mcp_tool装饰器能自动将 Python 类型注解转换为标准 JSON Schema,这是它区别于简单反射的关键能力。

看这个稍复杂的例子:tool_search_github.py,用于搜索 GitHub 仓库:

from typing import List, Optional, TypedDict from mcp_sdk import mcp_tool class SearchQuery(TypedDict): q: str sort: Optional[str] order: Optional[str] per_page: Optional[int] @mcp_tool( id="github_search_repos", name="Search GitHub Repositories", description="Search public repositories on GitHub using query string" ) def search_github_repos(query: SearchQuery) -> List[dict]: """ Search GitHub repos. Args: query: Search parameters including 'q' (required), 'sort', 'order', 'per_page' Returns: List of repository objects with keys: 'name', 'owner', 'stars', 'url' """ # 实际调用 requests.get(...) 的逻辑省略 pass

装饰器会自动解析SearchQuery类型,生成如下input_schema:

{ "type": "object", "properties": { "q": { "type": "string" }, "sort": { "type": ["string", "null"] }, "order": { "type": ["string", "null"] }, "per_page": { "type": ["integer", "null"] } }, "required": ["q"] }

这个过程不是简单字符串替换,而是调用pydantic的model_json_schema()方法(内部已预装兼容版本),确保生成的 schema 100% 符合 JSON Schema Validation Spec 和 MCP v0.3 Input Schema 要求 。你无需关心$ref、oneOf等高级特性——只要你的类型注解是标准 Python/typing 模块支持的,它就能正确转换。

实操心得:我最初尝试用dataclass替代TypedDict,发现某些嵌套场景下 schema 生成不准确。后来统一规定:所有工具输入必须使用TypedDict或基础类型(str/int/bool/list/dict),禁止使用dataclass或自定义 class。这是经过 17 个真实工具验证后的最佳实践,既保证 schema 稳定性,又降低开发者学习成本。

3.3 共享内存数据结构:如何让不同语言 agent 读懂同一份数据

虽然目前主要适配 Python agent,但共享内存段的设计必须考虑未来扩展性。我们采用Protocol Buffer(protobuf)v3序列化工具描述,而非 JSON 或纯二进制,原因有三:

  • 跨语言兼容性:Protobuf 有官方 Python/Go/TypeScript/C++ 支持,Cursor 插件(TS)、Codex(Go)、Hermes(Python)都能用各自语言的 protobuf runtime 解析同一份数据;
  • 紧凑高效:相比 JSON,protobuf 序列化后体积减少约 60%,共享内存段写入/读取更快;
  • 向后兼容:字段可设optional,新增字段不影响旧版 agent 解析。

定义的.proto文件核心部分如下(已简化):

syntax = "proto3"; package mcp.discovery; message ToolDescription { string id = 1; string name = 2; string description = 3; bytes input_schema = 4; // JSON Schema as raw bytes bytes output_schema = 5; int64 last_modified = 6; // Unix timestamp in microseconds } message ToolsList { repeated ToolDescription tools = 1; int64 version = 2; // Monotonic counter for change detection }

mcp-discoveryd每次更新工具列表时,会构造一个ToolsList对象,序列化为二进制,写入共享内存段。各 agent 的 SDK(如mcp-python-sdk、mcp-ts-sdk)在读取时,先反序列化为ToolsList,再遍历tools字段。由于input_schema和output_schema是原始 JSON 字节,agent 可直接将其透传给 MCP client,无需二次解析。

这个设计让“同步”变得极其轻量:agent 侧只需每 500ms 检查共享内存段的version字段是否变化,若变化则重新读取并反序列化——整个过程在 20ms 内完成,比轮询 HTTP 接口快两个数量级。

4. 实操过程:四步完成本地全 agent MCP 工具同步

4.1 第一步:安装与启动发现守护进程

整个方案的入口是mcp-discoveryd,一个独立的、无界面的守护进程。它不依赖任何 agent,也不修改你的开发环境,纯粹作为系统服务运行。

安装(推荐 pipx,避免污染全局 Python 环境):

# 确保已安装 pipx pip install pipx pipx ensurepath # 安装 discovery daemon pipx install mcp-discovery # 验证安装 mcp-discovery --version # 输出类似:mcp-discovery 0.4.2

启动守护进程:

# 后台启动,日志输出到 ~/.mcp/logs/discovery.log mcp-discovery start # 查看状态 mcp-discovery status # 输出:Running (PID: 12345) # 查看实时日志(可选) mcp-discovery logs --follow

mcp-discovery默认监听~/.mcp/tools/,你可通过--tools-dir参数指定其他路径(如--tools-dir /opt/my-tools),但强烈建议保持默认,以降低认知负担。

注意:首次启动时,mcp-discovery会自动创建~/.mcp/目录结构:

~/.mcp/ ├── tools/ # 工具文件存放处(你放代码的地方) ├── logs/ # 日志文件 ├── cache/ # 临时缓存(如下载的工具依赖) └── config.json # 运行时配置(极少需要手动编辑)

这个目录就是你的“本地 MCP 注册中心”,所有 agent 都会读取它。

4.2 第二步:编写你的第一个 MCP 工具(以文件读取为例)

现在,我们动手写一个真正有用的工具:安全读取本地文本文件。重点展示如何处理错误、设置超时、添加安全限制。

创建~/.mcp/tools/tool_read_file.py:

import os import time from pathlib import Path from mcp_sdk import mcp_tool # 定义允许读取的根目录(强制安全沙箱) ALLOWED_ROOT = Path.home() / "Documents" @mcp_tool( id="read_local_file", name="Read Local Text File", description="Safely read a text file from user's Documents directory" ) def read_local_file(path: str) -> str: """ Read a text file within allowed root directory. Args: path: Relative path from Documents directory, e.g., "notes/todo.txt" Returns: File content as string Raises: ValueError: If path is outside allowed root or contains dangerous patterns FileNotFoundError: If file does not exist UnicodeDecodeError: If file is not valid UTF-8 """ # 1. 安全校验:防止路径遍历 full_path = (ALLOWED_ROOT / path).resolve() if not str(full_path).startswith(str(ALLOWED_ROOT)): raise ValueError(f"Path traversal attempt: {path} resolves to {full_path}") # 2. 文件存在性与类型校验 if not full_path.is_file(): raise FileNotFoundError(f"File not found: {path}") if full_path.stat().st_size > 10 * 1024 * 1024: # 10MB 限制 raise ValueError("File too large (>10MB)") # 3. 读取并解码 try: with open(full_path, "r", encoding="utf-8") as f: content = f.read(100000) # 限制读取前 100KB,防大文件阻塞 return content except UnicodeDecodeError as e: raise UnicodeDecodeError(f"Invalid UTF-8 encoding in {path}: {e}")

保存文件后,等待约 100ms,mcp-discoveryd会自动发现并注册。你可以用命令行工具验证:

# 列出当前所有已注册工具 mcp-discovery list-tools # 输出: # - id: read_local_file # name: Read Local Text File # description: Safely read a text file from user's Documents directory # last_modified: 2024-05-20T14:22:33.123456Z

4.3 第三步:让现有 agent 接入发现机制(以 Hermes Agent 为例)

Hermes Agent 是 Python 实现,接入最简单。你需要做的,只是在 agent 启动时,加载mcp-python-sdk并启用自动发现。

安装 SDK:

pip install mcp-python-sdk

修改你的 Hermes Agent 启动脚本(如hermes_main.py):

from hermes.agent import HermesAgent from mcp_python_sdk import auto_discover_tools # 新增导入 # 创建 agent 实例 agent = HermesAgent( model="claude-3-haiku-20240307", # ... 其他初始化参数 ) # 关键一步:启用自动发现 # 这会启动一个后台线程,定期从共享内存读取工具列表 auto_discover_tools(agent.tool_registry) # 启动 agent agent.run()

auto_discover_tools()函数做了三件事:

  • 映射mcp-discoveryd创建的共享内存段;
  • 启动一个threading.Thread,每 500ms 检查version字段;
  • 当检测到变化,调用agent.tool_registry.register_tool()注册新工具,调用unregister_tool()移除已删除工具。

你无需修改 Hermes Agent 的任何源码,只需在启动流程中插入这一行。实测表明,从你保存tool_read_file.py到 Hermes Agent 的 UI 中出现“Read Local Text File”按钮,全程不超过 600ms。

4.4 第四步:跨 agent 调用验证(Claude Code ↔ Codex)

这才是本方案的价值高潮:让不同 agent 互相调用。

假设你已按上述步骤:

  • 在~/.mcp/tools/下放置了tool_read_file.py(被 Hermes Agent 发现);
  • 同时放置了tool_db_query.py(一个 Codex 实例专用的 PostgreSQL 查询工具);

现在,你在 Claude Code 的聊天窗口中输入:

“请帮我读取 ~/Documents/notes/weekly_summary.md 的内容,并总结成三点。”

Claude Code 的 MCP client 会:

  1. 查询本地共享内存,发现read_local_file工具;
  2. 构造 tool_call:{"name": "read_local_file", "arguments": {"path": "notes/weekly_summary.md"}};
  3. 调用该函数,获得文件内容;
  4. 将内容喂给 LLM,生成三点总结。

与此同时,Codex 实例(可能运行在另一个终端窗口,用codex-server --port 8001启动)也看到了read_local_file工具。你可以在 Codex 的 CLI 中执行:

codex call --tool read_local_file --args '{"path": "notes/weekly_summary.md"}'

它会返回完全相同的内容。两个 agent 共享同一份工具定义、同一份执行逻辑,只是调用入口不同。

实操心得:我们曾用此机制搭建了一个“混合编程助手”:Claude Code 负责理解需求和生成伪代码,Codex 负责执行数据库操作,Hermes Agent 负责调用本地 Git 命令提交变更。三者通过~/.mcp/tools/下的tool_git_commit.py、tool_db_execute.py、tool_read_file.py无缝协作,整个流程无需任何配置文件同步或 API 密钥交换。

5. 常见问题与排查技巧实录

5.1 工具注册后 agent 看不到?五步定位法

这是新手最常遇到的问题。别急着重装,按顺序检查这五点:

检查项操作命令/方法预期结果常见原因
1. discoveryd 是否运行mcp-discovery status输出Running (PID: XXX)进程意外退出,检查~/.mcp/logs/discovery.log最后 10 行是否有OSError
2. 工具文件语法是否正确python -m py_compile ~/.mcp/tools/tool_xxx.py无输出即成功Python 语法错误(如少括号)、@mcp_tool装饰器拼写错误(如写成@mcp_too)
3. 工具 ID 是否唯一mcp-discovery list-tools | grep "id:"无重复id字段两个工具文件用了相同id=参数,discoveryd会静默跳过第二个
4. agent 是否启用自动发现检查 agent 启动日志,搜索"auto_discover_tools"有"Started tool discovery thread"日志忘记调用auto_discover_tools(),或调用位置错误(必须在agent.run()之前)
5. 共享内存权限(Linux/macOS)ls -l /dev/shm/ | grep mcp显示mcp_tools_shm且权限为rw-rw-rw-SELinux 或某些安全策略限制,临时修复:sudo chmod 777 /dev/shm/mcp_tools_shm

提示:第 5 步权限问题在企业级 Linux 主机上出现概率高达 34%(基于我们对 212 个开发环境的抽样统计)。根本解决方案是让mcp-discoveryd启动时自动设置shm_open的O_CREAT \| O_RDWR标志并fchmod,已在 v0.4.3 版本修复。

5.2 工具调用时报错 “Input validation failed”?Schema 生成陷阱

这个错误几乎总是源于类型注解与实际运行时值的 mismatch。例如:

# ❌ 错误写法:声称返回 list,但实际返回 None @mcp_tool(id="bad_tool") def bad_tool() -> List[str]: if some_condition: return ["a", "b"] return None # 这里违反了 -> List[str] 声明! # ✅ 正确写法:用 Union 显式声明可为空 from typing import Union @mcp_tool(id="good_tool") def good_tool() -> Union[List[str], None]: ...

@mcp_tool装饰器在生成output_schema时,会严格遵循类型注解。如果函数实际返回None,但 schema 声明为{"type": "array", "items": {"type": "string"}},MCP client 就会校验失败。

另一个常见陷阱是Optional[str]与Union[str, None]的等价性。Python 3.10+ 中两者等价,但旧版本Optional[str]是Union[str, type(None)],type(None)在 protobuf 序列化时可能被误判。统一使用Union[T, None]是最稳妥的写法。

5.3 如何调试工具执行过程?日志与断点双轨制

mcp-discoveryd本身不执行工具,只负责发现和分发。真正的执行发生在 agent 进程内。因此,调试必须分两层:

  • 发现层调试:查看~/.mcp/logs/discovery.log,过滤INFO级别日志,关注Discovered tool,Registered tool,Removed tool关键字;
  • 执行层调试:在你的工具函数内加日志,但注意——不要用print(),因为 agent 可能捕获 stdout。应使用logging模块:
import logging logger = logging.getLogger(__name__) @mcp_tool(id="debug_tool") def debug_tool(x: int) -> str: logger.info(f"Debug tool called with x={x}") # ✅ 正确:日志会输出到 agent 的 log 文件 # print(f"Debug: {x}") # ❌ 错误:可能被 agent 重定向或丢弃 return f"Result: {x * 2}"

对于复杂逻辑,你还可以在工具函数内设断点。因为工具是在 agent 进程内执行的,所以 VS Code 的 Python debugger、PyCharm 的断点都能正常命中。只需确保你的调试器 attach 到的是 agent 进程(如hermes_main.py),而不是mcp-discoveryd。

5.4 安全边界:为什么~/.mcp/tools/是安全的?

有人担心:“把工具代码放在一个公共目录,会不会被恶意 agent 调用危险函数?” 这是个好问题。我们的安全模型基于三层隔离:

  1. 文件系统权限:~/.mcp/tools/目录默认权限为700(仅属主可读写),其他用户无法放入文件;
  2. 执行沙箱:如前所示,每个工具函数必须自行实现安全校验(如ALLOWED_ROOT限制),@mcp_tool不提供自动沙箱;
  3. agent 级白名单:Hermes Agent、Codex 等 SDK 允许配置allowed_tools: ["read_local_file", "git_status"],未在白名单中的工具即使被发现也不会注册到tool_registry。

这意味着:即使你误将一个tool_exec_shell.py放入目录,只要 agent 的白名单里没有它,它就永远不会出现在 UI 或 tool_call 列表中。这是一种“纵深防御”设计,把安全责任合理分配给目录权限、工具作者、agent 配置三方。

6. 进阶应用:从同步工具到构建本地 MCP 生态

6.1 技能组合:用 MCP 工具链构建自动化流水线

当所有 agent 都能看见同一套工具,真正的威力在于“组合”。我们曾为某高校实验室构建了一个论文写作辅助流水线:

  1. tool_fetch_arxiv.py:根据关键词搜索 arXiv 论文摘要;
  2. tool_summarize_text.py:用本地小模型摘要长文本;
  3. tool_generate_bibtex.py:根据论文元数据生成 BibTeX 条目;
  4. tool_insert_into_latex.py:将 BibTeX 插入指定.tex文件的\bibliography{}前。

这四个工具分别由不同开发者编写,存放在~/.mcp/tools/下。Claude Code 作为 orchestrator,能自动规划调用顺序:

  • 用户输入:“帮我找三篇关于 diffusion models 的最新论文,摘要并生成引用。”
  • Claude Code 解析出需调用fetch_arxiv→summarize_text×3 →generate_bibtex×3 →insert_into_latex;
  • 整个链路在 8.2 秒内完成,无需任何 workflow 引擎或 YAML 编排。

这证明:MCP 工具的“可发现性”是自动化编排的前提。没有统一发现机制,每个工具都是孤岛;有了它,LLM 就成了天然的 workflow 编译器。

6.2 跨设备同步:用 rsync 实现多机 MCP 工具共享

~/.mcp/tools/是一个纯文件目录,这意味着你可以用任何文件同步工具将其扩展到多台机器。我们推荐rsync,因其增量同步、低带宽占用、支持排除规则。

在主力开发机(Mac)上:

# 每小时同步一次到备用笔记本(假设 IP 192.168.1.100) 0 * * * * rsync -avz --delete --exclude='*.pyc' ~/.mcp/tools/ user@192.168.1.100:~/.mcp/tools/

在备用笔记本上,同样运行mcp-discoveryd。由于共享内存是本地的,两台机器各自维护自己的mcp-discoveryd进程,但工具文件内容一致。这样,你在 Mac 上写的tool_my_api.py,10 分钟后就会出现在笔记本的 Claude Code 工具列表中。

注意:不要用 Dropbox/OneDrive 同步~/.mcp/tools/,因为它们的文件锁机制可能导致mcp-discoveryd读取到半截文件。rsync的原子性重命名(--backup)能完美规避此问题。

6.3 未来演进:MCP 工具市场的雏形

当“写工具”变得像“写函数”一样简单,“发布工具”自然会走向标准化。我们已在内部测试一个mcp-publish命令:

# 将本地工具打包为 MCP 包 mcp-publish pack ~/.mcp/tools/tool_read_file.py --name "file-utils" --version "1.0.0" # 发布到私有 registry(一个简单的 S3 bucket) mcp-publish push file-utils-1.0.0.mcp.tgz s3://
RELATED READING

延伸阅读

更多一线实战笔记与深度复盘,助您持续精进