首页 / Hermes Agent 教程 / MCP 扩展:把外部工具接进来

Hermes Agent 教程

MCP 扩展:把外部工具接进来

本教程共 25 篇 · 第 19 篇 · 更新于 2026-07-26 · 约 15 分钟阅读

Hermes AgentHermes Agent 教程MCPModel Context Protocol外部工具OAuth工具集成

19. MCP 扩展:把外部工具接进来

本节目标:搞懂 MCP(Model Context Protocol)是什么、Hermes Agent 怎么通过它把外部工具接进来、怎么过滤要暴露给模型的工具、OAuth 怎么走、以及怎么反过来把 Hermes 自己当 MCP 服务器给别的 Agent 用。学完你能给 Hermes 接一个 GitHub 或文件系统的 MCP server,并按需裁剪工具表面。

MCP 是干什么的

先打个比方。你买了个万能遥控器,家里电视、空调、机顶盒各家有各家的协议,按理说你要为每个设备学一套按键逻辑。但遥控器支持一个统一协议,每个设备配一个适配器,遥控器只管发统一指令,适配器翻译成各家的话。

MCP(Model Context Protocol)就是 AI Agent 世界的这个统一协议。Hermes Agent 是遥控器(MCP client),外部那些工具服务器是设备(MCP server),它们之间用 MCP 协议通信。

具体来说,MCP 协议规定了三件事:

  • 怎么发现工具:server 告诉 client「我有哪些工具」
  • 怎么调用工具:client 发请求,server 返回结果
  • 怎么传输:两种方式,stdio 管道 或 HTTP

对接之后,对 Hermes 来说,MCP 工具和 70+ 内置工具没有任何区别。模型调 mcp_github_list_issues 和调内置的 web_search 走的是同一套注册和分发机制,它根本不知道背后是一个外部进程在干活。

为什么不直接写内置工具

你可能会问:要个 GitHub 工具,我写个 github_tool.py 调 GitHub API 不就行了?

能用,但有三个问题。

每个 API 都要从头写。GitHub API 几十个端点,每个都要写 handler、定义 schema、处理错误、处理分页。而社区已经把这些做好打包成 MCP server 了,启动就能用。

工具和 Agent 绑死。内置工具跑在 Agent 进程里,要是工具依赖 Node.js(GitHub MCP server 是 Node 写的),你的 Python Agent 还得装 Node 运行时。工具崩了,Agent 跟着崩。MCP server 跑在独立进程,崩了不影响 Agent,重启就好。

没有统一发现机制。今天接 GitHub、明天接 Jira、后天接 Slack,每个 API、认证、schema 格式都不一样。MCP 把发现、调用、传输三件事统一了。

两种 MCP 服务器

Stdio 服务器

Stdio server 是本地子进程,通过 stdin/stdout 管道和 Hermes 通信。

mcp_servers:
  github:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-github"]
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "***"

适合:服务器装在本地、想低延迟访问本地资源、你跟着 MCP server 文档里写的 command/args/env 配。

HTTP 服务器

HTTP MCP server 是远程端点,Hermes 直接连过去。

mcp_servers:
  remote_api:
    url: "https://mcp.example.com/mcp"
    headers:
      Authorization: "Bearer ***"

适合:server 托管在别处、组织内部暴露 MCP 端点、你不想让 Hermes 在本地起子进程。

快速上手

MCP 支持随标准安装一起装好,不用额外步骤。

  1. ~/.hermes/config.yaml 加一个 MCP server:
mcp_servers:
  filesystem:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-filesystem", "/home/user/projects"]
  1. 启动 Hermes:
hermes chat
  1. 让 Hermes 用上这个能力:
List the files in /home/user/projects and summarize the repo structure.

Hermes 会发现这个 MCP server 的工具,像用普通工具一样用它。

工具命名规则

MCP 工具注册进 Hermes 时,名字会加 mcp_<server名>_ 前缀,避免和内置工具撞名:

mcp_<server_name>_<tool_name>

几个例子:

ServerMCP 工具注册名
filesystemread_filemcp_filesystem_read_file
githubcreate-issuemcp_github_create_issue
my-apiquery.datamcp_my_api_query_data

