> ## Documentation Index
> Fetch the complete documentation index at: https://docs.enconvo.ai/llms.txt
> Use this file to discover all available pages before exploring further.

# Plugin Store Guidelines

> What Enconvo's reviewers check before a plugin appears in the store, and the review materials to send with it.

Enconvo reviews every version submitted to the plugin store before anyone else can install it. These guidelines apply to every kind of plugin the store takes: Enconvo, Codex, Claude Code and portable plugins, including plugins made only of skills. They follow [OpenAI's plugin guidelines](https://developers.openai.com/apps-sdk/app-submission-guidelines), so a plugin that meets them is also close to ready for OpenAI's plugin portal.

Before you submit, run:

```sh theme={null}
npx -p @enconvo/api enconvo plugin validate            # the store's rules
npx -p @enconvo/api enconvo plugin validate --openai   # also OpenAI's portal rules
npx -p @enconvo/api enconvo plugin scan                # starts the MCP server and checks its tools
```

`validate` catches what can be checked automatically, and `scan` checks the tools of a plugin with an MCP server; [Validation Codes](/extensions/validation-codes) explains each problem they report. Reviewers check the rest by hand, as described below.

## Purpose and quality

* The plugin does something useful that Enconvo can't already do, and does it completely. Placeholder, demo-only or trial versions are turned down.
* It behaves predictably. When something fails, such as a network error, a missing permission or a bad input, it says what happened instead of failing silently.
* It uses only code, names, icons and content you own or are licensed to use. It doesn't impersonate another product, and doesn't suggest that Enconvo or anyone else endorses it.

## Name, description and listing

* **Name and title**: clear and specific. Avoid a generic single word, and don't add "MCP", "MCP Server" or "Plugin" to a company name.
* **Description**: says what the plugin does, accurately. No comparisons with other products, claims you can't back up, or prices, discounts and free-trial offers.
* **Icon and screenshots**: the icon is a PNG, JPEG, WebP or GIF of up to 5 MB. Up to 8 screenshots go in a `metadata/` folder and are shown in file name order. They show the plugin as it really works.
* **Support**: give people a way to reach you: `interface.websiteURL` and `interface.supportURL`, or `homepage` or `repository` for the website. A Claude Code plugin can use its `supportUrl` and `documentationUrl` instead. The store page shows these links before anyone installs the plugin.
* **Version**: raise it for every update. While an update is in review, the store keeps offering the version before it. `plugin publish` asks the store which version it released before it builds or uploads anything, and stops with the next version to use when yours isn't higher.

## MCP tools

When the plugin brings an MCP server, reviewers read its tools as the AI model will.

* **Names** are plain verbs that say what the tool does, such as `get_order_status`, and are unique within the server. No jargon or unexplained abbreviations.
* **Descriptions** explain what the tool does, what it changes and what it can't do, and match its input schema and real behavior. They don't tell the model to prefer this plugin, to call other plugins, or to call the tool for everything.
* **One operation per tool**, each with its own input schema.
* **Annotations** are set explicitly on every tool:

| Annotation | `true` when the tool | `false` when it |
| - | - | - |
| `readOnlyHint` | only reads, computes or previews | changes anything: writes, saves a file, sends, posts, uploads, queues work or starts a job |
| `destructiveHint` | deletes, overwrites, cancels or sends something that can't be taken back, even if the user could undo it | only adds |
| `openWorldHint` | reaches public or open-ended content, such as a web search, or sends to any address | stays inside a bounded account, workspace or catalog, even one hosted elsewhere |

Enconvo relies on these annotations. A tool marked `readOnlyHint: true` runs without asking the user and alongside other reads. A tool marked `destructiveHint: true` asks the user first unless they have given the agent full access. A tool that changes something but claims to be read-only is turned down.

`enconvo plugin scan` starts the plugin's MCP server, lists its tools with their annotations, and names each tool that leaves one out ([`annotations_required`](/extensions/validation-codes#mcp-server-scan)).

A tool that shows its own interface names its view, a `ui://` resource, in `_meta.ui.resourceUri`, and the view declares the domains it reaches in `_meta.ui.csp` (`connectDomains`, `resourceDomains`, `frameDomains`). These are the MCP Apps keys every host reads. Enconvo also reads ChatGPT's `_meta["openai/outputTemplate"]` and `_meta["openai/widgetCSP"]`, and gives a view written for ChatGPT (`text/html+skybridge`, or a tool named only by `openai/outputTemplate`) the `window.openai` API it calls, so a plugin built for ChatGPT shows its interface in Enconvo unchanged. Set the MCP Apps keys as well, so other hosts show it too. `scan` reads each view as Enconvo does and says when one is set only the OpenAI way.

## Data and privacy

* **Ask only for what the task needs.** Tool inputs are specific to the task. Don't ask for the whole conversation, earlier messages or broad profile data "just in case".
* **Return only what answers the request.** Leave internal ids, trace data and debugging output out of tool results.
* **Never collect** payment card data, health records, government ids such as social security numbers, or passwords, API keys and one-time codes through tool inputs. Credentials go in the plugin's preferences, where Enconvo keeps them.
* **Privacy policy.** A plugin that sends data to a server needs a published privacy policy saying what it collects, why, who receives it, how long it is kept and how people can delete it. Put its address in `interface.privacyPolicyURL`, and the terms of service, if you have them, in `interface.termsOfServiceURL`. The store page links both.
* **No tracking** of people, their queries or their usage beyond what the plugin needs to work and what its privacy policy discloses.
* **Keep secrets out of the package.** Anyone can download it. `.env` files are left out automatically, but check that no other keys or private files end up in it.

## Sign-in and test accounts

If the plugin needs an account, sign-in is explicit and asks only for the permissions it needs. Reviewers need to try it without creating an account or receiving a two-factor code, so give them a demo account that has sample data:

```sh theme={null}
npx -p @enconvo/api enconvo plugin publish --review-notes "Sign in as review@example.com with the password …"
```

Reviewer notes go only to Enconvo's reviewers. They aren't part of the package or the store listing.

## Commerce

A plugin may work with an account people already pay for, and may say when a feature needs a different plan. If it sells something or takes payments, say so in the review materials (`commerce` and `commerce_description`, below): what is sold and where people pay. People pay on your own site; the plugin doesn't hide charges or charge Enconvo users more than everyone else.

OpenAI's portal is stricter. It takes plugins that sell physical goods only, and no subscriptions, credits or other digital products. Check its [commerce rules](https://developers.openai.com/apps-sdk/app-submission-guidelines) before you submit there.

## Safety

* The plugin is suitable for a general audience, including teenagers, and doesn't target children under 13.
* It does what the user asked and nothing else: no unrelated content, redirects or ads.
* Its descriptions, prompts and skills never ask for secrets, move money or data without the user's say, try to override Enconvo's safeguards, or hide what the plugin does, including through encoded instructions.
* It doesn't scrape websites or wrap other services against their terms, or get around their rate limits and access controls.
* It doesn't sell or enable anything in OpenAI's list of [prohibited goods and services](https://developers.openai.com/apps-sdk/app-submission-guidelines), such as weapons, drugs, gambling, adult content, malware or financial fraud.

## Review materials

Review materials tell reviewers what to try: prompts the plugin should handle, prompts it should leave alone, and a short demo video. They are stored with each version and shown only to reviewers.

In a Codex, Claude Code or portable plugin, they go where OpenAI's portal reads them, under `extensions["com.openai"]` of one `plugin.json`. OpenAI imports them from `.codex-plugin/plugin.json` when the plugin has one, as `enconvo plugin create` makes it; from the root `plugin.json` when that one has `extensions["com.openai"]` of its own or there is no Codex manifest; and from `.claude-plugin/plugin.json` in a plugin made only for Claude Code. Don't add `extensions["com.openai"]` to the root `plugin.json` just for them: Codex then ignores the listing in `.codex-plugin/plugin.json`. `enconvo plugin validate --openai` names the manifest they belong in.

```json theme={null}
{
  "name": "notes",
  "version": "1.2.0",
  "extensions": {
    "com.openai": {
      "review": {
        "test_cases": {
          "positive": [
            {
              "description": "Finds a note by topic",
              "prompt": "Find my notes about the Q3 launch",
              "tools_triggered": "search_notes, read_note",
              "expected_behavior": "Lists the matching notes and quotes the most relevant one"
            }
          ],
          "negative": [
            { "description": "Unrelated question", "prompt": "What's the weather in Paris?" }
          ]
        },
        "demo_recording_url": "https://example.com/notes-demo.mp4",
        "commerce": false
      },
      "publication": {
        "release_notes": "Search now matches note titles."
      }
    }
  }
}
```

In an Enconvo plugin, the same `review` object goes at the top level of `package.json`.

| Field | What it holds |
| - | - |
| `test_cases.positive` | Prompts the plugin should handle, each with a `description`, the `prompt`, `expected_behavior`, and the `tools_triggered`. Optional: `file_attachment_urls` and `expected_output_url`. |
| `test_cases.negative` | Prompts the plugin should leave to something else, each with a `description` and the `prompt`. |
| `demo_recording_url` | A video of the main prompts and tools. |
| `commerce`, `commerce_description` | Whether the plugin sells something, and what and where. |
| `publication.release_notes` | What changed. `plugin publish` uses them as the store's changelog unless you pass `-m`. |

Each list holds up to 20 cases. Text fields hold up to 4,000 characters, and every address is an `https://` address reviewers can open. `validate` and `publish` refuse materials that break these limits.

Sign-in details never go in the package, because anyone can download it. `validate` and `publish` refuse a `review` that has `test_credentials` or `reviewer_instructions`; pass them with `--review-notes` instead, or in the Review details form of OpenAI's dashboard.

In `plugin.json`, `publication` can also hold what OpenAI's portal imports for the listing, and `validate --openai` checks them as the portal does. Enconvo shows `translations` in its interface language for an installed plugin and before installing one from a source; the Enconvo store's pages don't use them yet:

| Field | What it holds |
| - | - |
| `countries` | Where the plugin is available, as uppercase country codes such as `["US", "GB"]`. `[]` makes it available everywhere. |
| `translations` | Listing text by locale, such as `{"fr-FR": {"subtitle": "…", "description": "…"}}`. A subtitle is one line of up to 30 characters, a description up to 4,000. |

A field set to `null` keeps the value already saved in OpenAI's dashboard. Plugin-level test cases are for a plugin with exactly one MCP server.

OpenAI's final submission of a plugin with an MCP server or app takes exactly 5 positive and 3 negative cases, a demo recording and release notes; a plugin with only skills needs just the release notes. `validate --openai` lists what is missing as warnings, because the portal's upload accepts a plugin without them and asks for them only at final submission.

## Submission checklist

Before you run `plugin publish`:

* `plugin validate` reports no errors, and you have read its warnings.
* `version` is higher than the last version you published, and every manifest in the package gives the same name and version.
* For an update, `plugin validate --previous` with the zip of the release before it reports nothing you didn't mean: an update replaces the whole plugin, so a skill or MCP server it leaves out is removed (`plugin_components_removed`).
* The title, description and icon say what the plugin does, and the screenshots show it working.
* A homepage or support address is set, and a privacy policy is linked if the plugin sends data anywhere.
* Every MCP tool has a clear name and description and sets `readOnlyHint`, `destructiveHint` and `openWorldHint`, and `plugin scan` lists them without problems.
* Skills say in their `description` when to use them.
* Required settings, such as an API key, are declared as settings, not asked for in chat.
* No keys, tokens or private files are in the folder.
* Review materials list prompts to try and prompts to leave alone, and a demo account is ready for `--review-notes` if sign-in is needed.
* You installed the package from its folder (`plugin install .`) and tried the test cases in Enconvo.

## What happens after you submit

1. `plugin publish` checks the plugin and that its version is higher than the one the store released, then uploads it and submits the version for review. Until a new plugin is approved, only you can see it.
2. A reviewer reads the listing, the package's files (with what changed since the released version), the review materials and your notes, and tries the test cases.
3. An approved version appears in the store, and installed copies are offered the update. If a version is turned down, you get an email with the reason. Fix it, raise the version and publish again.

Enconvo also looks into reports about published plugins. A plugin found to break these guidelines, including one approved before, can be removed from the store, and its developer is emailed the reason.

## Submitting to OpenAI as well

The same folder can go to OpenAI's plugin portal:

```sh theme={null}
npx -p @enconvo/api enconvo plugin validate --openai
npx -p @enconvo/api enconvo plugin scan --openai
npx -p @enconvo/api enconvo plugin pack --openai
```

`pack --openai` writes the zip the portal takes. Credentials for OpenAI's reviewers go in the portal's submission form, never in the package. Keep your own keys out of it too: `pack` and `publish` leave out `.env` files, and `validate` warns about a file that looks like a private key or a login (`archive_member_secret`).

The portal takes only letters, digits, `-` and `_` in the plugin's `name`, and OpenAI suggests lowercase letters, digits and single hyphens. Codex, Claude Code and Enconvo also install a dotted name such as `acme.tools`, but `validate --openai` and `pack --openai` refuse it (`plugin_name_format`), so name a plugin you mean to submit `acme-tools`.

After the upload, the portal connects to the plugin's MCP server and scans its tools; when the server changes, rescan it there. It reaches the server only at a public `https://` URL, so a server that runs on the user's computer has to be deployed first; `scan --openai` warns about one (`mcp_server_not_remote`). At final submission the portal asks for all three annotations on every tool. It no longer asks you to justify them; if its automated review flags one, you can appeal with an explanation. A plugin whose views embed pages still needs a justification for each embedded page: what it does, why the plugin embeds it, and who controls its domain. Pages from the MCP server's own registrable domain are allowed, and separate tenants on a shared host count as different domains. `scan --openai` lists the domains your views embed (`frame_domain_explanation_required`).

Before you can submit the plugin, the portal verifies that you control the MCP server's domain: it gives you a token to serve as plain text at `/.well-known/openai-apps-challenge` on the server's host or a parent domain. `scan --openai` says what those URLs answer, and `scan --challenge-token <token>` checks that one serves exactly the token before you select **Verify Domain**; see [Domain verification](/extensions/validation-codes#domain-verification).

To update a plugin you published there, upload a complete ZIP with a higher version and everything you mean to keep. If you didn't keep the zip of the published version, the portal gives it to you with **Download release ZIP** in that version's **…** menu; check the new version against it with `validate --openai --previous <zip>`. The portal can't add an MCP server to a published plugin that had none, or change a published server's URL (ask OpenAI's support for that), and `--previous` reports both; see [Updates](/extensions/validation-codes#updates).

OpenAI's portal connects only one MCP server per plugin to scan and review it, so `validate --openai` warns about a plugin with more. The same server declared for Codex in `mcp.json` and for Claude Code in `.mcp.json` counts once.

OpenAI's final submission asks more of the listing than its upload does, and `validate --openai` warns about each gap:

* `interface.longDescription`, and an `interface.category`; without one, the directory lists the plugin under Other.
* For a plugin with an MCP server, `websiteURL`, `supportURL`, `privacyPolicyURL` and `termsOfServiceURL`, each an `https://` address of up to 1,024 characters.
* A listing of your own. A Claude Code plugin with only `.claude-plugin/plugin.json` gets one the portal fills in with defaults (`claude_format_normalized`), so add a `.codex-plugin/plugin.json` with an `interface` to write it yourself.
* Screenshots only when the MCP server has its own interface: one PNG or JPEG for each starter prompt, exactly 706 pixels wide and 400 to 860 pixels tall. The portal decides from its scan, so `scan --openai` refuses screenshots when no tool shows an interface (`screenshots_not_allowed`). OpenAI's directory no longer shows screenshots and introduces a plugin by its starter prompts, so write those with care.

The directory shows the developer name of your verified OpenAI identity, whatever `interface.developerName` says. See [OpenAI's final submission](/extensions/validation-codes#openais-final-submission) for the codes.


This documentation is built and hosted on [Mintlify](https://mintlify.com), a developer documentation platform.