- README:http raw 链接安装、用户/profile 级安装范围说明;两条新警告 (description 触发安全扫描的原因、Hermes MCP session-id 已知 bug 阻塞 huanxi/huanxi-admin/zentao 的 HTTP MCP 连接,链接 hermes-agent#20349) - CLAUDE.md:开发指南补充同样两点 - decisions.md:新增两条决策记录,标注此前"待实测"的两点均已确认通过 Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_015D4xWG3gRWkUKJDgyvtyJw
28 KiB
name, description, type, last_updated, commit
| name | description | type | last_updated | commit |
|---|---|---|---|---|
| 架构决策 | Marketplace 设计中的关键技术决策及其原因:无版本号/userConfig/Bearer Token/SKILL.md规范/路径锁定/memcore三项优化/memcore-shared共享层/lint_report保活/Base commit兜底/obsidian社区对标审查/memory-lint速度分档过期检测/Codex插件骨架与memcore-codex独立目录/Codex侧bearer_token_env_var/不做Antigravity兼容/zentao-mcp网桥双认证格式兼容/Hermes插件骨架与packs选装/Hermes安全扫描description限制/Hermes MCP session-id已知bug | project | 2026-08-25 | 待commit |
关键架构决策
plugin.json 不设 version 字段
结论:所有 plugin.json 均不含 version 字段。
Why:Claude Code 官方文档说明,若 plugin.json 设置了固定 version,推送新 commit 不改 version 字符串时,已安装用户看不到更新。省略 version 后,Claude Code 自动用 git commit SHA 做版本判断,每次推送 main 分支即为新版本。
How to apply:新增插件时不要加 version 字段。memcore 和 huanxi 均已按此规范执行。
huanxi plugin 使用 userConfig 而非环境变量传 Token
结论:plugin.json 用 userConfig + sensitive: true 声明 Bearer Token 输入,mcpServers.headers 中用 ${user_config.token} 引用。
Why:sensitive: true 将 token 存入系统钥匙链(或 ~/.claude/.credentials.json),不会出现在 settings.json 中,避免随仓库提交泄露。Claude Code 安装插件时自动弹窗提示用户输入,体验好于环境变量。
How to apply:其他需要用户配置 API Key/Token 的插件,均应使用此模式,不要用 ${ENV_VAR} 方式。此模式仅限 Claude Code 侧——Codex 没有等价钥匙链机制,见 decisions.md#Codex 侧 MCP Token 用 bearer_token_env_var,不用 ${VAR} 模板(2026-08-22)。
"userConfig": {
"token": {
"type": "string",
"title": "寰汐 Personal Token",
"description": "在寰汐系统后台生成,hxp_ 前缀",
"sensitive": true
}
}
See Also:project_overview.md#已发布插件、decisions.md#zentao-mcp 网桥已打补丁,双认证格式兼容(2026-08-25)
huanxi MCP Server 认证:Bearer Token 直连,不改后端
结论:plugin.json 配置 Authorization: Bearer ${user_config.token} header,MCP server 已有的 _PersonalTokenVerifier 直接验证 hxp_ token,无需改后端。
Why:后端已有 ASGI 中间件模式(AdminMcpAuthMiddleware)和 PersonalTokenVerifier,Bearer Token 天然支持。OAuth2 Discovery Flow 是 FastMCP 框架层的特性,当客户端直接在 header 中传 Bearer Token 时可绕过 OAuth 流程。改后端风险高且无必要。
How to apply:未来新增 MCP server 插件时,只需在 plugin.json 配置 header,不需要修改后端认证逻辑。
memcore memory-update/lint 路径锁定:禁止写入系统自动记忆路径
结论:memory-update 和 memory-lint 的唯一操作路径锁定为 $PROJECT_DIR/.claude/memory/;严禁写入 ~/.claude/projects/*/memory/;两个技能执行期间不触发 auto memory 系统写入。
Why:Claude Code 的 auto memory 是 system-level 指令,在技能执行期间始终有效。若技能只在 description 中说"不推送远程"而无显式路径约束,AI 会在执行 memory-update/lint 时被 auto memory 指令并发触发,将内容写入系统级路径(~/.claude/projects/xxx/memory/),背离"项目本地 .claude/memory/ 是唯一权威"的初衷。
How to apply:两个技能均在正文最前加"⚠ 路径锁定"块(含禁止路径清单 + 执行前断言代码),memory-sync 的 Phase 3 加方向锁定注释(Phase 10 是唯一远程写入窗口)。未来新增操作本地记忆的技能也应遵循同等约束。
See Also:project_overview.md#已发布插件
memcore:synonyms.md 作为独立等价词表文件
结论:等价表述清单以独立的 synonyms.md(type: reference)存放于 .claude/memory/,而非内嵌到 feedback.md。
Why:feedback.md 存放协作规范(含 Why + How to apply),语义上属于"决策";synonyms.md 是纯配置数据,在 lint Phase 4 矛盾检测前作为输入加载。两者职责不同,混放会让 lint 的加载逻辑复杂化。type: reference 符合"外部参考资料"语义,且该类型已在 frontmatter 枚举中。
How to apply:新建项目记忆体系时,若项目有领域专有术语缩写(如 PG/PostgreSQL、KT/Kotlin),在 .claude/memory/synonyms.md 中维护等价组(每行逗号分隔)。幽灵检测自动排除该文件,不纳入 MEMORY.md 索引。
See Also:decisions.md#memcore memory-update/lint 路径锁定:禁止写入系统自动记忆路径
memcore:synthesis 三触发分工——快扫 vs 精扫
结论:synthesis 升级触发拆为三路:A 会话内主动、B Phase 3C 即时快扫(每次 memory-update 必跑)、C lint Top 反向触发(月度运行)。
Why:原双触发中,B 路径依赖 lint 生成 lint_report.md 的「条目级高频引用 Top」段,短会话或任务型对话中 lint 常被跳过,导致高频引用条目长期未升级为 synthesis。Phase 3C 快扫在每次 memory-update 结束时强制执行,覆盖短会话盲区;lint 精扫按源文件去重计数,用于月度深度检查。两者以 **Synthesized:** 标记作为唯一判重依据,不重复创建文件。
How to apply:实现新的 memory-update 类技能时,引用计数类检查应分"即时快扫"和"月度精扫"两档,分别对应"覆盖率"和"准确率"的不同优先级。
memcore memory-sync:冲突检测必须先于 git commit
结论:memory-sync Phase 0 重构为两步:Step 1 冲突优先检测(diff --diff-filter=U)→ Step 2 普通变更提交。冲突语义合并完成后才允许 commit,再进入 Phase 1。
Why:原设计中 Phase 0 先执行 git add + commit,Phase 0.5 才检测冲突。若文件存在 git merge conflict markers,git commit 会静默失败(git 拒绝提交含冲突标记的文件),整个 sync 流程进入不确定状态且无明显报错。改为"先检测再提交"消除了这条静默失败路径。
How to apply:设计任何含"检测 + 操作"两步的流程时,检测必须先于操作,且检测结果应作为操作的前置条件,而非事后处理。
memcore-shared:路径锁定 + 全局常量的单一来源
结论:把路径锁定、全局常量(SYNTHESIS_THRESHOLD / LINT_STALE_*_DAYS / MULTI_HOST_WARN_DAYS)、PROJECT_DIR 跨平台解析提取到独立 skill memcore-shared,三个主技能(memory-sync / memory-update / memory-lint)开头 Read ../memcore-shared/SKILL.md 引用其约束。description 中显式说明"内部 include,不由用户直接调用"。
Why:原设计中 memory-update 和 memory-lint 各自维护一份 20 行的"路径锁定"块,完全重复;阈值常量 ≥3、≥30/90 天、≥7 天 分散硬编码在多个文件多个位置,调整需多处改动。共享层独立成 skill 后:① 单点维护、② 阈值修改只动一处、③ 与 huanxi-shared 同模式,可演进性强。
How to apply:未来 memcore 类多 skill 插件如出现「共享约束 + 多处硬编码常量」时,提取为独立 <plugin>-shared skill;常量声明在共享 skill 顶部表格,子技能引用常量名而非裸数字。
See Also:decisions.md#memcore memory-update/lint 路径锁定:禁止写入系统自动记忆路径 feedback_plugin_dev.md#skill 不要重写 MCP 参数表,引用 docstring 为单一真相
memcore lint_report 增量保活:稳定 ID + resolved 跳过
结论:lint Phase 8 生成 NEED-HUMAN 条目时,末尾附 <!-- id: 8位sha1 -->(基于 phase + 文件 + 章节 + 关键事实计算)。Phase 8-pre 提取旧 lint_report.md 中带 <!-- resolved --> 标记的 ID 集合,新报告中同 ID 条目跳过。
Why:原 lint_report.md 每次覆盖写入,用户即使在 NEED-HUMAN 条目处理完或决定"不处理"后,下次 lint 仍会重新列出。导致信号疲劳,长期看反而忽视所有 lint 提示。引入稳定 ID + resolved 标记后,用户对每个条目的判断(处理/接受现状)能跨多次 lint 持续生效,lint_report 变成只列"真正待处理"的事项。
How to apply:任何"周期性扫描 + 报告生成"的 lint/check 系统,凡有用户主观判断维度(不只是机器判定)时,输出条目都应有稳定 ID + 用户标记跳过机制。ID 计算用「问题本体」字段(位置+事实),不要包含执行时间/扫描序号。
memory-update Phase 1 锚点丢失兜底
结论:memory-update Phase 1 读 Base commit 锚点时,若 MEMORY.md 头部该行缺失或为 N/A,从各 memory 文件 frontmatter 的 commit: 字段取最旧值兜底,避免退化为全量。
Why:MEMORY.md 头部 Base commit: HASH 是单一锚点来源,一旦用户手动编辑误删此行,整个 git diff 范围退化为全量审查,触发 memory-update 对所有文件做"按变更维度重写"。即使大部分文件没真实变化,也会被刷一次 last_updated 和 commit 字段,造成虚假改动。兜底机制从各文件 frontmatter 取最旧 commit,确保覆盖所有真实差量而非无差别全量。
How to apply:任何"单点配置 → 关键路径"的设计,必须考虑配置丢失时的退路。优先级:单点 → 多点冗余 → 兜底推导。memcore 当前是「单点 + 兜底推导」,无需冗余存储。
See Also:decisions.md#memcore memory-sync:冲突检测必须先于 git commit
SKILL.md frontmatter 只保留 name 和 description
结论:SKILL.md frontmatter 只写 name 和 description 两个字段,去掉 version。
Why:Claude Code 插件规范中 SKILL.md frontmatter 只定义了 name 和 description。version 字段不在规范内,silently ignored,且与 plugin.json 层面的版本管理重复。已从所有 huanxi skill 文件中移除。
How to apply:新建 SKILL.md 时只写这两个字段。description 字段写用户实际口语触发词(第三人称描述),不要用"当需要…时触发"的内部视角表达。
obsidian skill 集对标社区基准的审查 + 优化(2026-06-12)
结论:基于公开社区调研结果(kepano/obsidian-skills 31.8k★、AgriciDaniel/claude-obsidian 15 技能、qhuang20/obsidian-skills、pablo-mano/Obsidian-CLI-skill),对本地 9 技能完成全量审查并执行方案 B(中等扩展):
- P1 描述去冗余:8 个子技能 description 末尾"检测到 .obsidian/ 时与核心技能同步激活"全部删除——Claude Code 路由器按关键词独立打分,没有"伴随激活"机制,此声明纯占预算。
- P5 obsidian-plugins 缩范围:从"控制面板"宽泛定位收敛到"环境层(插件/主题/CSS/Templates/快捷键/命令)",加"首次配置 vault 批量装常用插件"高频场景。
- P6 obsidian-workflow-pkm 加引导:description 显式声明"多步骤复合需求优先匹配本技能,单一原子操作走子技能",让"清理 inbox"等短指令更易命中编排层。
- P3 核心 obsidian 补 OFM 语法速查:在第 4 章末尾增"Obsidian Flavored Markdown 语法速查"小节,覆盖 wikilinks/embeds/callouts/block refs/highlight/math 全表 + 写入时高频陷阱。
- P4 workflow-pkm 补 Workflow 8 Web Clip → Permanent:单篇网页剪藏轻量流,引用 defuddle(Obsidian 团队官方 web→md 清洗工具)+ WebFetch 兜底,明确与 Workflow 7(批量文献)的边界。
- P2 新增 obsidian-canvas skill:JSON Canvas 1.0 schema 速查 + 16 hex ID 生成 + 直接 Read/Write JSON 路线(社区共识:obsidian-cli 不原生支持 .canvas 写入)+ 安全 SOP 4 选项对照表(A git stash / B .bak / C 原子写 / D File Recovery)。
Why:
- 社区呈现两条路线:kepano「按文件格式分技能」(5 技能/精)、AgriciDaniel「按方法论分技能」(15 技能/全)。我们的「按工作域分技能」(9→10 技能)取中间路线,保留跨插件感知和职责互斥声明的独特优势。
- 8 处"伴随激活"提示是隐蔽设计错误——它假设了 Claude Code 路由器看不懂的联动语义,是单次审查中最大的描述质量收益点(每技能省 ~28 字符预算给真触发词)。
- canvas 是 Obsidian 开放格式且独立于 Markdown 体系,社区标杆都作为独立 skill 维护;不加 canvas 等于把"视觉知识图"这类高频场景拱手让人。
How to apply:
- 新增 skill 时,description 末尾不要写"检测到 X 时与 Y 同步激活"——Claude Code 路由按关键词独立打分。
- 触发词列表用
触发词:A、B、C显式列;不用于:X(技能名)显式互斥;这种结构提升路由准确率。 - 涉及 vault 内格式(.canvas/.base/.md)的能力,默认独立成 skill——不要塞进核心 obsidian。
- 学习模式契机:obsidian-canvas 的"AI 大批量写入回滚策略"4 选项对照表留给团队/用户决策,AI 不强制做掉,默认采用 C+D 作为决策前兜底。
See Also:feedback_plugin_dev.md#MCP 后端工具签名变更后必须全量扫描所有 skill、kepano/obsidian-skills、AgriciDaniel/claude-obsidian
memory-lint 过期检测改用提交速度分档,弃用固定 30/90 天阈值(2026-07-10)
结论:/memory-lint Phase 5 过期检测从固定 LINT_STALE_WARN_DAYS=30 / LINT_STALE_ERROR_DAYS=90 改为「自 last_updated 以来的全仓库提交速度」分档:LINT_STALE_MIN_DAYS=7(不足 7 天跳过检测)+ 速度 ≥LINT_HIGH_VELOCITY(1.0 次/天) → ERROR,≥LINT_LOW_VELOCITY(0.3 次/天) → WARN,低于此速度不判定过期,但 LINT_STALE_ABSOLUTE_DAYS(180 天) 绝对兜底。
Why:AI 辅助开发下代码迭代速度远超传统人工节奏,高频项目 7 天内可能已发生大量架构变更,30 天固定阈值严重滞后不报警;反过来低活跃期项目(如进入维护期)超过 30 天没提交,旧记忆大概率仍准确,固定天数会误报过期。纯日历天数无法区分"高频漂移"和"低频稳定"两种情况,需要用提交速度代理"内容漂移风险"。
How to apply:新增/调整任何"距离上次更新多久算过期"的判定逻辑时,优先考虑用活跃度信号(提交频次、变更行数等)分档,而非固定日历阈值。常量集中在 memcore-shared 全局常量表单点维护,调整数值只改一处。
See Also:decisions.md#memcore-shared:路径锁定 + 全局常量的单一来源
Codex/ChatGPT 桌面应用插件骨架——三个插件共享 skills/,memcore 独立目录(2026-08-22)
结论:huanxi、huanxi-admin、obsidian 的 Codex 版本通过在同一插件目录下新增 .codex-plugin/plugin.json(与 .claude-plugin/plugin.json 并列)实现,共用同一份 skills/;memcore 因为 Codex 插件校验器要求 skills 字段必须精确指向 ./skills(不能自定义子路径),且 Codex 版记忆体系架构(AGENTS.md 会话入口 / .codex/memory 目录约定 / 无远程同步)与 Claude 版本质不同,无法共用同一份 skills/ 内容,故新建独立插件目录 plugins/memcore-codex/;marketplace.json 里对外插件名仍叫 memcore(目录名与插件名不要求一致)。memcore-codex 以本机已装的 Codex 原生 memcore 技能为底稿,抽出 memcore-shared 共享 include,并吸纳了 Claude 版四项内容:过期检测速度分档、NEED-HUMAN 稳定 ID 保活、memory-update 锚点丢失兜底、更完整的 lint_report 模板。
Why:用本机已安装的 Codex plugin-creator 技能自带的 validate_plugin.py 实测确认——skills 字段规整化后必须精确等于 "skills",mcpServers 字符串路径必须精确等于 "./.mcp.json",且都必须在插件根目录(不能嵌套进 .codex-plugin/);顶层 interface 块(displayName/shortDescription/longDescription/developerName/category/capabilities/defaultPrompt)是必填项,hooks 字段不被接受,disable-model-invocation 只能是 false 或不写。旧的 feat/codex-marketplace 分支骨架因为缺 interface 块、.mcp.json 放错位置,实际过不了这个校验(已删除该分支)。Codex 侧技能级"内部 include 不给用户直接调用"靠 agents/openai.yaml 的 policy.allow_implicit_invocation: false + description 措辞实现,不能像 Claude 侧那样用 frontmatter 禁用模型调用。
How to apply:新增/修改 Codex 插件时,先跑本机 ~/.codex/skills/.system/plugin-creator/scripts/validate_plugin.py <plugin-path> 校验再算完成;技能内容若和 Claude 版能共用就共用同一 skills/,若架构本质不同(如需要独立会话入口/目录约定)就整个插件目录独立,不要硬塞进同一 skills/。
See Also:project_overview.md#已发布插件、decisions.md#memcore-shared:路径锁定 + 全局常量的单一来源、decisions.md#memcore lint_report 增量保活:稳定 ID + resolved 跳过、decisions.md#memory-update Phase 1 锚点丢失兜底
Codex 侧 MCP Token 用 bearer_token_env_var,不用 ${VAR} 模板(2026-08-22)
结论:Codex(CLI + ChatGPT 桌面应用)没有等价于 Claude userConfig 钥匙链的插件级敏感配置机制。.mcp.json 里 HTTP 类型 MCP server 的 Bearer Token 必须用专用字段 bearer_token_env_var: "ENV_VAR_NAME"(只放变量名,不放值,由 Codex 进程启动时读取该环境变量),而不是在 headers 里写 "Authorization": "Bearer ${VAR}" 模板插值——后者不被 Codex 支持,会把 ${VAR} 字面量原样发出去导致鉴权失败。
Why:查证 Codex 官方 MCP 配置文档(config.toml / codex mcp add 场景)确认专用字段是 bearer_token_env_var / env_http_headers;且有未解决的官方 issue(openai/codex#24401)明确指出插件打包的 MCP server 目前没有官方定义的用户密钥配置路径,环境变量注入(父进程启动前已设置)是当前唯一现实可用方式。实测踩坑:改完 .mcp.json 后本机 codex plugin add 缓存的旧版本(~/.codex/plugins/cache/<marketplace>/<plugin>/<version>/)不会自动更新,必须 codex plugin marketplace upgrade <marketplace> 刷新快照后重新 codex plugin add 才会生效。
How to apply:新增/修改任何 Codex 插件的 HTTP MCP server 配置,一律用 bearer_token_env_var 字段;环境变量的设置方式区分场景——CLI 用 shell export(写进启动脚本),ChatGPT 桌面应用(图形界面启动,不继承 shell)用 macOS launchctl setenv 或 Windows 系统环境变量。改完插件内容 push 后,本机测试前要先 codex plugin marketplace upgrade <marketplace> + 重新 codex plugin add <plugin>@<marketplace>,否则读到的还是装的时候那份缓存。
See Also:decisions.md#huanxi plugin 使用 userConfig 而非环境变量传 Token(对照:Claude 侧用 userConfig 钥匙链,Codex 侧被迫用环境变量,是两个平台能力差异,不是我们设计不一致)、decisions.md#zentao-mcp 网桥已打补丁,双认证格式兼容(2026-08-25)
不做 Antigravity(Google agy / Antigravity 2.0)兼容(2026-08-22)
结论:调研后决定暂不为 Google Antigravity CLI(agy)和 Antigravity 2.0 桌面应用建插件市场骨架。
Why:官方文档(antigravity.google/docs/cli/features/)确认 agy 支持插件(skills/agents/rules/MCP/hooks 打包),但没有 marketplace 概念——无 marketplace.json、无"注册市场源"命令,只有 agy plugin install <本地路径或 git URL> 直接安装;网上搜到的"agy 支持 marketplace.json"等说法查证后均来自第三方社区工具(如 agy-plugins-cli),非 Google 官方能力。Antigravity 2.0 桌面应用官方文档完全没提插件/市场机制。该产品线是 Google I/O 2026 才发布,文档还在变动(schema 页面实测 404)。
How to apply:Claude Code 和 Codex 是当前团队实际使用的主流工具,继续投入维护;未来遇到新 AI 编程工具想接入本 marketplace 时,先确认该工具官方是否有稳定的 marketplace/plugin 协议(有市场索引格式 + 远程仓库注册命令),协议不成熟就先不投入,避免跟着一个还在剧烈变动的规范返工。
See Also:project_overview.md#已发布插件
zentao-mcp 网桥已打补丁,双认证格式兼容(2026-08-25)
结论:zentao-mcp 网桥(部署在 pm.ops.yixiong-tech.com/mcp,基于 merzzzl/openapi-mcp-server 二次开发)已确认同时兼容两种认证格式:Claude 侧 .claude-plugin/plugin.json 用的自定义 headers: {"token": "..."},以及 Codex 侧 .mcp.json 的 bearer_token_env_var 机制底层发出的标准 Authorization: Bearer <token>。
Why:新增 zentao 插件时曾担心两侧 header 格式不一致会导致 Codex 链路认证失败——Claude 侧网桥原生认的是自定义 token header,而 Codex 的 bearer_token_env_var 只会发标准 Bearer 格式,两者字面不同。用户确认网桥已就此打过补丁,双格式都认,不存在兼容问题。
How to apply:zentao 插件的 Claude/Codex 双端 Token 配置模式确认与 huanxi 一致(decisions.md#huanxi plugin 使用 userConfig 而非环境变量传 Token + decisions.md#Codex 侧 MCP Token 用 bearer_token_env_var,不用 ${VAR} 模板(2026-08-22)),无需为 zentao 网桥单独定制认证适配。未来若网桥做重大改版,需重新确认这条双格式兼容性是否还成立。
See Also:project_overview.md#已发布插件
Hermes 插件骨架:Agent Plugins v1 + packs 选装(2026-08-25)
结论:huanxi/huanxi-admin/obsidian/zentao 在插件根目录新增裸 plugin.json(Agent Plugins v1.0.0 标准,字段仅 $schema+name+可选元数据),复用同一份 skills/;memcore 因架构差异走独立目录 plugins/memcore-hermes/(记忆目录优先级 .claude/memory → .codex/memory → 新建 .agents/memory,且显式提醒 Hermes 自己按 profile 隔离的全局 ~/.hermes/memories/ 不是项目记忆后端)。分发不走 hermes plugins install owner/repo(该命令按文档examples只支持整仓库=一个插件包),改用 packs/<name>.yaml + hermes plugins pack install(pack manifest 的 subdir 字段官方支持 monorepo 定位)。
Why:调研过程中依次排除了三条路径——① hermes plugins install owner/repo 无 subdir 支持(用户质疑"仓库多插件不合理"后深挖才找到 pack 机制,此前调研不够);② 自建 plugins.index_url 覆盖官方社区索引会让用户暂时搜不到 NousResearch 官方索引里的插件,属单值配置非叠加,用户认为代价太大;③ 最终定为每插件一个 packs/<name>.yaml,互不影响、无副作用。凭证类插件不打包 mcp.json 是因为 Agent Plugins v1 规范明文禁止内嵌密钥(headers/env 都不行),Hermes 原生 ~/.hermes/config.yaml 支持 ${VAR} 插值,改为 README 手动配置指引,用户体验类比 Codex 桌面应用的 launchctl setenv 变通方案。
How to apply:Hermes 的 ref 字段要求精确 40 位 commit SHA、不接受分支名,packs/*.yaml 需要在每次相关内容发布后手动 bump(不像 Claude Code 的 git SHA 自动追新)。两处此前未经验证的点已由用户用真实 Hermes 实测确认:① pack install 支持直接传 http(s) raw 链接,不用先 clone;② subdir 定位的目录只有 plugin.json(没有原生 plugin.yaml)能被正确安装(前提是通过安全扫描,见 decisions.md#Hermes 插件安装安全扫描:plugin.json description 不能含配置路径字面量(2026-08-25))。
See Also:project_overview.md#已发布插件、decisions.md#Hermes 插件安装安全扫描:plugin.json description 不能含配置路径字面量(2026-08-25)、decisions.md#Hermes MCP Streamable HTTP session-id 已知 bug——凭证类插件连接受阻(2026-08-25)
Hermes 插件安装安全扫描:plugin.json description 不能含配置路径字面量(2026-08-25)
结论:huanxi/huanxi-admin/zentao 的 plugin.json description 字段精简为一句话简介,删除了原先写的 ~/.hermes/config.yaml 等操作指引文字;具体配置步骤只保留在 README 里。
Why:用户实测 hermes plugins pack install 被安全扫描 BLOCKED——Hermes 官方文档确认 community source(非 Nous 官方审核)插件的扫描是零容忍策略,任意 1 个 finding 就拒绝安装且 --force 无法覆盖;报错精确指向 plugin.json:5(description 字段),命中的是 CRITICAL persistence 类别。description 里字面写的 ~/.hermes/config.yaml(点前缀配置文件路径字符串)大概率撞上了扫描器针对"持久化/自我修改配置"模式的启发式规则——这类字符串常见于恶意插件描述自己如何篡改用户配置实现驻留,扫描器无法区分"教用户怎么手动配置"和"指导 AI 怎么植入后门"两种语义。
How to apply:任何 Hermes 插件(尤其面向 community source 分发的)的 plugin.json description 只写功能简介,不要出现具体文件路径(尤其 ~/. 开头的配置/凭证类路径)、shell 命令片段或操作步骤——这类内容一律放 README,不进 plugin.json。遇到 BLOCKED 报错时先看 Verdict/findings 指向的具体文件和行号,大概率能定位到触发字符串。
See Also:decisions.md#Hermes 插件骨架:Agent Plugins v1 + packs 选装(2026-08-25)
Hermes MCP Streamable HTTP session-id 已知 bug——凭证类插件连接受阻(2026-08-25)
结论:huanxi/huanxi-admin/zentao 在 Hermes 上配置 HTTP 类型 MCP Server 后,initialize 握手能成功,但后续请求会报 400 并最终 park 连接。用 curl 直连 zentao-mcp 网桥验证过网桥本身没问题(认证正常、initialize 返回 200 且带 mcp-session-id),判断是 Hermes 客户端未正确捕获/回传 Streamable HTTP 协议要求的 mcp-session-id,与 NousResearch/hermes-agent#20349 描述的现象一致。用户决定暂不深究 workaround,等 Hermes 上游修复。
Why:MCP Streamable HTTP 传输协议是有状态会话——服务器在 initialize 响应头里下发 mcp-session-id,客户端后续请求必须原样带回 Mcp-Session-Id 请求头,服务器才认下一步请求;curl 手工构造带完整 header 的请求能跑通全流程,排除了网桥端的问题。protocol: legacy/skip_preflight: true 都试过无效,因为问题出在握手之后的会话保持,不是握手协商本身。
How to apply:这不是我们插件配置能修的问题,不要在 plugin.json/pack/README 里继续折腾 MCP 相关参数试图绕过。定期检查 #20349 状态,Hermes 发布修复版本后回来验证并更新 README 里的已知问题说明;obsidian/memcore-hermes 不含 MCP,不受影响,可以正常使用。
See Also:decisions.md#Hermes 插件骨架:Agent Plugins v1 + packs 选装(2026-08-25)