Package the plugin
On this page
Lesson rewritten November 3, 2026 based on the Package your plugin page in the OpenAI Plugins documentation. The previous lesson described building a “package” for a non-existent “ChatGPT Store” using a missing manifest. A plugin is packaged with a
plugin.jsonmanifest following the Agent Plugins schema. Source: Package your plugin.
Why package
Once you’ve written your skills and, if needed, built the MCP server, the final step is assembling them into an installable unit. Packaging gives your plugin a stable identity and tells ChatGPT and Codex which skills, MCP server connections, and resources belong together. The UI and authentication logic remain inside the MCP server; the manifest simply links that integration to the rest of the bundle.
Structure of a portable package
A portable plugin has a plugin.json manifest at its root, declaring the Agent Plugins schema. The folder can also contain a skills/ directory, an mcp.json file for MCP servers distributed with the plugin, and visual assets in assets/.
mon-plugin/
├── plugin.json
├── mcp.json
├── skills/
│ └── compte-rendu/
│ └── SKILL.md
└── assets/
├── icon.png
└── logo.png
The minimal manifest fits in just a few lines. The name, stable and in kebab-case, serves as both the identifier and the namespace for components; skills are discovered automatically in skills/ without needing explicit declaration.
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "notes-equipe",
"version": "0.1.0",
"description": "Team notes and meeting minutes"
}
Each skill lives in skills/<name>/SKILL.md, with a header providing its name and description, followed by instructions:
---
name: compte-rendu
description: Drafts meeting minutes from team notes.
---
Retrieve meeting notes using list_notes, group them by topic,
then draft a summary: decisions, action items with owners, and open points.
Declaring the MCP server
MCP servers distributed with the plugin are declared in mcp.json at the root, using the corresponding schema and specifying a transport type for each server:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
"mcpServers": {
"notes": {
"type": "streamable-http",
"url": "https://notes.example.com/mcp"
}
}
}
Server authentication is declared in this same file, under extensions["com.openai"].auth for each server—it belongs to the server, not the manifest. For public submission, you submit the remote HTTPS endpoint; local servers follow a separate path reserved for desktop plugins.
OpenAI-specific metadata
Directory presentation, mappings to already-registered MCP servers, and hooks are declared in the extensions.com.openai object of the root plugin.json, while the portable identity (name, version, description) remains at the root:
{
"$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
"name": "notes-equipe",
"version": "0.1.0",
"description": "Team notes and meeting minutes",
"extensions": {
"com.openai": {
"interface": {
"displayName": "Team Notes",
"shortDescription": "Notes and meeting minutes",
"developerName": "Your Team",
"category": "Productivity",
"capabilities": ["Read", "Write"],
"websiteURL": "https://example.com",
"privacyPolicyURL": "https://example.com/privacy",
"termsOfServiceURL": "https://example.com/terms",
"defaultPrompt": ["Use Team Notes to draft Monday's meeting minutes."],
"composerIcon": "./assets/icon.png",
"logo": "./assets/logo.png"
}
}
}
}
Paths in this object are relative to the plugin root and start with ./. Visual assets (composerIcon, logo, screenshots) should preferably go in ./assets/. The apps field is used only to point to a .app.json file containing mappings to MCP servers already registered in ChatGPT.
You can also declare an onboarding skill via extensions.com.openai.onboardingSkill (for example, ./skills/setup/SKILL.md): it guides the user after installation to choose a workspace or set preferences. Test it in a new conversation and during an in-progress installation.
An older format, with a manifest in .codex-plugin/plugin.json, is still accepted for compatibility: it is used only if the extensions.com.openai object is absent, as the two do not merge. New packages should use the root plugin.json.
The fast track: @plugin-creator
ChatGPT and Codex include a skill, @plugin-creator, that generates the structure for you—currently in the compatibility .codex-plugin/ format. To attach an MCP server to it, first connect that server in ChatGPT (Add custom MCP server, then Create as a plugin), note the technical connection ID from the browser URL—it starts with plugin_asdk_app—and provide it to @plugin-creator in Work mode (or to $plugin-creator in Codex). The skill can also create a personal marketplace entry, allowing you to install and test the plugin from your local directory source.
Local marketplaces
A marketplace is a JSON catalog of plugins. It serves for writing, testing, and team distribution alongside the public directory: in Work mode or in Codex, each marketplace appears as a selectable source in the plugin directory. This is how you will test the complete plugin before submitting it (Lesson 9).
Hooks: desktop only
Lifecycle hooks are supported only for plugins manually installed in the Codex desktop app, and a plugin containing them is not eligible for the public directory. If your plugin targets publication, leave them out.
Key takeaways
- A portable package:
plugin.json(Agent Plugins schema),skills/,mcp.json,assets/ - The MCP server is declared in
mcp.jsonwith its transport type; authentication goes there too - Presentation, mappings, and the onboarding skill go in
extensions.com.openai @plugin-creatorgenerates the structure; a local marketplace is for testing- A plugin with hooks is not eligible for the public directory