Codex Plugin Packaging skill for Codex

Ship skills and MCP servers as one install. Codex assembles finished skills and MCP servers into a plugin with a portable plugin.json, skills folder and mcp.json, declares server authentication without shipping secrets, and lists the plugin in a repo or personal marketplace. It installs the result on a throwaway CODEX_HOME with codex plugin commands, checks every skill and server loads, then prepares versioning and workspace or public publishing.

  • Codex Mastery
  • $codex-plugin-packaging
  • 218 lines
  • 2 files
  • ~2,150 tokens
  • MIT
  • Updated

Install

curl -fsSL codexskills.dev/install.sh \  | sh -s -- codex-plugin-packaging

Installs to ~/.agents/skills/codex-plugin-packaging/ for every project on this machine.

104-line POSIX script. No sudo, no telemetry. Read install.sh

Use it in Codex

Start a prompt with $codex-plugin-packaging, or pick it from /skills. You can also just describe the task: Codex reads the description below and loads the playbook when your request matches.

What Codex reads before loading it

Packages Codex skills, bundled MCP servers, registered connector mappings and optional lifecycle hooks into a plugin with a portable plugin.json, skills/ and mcp.json (or the .codex-plugin compatibility layout from $plugin-creator), exposes it through a repo or personal marketplace.json, installs and tests it with codex plugin and codex plugin marketplace commands, and prepares it for workspace or public publishing. Use when bundling several skills or a skill plus an MCP server for other people, building a team plugin catalog, or debugging a plugin that does not appear or load.

Use it when

  • Distributing several skills, or a skill plus MCP server, to other people
  • Building a curated plugin catalog for a repo or team
  • Converting a $plugin-creator scaffold to the portable layout
  • A plugin fails to appear, install or load its servers

Use something else when

How to check the result

Install from the marketplace into a fresh CODEX_HOME with codex plugin add, then confirm /plugins, /skills and /mcp list every bundled component and each skill runs on a sample prompt.

The mistake it prevents

Renaming a compatibility .mcp.json to mcp.json without adding the transport type and schema the portable format requires.

~/.agents/skills/codex-plugin-packaging/SKILL.md

--- name: codex-plugin-packaging description: Packages Codex skills, bundled MCP servers, registered connector mappings and optional lifecycle hooks into a plugin with a portable plugin.json,… license: MIT ---

A plugin is ready when it has a stable kebab-case name and version, its skills and MCP servers sit at the fixed portable paths, a marketplace entry installs it on a clean machine, its servers authenticate without secrets in the package, and every bundled skill triggers and works after install.

When a plugin is the right unit

NeedUse
One workflow for yourself or one repoA plain skill in .agents/skills/ or ~/.agents/skills/
Several related skills, versioned, for other teamsPlugin
A skill that depends on an MCP server or connectorPlugin bundling both
A catalog of plugins for a team or repoMarketplace file listing several plugins
Workspace-wide availability by rolePublish the plugin to the ChatGPT workspace (admin)
Public listing in the shared ChatGPT and Codex directorySubmission portal review
acme-support/
├── plugin.json           # identity + optional extensions.com.openai
├── skills/
│   ├── triage-ticket/SKILL.md
│   └── draft-reply/SKILL.md
├── mcp.json              # bundled MCP servers (optional)
├── hooks/hooks.json      # lifecycle hooks (optional, desktop manual installs only)
└── assets/               # icons, logo, screenshots
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "acme-support",
  "version": "0.3.0",
  "description": "Triage Acme support tickets and draft replies.",
  "author": { "name": "Acme Platform Team", "email": "[email protected]" },
  "license": "MIT",
  "keywords": ["support", "tickets"],
  "extensions": {
    "com.openai": {
      "interface": {
        "displayName": "Acme Support",
        "shortDescription": "Ticket triage and reply drafting",
        "developerName": "Acme Platform Team",
        "category": "Productivity",
        "capabilities": ["Read", "Write"],
        "defaultPrompt": ["Use Acme Support to triage the newest open tickets."],
        "composerIcon": "./assets/icon.png",
        "logo": "./assets/logo.png"
      },
      "onboardingSkill": "./skills/setup/SKILL.md"
    }
  }
}

Skills are discovered from root skills/; the manifest needs no skills field. Paths inside extensions.com.openai start with ./, resolve from the plugin root, and stay inside it. When extensions.com.openai is present it replaces any .codex-plugin/plugin.json overlay entirely; the two are not merged. Only reference an onboardingSkill that exists in the package.

mcp.json uses its own schema and a transport type per server:

{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
  "mcpServers": {
    "tickets": {
      "type": "streamable-http",
      "url": "https://mcp.acme.example/mcp",
      "extensions": { "com.openai": { "auth": { "type": "oauth", "client": { "mode": "dcr" } } } }
    },
    "local-index": {
      "type": "stdio",
      "command": "node",
      "args": ["./server/index.js"],
      "cwd": "${PLUGIN_ROOT}"
    }
  }
}

Auth types: none, oauth (client mode provided with clientId, dcr, or cimd), mixed, api_key with headerScheme. Never put a client secret or token in the package; the manifest has no clientSecret field and secrets are entered during connection setup. Public submission needs a remote HTTPS MCP endpoint; local stdio servers make a plugin desktop-only.

Compatibility layout ($plugin-creator)

