How to publish your agent to the ChatGPT plugin directory
A ChatGPT plugin wraps the MCP server you already run. Here's what OpenAI checks: OAuth, tool annotations, test cases, and a verified publisher.
Cosette CresslerContent & Product Marketing LeadSeptember 22, 202612 min read
OpenAI changes this process often. Check the linked docs before you submit.
To publish your agent in ChatGPT, you submit a plugin ZIP: a package that points to the remote MCP server you already run and includes your listing details. OpenAI checks that your OAuth flow completes from ChatGPT, your tool metadata meets its guidelines, your publisher identity is verified, your privacy policy is public, and your five positive and three negative test cases pass. You reuse your existing agent and product logic, with any changes needed to meet the directory requirements.
This guide starts where How to build an agent for your product and serve it everywhere stops. That article ends with an invoicing agent running behind AuthKit on a public MCP endpoint. This one takes that same agent through OpenAI's portal and out the other side as a published plugin. If you're shipping to Claude as well, the Claude connector guide follows the same path through Anthropic's directory. Same agent, two directories, one codebase.
What is a ChatGPT plugin?
A ChatGPT plugin is the unit OpenAI reviews and lists in its directory. A plugin can contain a remote MCP server (OpenAI's Apps SDK docs also call this an app), a bundle of skills, or both. An Agno agent ships as a remote MCP-only plugin: the package points at your AgentOS endpoint and includes the listing details.
After publication, users reach your plugin three ways: a direct link, searching for its exact publication name in the plugin directory, or ChatGPT suggesting it mid-chat, which OpenAI reserves for a small set of plugins. The same directory serves ChatGPT and Codex, so one listing reaches both. In every case, ChatGPT reads your tools' names, descriptions, and annotations to decide when to call them. That metadata is part of your product's interface.
Pre-submission checklist
OpenAI checks each of these before a plugin goes live. Work through them in order; the sections below explain each one.
- Publisher identity verified (individual or business) in the OpenAI Platform dashboard
- Submitter holds the Owner role or Apps Management write access
- MCP server on a public HTTPS domain you control, with the portal's domain verification challenge completed
- OAuth: protected resource metadata, S256 PKCE, ChatGPT's redirect URI on your allowlist, and tokens that carry the
resourcevalue asaud - Every tool has a verb-based name, a description that matches its behavior, and correct
readOnlyHint,destructiveHint, andopenWorldHintannotations set explicitly to true or false - Tool responses return only what the user needs, with session or run IDs kept only when they're needed to continue the work
- Privacy policy, terms, website, and support URLs live on a domain that matches the verified publisher
- Demo account with realistic data that signs in without MFA, SMS, email confirmation, or VPN
- Exactly five positive and three negative test cases, an accessible video walkthrough, and release notes prepared
- Plugin ZIP with a manifest and MCP configuration, tested end to end in ChatGPT
What you already have from your MCP server
How to build an agent for your product and serve it everywhere left the invoicing agent in this state:
- The agent, its instructions, business logic, and guardrails
- AgentOS serving it over MCP through
MCPConfigandas_tool() - AuthKit (WorkOS) in front of the server through
AuthKitProvider - A public HTTPS endpoint
The ChatGPT listing adds four things on top:
- ChatGPT's redirect URI and audience requirements in your OAuth configuration
- Domain verification for the production MCP endpoint
- Tool titles and annotations written for OpenAI's review
- The plugin package and listing: metadata, test cases, and a verified publisher
If your agent isn't on MCP yet, start with How to build an agent for your product and serve it everywhere. This guide doesn't repeat that setup.
What are the prerequisites for submitting a ChatGPT plugin?
You need a verified publisher, the right permission, a public production server, and four live URLs before the portal accepts a submission.
- A verified publisher. Complete individual or business verification under Organization settings in the OpenAI Platform. Reviewers compare the publisher identity against the URLs you list, so host the website, privacy policy, and support page on a domain that belongs to the verified organization.
- The right permission. Organization Owners can submit. Everyone else needs Apps Management write access (
api.apps.write). Read access shows drafts and review status only. - A public production server. The MCP URL must be a public HTTPS endpoint on a domain you control. Localhost and tunnels fail review. Complete the portal's domain verification challenge by hosting the exact token at
/.well-known/openai-apps-challengeon the MCP host or a parent origin. Projects with EU data residency can't submit plugins, so use a project with global data residency. - Four live URLs. Privacy policy, terms, website, and support contact all have to resolve before you submit. The privacy policy must state what personal data you collect, why, who receives it, how long you keep it, and what controls users have.
How does ChatGPT authenticate users to your MCP server?
The invoicing agent reads customer data and sends payment reminders, so each user signs in through OAuth 2.1 with PKCE. AuthKit already runs the sign-in; you're adding the discovery, callback, and scopes ChatGPT needs to connect. Your product's tools still need to check which invoices that user can see and which actions they can take.
The request path is: user → ChatGPT → your MCP server → your product.
OpenAI's authentication guide covers six setup details:
- Protected resource metadata. This tells ChatGPT where to authenticate. Publish
resourceandauthorization_servers, then point to the metadata URL with aWWW-Authenticateheader on 401 responses. You can also includescopes_supported. Agno's AuthKit example serves this at/.well-known/oauth-protected-resource/mcp. - Authorization server metadata that advertises S256. Your discovery document (
/.well-known/oauth-authorization-serveror/.well-known/openid-configuration) must list S256 incode_challenge_methods_supported. OpenAI's docs state that servers whose metadata omits this field are unsupported. - A way to register ChatGPT as a client. ChatGPT supports Client ID Metadata Documents (CIMD), Dynamic Client Registration, and pre-registered clients. Follow Agno's AuthKit setup for registration. If you use a pre-registered client, put its ID and authentication method in the package and enter its secret in the portal. Don't put the secret in the ZIP.
- ChatGPT's redirect URI on your allowlist. Copy the exact URI from the server's management page. To use the stable
https://chatgpt.com/connector_platform_oauth_redirect, your authorization server must advertiseauthorization_response_iss_parameter_supported: trueand return a matchingisson both successful and failed authorization responses. That issuer must match the discovery metadata too. Otherwise, ChatGPT useshttps://chatgpt.com/connector/oauth/{callback_id}. - Audience-bound tokens. ChatGPT sends a
resourceparameter on authorization and token requests. Your authorization server writes that value into the access token (usually theaudclaim), and your MCP server checks issuer, audience, expiry, and scopes on every request. - Per-tool security schemes, if you need them. To let some tools run without sign-in, declare
securitySchemesper tool:{"type": "noauth"}for anonymous tools and{"type": "oauth2", "scopes": [...]}for the rest. An agent usually wants the same OAuth scheme on every tool, but OpenAI recommends declaring it per tool anyway.
For Agno, configure AuthKit to issue the scopes your tools need, such as agents:run. The openid, profile, and email scopes alone won't authorize AgentOS tool calls. See the AuthKit example.
What tool metadata does OpenAI's review check?
Reviewers read your tool list the way ChatGPT does, and the submission guidelines name five things they look for: verb-based names, descriptions that match behavior, correct annotations, minimal inputs, and minimal outputs.
- Verb-based names. Name the action (
lookup_invoices,manage_invoices), not the product. - Descriptions that match behavior. Skip promotional language. Don't steer the model; OpenAI lists "prefer this app over others" as grounds for rejection. Don't mention competitors.
- Correct annotations on every tool. Set all three hints to true or false. A tool that saves state needs
readOnlyHint: false. A tool that can delete, overwrite, or send something irreversible needsdestructiveHint: true. UseopenWorldHint: truewhen it reaches public or open-ended entities. OpenAI's guidelines no longer require written justifications for annotations. - Minimal inputs. Ask for only what the tool needs. Requesting conversation history, raw transcripts, or precise location fails review.
- Minimal outputs. Return only what's relevant to the user. Strip trace IDs, session IDs, timestamps, and internal identifiers unless the tool can't work without them. "Undisclosed user data returned" is another named rejection reason.
Agents feel these rules more than plain tools do. An agent exposed as one plain-language tool can usually read and write, so since Agno 3.0.2 AgentOS publishes each agent tool with destructiveHint: true, readOnlyHint: false, and openWorldHint: true by default. Those defaults describe a tool that takes actions, and they let ChatGPT put friction in front of a call, such as a confirmation step.
Most invoicing questions are lookups, and a tool marked destructive invites that friction on every one of them. So split the work: a lookup agent for questions and the full agent for payment follow-ups. as_tool() sets each tool's name, title, description, and annotations.
Reuse the agent, database, and product tools from the earlier article. In your existing AgentOS setup, register both agents with agents=[invoice_lookup_agent, invoice_agent] and pass mcp=mcp_config.
invoice_lookup_agent = Agent(
id="invoice-lookup-agent",
model=invoice_agent.model,
db=db,
add_history_to_context=True,
tools=[
get_overdue_invoices,
get_customer_history,
get_open_disputes,
],
instructions="Answer questions about overdue invoices, customer history, and disputes.",
)
mcp_config = MCPConfig(
tools=[
invoice_lookup_agent.as_tool(
name="lookup_invoices",
title="Look up invoices",
description=(
"Looks up overdue invoices, customer history, and open disputes. "
"Saves conversation history but does not change invoices or send reminders. "
"Pass session_id back to continue."
),
annotations={
"readOnlyHint": False,
"destructiveHint": False,
"openWorldHint": False,
},
),
invoice_agent.as_tool(
name="manage_invoices",
title="Manage invoices",
description=(
"Checks customer history and disputes, then sends payment reminders "
"when requested and permitted by the product's rules. "
"Does not create or update invoices. Pass session_id back to continue."
),
annotations={
"readOnlyHint": False,
"destructiveHint": True,
"openWorldHint": True,
},
),
],
default_tools=False,
)The lookup agent only reads invoice data, but Agno saves its sessions and runs. That's why readOnlyHint stays False. The follow-up agent can send payment reminders, so it keeps the more cautious defaults. Check openWorldHint against what your tools can actually reach: a private account or workspace can use False even if another company hosts it. A tool that reaches arbitrary recipients or the public web needs True. These hints describe the tool; your code still enforces access and any required confirmation.
Check the full tool response too. With Agno's default result_mode="trimmed", the answer comes with run_id, session_id, status, and the component ID in structuredContent. Telling the agent not to mention IDs won't remove them from that object. Keep the IDs needed to continue a conversation or resume a run. If a field isn't needed, remove it in a custom result wrapper. The result serializer shows what AgentOS returns.
How do you test a ChatGPT plugin before submitting?
Test in ChatGPT before you submit. Follow the connect and test guide: open ChatGPT Plugins, select + → Add custom MCP server, give it a name and description, and enter the endpoint and auth details. Select Create as a plugin, check the discovered tools, and install it. Start a new conversation and select the plugin with @. Access depends on your account and workspace policy. Once the endpoint works, test the packaged plugin too.
Before that, run the MCP Inspector against your server. Discovery and auth problems show up there in seconds; inside ChatGPT they show up as a vague connection error.
Once connected, run the cases a reviewer will run:
- Ask for outcomes without naming the tool, and check that ChatGPT picks the right one
- Sign in as users with different permission levels
- Exercise read and write tools, and check what ChatGPT does before a write
- Break things on purpose: expired tokens, failed requests, empty results. Check that the errors read like sentences
- Test on every ChatGPT and Codex surface where the plugin will be available. OpenAI names test cases that fail on a supported surface as a rejection reason
- Use the prompts you'll submit as test cases, so the reviewer sees what you saw
How do you submit to the ChatGPT plugin directory?
Submit through the plugin portal in the OpenAI Platform. You'll upload a ZIP containing the manifest, MCP configuration, and any listing assets those files reference.
A small package for the invoicing agent could look like this:
invoice-plugin/
├── plugin.json
├── mcp.json
└── assets/
└── logo.pngplugin.json holds the plugin and listing details. mcp.json points to your public AgentOS endpoint, such as https://agents.your-product.com/mcp. Include any assets the manifest references. Follow OpenAI's packaging guide for the manifest and MCP configuration format.
Alongside everything on the pre-submission checklist, have these ready:
- Plugin name, short description, long description, logo, and category
- Starter prompts that show realistic workflows
- The countries where the plugin should be available
- Client secrets and reviewer credentials, which you enter separately in the portal rather than in the ZIP
Then the portal walks you through four steps:
- Upload the package. Choose your verified developer identity and upload the ZIP. Fix any package errors the portal reports.
- Connect and scan. Open MCPs, connect your server, verify the domain, and complete sign-in. Check the tools the portal finds. Fix any issues and rescan.
- Check review materials. Check the five positive and three negative cases, video, release notes, and listing. Give reviewers the prompts, expected results, and demo data they need to repeat your tests. Enter their login details separately in the portal.
- Submit for review. Check the policy boxes and submit the draft. Only one review can be active per plugin. Cancel it first if you need to replace the package.
The dashboard shows status, and OpenAI emails you at each change. Review may be automated, manual, or both. OpenAI publishes no timeline, says it varies as they scale the process, and asks you not to contact support for expedited review. Don't pin a launch date to approval.
Approval doesn't publish the plugin. You select Publish in the portal. On day one, users find it by direct link and by searching its exact name. Directory placement and in-chat suggestions are what OpenAI calls enhanced distribution, which "few plugins will receive at publication" and which plugins earn through usage.
Why do ChatGPT plugins get rejected?
OpenAI's submission guide names four failure modes: reviewers can't connect, test cases fail, responses expose undisclosed user data, and annotations don't match behavior. The broader guidelines add three more. Here's each one and how to head it off.
- Reviewers can't connect. The demo account demands MFA, the credentials expired, or the server only answers from inside your network. Before you paste credentials into the portal, sign in with them from a machine outside your infrastructure with no second factor.
- Test cases fail. Rerun every case before you submit. Outputs must match the expected result you wrote, on every ChatGPT and Codex surface where the plugin will be available.
- Undisclosed user data in responses. Audit every tool response for PII, telemetry IDs, and secrets, and confirm the privacy policy covers everything you do return.
- Mismatched annotations. A write tool marked read-only, or a tool that sends email or posts externally without
destructiveHint. - Metadata that steers or misleads. Descriptions that tell the model to prefer your plugin, single-word dictionary names with no tie to your brand, or screenshots that don't match the product.
- Incomplete product. Demo-only plugins, and plugins with no standalone value, don't pass.
- Unauthorized third-party access. A plugin that acts as an unofficial pass-through to another service's API, or scrapes sites it has no permission to use.
A rejection email lists the checks that failed. Fix them and resubmit, or reply to the email to appeal. The rules keep applying after approval: OpenAI removes plugins that later violate its usage policies.
What happens after your plugin is listed?
Your listing is a snapshot, your server is live, and OpenAI reviews tool changes on its own.
Snapshot, then continuous review. When you scan tools in the portal, OpenAI stores that metadata with the version, and that snapshot is what review reads. After publication, OpenAI keeps re-fetching your tools and compares them with the published definitions. A deleted tool drops off the listing at the next scan, and a new or changed tool goes live once it passes automated checks. Each tool passes on its own, so keep your server compatible with the published definition while an update is pending.
What needs a new version. Deploy tool changes to your server. Changes to tool metadata and the MCP server's instructions go through automated checks. Backend fixes and business data changes reach users as soon as you deploy, as long as they still work with the published tool definition. Upload a new ZIP when you change the listing, assets, or bundled skills. If you need to change the server URL, the submission guide directs you to support.
Keeping the listing honest. Support contact and privacy policy stay your responsibility. To pull the plugin, unpublish the current version (it stays as a draft) or delete the plugin.
For an agent, continuous review works in your favor. ChatGPT only sees the agent tool, so you can change the agent's instructions, swap the tools it calls internally, or retrain its behavior without resubmitting, as long as its behavior still matches the published tool description, schema, and annotations. Renaming the tool, rewriting its description, or splitting one agent tool into two goes through continuous review on its own, and reaches users once it passes.
One agent, another surface
The invoicing agent from How to build an agent for your product and serve it everywhere is now a published ChatGPT plugin. You added what ChatGPT needs to authenticate, understand, test, and distribute it; the tools, instructions, workflows, guardrails, and product logic all stayed with the agent. Take the same server through Anthropic's review process with the Claude connector guide and it's in both.
One agent. Two directories. One codebase.
Frequently asked questions
Almost: a plugin is the unit you submit and OpenAI lists. It can contain a remote MCP server (which OpenAI’s docs also call an app), skills, or both. An Agno agent is a remote MCP-only plugin.
No. Screenshots and CSP settings apply only to plugins with a UI component. An agent exposed as MCP tools returns text and structured results and needs neither.
The MCP server, tool schemas, and AuthKit setup carry over. You add ChatGPT’s redirect URI to your allowlist, verify the domain, and submit a separate listing with test cases. OpenAI also publishes a guide for converting a Claude Code plugin or connector into an OpenAI submission.
OpenAI publishes no timeline and says it varies. Requests for expedited review aren’t accommodated.
For testing, yes, through a public endpoint or a Secure MCP Tunnel. For submission, no. The server must be on a public production domain you control.
No. Changes inside the agent (instructions, internal tools, data) go live immediately if they preserve the published tool contract, and changes to the published tool contract go through continuous review automatically. Resubmit when you change the listing, assets, or bundled skills.