在这里插入图片描述

本篇收获清单

  • 理解 MCP 协议的核心价值,以及 Hermes 为何选择它而非自研
  • 掌握 MCPServerTask 完整生命周期,含线程安全、热更新、断线重连
  • 读懂采样(Sampling)反向调用机制的工程实现
  • 拥有 3 个完整可运行的实战场景(SQL 查询、文件系统、双向协作)
  • 理解 Hermes 的 5 层安全护栏设计

1. 引导:从"封闭大脑"到"无限接口"的跨越

在 AI Agent 的早期开发阶段,扩展工具集往往像是一场"侵入式手术"。开发者必须手动修改核心源码,硬编码工具 Schema,最后重启整个服务。这种"封闭大脑"的架构不仅带来了沉重的技术债,更让 Agent 在面对动态变化的业务需求时显得力不从心。

想象这样一个痛点:你的 Agent 跑在生产环境,业务方临时要接入一个新的数据库查询服务。传统方案意味着:写驱动代码 → 合并分支 → 走 CI/CD → 重启服务 → 验证。整个流程少则数小时,多则数天。

MCP(Model Context Protocol,模型上下文协议)的横空出世,彻底终结了这种痛苦。作为 Hermes Agent 架构中的关键拼图,我更愿意将其描述为 Agent 世界的"通用 USB-C 接口"。通过 MCP,工具不再是死板的代码片段,而是演化成了可插拔、可标准化的运行时资源。

Hermes 架构哲学:正如 01-architecture-overview.md 中所述,Hermes 的核心设计信条是"不是封装,而是运行时(Not a wrapper, but a runtime)"。MCP 协议的集成正是这一哲学的终极体现——它允许 Agent 在不重构代码的前提下,于运行时动态生长出新的能力。


2. 解码 MCP:Agent 世界的"USB 接口标准"

在 Hermes 的 tools/ 目录中,MCP 定义了一套跨语言、跨平台的通讯规范。无论工具后端是用 Python、TypeScript 还是 Go 编写,只要遵循 MCP,就能实现"一秒接入"。

2.1 传统静态工具 vs MCP 动态工具

维度 传统静态工具 MCP 动态工具
注册方式 模块 import 时自注册(静态硬编码) 运行时连接服务器动态发现
扩展成本 高(需编写驱动代码并重启) 极低(仅需在 config.yaml 中添加几行配置)
运行环境隔离 与 Agent 共享进程环境 进程级隔离(stdio)或网络隔离(HTTP)
动态更新能力 需重启服务生效 支持热更新,无需中断对话流
安全边界 依赖代码审查 协议层内置凭证过滤 + OSV 漏洞扫描
多语言支持 需同语言实现 任意语言实现 MCP 接口即可

2.2 两种传输模式详解

Hermes 的 MCP 实现支持两种传输模式:

┌─────────────────────────────────────────────────────────┐
│                    Hermes Agent                         │
│                                                         │
│   ┌─────────────┐           ┌──────────────────────┐   │
│   │  stdio 模式  │           │  HTTP/StreamHTTP 模式 │   │
│   │             │           │                      │   │
│   │ 本地子进程   │           │  远程/云端 MCP 服务   │   │
│   │ 最低延迟    │           │  OAuth 2.1 PKCE 认证  │   │
│   │ 开发调试优先 │           │  适合生产部署         │   │
│   └──────┬──────┘           └──────────┬───────────┘   │
│          │ JSON-RPC via                │ JSON-RPC via   │
│          │ stdin/stdout                │ HTTPS          │
└──────────┼─────────────────────────────┼───────────────┘
           ▼                             ▼
    ┌─────────────┐              ┌──────────────┐
    │ filesystem  │              │  GitHub MCP  │
    │ MCP Server  │              │    Server    │
    └─────────────┘              └──────────────┘

源码细节tools/mcp_tool.py 中的 MCPServerTask._run_stdio()_run_http() 分别实现了这两种传输。HTTP 传输会根据 MCP SDK 版本自动选择 streamable_http_client(mcp >= 1.24.0)或兼容的旧 API。


3. MCPServerTask 的完整生命周期

tools/mcp_tool.py 源码中,MCPServerTask 承载了外部连接的整个生命周期管理。以下时序图完整呈现了从启动到断连重连的全过程:

MCP Server ToolRegistry Hermes (AIAgent) MCP Server ToolRegistry Hermes (AIAgent) ① 初始化与能力交换 ② 动态工具发现 ③ 线程安全注册(RLock 保护) threading.RLock() 加锁 ④ 正常运行中接收变更通知 asyncio.Lock 防竞态 ⑤ 连接断开与指数退避重连 指数退避等待 alt [成功] [失败] loop [最多重试 5 次,间隔上限 60s] initialize (capabilities exchange) capabilities response (protocol version) tools/list (request schemas) tool definitions (names, descriptions, JSON schemas) ToolRegistry.register(ToolEntry) registration complete(含前缀 mcp_<server>_<tool>) notifications/tools/list_changed deregister old tools tools/list (重新拉取) updated tool definitions register new tools connection lost reconnect attempt capabilities response tools/list tool definitions re-register tools

