本文作者:meng shao(@shao__meng)。版权归作者所有,未经授权禁止转载。


Agent Plugins 是由 OpenAI、AWS、Cursor、GitHub、VS Code 和 Vercel 等厂商共同推动的开放标准,把 Agent Skills 和 MCP 服务器配置打包成一次构建、多个 AI Agent 客户端间通用的可移植插件。

https://agent-plugins.org/

# 核心模型:一个目录就是一个插件

插件不是压缩包,也不是注册表里的条目,是一个普通的文件系统目录。规范明确解释了为什么:目录可以用 ls、cat、git 等标准工具检查,开发时可以原地编辑,天然兼容版本控制。

my-plugin/

├── plugin.json # 必需:清单文件

├── skills/ # 可选:Agent Skills

│ └── summarize/

│ ├── SKILL.md

│ ├── scripts/

│ └── references/

├── mcp.json # 可选:MCP 服务器配置

└── com.example.client/ # 可选:客户端扩展目录

三个关键约束:

1. 固定位置发现:skills/ 和 mcp.json 的位置不可配置,plugin.json 不能覆盖它们,也不能内联组件配置。这消除了"发现间接层"——每个客户端不需要实现一套配置解析逻辑去猜组件在哪。

2. 路径收容(containment):包内任何文件解析后(包括符号链接、junction 等)必须落在插件根目录内。以 ../ 逃逸根目录的路径会被拒绝。这是规范中安全相关最强的一条。

3. v1 只有两种组件:Skills 和 MCP 服务器。规范明确说明 commands、hooks、agents、rules、LSP 等组件类型"太客户端特有,尚未形成稳定的可移植契约",因此被排除在 v1 之外。

# 清单文件 plugin.json

· 采用封闭 schema:只允许 $ schema、name、version、description、author、homepage、repository、license、keywords、extensions 这十个顶层字段。

· 必填的只有 $ schema(指向规范版本的规范 schema URL)和 name。

· 命名约束:1–64 字符,仅小写字母、数字、连字符和点,首尾必须是字母数字,不允许连续 -- 或 ..。

· 错误处理分级很讲究:未知顶层字段是非致命的(报告并忽略,继续加载),其他 schema 违规是致命的(拒绝整个插件)。封闭 schema 的好处是支持拼写检查和 IDE 自动补全,同时未知字段不致命保证了向前兼容。

· 客户端加载插件时不得联网拉取 schema——$schema 只是用来选择本地已支持的验证规则的标识符。这一条避免了加载时的网络依赖和供应链风险。

# 两种组件的处理方式

Skills:规范不重定义 Skills 格式,直接引用外部的 Agent Skills 规范。Agent Plugins 只规定发现位置(skills/ 的直接子目录中含 SKILL.md 者即为一个 Skill,不递归深层搜索)和失败隔离(单个 Skill 无效只跳过该 Skill)。

MCP 服务器:mcp.json 定义了一个封闭的传输类型联合:

· stdio:command | 本地子进程;可选 args、env、cwd

· streamable-http:url | 当前主流远程传输;可选字面量 headers

· sse:url | 已废弃的 HTTP+SSE 旧传输,客户端可选支持

# PLUGIN_ROOT 与 PLUGIN_DATA

规范定义了两个由客户端注入的环境变量,这是运行时模型的核心:

· PLUGIN_ROOT:插件根目录的绝对路径,用于引用包内自带的脚本、二进制、配置文件。

· PLUGIN_DATA:客户端管理的、该插件实例专属的可写持久目录,跨插件更新保留内容,用于存放安装的依赖、缓存、生成的代码等。

${PLUGIN_ROOT} 和 ${PLUGIN_DATA} 占位符只在 args、env 值、cwd 中展开,展开是单次、非递归的纯文本替换。env 中不允许出现名为 PLUGIN_ROOT/PLUGIN_DATA 的条目(客户端最后设置,插件无法覆盖)。

# 客户端扩展机制

可移植核心刻意保持最小,客户端的私有能力通过反向域名命名空间(如 com.example.client)挂载:

· 清单数据放在 plugin.json 的 extensions 字段下,按命名空间隔离;

· 文件放在与命名空间同名的顶层目录下;

· 客户端对自己不实现的命名空间直接忽略且不校验其内容。

反向域名避免了维护一个中心化的客户端名称注册表。这个机制和 Java 包名、Android 应用 ID 的思路一致,成熟且去中心化。

# 失败隔离

规范对错误处理的设计相当精细,贯彻"最窄失败边界"原则:

· plugin.json 逃逸根目录 → 拒绝整个插件;

· 某个组件类型的固定位置类型不对 → 只禁用该组件类型;

· 某个 SKILL.md 不合规 → 只跳过该 Skill;

· 某个 MCP 服务器条目无效或连接失败 → 只跳过该条目;其他组件继续正常加载。

设计决策部分给出的理由很务实:一个同时提供 Skill 和 MCP 服务器的插件,不应因为一台服务器不可用就整体报废。