实际你不用手动调带前缀的名字,Hermes 看到工具在正常推理时会自己选。

Note

名字清洗:server 名和工具名里的连字符(-)和点(.)在注册前会被替换成下划线,保证名字是合法的 LLM 函数调用标识符。比如 my-api 暴露的 list-items.v2 会变成 mcp_my_api_list_items_v2。写 include/exclude 过滤器时要用原始 MCP 工具名(带连字符/点),不是清洗后的版本。

工具发现与动态刷新

Hermes 在启动时发现 MCP server 并把工具注册进正常工具 registry。运行中如果 MCP server 的可用工具变了,server 会发 notifications/tools/list_changed 通知,Hermes 收到后自动重新拉取工具列表更新 registry,不用手动 /reload-mcp

这对那种能力动态变化的 server 有用—比如加载了新数据库 schema 后多了一组工具,或服务下线后工具消失。刷新有锁保护,同一个 server 连发通知不会触发重叠刷新。

每个配置的 MCP server 只要贡献至少一个注册工具,就会创建一个运行时工具集(toolset)叫 mcp-<server>,方便在工具集层面统一管理。

工具过滤:少即是多

好的 MCP 用法不是「全连上」,而是「连对的,暴露最小的有用表面」。一个 MCP server 可能暴露 50 个工具,全注册进来模型的工具列表会很长、选择变难、token 消耗变大。

整个 server 禁用

mcp_servers:
  legacy:
    url: "https://mcp.legacy.internal"
    enabled: false

enabled: false 时 Hermes 完全跳过这个 server,不连、不发现、不注册。配置留在那以后还能用。

白名单(推荐)

mcp_servers:
  github:
    command: "npx"
    args: ["-y", "@modelcontextprotocol/server-github"]
    env:
      GITHUB_PERSONAL_ACCESS_TOKEN: "***"
    tools:
      include: [create_issue, list_issues]

只注册列出来的工具。对敏感系统(财务、客户、破坏性操作)这是最安全的默认。

黑名单

mcp_servers:
  stripe:
    url: "https://mcp.stripe.com"
    tools:
      exclude: [delete_customer]

注册除列出来的之外的所有工具。

优先级

两个都写时 include 赢:

tools:
  include: [create_issue]
  exclude: [create_issue, delete_issue]

结果:create_issue 仍允许,delete_issueinclude 优先被忽略。

关掉工具外的辅助包装

Hermes 还会给 MCP server 注册一些工具外的辅助工具:

  • 资源类:list_resourcesread_resource
  • 提示词类:list_promptsget_prompt

不想让模型碰这些表面,可以单独关掉:

mcp_servers:
  docs:
    url: "https://mcp.docs.example.com"
    tools:
      prompts: false
      resources: false

能力感知注册

即便你写 resources: trueprompts: true,Hermes 也只在 MCP 会话真的支持对应能力时才注册这些辅助工具。所以一个只暴露工具、不支持 resources/prompts 的 server,不会多出这些包装。这让工具列表保持诚实。

全过滤掉怎么办

如果配置过滤掉了所有可调用工具,又关掉了所有辅助包装,Hermes 不会为这个 server 创建一个空的运行时 MCP 工具集。工具列表保持干净。

改完配置怎么生效

/reload-mcp

这个斜杠命令从配置重新加载 MCP server、刷新可用工具列表。改了 include/excludeenabled 标志、resources/prompts 开关、auth 头或 env 之后都要跑一下。

OAuth 认证

很多托管 MCP server(Linear、Sentry、Atlassian、Asana、Figma、Stripe 等)要求 OAuth 2.1,而不是静态 bearer token。配 auth: oauth,Hermes 通过 MCP Python SDK 处理发现、动态客户端注册、PKCE、token 交换、刷新、step-up auth:

mcp_servers:
  linear:
    url: "https://mcp.linear.app/mcp"
    auth: oauth

第一次连接时,Hermes 打印一个授权 URL,能开浏览器就开,然后在本地 loopback 端口等 OAuth 回调。token 缓存在 ~/.hermes/mcp-tokens/<server>.json,权限 0o600,后续运行静默复用直到刷新失败。

远程 / 无头主机