3.1 架构师笔记:三个核心工程细节

① 为何必须用 threading.RLock()

Hermes 中,MCP 刷新由后台线程监听 notifications/tools/list_changed 信号触发,而主线程的 LLM 推理循环可能正处于读取工具表的关键时刻。RLock(可重入锁)确保了在高并发或动态刷新时,注册表不会出现读写冲突。普通 Lock 在同一线程内无法重入,而注册流程可能递归调用——这是选择 RLock 而非 Lock 的核心原因。

② 冲突解决策略(命名权威性)

这是一个极其核心的设计决策:

  • Hermes 会主动跳过与内置工具名冲突的 MCP 工具,以保护核心功能的权威性
  • MCP 工具之间允许互相覆盖(Shadowing)——不同 MCP 服务器可能提供同名工具,以后加载的为准
  • 此设计确保了内置工具永远不会被外部服务器"劫持"

③ 命名前缀规则

为避免冲突,Hermes 自动为 MCP 工具添加前缀:

mcp_<server_name>_<tool_name>
服务器名 原始工具名 注册后名称
filesystem read_file mcp_filesystem_read_file
sql-explorer execute_query mcp_sql_explorer_execute_query
my.server list_items mcp_my_server_list_items

连字符(-)和点号(.)会被统一替换为下划线(_)。


4. 核心技术:动态工具发现与"无感热更新"

Hermes 实现工具"热插拔"的关键在于对 notifications/tools/list_changed 信号的精准捕捉。

4.1 刷新闭环逻辑

接收 notifications/tools/list_changed

获取 asyncio.Lock

快照当前工具名集合

调用 tools/list 重新拉取

对比新旧工具集

注销失效工具
registry.deregister

注册新增工具
registry.register

释放 asyncio.Lock

输出审计日志
新增N个 / 移除M个

刷新过程受 asyncio.Lock 保护,防止同一服务器的密集通知导致竞态条件。日志明确输出新增和移除的工具列表,方便运维审计。

4.2 五层安全护栏

在动态扩展性与安全性之间,Hermes 构建了完整的纵深防御体系:

安全纵深防御(从外到内)

① OSV 恶意软件扫描
check_package_for_malware()
启动子进程前检查

② 环境变量过滤
_build_safe_env()
只传递安全基线变量

③ Prompt Injection 扫描
_scan_mcp_description()
检测越狱指令模式

④ 危险操作人工审批
tools/approval.py TUI 弹窗
删除/敏感操作必须确认

⑤ 错误消息脱敏
_sanitize_error()
自动替换 API Key / Token

层级 机制 防范威胁
① OSV 扫描 check_package_for_malware() 已知恶意 npm/pypi 包
② 环境变量过滤 只传 PATH, HOME 等安全基线 + 显式配置项 API Key 意外泄露给子进程
③ Prompt Injection 扫描 正则检测越狱指令、base64 引用、危险 import 恶意 MCP 服务器注入攻击
④ 危险操作审批 TUI 弹窗人工确认 误删文件、误操作系统
⑤ 错误脱敏 _CREDENTIAL_PATTERN 正则替换为 [REDACTED] 错误日志中的凭证泄露

源码细节tools/mcp_tool.py:170-186 定义了 _CREDENTIAL_PATTERN,覆盖了 GitHub PAT(ghp_***)、OpenAI-style key(sk-***)、Bearer token、password=*** 等常见凭证格式。


5. 采样(Sampling)与反向调用机制

MCP 协议中最令人兴奋的特性莫过于"采样支持(Sampling)"。它打破了传统"Agent 调用工具"的单向模式,实现了双向协作

5.1 反向调用时序图

LLM (推理引擎) MCP Tool Server Hermes Agent 用户 LLM (推理引擎) MCP Tool Server Hermes Agent 用户 执行测试套件... SamplingHandler 接管 继续执行剩余测试... "帮我跑一套自动化测试,遇到模糊断言自行决策" 调用 mcp_testing_run_suite() sampling/createMessage (上下文 + 决策请求) asyncio.to_thread() → LLM 推理 决策结果 采样响应(含 LLM 决策) 工具执行完成(含完整报告) 输出测试报告 + 决策说明

5.2 SamplingHandler 的安全护栏

SamplingHandler 内置了多层限制,防止 MCP 工具滥用 LLM 资源:

护栏 默认值 说明
速率限制(滑动窗口) 10 RPM 防止密集采样耗尽配额
模型白名单 allowed_models 配置 防止请求调用未授权模型
工具调用轮次上限 max_tool_rounds = 5 防止递归调用爆栈
请求超时 30 秒 防止长时间阻塞
审计日志 请求数、错误数、token 消耗 可观测性保障

工程亮点:采样请求运行在 MCP 后台事件循环中,但同步的 LLM 调用通过 asyncio.to_thread() 卸载到工作线程,避免阻塞事件循环——这是典型的"异步友好型阻塞操作"处理模式。


6. 实战演练:三个完整场景

6.1 基础配置:开启 MCP 能力

Hermes 的 MCP 配置统一存放在 ~/.hermes/config.yamlmcp_servers 键下:

# ~/.hermes/config.yaml
mcp_servers:
  # 场景一:本地文件系统访问(stdio 模式)
  filesystem:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
  
  # 场景二:GitHub 集成(带工具白名单过滤)
  github:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-github"]
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "${GITHUB_PAT}"  # 从环境变量读取,不硬编码
    tools:
      include: [list_issues, create_issue, update_issue]  # 白名单:只暴露必要工具
      prompts: false      # 禁用辅助工具
      resources: false    # 禁用资源工具

  # 场景三:远程 SQL 查询服务(HTTP 模式 + OAuth)
  sql-explorer:
    url: "https://mcp.internal.company.com/sql"
    auth: oauth           # 启用 OAuth 2.1 PKCE 流程
    tools:
      exclude: [drop_table, truncate_table]  # 黑名单:排除危险操作

工具过滤规则优先级:

  • include(白名单)和 exclude(黑名单)同时存在时,include 优先
  • resources: false 禁用 list_resources / read_resource
  • prompts: false 禁用 list_prompts / get_prompt

6.2 场景一:文件系统 + SQL 联合分析

需求:读取本地日志文件,结合数据库数据生成分析报告。

# 第一步:添加并测试 MCP 服务器
hermes mcp add filesystem --command npx --args @modelcontextprotocol/server-filesystem /home/user
hermes mcp test filesystem
# 输出示例:
# ✅ 连接成功,发现 8 个工具:
#   mcp_filesystem_read_file, mcp_filesystem_write_file,
#   mcp_filesystem_list_directory, mcp_filesystem_search_files ...

hermes mcp add sql-explorer --url https://mcp.internal.company.com/sql --auth oauth
hermes mcp test sql-explorer
# 输出示例:
# 🔐 OAuth 认证中... 请在浏览器完成授权
# ✅ 连接成功,发现 5 个工具:
#   mcp_sql_explorer_execute_query, mcp_sql_explorer_list_tables ...

对话示例:

用户:读取 /home/user/logs/access.log 最后 1000 行,
      结合 orders 表中昨天的数据,分析哪些 API 端点
      的错误率与订单失败率存在相关性,给出可视化建议。

Hermes:我来分别获取日志和数据库数据,然后进行相关性分析。

[调用 mcp_filesystem_read_file: /home/user/logs/access.log (tail 1000)]
[调用 mcp_sql_explorer_execute_query: SELECT endpoint, error_count, ... FROM orders WHERE date = yesterday]

分析结果:
发现 3 个强相关端点(相关系数 > 0.8):
1. /api/checkout → 错误率 12.3% | 订单失败率 11.8%(r=0.94)
2. /api/payment  → 错误率 8.1%  | 订单失败率 7.9%(r=0.91)
...

建议用折线图叠加展示两条曲线,时间粒度为小时级...

6.3 场景二:GitHub Issues 自动化工作流

需求:监控线上错误,自动创建结构化 GitHub Issue。

# 配置 GitHub MCP
hermes mcp add github \
  --command npx \
  --args @modelcontextprotocol/server-github \
  --env GITHUB_PERSONAL_ACCESS_TOKEN=ghp_xxx

# 交互式选择启用的工具
hermes mcp configure github
# > 请选择要启用的工具(空格选择,回车确认):
# [x] list_issues
# [x] create_issue  
# [x] update_issue
# [ ] delete_issue   ← 主动排除危险操作
# [ ] merge_branch   ← 权限过高,暂不开放

对话示例(在 CI 告警触发后):

用户:线上刚收到一批 OOMKilled 告警,k8s pod hermes-worker-7 在过去
     1 小时崩溃了 3 次。帮我在 github.com/myorg/hermes 创建一个 Bug Issue,
     包含完整的问题描述和初步排查方向。