$plugin-creator in Codex (or @plugin-creator in ChatGPT Work) scaffolds .codex-plugin/plugin.json plus optional .mcp.json, .app.json (registered connector mappings, IDs starting plugin_asdk_app), skills/, hooks/, scripts/, assets/, and can create a personal marketplace entry for testing. It is supported; to make it portable, move to root plugin.json and mcp.json with their schemas. Do not just rename .mcp.json: portable servers need a type.

Workflow

- [ ] 1. Finish and lint each skill on its own first
- [ ] 2. Create the plugin folder and manifest
- [ ] 3. Add MCP servers with auth declared, no secrets
- [ ] 4. Add a marketplace entry
- [ ] 5. Install on a clean profile and test every skill and server
- [ ] 6. Version, then publish to the intended audience

4. Marketplace

Repo catalog at $REPO_ROOT/.agents/plugins/marketplace.json, personal catalog at ~/.agents/plugins/marketplace.json:

{
  "name": "acme-internal",
  "interface": { "displayName": "Acme Internal Plugins" },
  "plugins": [
    {
      "name": "acme-support",
      "source": { "source": "local", "path": "./plugins/acme-support" },
      "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" },
      "category": "Productivity"
    },
    {
      "name": "acme-oncall",
      "source": { "source": "git-subdir", "url": "https://github.com/acme/codex-plugins.git", "path": "./plugins/oncall", "ref": "v1.2.0" },
      "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" },
      "category": "Productivity"
    }
  ]
}
  • source.path is relative to the marketplace root (the repo root for a repo catalog), starts with ./, and stays inside that root.
  • Every entry needs policy.installation (AVAILABLE, INSTALLED_BY_DEFAULT, NOT_AVAILABLE), policy.authentication, and category.
  • Other sources: "source": "url" for a plugin at a Git repo root, "source": "npm" with package and optional version/registry (downloaded without lifecycle scripts).
  • Entries Codex cannot resolve are skipped, not fatal; check the list after changes.

5. Install and test

codex plugin marketplace add ./                          # or owner/repo, owner/repo@ref, a Git URL
codex plugin marketplace list
codex plugin list --marketplace acme-internal --json --available
codex plugin add acme-support@acme-internal --json
codex plugin remove acme-support@acme-internal
codex plugin marketplace upgrade acme-internal          # refresh Git-backed catalogs

Installed copies live under ~/.codex/plugins/cache/<marketplace>/<plugin>/<version>/ (local for local sources); edit the source, then reinstall or restart so the cache refreshes. In the TUI, /plugins shows installed plugins and their tools, /skills should list the bundled skills, and /mcp the bundled servers. The ChatGPT desktop app reads the same marketplace files after a restart.

Per-repo enablement and server policy go in config, not the package:

[plugins."acme-support@acme-internal"]
enabled = true

[plugins."acme-support@acme-internal".mcp_servers.tickets]
default_tools_approval_mode = "prompt"
enabled_tools = ["get_ticket", "search_tickets"]

Test on a clean CODEX_HOME so leftovers from development do not hide missing files:

export CODEX_HOME="$(mktemp -d)"     # throwaway home; run codex login inside it
codex plugin marketplace add ./
codex plugin add acme-support@acme-internal
codex                                 # check /plugins, /skills, /mcp
unset CODEX_HOME

6. Publish

  • Bump version for every release; Git-backed entries can pin ref or sha.
  • Workspace: a workspace admin publishes from ChatGPT Plugins > Personal > Publish and chooses roles. This does not list it publicly.
  • Public directory: submit through the plugin submission portal. Plugins with lifecycle hooks are not eligible.

Gotchas

  • Plugin hooks run only for plugins installed manually in the desktop app, are skipped until the user reviews and trusts them, and receive PLUGIN_ROOT and PLUGIN_DATA. Hook scripts must exist where Codex executes; a web install does not deploy them.
  • An explicit hooks value in the manifest replaces default hooks/hooks.json discovery.
  • Skill names inside a plugin are namespaced by the plugin name, but keep them distinct from popular standalone skills anyway to avoid confusing implicit matching.
  • Project .codex/config.toml plugin settings apply only in trusted projects.
  • Marketplace refresh can install files for configured plugins even when enabled = false; disabling is not uninstalling.
  • Review third-party plugins like dependencies: skills are instructions, servers are code.

Output contract

**Plugin:** acme-support 0.3.0 (portable layout; 2 skills, 1 HTTP MCP server with DCR OAuth)
**Marketplace:** .agents/plugins/marketplace.json (acme-internal)
**Clean install test:** codex plugin add ok; /skills lists 2; /mcp shows tickets connected after login
**Publish:** workspace roles Support, Platform (pending admin)

What the zip contains

SKILL.md plus the interface file below. Nothing runs on install.

agents/openai.yamlgenerated from this page

Gives the Codex app a display name, a one-line description and a starter prompt for this skill.

interface:
  display_name: "Codex Plugin Packaging"
  short_description: "Ship skills and MCP servers as one install"
  default_prompt: "$codex-plugin-packaging Package our three support skills and the tickets MCP server as a Codex plugin"

About this skill

Written and maintained by the Codex Skills editors to the same editorial standard as every skill here: a selective description, a real workflow, known pitfalls and a way to verify the work. First published , last updated . MIT licensed. Something wrong or out of date? Tell us.

esc
Just launched

Codex Skills Pro is live

79 premium skills for Codex, each with scripts, references and an evidence report that proves the result.

Bought one by one$882.21
All 79 skills$49
94% off

That's $0.62 a skill, paid once.

Get all 79 for $49Or pick single skills from $4.99 →

Secure checkout by Creem