ECC 插件清单约束指南:Claude Code plugin.json 验证器的未公开规则与正确姿势
本文围绕 ECC 仓库中的 PLUGIN_SCHEMA_NOTES.md 展开,系统讲解 Claude Code 插件清单(plugin.json)验证器那些未在公开 schema 文档中记载、但实际强制执行的约束:必填的 version 字段、必须为数组的组件字段、严禁添加的 agents/hooks 字段、用于 MCP 隔离的空 mcpServers 声明等。读完本文,你能够安全地修改、校验 ECC 或任何 Claude Code 插件的清单文件,避开「本地验证通过、安装时报 Invalid input」这类隐蔽故障,并理解 ECC 如何用回归测试把这些规则固化下来。
背景:一个「严格且有主见」的验证器
Claude Code 插件清单验证器的典型故障模式是:
清单文件看起来完全合理,但验证器却拒绝它,只抛出一个模糊的错误,例如
agents: Invalid input
这类问题很难排查,因为公开 schema 参考并没有完整描述验证器的全部行为。ECC 仓库把基于真实安装失败、验证器实际行为、以及和已知可用插件对比得出的约束记录在 PLUGIN_SCHEMA_NOTES.md 中,目的正是「防止静默破坏和重复回归」。该文档的定位很明确:如果你要编辑 plugin.json,先读这份笔记。
必填字段:version
version 字段是验证器的强制要求,即使某些官方示例里省略了它。缺失时,安装可能失败在 marketplace 安装阶段或 CLI 校验阶段。
{
"version": "1.1.0"
}
从仓库自身的 schema 可以进一步看出取值约束:plugin.schema.json 中 version 的 pattern 为 ^[0-9]+\.[0-9]+\.[0-9]+(?:-[0-9A-Za-z.-]+)?$,即标准 SemVer(可选带预发布后缀)。当前 ECC 的实际清单中 version 为 "2.2.1",与 marketplace.json 中声明的版本保持一致——两处版本号同步也是仓库测试所约束的。
字段形状规则:commands / skills / hooks 必须永远是数组
以下字段必须始终是数组:
commandsskillshooks(如果存在)
即使只有一个条目,字符串值也不被接受。这条规则一致地适用于所有组件路径字段。仓库的 plugin.json 遵循了这一点:
{
"name": "ecc",
"version": "2.2.1",
"mcpServers": {},
"skills": ["./skills/"],
"commands": ["./commands/"]
}
对应的回归测试位于 tests/plugin-manifest.test.js(claude plugin.json commands is an array),确保 commands 字段一旦退化为字符串就会被测试捕获。
路径解析规则
commands和skills接受目录路径,但仅当它们被包裹在数组中;- 显式文件路径最安全、也最具前瞻性(most future-proof)。
这与「避免依赖推断路径」的防反模式列表一脉相承:能用显式路径就不要让验证器去猜。
Agent 的 tools frontmatter:使用标量,而不是数组
上一条数组规则只适用于 plugin.json,不适用于 agent 的 Markdown frontmatter。Claude Code 的 agent 文件使用逗号分隔的标量来声明工具白名单:
tools: Read, Glob, Grep
不要写成 YAML 序列,例如 tools: [Read, Glob, Grep]。省略 tools 字段会让 agent 获得所有工具的访问权限,但 ECC 的 agent 都显式声明白名单,且仓库验证器要求该字段存在。
仓库中的 agent 文件与这一规则完全一致,例如 agents/code-reviewer.md:
---
name: code-reviewer
description: Expert code review specialist. ...
tools: Read, Grep, Glob, Bash
model: sonnet
---
这里 tools: Read, Grep, Glob, Bash 正是标准的逗号分隔标量写法,68 个 agent 文件均遵循同样的形状。
agents 字段:不要添加
警告:不要在
plugin.json中添加"agents"字段。Claude Code 插件验证器会完全拒绝它。
agents 不是 Claude Code 插件清单 schema 的一部分。它的任何形式——字符串路径、路径数组、目录数组——都会导致校验错误:
agents: Invalid input
agents/ 目录下的 agent .md 文件会按约定自动发现(与 hooks 的机制类似),不需要在清单中声明。
历史沿革:本仓库曾把 agents 以文件路径数组的形式显式列入 plugin.json。这种做法通过了仓库自己的 schema 校验,却通不过 Claude Code 实际验证器——因为后者根本不认识这个字段。该字段已在 PR #1459 中移除。
hooks 字段:不要添加(有回归测试强制)
警告:不要在
plugin.json中添加"hooks"字段。这一条由回归测试强制保护。
Claude Code v2.1+ 会按约定自动加载任何已安装插件的 hooks/hooks.json。如果你同时在 plugin.json 里再声明一次,就会触发重复加载错误:
Duplicate hooks file detected: ./hooks/hooks.json resolves to already-loaded file.
The standard hooks/hooks.json is loaded automatically, so manifest.hooks should
only reference additional hook files.
反复横跳的历史
这条规则在仓库中造成了多轮「修复/回滚」循环:
| Commit | 动作 | 触发原因 |
|---|---|---|
22ad036 |
添加 hooks | 用户报告 "hooks not loading" |
a7bc5f2 |
移除 hooks | 用户报告 "duplicate hooks error"(#52) |
779085e |
添加 hooks | 用户报告 "agents not loading"(#88) |
e3a1306 |
移除 hooks | 用户报告 "duplicate hooks error"(#103) |
根因:Claude Code CLI 在不同版本间改变了行为——
- v2.1 之前:需要在清单中显式声明
hooks; - v2.1 及以后:按约定自动加载,重复声明会直接报错。
当前规则:由测试固化
这个「不要再加回去」的规则被写进了两处回归测试:
- tests/hooks/hooks.test.js 中的
plugin.json does NOT have explicit hooks declaration,断言plugin.json不含hooks字段; - tests/plugin-manifest.test.js 中的同名测试,直接断言
!('hooks' in claudePlugin),并注明「Claude Code v2.1+ 按约定自动加载hooks/hooks.json」。
注意边界:如果你在添加的是额外的 hook 文件(不是 hooks/hooks.json 本身),那些可以在清单中声明;但标准的 hooks/hooks.json 绝不能声明。ECC 的 hook 入口正是 hooks/hooks.json,配合 hooks/codex-hooks.json 支持 Codex 侧的安装。
mcpServers 字段:保留显式的空对象(MCP 隔离开关)
ECC 在仓库根目录保留 .mcp.json 用于 Codex 插件安装和手工 MCP 配置,此外还有更完整的 mcp-configs/mcp-servers.json(包含 GitHub、Jira、Supabase、firecrawl 等多个 server 定义)。
但 Claude Code 也会按约定自动发现插件根目录的 .mcp.json,这会把同样的 MCP server 打包进 Claude 插件安装。因此 plugin.json 中刻意保留了这个显式空对象:
{
"mcpServers": {}
}
这个 opt-out 的作用是阻止 Claude 插件安装自动加载 ECC 根目录的 MCP 定义。为什么必须这样做?因为 Claude 插件 slug 虽然已刻意取短名(ecc),但遗留安装和严格的 provider 网关在更长的插件标识符上会生成超长 MCP 工具名并直接拒绝。例如形如 mcp__plugin_everything-claude-code_github__create_pull_request_review 的工具名超过 64 个字符,会被严格的 OpenAI 兼容网关拒收。
这一行为被 tests/plugin-manifest.test.js 精确验证:测试先断言该历史超长工具名确实超过 64 字符,再断言 mcpServers 键必须显式存在且严格等于 {}(Object.prototype.hasOwnProperty + deepStrictEqual),保证未来重构不会无意移除这个 opt-out。想要使用打包 MCP server 的用户,应手动从 .mcp.json 或 mcp-configs/mcp-servers.json 配置。
验证器行为特征小结
claude plugin validate比部分 marketplace 预览更严格;- 路径有歧义时,本地验证可能通过、安装时却失败;
- 错误信息往往很笼统(
Invalid input),不指示根因; - 跨平台安装(尤其是 Windows)对路径假设更不宽容。
一句话心态:假定验证器是敌对且字面化的(Assume the validator is hostile and literal)。
已知反模式清单
以下写法「看起来正确」但会被拒绝:
- 用字符串值代替数组;
- 以任何形式添加
"agents"—— 不是被识别的清单字段,会报Invalid input; - 缺少
version; - 依赖推断路径(inferred paths);
- 假设 marketplace 行为与本地验证一致;
- 添加
"hooks": "./hooks/hooks.json"—— 该文件已被约定自动加载,会触发重复错误; - 移除
"mcpServers": {}—— 会重新启用 Claude 插件安装的根目录.mcp.json自动发现,可能产生超长的 MCP 工具名。
原则:避免取巧,保持显式(Avoid cleverness. Be explicit.)。
最小可用示例与 ECC 实际清单对照
文档给出的「已知可用最小示例」:
{
"version": "1.1.0",
"commands": ["./commands/"],
"skills": ["./skills/"]
}
该结构已经过 Claude 插件验证器验证。注意其中既没有 "hooks" 字段,也没有 "agents" 字段——两者都按约定自动加载,显式添加任何一个都会导致错误。
ECC 实际发布的 plugin.json 在此骨架上额外携带了元数据(description、author、homepage、repository、license、keywords)和两个 userConfig 偏好项:hooks_enabled(布尔,默认 true,控制是否启用本地 hook 自动化)和 hook_profile(字符串,取 minimal/standard/strict,非法值安全回退到 standard)。tests/plugin-manifest.test.js 专门断言 userConfig 只暴露这两个 key,且字段形状固定为 type/title/description/default 四项——注释说明 Claude 的 userConfig 不支持 enum,所以 hook_profile 只能声明为 string 并在运行时做回退。
贡献者检查清单
在提交任何触碰 plugin.json 的变更之前:
- 确保所有组件字段都是数组;
- 包含
version; - 不要添加
agents或hooks字段(两者都按约定自动加载); - 除非有意改变 Claude 插件的 MCP 打包行为,否则保留
"mcpServers": {}; - 运行验证:
claude plugin validate .claude-plugin/plugin.json
如有疑问,宁要冗长、不要方便(choose verbosity over convenience)。
为什么这份文档值得长期维护
ECC 是一个被广泛 fork、并常被当作参考实现的仓库,把验证器的「怪癖」文档化可以:阻止问题反复出现、降低贡献者的挫败感、并在生态演进时保住插件的稳定性。文档的最后一句规则值得记住:如果验证器本身变了,先更新这份文档——它和 tests/plugin-manifest.test.js、tests/hooks/hooks.test.js 中的断言一起,构成了「文档 + 测试」双层防线,防止上述任何一条约束在后续版本中被无意破坏。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0622
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00