Hermes 跑在另一台机器上、浏览器在你笔记本上时,loopback 回调够不到你。三种办法完成流程:

  • 粘贴回填(零配置):交互式终端里 Hermes 会打印「Or paste the redirect URL here…」和授权 URL。在浏览器开 URL、批准、复制浏览器最终落到的那条 URL(重定向会报连接错误,正常),粘到提示符。裸 ?code=…&state=… 查询串也行
  • SSH 端口转发:另开一个终端 ssh -N -L <port>:127.0.0.1:<port> user@host,然后正常走重定向流程
  • 代理回调(redirect_uri:当公共 HTTPS 端点能转发到主机(比如 Tailscale Funnel 或反代指向回调端口),设 oauth.redirect_uri,浏览器重定向自己就能到 Hermes,不用隧道或粘贴:
mcp_servers:
  myserver:
    url: "https://mcp.example.com/mcp"
    auth: oauth
    oauth:
      redirect_port: 8765
      redirect_uri: "https://oauth.example.ts.net/callback"

完全无头的 Gateway(消息机器人、根本没有交互终端)可以用可选的 mcp-oauth-remote-gateway 技能,让 Agent 手动走完流程并把 token 写到 Hermes 期望的位置。

Warning

WAF 拒绝 127.0.0.1 重定向 URI:少数 provider 的授权服务器前面有 WAF,会 403 任何查询串里带字面 127.0.0.1 的授权请求(Reclaim.ai 的 AWS API Gateway 是已知例子)。设 oauth.redirect_host: localhost 改用 http://localhost:<port>/callback 就行。

Warning

不支持自动注册的 provider(Google Drive、Atlassian):有些 server 拒绝裸 auth: oauth 依赖的动态客户端注册步骤(RFC 7591)。Google 官方 Drive server 直接返回 400,没 OAuth client 被创建、没 token 被拿到。症状很隐蔽:这些 server 不带 auth 也能服务 tools/list,所以 hermes mcp login 能列出工具看起来像成功了,但之后的真实工具调用全部超时。hermes mcp login 现在能检测这个(会检查 token 真的落到磁盘了)并提示你自备 OAuth client。在 provider 控制台建一个,加到配置:

mcp_servers:
  googledrive:
    url: "https://drivemcp.googleapis.com/mcp/v1"
    auth: oauth
    oauth:
      client_id: "<your-oauth-client-id>"
      client_secret: "<your-oauth-client-secret>"

然后 hermes mcp login googledrive—有了预注册 client,Hermes 跳过注册、走正常浏览器授权。

Warning

配置自动重载竞态:从运行中的 Hermes 会话里编辑 ~/.hermes/config.yaml 时,CLI 自动重载 MCP 连接有 30 秒超时,不够走交互式 OAuth。先把条目加上,再从新终端跑 hermes mcp login <server>—它会等满 5 分钟让你完成认证。

mTLS / 客户端证书

要求双向 TLS(客户端证书认证)的远程 HTTP MCP server 通过 client_cert / client_key 支持。client_cert 接受三种形态:

单个合并 PEM 路径(一个文件同时含证书和私钥):

mcp_servers:
  internal_api:
    url: "https://mcp.internal.example.com/mcp"
    client_cert: "~/.certs/mcp-client.pem"

[cert, key] 二元组(证书和私钥分文件,等价于设 client_cert + client_key):

mcp_servers:
  internal_api:
    url: "https://mcp.internal.example.com/mcp"
    client_cert: ["~/.certs/mcp-client.crt", "~/.certs/mcp-client.key"]

[cert, key, password] 三元组(私钥加密时第三元素是口令):

mcp_servers:
  internal_api:
    url: "https://mcp.internal.example.com/mcp"
    client_cert: ["~/.certs/mcp-client.crt", "~/.certs/mcp-client.key", "${MCP_KEY_PASSWORD}"]

路径支持 ~ 展开;文件缺失会抛清晰的、server 范围的错误,而不是晦涩的 TLS 握手失败。

Catalog:一键装 Nous Research 审过的 MCP

Hermes 自带一个精选 catalog,里面是 Nous Research 员工审过合并的 MCP server。默认全禁用,按需装:

hermes mcp                # 交互式选择器(默认)
hermes mcp catalog        # 纯文本列表,可脚本化
hermes mcp install n8n    # 按名字装一个 catalog 条目

选择器每行显示当前状态:

n8n          available              Manage and inspect n8n workflows from Hermes
linear       enabled                Linear issue/project management (remote OAuth)
github       installed (disabled)   GitHub repo + PR tools

在某行回车可以装(并走必要的凭据配置)、启用、禁用或卸载。Catalog 条目存在 hermes-agent repo 的 optional-mcps/ 下—出现在那个目录就意味着过了 Nous Research 审批。没有社区提交层,靠合并 PR 加条目。

Catalog 条目可能要求:

  • API key:Hermes 在安装时提示,把值写到 ~/.hermes/.env。非敏感值(base URL)也写到同文件
  • OAuth(远程 MCP):在配置里写成 auth: oauth,MCP 客户端首次连接时开浏览器
  • OAuth(Google/GitHub 等第三方 provider):Hermes 指你到 hermes auth <provider>,如果你还没认证过

安装时选工具

凭据配好后,Hermes 探测 MCP server 列出它暴露的每个工具,给一个勾选清单:

Select tools for 'linear' (SPACE toggle, ENTER confirm)
  [x] find_issues       Find issues matching a query
  [x] get_issue         Get a single issue
  [x] create_issue      Create a new issue
  [ ] delete_workspace  Delete a Linear workspace
  ...

预勾选的行来自:

  1. 你之前装过这个条目的选择(重装保留你之前的,manifest 默认不覆盖)
  2. manifest 的 tools.default_enabled(有的 catalog 条目预裁剪掉修改性或罕用工具)
  3. 都没有就全选

回车提交后,只有勾选的工具进 mcp_servers.<name>.tools.include。全选的话不写过滤器(最干净的配置形态,行为一样)。

Note

探测失败(server 不可达、OAuth 还没完成、后端服务没跑)时安装仍会成功:manifest 的 tools.default_enabled 直接应用(如果有声明),否则不写过滤器。等 server 可达了再跑 hermes mcp configure <name> 精修。

信任模型

装 catalog 条目会跑 manifest 指定的一切—git clone、条目的 bootstrap 命令(pip installnpm install 等)、最终是 MCP server 自己的代码。Manifest 靠 hermes-agent repo 的 PR 审核把关,Nous Research 在每个条目发布前都审过—但你装之前还是该读一下 manifest,特别是 source: 字段的仓库、install.bootstrap: 命令、任何 transport.command: 调用。

Manifest 在 GitHub 的 optional-mcps/<name>/manifest.yaml。选择器安装时也会打印 manifest 的 source: URL 方便你核对上游 repo。Web Dashboard 的 MCP 页面同样展示每个 catalog 条目的细节—传输、auth 类型、端点 URL(HTTP)或 command + args(stdio)、git 安装源/ref 和 bootstrap 命令、setup 说明—source: 渲染成可点链接,让你点 Install 前能看清这个条目连什么、跑什么。

运行时环境变量替换

条目的 transport.commandtransport.argstransport.urlheaders 里,${VAR} 占位符在 server 连接时从环境变量(包括 ~/.hermes/.env 里的一切)解析。这对 catalog 条目引用用户在别处配的值有用,比如 ${HOME}/foo${MY_PROVIDER_TOKEN}

注意这跟 catalog manifest 里的 ${INSTALL_DIR} 不同—后者在安装时被替换成 catalog 把条目 repo clone 进的路径。

之后改工具选择

hermes mcp configure linear

重新打开同一个勾选清单,预勾选你当前的选择。想多启用些工具、或 server 加了新工具你想纳入时用。

更新 catalog manifest

MCP 永不自动更新。Hermes 升级后如果 manifest 版本变了,重跑 hermes mcp install <name> 刷新。

要给 catalog 加 MCP,往 optional-mcps/ 提 PR。

Manifest 版本兼容

Manifest 钉一个 manifest_version。Catalog 向前兼容:PR 加了个 manifest_version 比你装的 Hermes 理解的更新的条目时,选择器会给该条目显示警告(⚠ '<name>' requires a newer Hermes)而不是静默隐藏。看到就跑 hermes update 装最新 Hermes。

内置预设

对知名的 MCP server,hermes mcp add 接受一个 --preset 标志填好传输细节,省得你查 command 和 args。预设只提供默认值—同一命令行上再传别的(env、headers、过滤)仍然优先。

预设装的是什么
codexCodex CLI 的 MCP server(codex mcp-server 走 stdio)。需要 PATH 里有 codex CLI
hermes mcp add codex --preset codex

这等价于写出:

mcp_servers:
  codex:
    command: "codex"
    args: ["mcp-server"]

你可以挑任何本地名(hermes mcp add my-codex --preset codex 也行);预设只提供 command/args 默认值。

让 stdio server 回收内存

基于浏览器的 MCP server(比如 @playwright/mcp)首次工具调用后会留一个完整 Chromium 常驻—几百 MB 永不释放。开启自动回收,server 会在空闲/寿命限制后被拆掉,下次有工具调用时透明重启(工具全程保持注册):

mcp_servers:
  playwright:
    command: "npx"
    args: ["-y", "@playwright/mcp@latest", "--headless"]
    idle_timeout_seconds: 900     # 15 分钟无工具调用后回收
    max_lifetime_seconds: 86400   # 不管怎样至少每天回收一次

并行工具调用

默认 MCP 工具顺序执行—一次一个。如果你的 MCP server 暴露的工具安全可并发(比如只读查询、独立 API 调用),可以开启并行执行:

mcp_servers:
  docs:
    command: "docs-server"
    supports_parallel_tool_calls: true

开启后,Hermes 可能在单个工具调用批次里同时跑这个 server 的多个工具,就像对内置只读工具(web_searchread_file 等)那样。

Warning

只对工具能同时安全跑的 MCP server 开启并行。如果工具读写共享状态、文件、数据库或外部资源,开启前先审视读写竞态。

MCP Sampling

MCP server 可以通过 sampling/createMessage 协议反过来请求 Hermes 做 LLM 推理。这让没有自己模型访问的 server 也能要 LLM 能力。

Sampling 对所有 MCP server 默认启用(MCP SDK 支持时)。按 server 在 sampling 键下配:

mcp_servers:
  my_server:
    command: "my-mcp-server"
    sampling:
      enabled: true            # 启用 sampling(默认 true)
      model: "openai/gpt-4o"  # 覆盖 sampling 请求用的模型(可选)
      max_tokens_cap: 4096     # 每次 sampling 响应最大 token(默认 4096)
      timeout: 30              # 每次请求超时秒数(默认 30)
      max_rpm: 10              # 限速:每分钟最大请求数(默认 10)
      max_tool_rounds: 5       # sampling 循环里最大工具轮数(默认 5)
      allowed_models: []       # server 可请求的模型名白名单(空 = 任意)
      log_level: "info"        # 审计日志级别:debug、info、warning(默认 info)

sampling 处理器带滑窗限速器、每请求超时、工具循环深度限制,防止失控使用。指标(请求数、错误数、用掉的 token)按 server 实例追踪。

给特定 server 关掉 sampling:

mcp_servers:
  untrusted_server:
    url: "https://mcp.example.com"
    sampling:
      enabled: false

反过来:把 Hermes 当 MCP 服务器

除了MCP server,Hermes 也能MCP server。这让其他支持 MCP 的 Agent(Claude Code、Cursor、Codex 或任何 MCP 客户端)能用 Hermes 的消息能力—列对话、读消息历史、跨所有已连平台发消息。

什么时候用

  • 你想让 Claude Code、Cursor 或别的编码 Agent 通过 Hermes 收发 Telegram/Discord/Slack 消息
  • 你想要一个 MCP server 一次性桥接到 Hermes 所有已连的消息平台
  • 你已经有个跑着的 Hermes Gateway 连着各平台

快速开始

hermes mcp serve

启动一个 stdio MCP server。MCP 客户端(不是你)管进程生命周期。

MCP 客户端配置

把 Hermes 加到你的 MCP 客户端配置。比如 Claude Code 的 ~/.claude/claude_desktop_config.json

{
  "mcpServers": {
    "hermes": {
      "command": "hermes",
      "args": ["mcp", "serve"]
    }
  }
}

装在特定位置时:

{
  "mcpServers": {
    "hermes": {
      "command": "/home/user/.hermes/hermes-agent/venv/bin/hermes",
      "args": ["mcp", "serve"]
    }
  }
}

暴露的工具

这个 MCP server 暴露 10 个工具:

工具描述
conversations_list列活跃消息对话。按平台过滤或按名搜索
conversation_get按会话 key 取单个对话详情
messages_read读对话最近的消息历史
attachments_fetch从特定消息提取非文本附件(图、媒体)
events_poll从游标位置轮询新对话事件
events_wait长轮询/阻塞直到下一个事件到达(近实时)
messages_send通过平台(如 telegram:123456discord:#general)发消息
channels_list列所有平台可用的消息目标
permissions_list_open列这次桥接会话期间观察到的待批准请求
permissions_respond批准或拒绝一个待批准请求

事件系统

MCP server 含一个实时事件桥,轮询 Hermes 的会话数据库找新消息。这让 MCP 客户端近实时感知到来的对话:

# 轮询新事件(非阻塞)
events_poll(after_cursor=0)

# 等下一个事件(阻塞到超时)
events_wait(after_cursor=42, timeout_ms=30000)

事件类型:messageapproval_requestedapproval_resolved。事件队列在内存里,桥连接时启动。更早的消息通过 messages_read 拿。

选项

hermes mcp serve              # 正常模式
hermes mcp serve --verbose    # 在 stderr 开调试日志

工作原理

MCP server 直接从 Hermes 的会话存储读对话数据(~/.hermes/sessions/sessions.json 和 SQLite 数据库)。后台线程轮询数据库找新消息,维护内存事件队列。发消息用和 cron 投递、hermes send CLI 同款的内部发送引擎(tools/send_message_tool.py)。

读操作(列对话、读历史、轮询事件)不需要 Gateway 跑着。发操作需要 Gateway 跑着,因为平台适配器要活跃连接。

当前限制

  • 内置的 hermes mcp serve 今天只暴露 stdio-only MCP server。要 HTTP MCP server,跑个独立适配器—或更常见,用 Hermes 的 MCP 客户端侧,它已经同时说 stdio 和 HTTP
  • 事件轮询约 200ms 间隔,靠 mtime 优化的 DB 轮询(文件没变就跳过活)
  • 还没有 claude/channel 推送通知协议
  • 只发文本(messages_send 不能发媒体/附件)

安全模型

stdio env 过滤:对 stdio server,Hermes 不会盲目传你的整个 shell 环境。只传显式配置的 env 加一个安全基线。这减少意外的密钥泄露。

配置层面的暴露控制:过滤支持也是安全控制—禁用不想让模型看到的危险工具、给敏感 server 暴露最小白名单、不想要那个表面时关掉 resource/prompt 包装。

常见坑

把 LLM API key 传给 MCP server 的 envenv: { OPENAI_API_KEY: "..." } 把你的 LLM 密钥泄露给了外部进程。修:MCP server 的 env 只传它需要的(如 GITHUB_TOKEN)。Hermes 默认只传安全基础变量(PATH、HOME 等),用户显式配的才传。

不过滤工具全注册。一个 server 暴露 50 个工具全注册,模型工具列表变长、选择变难、token 消耗变大。修:用 tools.include 白名单只启用需要的。

MCP server 崩了不知道。stdio 子进程崩了,Agent 调用时才发现连接断了返回错误。Hermes 的 MCPServerTask 有自动重连—初始连接失败重试 3 次,运行中断线重试 5 次,指数退避。

MCP 工具和内置工具撞名。MCP server 提供的 read_file 和内置的 read_file 撞了。修:MCP 工具自动加 mcp_<server>_ 前缀。如果仍然撞了,内置工具优先,MCP 工具被跳过。

ssl_verify: false 用在真实服务上。这完全禁用服务器证书验证。别用在真实服务上。

改了配置不重载。改完 include/excludeenabled、auth 头、env 之后忘了 /reload-mcp,老行为继续跑。

推荐的第一个 MCP

对大多数用户,好的第一个 server 是:filesystem、git、GitHub、fetch/文档 MCP server、一个窄的内部 API。

不好的第一个:带一堆破坏性动作又没法过滤的庞大业务系统、你不够了解没法约束的任何东西。