# How to package and publish a ChatGPT plugin

> Package skills and an MCP server as a plugin with plugin.json, test it from a local marketplace, then upload, pass review and publish it to the directory.

Published: 2026-10-04 · By: Divy Chaurasia · Level: advanced · Time: 60 min · Tags: plugins, publishing, submission

A public plugin is published once and listed in the single directory that ChatGPT and Codex share. Publishing means packaging your skills and MCP server into a ZIP, uploading it to the OpenAI Platform, clearing automated checks, passing human review, and then choosing when to go live.

This guide assumes you already have a working MCP server, skills, or both. If not, start with [Build your first ChatGPT app](/tutorials/build-your-first-chatgpt-app) or [Write a skill for ChatGPT and Codex](/tutorials/write-a-skill-for-chatgpt-and-codex).

## Before you start

- **Verified identity.** Complete individual or business verification in the Platform's organization settings. You can only publish under a verified name; anything else is rejected.
- **Permissions.** Organization owners can submit. Other members need the **Apps Management Write** permission, which an owner grants through organization roles.
- **A public HTTPS endpoint** for any MCP server, typically ending in `/mcp`. Tunnels (ngrok, Secure MCP Tunnel) are for testing only and are not accepted.
- **A project with global data residency.** Projects with EU data residency can't currently submit plugins that include an MCP server.
- **Policy pages.** For an MCP plugin: a website, support page, privacy policy and terms of service, all on HTTPS.
- **The rules.** Read OpenAI's [plugin guidelines](https://developers.openai.com/plugins/plugin-guidelines) before you build the listing. Most rejections trace back to them.

## 1. Choose the shape

| Shape | Use when |
| --- | --- |
| Skills only | Instructions and the model's existing tools are enough. |
| MCP server only | You need tools, but no extra workflow guidance. |
| Skills and MCP server | Skills should guide the model through your tools. |
| MCP server with UI | A visual component clearly helps part of the workflow. |

Start with the smallest shape that covers your use cases. Note one current limit: you can't add an MCP server to a plugin that was first submitted as skills-only, so include the server in your first ZIP if you'll ever need it.

## 2. Lay out the package

New plugins should use the portable Agent Plugins layout:

```text
acme-notes/
├── plugin.json
├── mcp.json
├── skills/
│   └── get-started/
│       └── SKILL.md
└── assets/
    ├── icon.png
    └── logo.png
```

Keep `plugin.json`, `mcp.json`, `skills/` and `assets/` at the plugin root. Every path in the manifest is relative to that root and starts with `./`.

The older Codex layout (`.codex-plugin/plugin.json` with `.mcp.json`) is still supported as a fallback, and it's what every plugin in the `openai/plugins` repository uses. Look at the Figma and Notion plugins there for complete real examples.

