应用绑定型 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。
三件需要提前知道的事:
- 前置条件得你自己备好。 Blender 5.1+、官方 MCP add-on(要在 Blender 里启用并允许在线访问)、以及后端机器上的 Git 和 uv。装这个包不会替你装 Blender、改 Blender 偏好、装 add-on,也不会打开场景。
- 第一次连接前先把 uv 环境预热。 上游第一次下载/构建要几分钟,在 Windows 上实测就是这么慢,正常 MCP 连接超时会把它打断:
uv tool run --from "git+https://projects.blender.org/lab/blender_mcp.git@2cea8d566dde07fbac28a61d698909d69724e853#subdirectory=mcp" blender-mcp --help
- 这个 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 指南里。