应用绑定型 MCP server:为什么 Hermes 现在会先检查你装没装那个软件


你把 Blender 装好了,也在 Blender 里打开了官方那个 MCP 插件,然后照着教程让 Hermes 装上一个 Blender 插件。安装成功,工具列表里确实多出了 get_objects_summary。你问它:“我现在的场景里有哪些物体?“它回你一句:“工具未连接。”

你去翻 mcp.json,地址没错;重启 Blender,还是不行。真正的原因是:那个端口根本不在 mcp.json 里——Blender 每次启动都会挑一个端口,然后把它写到一个自己管理的运行时文件里。你手写进配置的那个地址,从第一天起就是过期的。

这类“配置看着没问题、就是连不上”的坑,最近三个改动正是冲它去的:插件可以声明自己依赖哪个应用(#119049)、运行时只认应用自己写出的端点文件(#119050)、官方插件目录里也多了第一个真实案例 Blender Lab(#119365)。

为什么“地址写在配置里”这件事本身就不成立

本地工具类应用和 Hermes 之间通常靠一条 loopback 桥通信。桌面应用为了不和别人抢端口,习惯是每次启动动态分配一个,然后把自己当前的 http 地址、临时 token 和进程 pid 写到一个运行时文件里(Blender 那边就是本机 9876 端口上的桥)。这意味着两件事:

  • 你手写的静态 URL 迟早会失效,而且失效得很安静——配置语法全对,只有连接是死的。
  • 一个“连不上”的工具以前只能告诉你“未连接”,你不会知道是应用没开、端口换了,还是版本太老。

所以 Hermes 的做法变成了:别猜,去问应用自己。

插件可以在 plugin.json 里声明“我需要哪个应用”

便携插件(就是那种只有一个 plugin.json 加若干文件的插件)现在多了一个声明区,写在 extensions."com.nousresearch.hermes".servers 下面,服务器名字必须和插件 mcp.json 里的条目对得上:

"extensions": {
  "com.nousresearch.hermes": {
    "servers": {
      "blender": {
        "app": {
          "darwin": {
            "presence": "bundle",
            "location": "/Applications/Blender.app",
            "version": { "kind": "plist" }
          },
          "win32": {
            "presence": "executable",
            "location": "%ProgramFiles%/Blender Foundation/Blender/blender.exe",
            "version": { "kind": "uninstall_registry", "display_name_prefix": "Blender" }
          },
          "linux": {
            "presence": "executable",
            "location": "/usr/bin/blender",
            "version": { "kind": "none" }
          }
        },
        "requires": { "app": true, "min_version": "5.1" },
        "liveness": {
          "kind": "server_json",
          "path": "%LOCALAPPDATA%/Blender/server.json",
          "fields": { "url": "http", "token": "token", "pid": "pid" }
        }
      }
    }
  }
}

几个关键点,都是解析器会逐条较真的地方:

  • app 按操作系统分开写,只接受 darwin / win32 / linux 三个键,至少要有一块。location 必须是绝对路径,或者以 ~、%VAR%、$VAR 开头;带 .. 或 URL 协议会被直接拒掉。
  • presence 只有两种:bundle(macOS 的应用包)和 executable。
  • 版本来源(version.kind)有四类:plist(只能用在 darwin)、uninstall_registry(只能用在 win32,且必须给 display_name_prefix)、pe_resource(win32)、以及 none。只要 requires.min_version 写了,每个 OS 块就必须真的能读出应用版本,none 会被拒绝。
  • liveness.kind 是 server_json 或 none。选 server_json 就必须给 path;可选的 endpoint_path 默认 /mcp,pid_key / url_key / token_key 默认分别是 pid / http / token。

声明和数据是分开的两件事:app 描述“应用在哪”,requires 描述“我要求它在不在”,这个划分是刻意的。

安装前就拦住:装不上就别装

以前应用缺失只是运行期的问题——插件照样装进目录,工具照样注册,等你用的时候才发现是空的。现在安装器会在原子替换之前把每个已声明的 server 检查一遍,任何一种情况都会直接拒绝:

拒绝原因 触发条件
missing_app 声明要求的应用在本机找不到
version_too_old 应用在,但版本低于 requires.min_version
unsupported_os 这台机器不在声明覆盖的 OS 里
平台不匹配 目录条目的 platforms 列表和本机 OS 家族不符(windows / macos 会分别当成 win32 / darwin 处理)

目录安装的检查发生在 clone 和写入目标目录之前,所以不会给你留一个半装好的插件目录。校验命令也会顺带把可用性当成一条信息项报出来:

hermes plugins validate --install-deps

运行时:只认应用自己写出的那个文件

这是这次改动里最“硬”的一条。每次要连接一个已声明的 server,Hermes 会先重新读一遍那个运行时文件,再拿里面的地址去连:

  • 运行时文件是权威的。 文件不存在、内容不是一个合法端点、或者里面的 pid 已经死了,Hermes 会抛出一个 LiveEndpointUnavailable,而不会退回去用配置里那个静态 URL。这一点是有明确测试守着的:test_missing_runtime_file_never_falls_back。
  • token 先登记再使用。 应用写在运行时文件里的临时 token,会在 Hermes 用它拼 Authorization 头之前先注册进脱敏器——这样它不会在你粘贴日志或看会话记录时漏出去。
  • 应用需求会参与工具准入。 声明了应用要求的 server,其工具在应用不满足条件时不会被放进可用工具集。

工具不见了,也要说清楚为什么

工具不可用的时候,最糟的体验不是“没有工具”,而是“不知道为什么没有”。现在已声明但不可用的工具会返回一份结构化状态:当前状态、应用信息、一条明确建议的用户动作,以及重试策略。延迟工具目录和“搜索无结果”的摘要里,也会为这类隐藏的 server 保留一行稳定的说明。

可区分的状态有五种:

状态 含义 大致该怎么办
missing_app 应用没装 先装应用,再装/启用插件
version_too_old 应用版本不够 升级应用
app_not_running 应用没在跑 打开应用,等桥起来
endpoint_unavailable 运行时文件缺失或端点无效 在应用里把桥重启一次
no_interactive_session 需要一个交互式会话(图形登录) 在桌面会话里跑,别在纯后台服务里跑

注意区分:retry 只有 after_user_action(做完上面那件事再重试就行)和 never_here(这台机器上永远不会成,比如 OS 根本不支持)两种。这让“再试一次”和“别试了”有了区别。

目录里已经有的真实例子:Blender Lab

官方插件目录现在有 blender 这个条目,背后是 hermes-plugin-blender 这个公开仓库:

# 装的是目录条目的固定 sha,不是分支顶点
hermes plugins install NousResearch/hermes-plugin-blender
hermes plugins enable blender

它做的事很朴素:用 stdio 拉起官方 Blender Lab MCP server(上游源码固定在 commit 2cea8d56,即 v1.0.3),server 再去连 Blender add-on 开在本机 127.0.0.1:9876 的桥。仓库里还带了一个原始的 workflow skill。

三件需要提前知道的事:

  1. 前置条件得你自己备好。 Blender 5.1+、官方 MCP add-on(要在 Blender 里启用并允许在线访问)、以及后端机器上的 Git 和 uv。装这个包不会替你装 Blender、改 Blender 偏好、装 add-on,也不会打开场景。
  2. 第一次连接前先把 uv 环境预热。 上游第一次下载/构建要几分钟,在 Windows 上实测就是这么慢,正常 MCP 连接超时会把它打断:
uv tool run --from "git+https://projects.blender.org/lab/blender_mcp.git@2cea8d566dde07fbac28a61d698909d69724e853#subdirectory=mcp" blender-mcp --help
  1. 这个 wrapper 目前还没有用上本文说的那套应用门禁。 它的 README 明确写着:自动的应用/版本/存活检测既未实现也未验证,当前靠人工准备前置条件。也就是说,app / requires / liveness 这套契约是核心能力已经落地,而这个具体插件还没接——这正是“能力先到位、插件逐个跟进”的典型节奏。

安全上也要说清楚:Blender Lab 自己声明,它的工具会在 Blender 里执行任意 Python,不防文件删除和数据外泄。请只用于可信工作流,把桥保持在本机 loopback 上,试验用一次性场景。这不是沙箱。

平台差异与几个仍然开放的边角

  • 宿主检查不是三平台对称的。 Windows 要求一个非零的进程会话(活动控制台会话或进程窗口站二者之一即可);Linux 要求能读到一个进程会话 ID,并且当前用户的运行时目录可读;macOS 直接返回 true——标准库没有不启动子进程就能问到当前图形会话的接口。
  • 认证被关闭的应用构建可能不生成运行时文件。 PR 里把这一点列为待硬件验证的开放问题,也就是说这类构建可能一直落在 endpoint_unavailable。
  • 生效边界没有变。 插件在运行中被加载时,网关侧的处理器(斜杠命令、按钮回调、入站变换)会立刻接线,但工具和系统提示仍然是“下一个会话生效”(提示词缓存的原因),便携 MCP server 则需要一次 mcp.reload。

小结

如果你的插件要连的是桌面软件,问题从来不是“配置写没写对”,而是“配置本来就不该由人来写”。现在插件可以声明它的应用依赖,安装器在动手前先把关,运行时只认应用自己写出的端点,连不上时至少会告诉你是哪一环断了。

想先补齐插件侧的背景,可以看插件目录的信任模型和远程 MCP 目录那一篇;如果担心的是这类本地工具的安全边界,MCP 目录安全更对口。把技能直接放进仓库的项目级用法,则在项目级 Skills 指南里。