> **plugin:** [Figma](https://chatgptplugins.directory/plugins/figma): Figma design-to-code workflows
## 3. Write plugin.json

Portable identity goes at the root. Everything OpenAI-specific (listing text, icons, review material) goes under `extensions.com.openai`:

```json
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "acme-notes",
  "version": "1.0.0",
  "description": "Search and summarize notes from your Acme workspace.",
  "author": { "name": "Acme", "email": "support@example.com", "url": "https://example.com" },
  "homepage": "https://example.com/notes",
  "license": "MIT",
  "extensions": {
    "com.openai": {
      "interface": {
        "displayName": "Acme Notes",
        "shortDescription": "Find and summarize your notes",
        "longDescription": "Search notes in your Acme workspace and summarize decisions with links to the source. Only accesses notes available to your connected account.",
        "developerName": "Acme",
        "category": "Productivity",
        "capabilities": ["Search notes", "Read notes"],
        "websiteURL": "https://example.com/notes",
        "supportURL": "https://example.com/support",
        "privacyPolicyURL": "https://example.com/privacy",
        "termsOfServiceURL": "https://example.com/terms",
        "defaultPrompt": ["Find my notes about the launch."],
        "composerIcon": "./assets/icon.png",
        "logo": "./assets/logo.png"
      },
      "onboardingSkill": "./skills/get-started/SKILL.md",
      "publication": {
        "release_notes": "Initial release with note search and summaries."
      }
    }
  }
}
```

Constraints that trip people up:

- `name` is kebab-case and stable. Hosts use it as the plugin ID and namespace. Max 64 characters.
- `version` must be semver (`1.0.0`).
- `displayName` and `shortDescription` are at most 30 characters each; `longDescription` at most 4,000.
- `defaultPrompt`: up to three starter prompts, 128 characters each, with no `@` mentions.
- Icons must be square, at least 48×48 pixels, and PNG, JPEG, WebP or SVG up to 5 MiB.
- Don't put credentials or reviewer instructions in the package. Fields named `test_credentials` and `reviewer_instructions` are rejected.

## 4. Add the MCP server and skills

Declare the remote server in `mcp.json`. The portable format requires a transport `type`, so don't just rename an old `.mcp.json`:

```json
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/mcp.schema.json",
  "mcpServers": {
    "notes": {
      "type": "streamable-http",
      "url": "https://mcp.example.com/mcp"
    }
  }
}
```

A package can declare several MCP servers, but only one can be connected per plugin in the submission portal.

Each skill lives in `skills/<name>/SKILL.md` with `name` and `description` in its YAML front matter. Portable packages discover skills from `skills/` automatically, so the manifest doesn't need a `skills` field.

> **Warning:** ZIPs that contain app references (an `apps` field or `.app.json`) or lifecycle hooks can't currently be submitted. Those are fine for local and workspace plugins, but for public submission, declare the server URL in `mcp.json`.

### Scaffolding with plugin-creator

The built-in `$plugin-creator` skill in Codex (or `@plugin-creator` in a ChatGPT Work chat) can scaffold a plugin and a local marketplace entry for testing. It generates the older `.codex-plugin/plugin.json` layout, and it can wire in an MCP server you registered in developer mode by its `plugin_asdk_app...` ID. That wiring uses `.app.json`, so convert to `mcp.json` before a public submission.

## 5. Test it from a local marketplace

Install and test the complete plugin before uploading. Create `$REPO_ROOT/.agents/plugins/marketplace.json` (or `~/.agents/plugins/marketplace.json` for a personal one):

```json
{
  "name": "local-repo",
  "interface": { "displayName": "Local plugins" },
  "plugins": [
    {
      "name": "acme-notes",
      "source": { "source": "local", "path": "./plugins/acme-notes" },
      "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" },
      "category": "Productivity"
    }
  ]
}
```

`source.path` is relative to the marketplace root, not to `.agents/plugins/`. Restart the ChatGPT desktop app, pick your marketplace in the Plugin Directory, install the plugin, and run your test prompts in a new chat. After each change, restart the app so it picks up the new files.

Check that each skill triggers on the right requests and stays quiet on the wrong ones, that skills call the right tools, and that sign-in works after install. The Plugin Eval plugin can help benchmark skills locally.

> **plugin:** [Plugin Eval](https://chatgptplugins.directory/plugins/plugin-eval): Start from chat, then evaluate or benchmark locally
## 6. Upload the ZIP

Zip the plugin folder. The archive must contain exactly one plugin root, either at the top level or in a single top-level directory with no sibling files:

```bash
zip -r acme-notes.zip acme-notes -x '*.DS_Store'
```

Then:

1. Open **Plugins** on the OpenAI Platform (`platform.openai.com/plugins`) and select **Upload new or existing plugin**.
2. Choose your verified **Developer identity**. The directory shows this name.
3. Select **Upload plugin** and choose the ZIP. If validation fails, fix the reported errors and upload again.

## 7. Clear the automated checks

The plugin page has two areas:

- **Metadata & Skills** shows findings for the uploaded package. Use **Copy issues**, fix the package, and select **Upload plugin to fix issues**. Required skill scans must finish before you can submit.
- **MCPs** is where you connect the server. Select it, choose **Connect**, confirm the URL and auth, and complete domain verification. To verify, serve the exact challenge token as plain text (not JSON) at `https://<your-host>/.well-known/openai-apps-challenge`. Wait for the tool scan, then read **Issues**, fix them on your server, and select **Rescan**.

## 8. Submit for review

Plugins with an MCP server need review material. You can include most of it in `plugin.json` under `extensions.com.openai.review`, or fill it in under **Review information** > **Review details**:

- **Exactly five positive test cases**: prompt, expected tools, expected result.
- **Exactly three negative test cases**: prompts where the plugin should decline or ask for clarification.
- **A video walkthrough** URL showing the test cases.
- **Release notes.**
- **Reviewer credentials**, entered in the dashboard only: a demo account with sample data that works immediately, without MFA, email or SMS codes, or private network access.

Then select the draft, choose **Submit for review**, and complete the policy attestations. Track progress under **Review status**; feedback arrives by email. Only one review can be active per plugin. To appeal a rejection, reply to the rejection email.

## 9. Publish and keep it current

Approval doesn't publish anything. Open the approved version and select **Publish plugin**.

After that:

- **Server changes** are picked up automatically. OpenAI scans your MCP server daily, and eligible tool updates go live once they pass checks. Select **Rescan** to check sooner. New tools stay unavailable until approved, so keep your server compatible with the currently approved schemas.
- **Metadata or skill changes** need a new ZIP with a bumped version, which goes through checks and review again.
- **Changing the MCP server URL** isn't supported in the update flow; contact support.

Published plugins are found by direct link or by searching their exact name. Placement on the directory's main pages is chosen by OpenAI and can't be requested.

## What gets rejected

From OpenAI's guidelines and review FAQ, the common reasons are:

- Reviewers can't connect, or the test credentials need MFA or have expired.
- Test cases don't produce the expected results on every supported surface.
- Tool results return data your privacy policy doesn't disclose, or include debug fields such as session, trace or request IDs.
- `readOnlyHint`, `destructiveHint` or `openWorldHint` don't match what a tool does. Set all three explicitly on every tool.
- Vague tool descriptions, or descriptions that steer the model away from other plugins.
- Generic names, or names with "MCP", "MCP Server" or "Plugin" appended.
- Demo or trial plugins, ads, or selling digital goods and subscriptions.
- Unofficial connectors to someone else's service.
- Collecting restricted data such as payment card details, health records, government IDs or passwords.

> **plugin:** [Notion](https://chatgptplugins.directory/plugins/notion): Notion workflows for specs, research, meetings, and knowledge capture
## Next steps

- Debug review failures with [Test and debug an MCP server](/tutorials/test-mcp-server-with-inspector).
- Distribute privately to a team through a marketplace instead; see [Use plugins and MCP servers in Codex](/tutorials/use-plugins-in-codex).
- Browse published examples in the [plugins directory](/plugins).

## Listings in this tutorial

- [Figma](https://chatgptplugins.directory/plugins/figma.md): Figma design-to-code workflows
- [Notion](https://chatgptplugins.directory/plugins/notion.md): Notion workflows for specs, research, meetings, and knowledge capture
- [Plugin Eval](https://chatgptplugins.directory/plugins/plugin-eval.md): Start from chat, then evaluate or benchmark locally

---
Source: https://chatgptplugins.directory/tutorials/publish-a-chatgpt-plugin · Index: https://chatgptplugins.directory/llms.txt · Independent and community-run. Not affiliated with or endorsed by OpenAI. ChatGPT is a trademark of OpenAI.
