plugins/build.md +598 −0 added
1# Build plugins
2
3This page is for plugin authors. If you want to browse, install, and use
4plugins in Codex, see [Plugins](https://developers.openai.com/codex/plugins). If you are still iterating on
5one repo or one personal workflow, start with a local skill. Build a plugin
6when you want to share that workflow across teams, bundle app integrations or
7MCP config, package lifecycle hooks, or publish a stable package.
8
9## Create a plugin with `$plugin-creator`
10
11For the fastest setup, use the built-in `$plugin-creator` skill.
12
13<CodexScreenshot
14 alt="plugin-creator skill in Codex"
15 lightSrc="/images/codex/plugins/plugin-creator.png"
16 darkSrc="/images/codex/plugins/plugin-creator-dark.png"
17/>
18
19It scaffolds the required `.codex-plugin/plugin.json` manifest and can also
20generate a local marketplace entry for testing. If you already have a plugin
21folder, you can still use `$plugin-creator` to wire it into a local
22marketplace.
23
24<CodexScreenshot
25 alt="how to invoke the plugin-creator skill"
26 lightSrc="/images/codex/plugins/plugin-creator-invoke.png"
27 darkSrc="/images/codex/plugins/plugin-creator-invoke-dark.png"
28/>
29
30### Build your own curated plugin list
31
32A marketplace is a JSON catalog of plugins. `$plugin-creator` can generate one
33for a single plugin, and you can keep adding entries to that same marketplace
34to build your own curated list for a repo, team, or personal workflow.
35
36In Codex, each marketplace appears as a selectable source in the plugin
37directory. Use `$REPO_ROOT/.agents/plugins/marketplace.json` for a repo-scoped
38list or `~/.agents/plugins/marketplace.json` for a personal list. Add one
39entry per plugin under `plugins[]`, point each `source.path` at the plugin
40folder with a `./`-prefixed path relative to the marketplace root, and set
41`interface.displayName` to the label you want Codex to show in the marketplace
42picker. Then restart Codex. After that, open the plugin directory, choose your
43marketplace, and browse or install the plugins in that curated list.
44
45You don't need a separate marketplace per plugin. One marketplace can expose a
46single plugin while you are testing, then grow into a larger curated catalog as
47you add more plugins.
48
49<CodexScreenshot
50 alt="custom local marketplace in the plugin directory"
51 lightSrc="/images/codex/plugins/codex-local-plugin-light.png"
52 darkSrc="/images/codex/plugins/codex-local-plugin.png"
53/>
54
55### Add a marketplace from the CLI
56
57Use `codex plugin marketplace add` when you want Codex to install and track a
58marketplace source for you instead of editing `config.toml` by hand.
59
60```bash
61codex plugin marketplace add owner/repo
62codex plugin marketplace add owner/repo --ref main
63codex plugin marketplace add https://github.com/example/plugins.git --sparse .agents/plugins
64codex plugin marketplace add ./local-marketplace-root
65```
66
67Marketplace sources can be GitHub shorthand (`owner/repo` or
68`owner/repo@ref`), HTTP or HTTPS Git URLs, SSH Git URLs, or local marketplace root
69directories. Use `--ref` to pin a Git ref, and repeat `--sparse PATH` to use a
70sparse checkout for Git-backed marketplace repos. `--sparse` is valid only for
71Git marketplace sources.
72
73To refresh or remove configured marketplaces:
74
75```bash
76codex plugin marketplace upgrade
77codex plugin marketplace upgrade marketplace-name
78codex plugin marketplace remove marketplace-name
79```
80
81### Create a plugin manually
82
83Start with a minimal plugin that packages one skill.
84
851. Create a plugin folder with a manifest at `.codex-plugin/plugin.json`.
86
87```bash
88mkdir -p my-first-plugin/.codex-plugin
89```
90
91`my-first-plugin/.codex-plugin/plugin.json`
92
93```json
94{
95 "name": "my-first-plugin",
96 "version": "1.0.0",
97 "description": "Reusable greeting workflow",
98 "skills": "./skills/"
99}
100```
101
102Use a stable plugin `name` in kebab-case. Codex uses it as the plugin
103identifier and component namespace.
104
1052. Add a skill under `skills/<skill-name>/SKILL.md`.
106
107```bash
108mkdir -p my-first-plugin/skills/hello
109```
110
111`my-first-plugin/skills/hello/SKILL.md`
112
113```md
114---
115name: hello
116description: Greet the user with a friendly message.
117---
118
119Greet the user warmly and ask how you can help.
120```
121
1223. Add the plugin to a marketplace. Use `$plugin-creator` to generate one, or
123 follow [Build your own curated plugin list](#build-your-own-curated-plugin-list)
124 to wire the plugin into Codex manually.
125
126From there, you can add MCP config, app integrations, or marketplace metadata
127as needed.
128
129### Install a local plugin manually
130
131Use a repo marketplace or a personal marketplace, depending on who should be
132able to access the plugin or curated list.
133
134<Tabs
135 id="codex-plugins-local-install"
136 param="install-scope"
137 defaultTab="workspace"
138 tabs={[
139 {
140 id: "workspace",
141 label: "Repo",
142 },
143 {
144 id: "global",
145 label: "Personal",
146 },
147 ]}
148>
149 <div slot="workspace">
150 Add a marketplace file at `$REPO_ROOT/.agents/plugins/marketplace.json`
151 and store your plugins under `$REPO_ROOT/plugins/`.
152
153 **Repo marketplace example**
154
155 Step 1: Copy the plugin folder into `$REPO_ROOT/plugins/my-plugin`.
156
157```bash
158mkdir -p ./plugins
159cp -R /absolute/path/to/my-plugin ./plugins/my-plugin
160```
161
162 Step 2: Add or update `$REPO_ROOT/.agents/plugins/marketplace.json` so
163 that `source.path` points to that plugin directory with a `./`-prefixed
164 relative path:
165
166```json
167{
168 "name": "local-repo",
169 "plugins": [
170 {
171 "name": "my-plugin",
172 "source": {
173 "source": "local",
174 "path": "./plugins/my-plugin"
175 },
176 "policy": {
177 "installation": "AVAILABLE",
178 "authentication": "ON_INSTALL"
179 },
180 "category": "Productivity"
181 }
182 ]
183}
184```
185
186 Step 3: Restart Codex and verify that the plugin appears.
187
188 </div>
189
190 <div slot="global">
191 Add a marketplace file at `~/.agents/plugins/marketplace.json` and store
192 your plugins under `~/.codex/plugins/`.
193
194 **Personal marketplace example**
195
196 Step 1: Copy the plugin folder into `~/.codex/plugins/my-plugin`.
197
198```bash
199mkdir -p ~/.codex/plugins
200cp -R /absolute/path/to/my-plugin ~/.codex/plugins/my-plugin
201```
202
203 Step 2: Add or update `~/.agents/plugins/marketplace.json` so that the
204 plugin entry's `source.path` points to that directory.
205
206 Step 3: Restart Codex and verify that the plugin appears.
207
208 </div>
209</Tabs>
210
211The marketplace file points to the plugin location, so those directories are
212examples rather than fixed requirements. Codex resolves `source.path` relative
213to the marketplace root, not relative to the `.agents/plugins/` folder. See
214[Marketplace metadata](#marketplace-metadata) for the file format.
215
216After you change the plugin, update the plugin directory that your marketplace
217entry points to and restart Codex so the local install picks up the new files.
218
219### Marketplace metadata
220
221If you maintain a repo marketplace, define it in
222`$REPO_ROOT/.agents/plugins/marketplace.json`. For a personal marketplace, use
223`~/.agents/plugins/marketplace.json`. A marketplace file controls plugin
224ordering and install policies in Codex-facing catalogs. It can represent one
225plugin while you are testing or a curated list of plugins that you want Codex
226to show together under one marketplace name. Before you add a plugin to a
227marketplace, make sure its `version`, publisher metadata, and install-surface
228copy are ready for other developers to see.
229
230```json
231{
232 "name": "local-example-plugins",
233 "interface": {
234 "displayName": "Local Example Plugins"
235 },
236 "plugins": [
237 {
238 "name": "my-plugin",
239 "source": {
240 "source": "local",
241 "path": "./plugins/my-plugin"
242 },
243 "policy": {
244 "installation": "AVAILABLE",
245 "authentication": "ON_INSTALL"
246 },
247 "category": "Productivity"
248 },
249 {
250 "name": "research-helper",
251 "source": {
252 "source": "local",
253 "path": "./plugins/research-helper"
254 },
255 "policy": {
256 "installation": "AVAILABLE",
257 "authentication": "ON_INSTALL"
258 },
259 "category": "Productivity"
260 }
261 ]
262}
263```
264
265- Use top-level `name` to identify the marketplace.
266- Use `interface.displayName` for the marketplace title shown in Codex.
267- Add one object per plugin under `plugins` to build a curated list that Codex
268 shows under that marketplace title.
269- Point each plugin entry's `source.path` at the plugin directory you want
270 Codex to load. For repo installs, that often lives under `./plugins/`. For
271 personal installs, a common pattern is `./.codex/plugins/<plugin-name>`.
272- Keep `source.path` relative to the marketplace root, start it with `./`, and
273 keep it inside that root.
274- For local entries, `source` can also be a plain string path such as
275 `"./plugins/my-plugin"`.
276- Always include `policy.installation`, `policy.authentication`, and
277 `category` on each plugin entry.
278- Use `policy.installation` values such as `AVAILABLE`,
279 `INSTALLED_BY_DEFAULT`, or `NOT_AVAILABLE`.
280- Use `policy.authentication` to decide whether auth happens on install or
281 first use.
282
283The marketplace controls where Codex loads the plugin from. A local
284`source.path` can point somewhere else if your plugin lives outside those
285example directories. A marketplace file can live in the repo where you are
286developing the plugin or in a separate marketplace repo, and one marketplace
287file can point to one plugin or many.
288
289Marketplace entries can also point at Git-backed plugin sources. Use
290`"source": "url"` when the plugin lives at the repository root, or
291`"source": "git-subdir"` when the plugin lives in a subdirectory:
292
293```json
294{
295 "name": "remote-helper",
296 "source": {
297 "source": "git-subdir",
298 "url": "https://github.com/example/codex-plugins.git",
299 "path": "./plugins/remote-helper",
300 "ref": "main"
301 },
302 "policy": {
303 "installation": "AVAILABLE",
304 "authentication": "ON_INSTALL"
305 },
306 "category": "Productivity"
307}
308```
309
310Git-backed entries may use `ref` or `sha` selectors. If Codex can't resolve a
311marketplace entry's source, it skips that plugin entry instead of failing the
312whole marketplace.
313
314### How Codex uses marketplaces
315
316A plugin marketplace is a JSON catalog of plugins that Codex can read and
317install.
318
319Codex can read marketplace files from:
320
321- the curated marketplace that powers the official Plugin Directory
322- a repo marketplace at `$REPO_ROOT/.agents/plugins/marketplace.json`
323- a legacy-compatible marketplace at `$REPO_ROOT/.claude-plugin/marketplace.json`
324- a personal marketplace at `~/.agents/plugins/marketplace.json`
325
326You can install any plugin exposed through a marketplace. Codex installs
327plugins into
328`~/.codex/plugins/cache/$MARKETPLACE_NAME/$PLUGIN_NAME/$VERSION/`. For local
329plugins, `$VERSION` is `local`, and Codex loads the installed copy from that
330cache path rather than directly from the marketplace entry.
331
332You can enable or disable each plugin individually. Codex stores each plugin's
333on or off state in `~/.codex/config.toml`.
334
335## Package and distribute plugins
336
337### Plugin structure
338
339Every plugin has a manifest at `.codex-plugin/plugin.json`. It can also include
340a `skills/` directory, a `hooks/` directory for lifecycle hooks, an `.app.json`
341file that points at one or more apps or connectors, an `.mcp.json` file that
342configures MCP servers, and assets used to present the plugin across supported
343surfaces.
344
345<FileTree
346 class="mt-4"
347 tree={[
348 {
349 name: "my-plugin/",
350 open: true,
351 children: [
352 {
353 name: ".codex-plugin/",
354 open: true,
355 children: [
356 {
357 name: "plugin.json",
358 comment: "Required: plugin manifest",
359 },
360 ],
361 },
362 {
363 name: "skills/",
364 open: true,
365 children: [
366 {
367 name: "my-skill/",
368 open: true,
369 children: [
370 {
371 name: "SKILL.md",
372 comment: "Optional: skill instructions",
373 },
374 ],
375 },
376 ],
377 },
378 {
379 name: "hooks/",
380 open: true,
381 children: [
382 {
383 name: "hooks.json",
384 comment: "Optional: lifecycle hooks",
385 },
386 ],
387 },
388 {
389 name: ".app.json",
390 comment: "Optional: app or connector mappings",
391 },
392 {
393 name: ".mcp.json",
394 comment: "Optional: MCP server configuration",
395 },
396 {
397 name: "assets/",
398 comment: "Optional: icons, logos, screenshots",
399 },
400 ],
401 },
402 ]}
403/>
404
405Only `plugin.json` belongs in `.codex-plugin/`. Keep `skills/`, `hooks/`,
406`assets/`, `.mcp.json`, and `.app.json` at the plugin root.
407
408Published plugins typically use a richer manifest than the minimal example that
409appears in quick-start scaffolds. The manifest has three jobs:
410
411- Identify the plugin.
412- Point to bundled components such as skills, apps, MCP servers, or hooks.
413- Provide install-surface metadata such as descriptions, icons, and legal
414 links.
415
416Here's a complete manifest example:
417
418```json
419{
420 "name": "my-plugin",
421 "version": "0.1.0",
422 "description": "Bundle reusable skills and app integrations.",
423 "author": {
424 "name": "Your team",
425 "email": "team@example.com",
426 "url": "https://example.com"
427 },
428 "homepage": "https://example.com/plugins/my-plugin",
429 "repository": "https://github.com/example/my-plugin",
430 "license": "MIT",
431 "keywords": ["research", "crm"],
432 "skills": "./skills/",
433 "mcpServers": "./.mcp.json",
434 "apps": "./.app.json",
435 "hooks": "./hooks/hooks.json",
436 "interface": {
437 "displayName": "My Plugin",
438 "shortDescription": "Reusable skills and apps",
439 "longDescription": "Distribute skills and app integrations together.",
440 "developerName": "Your team",
441 "category": "Productivity",
442 "capabilities": ["Read", "Write"],
443 "websiteURL": "https://example.com",
444 "privacyPolicyURL": "https://example.com/privacy",
445 "termsOfServiceURL": "https://example.com/terms",
446 "defaultPrompt": [
447 "Use My Plugin to summarize new CRM notes.",
448 "Use My Plugin to triage new customer follow-ups."
449 ],
450 "brandColor": "#10A37F",
451 "composerIcon": "./assets/icon.png",
452 "logo": "./assets/logo.png",
453 "screenshots": ["./assets/screenshot-1.png"]
454 }
455}
456```
457
458`.codex-plugin/plugin.json` is the required entry point. The other manifest
459fields are optional, but published plugins commonly use them.
460
461### Manifest fields
462
463Use the top-level fields to define package metadata and point to bundled
464components:
465
466- `name`, `version`, and `description` identify the plugin.
467- `author`, `homepage`, `repository`, `license`, and `keywords` provide
468 publisher and discovery metadata.
469- `skills`, `mcpServers`, `apps`, and `hooks` point to bundled components
470 relative to the plugin root.
471- `interface` controls how install surfaces present the plugin.
472
473Use the `interface` object for install-surface metadata:
474
475- `displayName`, `shortDescription`, and `longDescription` control the title
476 and descriptive copy.
477- `developerName`, `category`, and `capabilities` add publisher and capability
478 metadata.
479- `websiteURL`, `privacyPolicyURL`, and `termsOfServiceURL` provide external
480 links.
481- `defaultPrompt`, `brandColor`, `composerIcon`, `logo`, and `screenshots`
482 control starter prompts and visual presentation.
483
484### Path rules
485
486- Keep manifest paths relative to the plugin root and start them with `./`.
487- Store visual assets such as `composerIcon`, `logo`, and `screenshots` under
488 `./assets/` when possible.
489- Use `skills` for bundled skill folders, `apps` for `.app.json`,
490 `mcpServers` for `.mcp.json`, and `hooks` for lifecycle hooks.
491- Plugin hooks are off by default in this release; bundled hooks won't run
492 unless `[features].plugin_hooks = true`.
493- When plugin hooks are enabled, omit `hooks` to use the default
494 `./hooks/hooks.json` file when present.
495
496### Bundled MCP servers and lifecycle hooks
497
498`mcpServers` can point to an `.mcp.json` file that contains either a direct
499server map or a wrapped `mcp_servers` object.
500
501Direct server map:
502
503```json
504{
505 "docs": {
506 "command": "docs-mcp",
507 "args": ["--stdio"]
508 }
509}
510```
511
512Wrapped server map:
513
514```json
515{
516 "mcp_servers": {
517 "docs": {
518 "command": "docs-mcp",
519 "args": ["--stdio"]
520 }
521 }
522}
523```
524
525After installation, users can enable or disable a bundled MCP server and tune
526tool approval policy from their Codex config without editing the plugin. Use
527`plugins.<plugin>.mcp_servers.<server>` for plugin-scoped MCP server policy:
528
529```toml
530[plugins."my-plugin".mcp_servers.docs]
531enabled = true
532default_tools_approval_mode = "prompt"
533enabled_tools = ["search"]
534
535[plugins."my-plugin".mcp_servers.docs.tools.search]
536approval_mode = "approve"
537```
538
539Plugin hooks are off by default in this release. When
540`[features].plugin_hooks = true` and your plugin is enabled, Codex can load
541lifecycle hooks from your plugin alongside user, project, and managed hooks.
542
543```toml
544[features]
545plugin_hooks = true
546```
547
548The default plugin hook file is `hooks/hooks.json`:
549
550```json
551{
552 "hooks": {
553 "SessionStart": [
554 {
555 "hooks": [
556 {
557 "type": "command",
558 "command": "python3 ${PLUGIN_ROOT}/hooks/session_start.py",
559 "statusMessage": "Loading plugin context"
560 }
561 ]
562 }
563 ]
564 }
565}
566```
567
568If you define `hooks` in `.codex-plugin/plugin.json`, Codex uses that manifest
569entry instead of the default `hooks/hooks.json`. The manifest field can be a
570single path, an array of paths, an inline hooks object, or an array of inline
571hooks objects.
572
573```json
574{
575 "name": "repo-policy",
576 "hooks": ["./hooks/session.json", "./hooks/tools.json"]
577}
578```
579
580Hook paths follow the same manifest path rules as `skills`, `apps`, and
581`mcpServers`: start with `./`, resolve relative to the plugin root, and stay
582inside the plugin root.
583
584Plugin hook commands receive the Codex-specific environment variables
585`PLUGIN_ROOT` and `PLUGIN_DATA`. `PLUGIN_ROOT` points to the installed plugin
586root, and `PLUGIN_DATA` points to the plugin's writable data directory. Codex
587also sets `CLAUDE_PLUGIN_ROOT` and `CLAUDE_PLUGIN_DATA` for compatibility with
588existing plugin hooks.
589
590Plugin hooks use the same event schema as regular hooks. See
591[Hooks](https://developers.openai.com/codex/hooks) for supported events, inputs, outputs, trust review, and
592current limitations.
593
594### Publish official public plugins
595
596Adding plugins to the official Plugin Directory is coming soon.
597
598Self-serve plugin publishing and management are coming soon.