如果你已经写过 Codex skill,很快就会遇到一个问题:这个 workflow 只在自己机器上好用,怎么稳定地给团队复用?
一开始,把`SKILL.md`放在本机 skill 目录就够了。但当你的能力开始包含更多东西时,单独一个 skill 就不够清晰了:
* 你有一组固定的 MCP server 配置。
* 你想把生命周期 hook 一起发给团队。
* 你想挂一个 app integration。
* 你希望用户能在 Codex 的`/plugins`里看到、安装、启用、更新。
* 你不想让每个人手动拷贝零散文件。
这时就该把它做成**Codex plugin**。
本文整理 Codex plugin 的核心模型、目录结构、开发流程和一个完整实战案例。重点不是罗列字段,而是把插件到底解决什么问题、每个文件负责什么、什么时候该用 skill / MCP / hook / app 讲清楚。
## 一句话结论
Codex plugin 不是一个新的执行引擎。它更像一个**可安装的能力包**。
它可以把这些东西打在一起:
* `skills/`:可复用工作流和领域操作规约。
* `.mcp.json`:MCP server 配置,让 Codex 多一组工具。
* `hooks.json`或`hooks/hooks.json`:生命周期脚本,比如拦截危险命令、记录审计、停止时做检查。
* `.app.json`:App integrations 的声明入口。
* `assets/`:图标、截图、品牌展示素材。
* `.codex-plugin/plugin.json`:插件 manifest,声明这个包叫什么、版本是多少、包含哪些组件。
再通过`marketplace.json`把插件放进一个可浏览、可安装、可更新的 catalog。
可以这样记:
`skill 解决“怎么做”MCP 解决“能调用什么工具”hook 解决“什么时候插入规则或自动动作”app 解决“连接哪个外部应用”plugin 解决“怎么把这些能力打包、安装、分发”marketplace 解决“Codex 从哪里发现这些插件”`
## 什么时候只写 Skill,什么时候做 Plugin
官方文档里的判断很实用:如果还在一个 repo 或个人 workflow 里迭代,先写 local skill;当你要跨团队共享、打包 MCP config、打包 app integration、打包 lifecycle hooks,或者发布一个稳定能力包时,再做 plugin。
换成工程判断:
| 场景 | 推荐形态 |
| — | — |
| 只是自己常用的一段操作规约 | local skill |
| 一个 repo 内部的工程约定 | repo-local skill |
| 需要给多人安装 | plugin |
| skill 还依赖 MCP server | plugin |
| skill 还依赖 hooks | plugin |
| 需要在`/plugins`里可见、可安装、可更新 | plugin + marketplace |
| 需要发布一组插件 | curated marketplace |
不要过早做 plugin。plugin 的价值在于分发和组合,不在于让一个还没稳定的 prompt 看起来更正式。
## 插件目录结构
一个完整插件可以长这样:
`repo-pr-guardian/├── .codex-plugin/│ └── plugin.json├── skills/│ └── pr-guardian/│ └── SKILL.md├── hooks/│ ├── hooks.json│ └── scripts/│ └── block-dangerous-git.py├── assets/│ ├── icon.png│ └── screenshot1.png├── .mcp.json└── .app.json`
最小插件只需要:
`my-first-plugin/├── .codex-plugin/│ └── plugin.json└── skills/ └── hello/ └── SKILL.md`
对应的`plugin.json`:
`{ “name”: “my-first-plugin”, “version”: “1.0.0”, “description”: “Reusable greeting workflow”, “skills”: “./skills/”}`
这个最小结构已经是一个插件。后面的 MCP、hook、app 都是增强能力,不是必须项。
## `plugin.json`负责什么
`.codex-plugin/plugin.json`是插件 manifest。它回答四个问题:
* 这个插件是谁:`name`、`version`、`description`、`author`。
* 这个插件包含什么:`skills`、`hooks`、`mcpServers`、`apps`。
* 这个插件如何展示:`interface.displayName`、`shortDescription`、`brandColor`、`logo`、`screenshots`。
* 用户怎么信任和理解它:`homepage`、`repository`、`license`、`privacyPolicyURL`、`termsOfServiceURL`。
一个较完整的形态:
`{ “name”: “repo-pr-guardian”,”version”: “1.0.0”,”description”: “Review pull requests with repo policy, safe git hooks, and optional MCP tools.”,”author”: { “name”: “Your Team”, “email”: “team@example.com”, “url”: “example-dot-com” },”homepage”: “example-dot-com/repo-pr-guardian”,”repository”: “github-dot-com/example/repo-pr-guardian”,”license”: “MIT”,”keywords”: [“codex”, “review”, “pull-request”],”skills”: “./skills/”,”hooks”: “./hooks/hooks.json”,”mcpServers”: “./.mcp.json”,”apps”: “./.app.json”,”interface”: { “displayName”: “Repo PR Guardian”, “shortDescription”: “Review PRs with repo policy and safety checks.”, “longDescription”: “A Codex plugin that packages PR review workflow guidance, optional MCP tools, and lifecycle hooks for safer repository work.”, “developerName”: “Your Team”, “category”: “Productivity”, “capabilities”: [“Review”, “MCP”, “Hooks”], “defaultPrompt”: [ “Review this PR against repo policy.”, “Check risky git commands before running.”, “Summarize review findings for maintainers.” ], “brandColor”: “#2563EB”, “composerIcon”: “./assets/icon.png”, “logo”: “./assets/icon.png”, “screenshots”: [“./assets/screenshot1.png”] }}`
几个细节要记住:
* `name`用稳定的 kebab-case,Codex 会把它当插件标识和组件 namespace。
* 路径建议用`./`开头,并保持在 plugin root 内。
* `skills`、`hooks`、`mcpServers`、`apps`是组件入口,不是展示字段。
* `interface`是给插件目录和安装页面看的,不参与 workflow 本身。
## `skills/`:插件的操作大脑
Skill 是插件里最重要也最容易复用的部分。
一个`SKILL.md`通常长这样:
`—name: pr-guardiandescription: Review pull requests against repository policy, test expectations, and risk boundaries.—Use this skill when the user asks for PR review, merge readiness, or risk assessment.Start by reading the diff and relevant tests. Report findings first, ordered by severity.Prefer concrete file and line references. Do not rewrite unrelated code.If the PR touches authentication, secrets, deployment, or data deletion paths,include an explicit risk section.`
Skill 的价值不是“多一段 prompt”,而是把一个稳定流程固定下来:
* 什么时候使用。
* 先读什么。
* 输出格式是什么。
* 不能做什么。
* 哪些风险必须显式检查。
如果你的 plugin 只能保留一个部分,优先保留 skill。
## `.mcp.json`:把工具能力打进插件
MCP server 让 Codex 能调用外部工具。`.mcp.json`是插件内 MCP 配置的入口。
示例:
`{ “mcpServers”: { “repo-policy”: { “command”: “node”, “args”: [“./server/index.js”], “env”: { “REPO_POLICY_MODE”: “strict” } } }}`
这类配置适合:
* 把内部检索工具接给 Codex。
* 把工单、CI、仓库、文档系统接给 Codex。
* 给 skill 提供一组稳定工具,而不是让用户自己手动配 MCP。
但 MCP 有一个现实边界:它经常涉及认证、网络、二进制依赖或本地服务。插件可以打包配置,不等于用户安装后立刻能用。你需要在 README 或 skill 里写清楚:
* 需要哪些环境变量。
* 需要先启动哪个本地服务。
* 认证是在安装时还是首次使用时完成。
* 失败时如何自检。
## `hooks`:把生命周期规则打进插件
Hooks 是插入 Codex Agent loop 的脚本。它能做很多治理类工作:
* 在`PreToolUse`拦截危险命令。
* 在`PostToolUse`检查命令输出。
* 在`UserPromptSubmit`做 prompt 审计。
* 在`Stop`做收尾检查或生成摘要。
* 在`SessionStart`加载项目上下文。
插件可以自带 hooks,但这里有两个关键限制:
* 当前 release 里,plugin hooks 默认关闭,需要`[features].plugin_hooks = true`。
* 插件 hooks 是 non-managed hooks,需要用户 review / trust 后才会运行。
插件内默认 hook 路径是:
`hooks/hooks.json`
你也可以在`plugin.json`里覆盖:
`{ “name”: “repo-pr-guardian”, “hooks”: “./hooks/hooks.json”}`
一个`hooks/hooks.json`示例:
`{ “hooks”: { “PreToolUse”: [ { “matcher”: “Bash”, “hooks”: [ { “type”: “command”, “command”: “python3 “$PLUGIN_ROOT/hooks/scripts/block-dangerous-git.py””, “timeout”: 10, “statusMessage”: “Checking git command” } ] } ] }}`
插件 hook 命令会拿到几个非常有用的环境变量:
* `PLUGIN_ROOT`:当前已安装插件根目录。
* `PLUGIN_DATA`:插件可写数据目录。
* `CLAUDE_PLUGIN_ROOT`、`CLAUDE_PLUGIN_DATA`:兼容已有插件 hooks。
这意味着 hook 脚本不要假设自己运行在固定路径里。用`PLUGIN_ROOT`找随插件分发的脚本和配置,用`PLUGIN_DATA`放运行时产生的数据。
## `.app.json`:声明 App Integration
`.app.json`是插件内 app integrations 的 manifest 入口。它不是 skill,也不是 MCP server。
可以把它理解成:
`.app.json 负责“这个插件带哪些外部应用连接”.mcp.json 负责“这个插件带哪些工具服务”SKILL.md 负责“Codex 应该怎么使用这些能力”`
本地 scaffold 里的最小占位通常是:
`{ “apps”: {}}`
什么时候需要`.app.json`?
* 你的插件要打包一个外部 app connector。
* 你希望用户安装插件时看到 app integration 的连接入口。
* 这个能力不是简单的 MCP stdio server,而是 Codex app 层面的集成。
如果你的插件只是 skill + MCP + hooks,通常可以先不写`.app.json`。
## `marketplace.json`:让 Codex 发现插件
插件本身只是一个目录。Codex 要发现它,需要 marketplace。
Repo 级 marketplace:
`$REPO_ROOT/.agents/plugins/marketplace.json`
个人级 marketplace:
`~/.agents/plugins/marketplace.json`
示例:
`{ “name”: “team-codex-plugins”,”interface”: { “displayName”: “Team Codex Plugins” },”plugins”: [ { “name”: “repo-pr-guardian”, “source”: { “source”: “local”, “path”: “./plugins/repo-pr-guardian” }, “policy”: { “installation”: “AVAILABLE”, “authentication”: “ON_INSTALL” }, “category”: “Productivity” } ]}`
注意两个路径规则:
* `source.path`指向插件目录。
* `source.path`是相对 marketplace root 解析的,不是相对`.agents/plugins/`目录。
维护 marketplace 时,一个 catalog 可以先只有一个插件,后面再逐步扩成团队插件集合。
CLI 也可以添加 marketplace:
`codex plugin marketplace add owner/repocodex plugin marketplace add owner/repo –ref maincodex plugin marketplace add git@example-host:example/plugins.git –sparse .agents/pluginscodex plugin marketplace add ./local-marketplace-root`
刷新和移除:
`codex plugin marketplace upgradecodex plugin marketplace upgrade marketplace-namecodex plugin marketplace remove marketplace-name`
## 推荐开发流程
一个稳妥的开发流程是:
1. 先写 local skill,把 workflow 跑顺。
2. 把 skill 放进插件目录`skills/
3. 写`.codex-plugin/plugin.json`,先只声明`skills`。
4. 如需工具,再补`.mcp.json`。
5. 如需治理,再补`hooks/hooks.json`和 hook scripts。
6. 如需 app integration,再补`.app.json`。
7. 写 repo-local 或 personal`marketplace.json`。
8. 重启 Codex,在`/plugins`安装并启用。
9. 新开线程实测 skill 是否能被触发,MCP 是否可用,hooks 是否需要 trust。
10. 发布前检查 manifest、版本、路径、secret、安装说明。
如果使用本机的`$plugin-creator`scaffold,命令形态大概是:
`python3 ~/.codex/skills/.system/plugin-creator/scripts/create_basic_plugin.py repo-pr-guardian –path ./plugins –with-skills –with-hooks –with-mcp –with-apps –with-marketplace`
这个 scaffold 会创建`.codex-plugin/plugin.json`,并可选生成`skills/`、`hooks/`、`.mcp.json`、`.app.json`、`assets/`和 marketplace entry。生成后要把`[TODO: …]`全部替换掉。
## 实战案例:Repo PR Guardian
下面做一个实战插件:`repo-pr-guardian`。
目标是把团队的 PR review 工作流打成插件。它包含:
* 一个`pr-guardian`skill:规范 PR review 输出。
* 一个 MCP 配置占位:未来接仓库策略服务。
* 一个`PreToolUse`hook:拦截明显危险的 git 命令。
* 一个 marketplace entry:让团队成员能在 Codex`/plugins`里安装。
### 目录结构
`plugins/repo-pr-guardian/├── .codex-plugin/│ └── plugin.json├── skills/│ └── pr-guardian/│ └── SKILL.md├── hooks/│ ├── hooks.json│ └── scripts/│ └── block-dangerous-git.py└── .mcp.json.agents/plugins/└── marketplace.json`
### `plugin.json`
`{ “name”: “repo-pr-guardian”,”version”: “1.0.0”,”description”: “Review pull requests with repo policy and safe git command checks.”,”author”: { “name”: “Example Engineering”, “email”: “eng@example.com”, “url”: “example-dot-com” },”homepage”: “example-dot-com/repo-pr-guardian”,”repository”: “github-dot-com/example/repo-pr-guardian”,”license”: “MIT”,”keywords”: [“codex”, “review”, “pull-request”, “hooks”],”skills”: “./skills/”,”hooks”: “./hooks/hooks.json”,”mcpServers”: “./.mcp.json”,”interface”: { “displayName”: “Repo PR Guardian”, “shortDescription”: “Review PRs with repo policy and safe git checks.”, “longDescription”: “Packages a PR review workflow, optional repository policy MCP tools, and a lifecycle hook that blocks risky git commands.”, “developerName”: “Example Engineering”, “category”: “Productivity”, “capabilities”: [“Review”, “Hooks”, “MCP”], “defaultPrompt”: [ “Review this PR against repo policy.”, “Check this change for merge risk.”, “Summarize blocker findings for maintainers.” ], “brandColor”: “#2563EB” }}`
### Skill
`skills/pr-guardian/SKILL.md`:
`—name: pr-guardiandescription: Review pull requests against repository policy, test expectations, and risk boundaries.—Use this skill when the user asks for PR review, merge readiness, or risk assessment.Review behavior:- Read the diff before drawing conclusions.- Prioritize bugs, regressions, missing tests, and operational risks.- Report findings first, ordered by severity.- Use concrete file and line references when available.- Keep summaries short and secondary.- Do not rewrite unrelated code during review.Escalate risk when changes touch authentication, secrets, deletion, deployment,billing, permissions, or data migration paths.`
### MCP 配置
`.mcp.json`:
`{ “mcpServers”: { “repo-policy”: { “command”: “node”, “args”: [“./tools/repo-policy-mcp/index.js”], “env”: { “REPO_POLICY_MODE”: “strict” } } }}`
这里的 MCP server 是示例占位。真实项目里,`command`可以换成你的本地二进制、Node server、Python server 或远程 MCP 配置。发布前要确认依赖是否随插件打包,还是要求用户单独安装。
### Hook 配置
`hooks/hooks.json`:
`{ “hooks”: { “PreToolUse”: [ { “matcher”: “Bash”, “hooks”: [ { “type”: “command”, “command”: “python3 “$PLUGIN_ROOT/hooks/scripts/block-dangerous-git.py””, “timeout”: 10, “statusMessage”: “Checking git command” } ] } ] }}`
`hooks/scripts/block-dangerous-git.py`:
`#!/usr/bin/env python3import jsonimport reimport syspayload = json.load(sys.stdin)tool_input = payload.get(“tool_input”) or {}command = tool_input.get(“command”) or””blocked = [ r”bgits+resets+–hardb”, r”bgits+cleans+-fdxb”, r”bgits+pushs+–forceb”,]if any(re.search(pattern, command) for pattern in blocked): print(json.dumps({ “hookSpecificOutput”: { “hookEventName”: “PreToolUse”, “permissionDecision”: “deny”, “permissionDecisionReason”: “Dangerous git command blocked by repo-pr-guardian.” } }))`
这个 hook 只做一件事:在 Bash 工具调用前检查命令文本,发现明显高风险 git 命令就拒绝。
要让插件 hooks 生效,用户的 Codex 配置里还需要:
`[features]plugin_hooks = true`
安装后第一次运行时,还需要在`/hooks`里 review / trust。
### Marketplace
`.agents/plugins/marketplace.json`:
`{ “name”: “team-codex-plugins”,”interface”: { “displayName”: “Team Codex Plugins” },”plugins”: [ { “name”: “repo-pr-guardian”, “source”: { “source”: “local”, “path”: “./plugins/repo-pr-guardian” }, “policy”: { “installation”: “AVAILABLE”, “authentication”: “ON_INSTALL” }, “category”: “Productivity” } ]}`
然后重启 Codex,打开:
`/plugins`
选择`Team Codex Plugins`,安装`Repo PR Guardian`。
### 验证方式
安装后做三类验证:
1. Skill 验证
`Use pr-guardian to review the current diff.`
或者:
`@repo-pr-guardian review this PR against repo policy.`
观察 Codex 是否按 review-first 格式输出 findings。
1. Hook 验证
开启`plugin_hooks`并 trust hook 后,尝试让 Codex 运行:
`git reset –hard HEAD`
期望行为是 hook 返回 deny,Codex 不执行该命令。
1. MCP 验证
如果 MCP server 已经实现,确认 Codex 能看到对应工具,并在 skill 触发时合理使用。
如果 MCP 只是占位,发布前不要把它伪装成可用能力。要么删掉`.mcp.json`,要么在插件说明里明确“需要单独安装 repo-policy MCP server”。
## 发布前检查清单
发布或给团队安装前,至少检查这些点:
| 检查项 | 原因 |
| — | — |
| `plugin.json`的`name`与 marketplace entry 一致 | 避免安装和更新时识别混乱 |
| `version`已更新 | 方便用户判断是否拿到新版本 |
| 所有路径以`./`开头并留在 plugin root 内 | 避免路径解析和安全问题 |
| 没有提交真实`.env`、token、cookie | 插件会分发给别人 |
| `.env.example`或 README 写清需要的变量 | 降低安装成本 |
| hook 脚本使用`PLUGIN_ROOT`/`PLUGIN_DATA` | 避免安装路径变化后失效 |
| hooks 文档说明需要`plugin_hooks = true`和`/hooks`trust | 避免用户以为安装后自动生效 |
| MCP 依赖写清楚 | 避免安装后工具不可用 |
| marketplace`policy.installation`和`policy.authentication`合理 | 影响安装和认证体验 |
| 本地重启 Codex 后在`/plugins`里验证 | 插件发现依赖重启 |
## 常见误区
### 误区 1:把 Plugin 当成 Skill 的新名字
Skill 是工作流,plugin 是分发包。一个 plugin 可以只有 skill,但 plugin 不等于 skill。
### 误区 2:以为 MCP 打进插件就一定开箱可用
`.mcp.json`只是配置入口。真实可用还取决于命令、依赖、网络、认证和环境变量。
### 误区 3:以为插件 hook 默认会跑
当前 release 里,plugin hooks 默认关闭。需要`[features].plugin_hooks = true`,还要 trust。
### 误区 4:把`.app.json`和`.mcp.json`混在一起
MCP 是工具服务配置,app 是应用集成声明。只做 MCP 工具接入时,不一定需要`.app.json`。
### 误区 5:一个插件一个 marketplace
不需要。marketplace 是 catalog,一个 marketplace 可以只有一个插件,也可以是团队插件集合。
## 最后的心智模型
做 Codex plugin 时,按这个顺序想:
`我要复用一个 workflow -> 写 skillworkflow 需要工具 -> 加 .mcp.jsonworkflow 需要生命周期治理 -> 加 hooks/hooks.json 和脚本workflow 需要外部 app integration -> 加 .app.json我要让别人安装 -> 写 .codex-plugin/plugin.json我要让 Codex 发现它 -> 写 marketplace.json`
如果你只想记一句话:
> Codex plugin 的核心价值,不是把 prompt 包起来,而是把一套可复用的工作方法、工具接入、生命周期规则和安装入口,变成一个可以被团队稳定安装和更新的能力包