Hermes:[调用 mcp_github_list_issues: 检查是否有重复 Issue]
        未发现重复。正在创建新 Issue...
        
        [调用 mcp_github_create_issue:
          title: "[Bug] hermes-worker-7 OOMKilled 连续崩溃 (3次/1h)"
          body: "## 问题描述\n生产环境 Pod 内存溢出...\n## 初步排查方向\n1. 检查内存 limit 配置..."
          labels: ["bug", "production", "p1"]
        ]
        
        ✅ Issue 已创建:https://github.com/myorg/hermes/issues/342
        建议下一步:检查 Pod 的 resource.limits.memory 配置是否合理。

6.4 场景三:Hermes 作为 MCP 服务器(反向暴露)

Hermes 不仅能连接 MCP 服务器,还能作为 MCP 服务器被其他客户端调用。

# 启动 Hermes 的 MCP 服务端模式
hermes mcp serve
# 输出:
# ✅ Hermes MCP Server 已启动(stdio 模式)
# 暴露 10 个工具:conversations_list, messages_send, channels_list,
#                  events_poll, contacts_search ...

在 Claude Code 中接入 Hermes:

// .claude/config.json(Claude Code 的 MCP 配置)
{
  "mcpServers": {
    "hermes": {
      "command": "hermes",
      "args": ["mcp", "serve"]
    }
  }
}

接入后,Claude Code 可以通过 MCP 协议直接读取你的 Telegram/Discord 消息历史、发送消息,实现跨 Agent 协作。

stdio JSON-RPC

API

API

API

Claude Code
(MCP Client)

Hermes
(MCP Server)

Telegram

Discord

Slack

6.5 热重载:无需重启的工具更新

在对话过程中,如果修改了 config.yaml,可以立即热重载而不中断会话:

/reload-mcp
Hermes:🔄 正在热重载 MCP 配置...
        ✅ filesystem: 8 个工具(无变化)
        ✅ github: 3 个工具(无变化)
        ➕ sql-explorer: 新增,发现 5 个工具
        热重载完成。新工具立即可用,无需重启。

7. CLI 完整参考

# 查看已配置的服务器及状态
hermes mcp list

# 添加新服务器(交互式)
hermes mcp add <name> --command npx --args @modelcontextprotocol/server-filesystem

# 添加远程服务器(HTTP + OAuth)
hermes mcp add <name> --url https://mcp.example.com --auth oauth

# 测试连接并列出发现的工具
hermes mcp test <name>

# 交互式选择启用的工具
hermes mcp configure <name>

# 移除配置
hermes mcp remove <name>

# 启动 Hermes 作为 MCP 服务器
hermes mcp serve

# 对话中热重载(无需退出会话)
/reload-mcp

调试技巧:如果 hermes mcp test 失败,CLI 会提示"保存但禁用"选项。待服务器修复后,再通过 hermes mcp test 重新验证并启用。


8. 自动重连与容错机制

MCPServerTask 内置了指数退避重连机制,确保临时网络抖动不会永久杀死 MCP 集成:

成功

失败

成功(最多3次)

3次均失败

连接中断

触发重连

成功(最多5次,上限60s)

5次均失败

hermes mcp test 手动恢复

初始连接

运行中

初始重试

禁用

断线检测

指数退避重连

阶段 最大重试次数 退避策略
首次连接失败 3 次 线性递增
运行中连接断开 5 次 指数退避,上限 60 秒

9. 总结:Agent 时代的"开发者飞轮"

MCP 协议不是实验性功能,而是已经在 Hermes 生产环境中稳定运行的核心机制。通过 tools/mcp_tool.py 中近 2000 行的精密实现,Hermes 不仅完成了协议层面的精准对接,更通过以下工程细节,将 MCP 从"能用的协议"提升为"可信赖的架构扩展层":

工程能力 实现机制 价值
线程安全动态注册 threading.RLock() 并发刷新不崩溃
环境隔离 _build_safe_env() 凭证不泄露
错误脱敏 _CREDENTIAL_PATTERN 正则 日志安全可审计
采样治理 SamplingHandler 多层护栏 LLM 资源不被滥用
自动重连 指数退避机制 网络抖动自愈
双向能力 hermes mcp serve Hermes 既是客户端也是服务端

更深层的思考:当 Agent 能够像插拔 USB 盘一样,自主发现并优化全球的 MCP 工具资源时,人类开发者的角色将从"搬砖工"正式转变为"架构编排者"。工具的边界,即 Agent 能力的边界。

现在,打开你的 ~/.hermes/config.yaml,添加第一个 MCP 服务器,然后运行 hermes chat,亲眼见证 Agent 能力边界的实时扩展。

Logo

AtomGit 是由开放原子开源基金会联合 CSDN 等生态伙伴共同推出的新一代开源与人工智能协作平台。平台坚持“开放、中立、公益”的理念,把代码托管、模型共享、数据集托管、智能体开发体验和算力服务整合在一起,为开发者提供从开发、训练到部署的一站式体验。

更多推荐