Skip to main content
All posts

How to Submit a ChatGPT App as a Plugin (July 2026)

Abe Wheeler
ChatGPT AppsChatGPT App FrameworkChatGPT App TestingMCP AppsMCP App FrameworkMCP App TestingPlugin SubmissionOpenAI Apps SDK
ChatGPT Apps are submitted for public review as plugins backed by an MCP server.

ChatGPT Apps are submitted for public review as plugins backed by an MCP server.

Developers searching for “submit ChatGPT App” usually run into two different systems at once:

  • The app itself, which is an MCP-backed ChatGPT App with tools, resources, auth, and optional UI.
  • The publishing wrapper, which is now a plugin submission in the OpenAI Platform.

That split matters because the thing you submit is not a development app ID. You submit the production MCP server that backs the app.

TL;DR: ChatGPT Apps are now submitted and published as plugins. For an app-only plugin, choose With MCP in the plugin submission portal, provide your production /mcp URL, scan tools, verify the domain, define exact CSP domains, provide reviewer credentials when auth is required, add five positive and three negative test cases, and submit the draft for review. Use Developer Mode for private or workspace-only apps. Use the MCP Apps standard fields by default, then add ChatGPT-specific extensions only where you need them.

What You Are Actually Submitting

The current OpenAI flow treats a plugin as a package that can contain:

  • Skills only
  • An app backed by an MCP server
  • An app plus bundled skills

For a ChatGPT App, you use the app-backed path. In the portal, that means With MCP.

The portal asks for your production MCP server URL, not an existing plugin_asdk_app... development app ID. It connects to the server, scans tool metadata, checks app requirements, stores the discovered metadata with the draft version, and sends that version through review.

That has a practical consequence: treat the MCP server metadata as a versioned contract. Tool calls and UI resources still come from your live server, but the metadata that was scanned for a submitted version is the metadata reviewers and published users see for that version.

Before the first public submission, make sure the production origin is stable. OpenAI documents that the app MCP server origin cannot change between versions. You can change the endpoint path in a new version, but changing scheme, host, or port means submitting a new plugin.

When Not to Submit

Do not use the public review flow for every test build.

Use Developer Mode when:

  • The app is private to your team.
  • You are testing a local tunnel or staging endpoint.
  • The app is still changing tool names, descriptions, auth, or UI flows.
  • You only need workspace access, not public distribution.

Submit for review when the app is ready for public availability in the countries or regions you choose in the portal.

That boundary saves time. Developer Mode is for iteration. Plugin submission is for a production app with stable metadata, working reviewer access, and review-ready test cases.

Submission Prerequisites

Before opening the portal, collect the parts that usually slow teams down.

RequirementWhat to prepare
Apps Management accessThe submitter needs write access for app management in the OpenAI Platform organization
Verified identityIndividual or business verification for the publisher name
Production MCP serverA public HTTPS MCP endpoint, usually ending in /mcp
Domain verification accessAbility to host the challenge token under /.well-known/openai-apps-challenge
Auth detailsOAuth setup or reviewer credentials that work without MFA, SMS, email codes, VPN, or private network access
CSPExact domains the app fetches from or loads resources from
Tool metadataNames, descriptions, schemas, annotations, UI resource links, and output contracts
Test casesFive positive and three negative cases with expected behavior
Listing copyName, short description, long description, category, logo, website, support URL, privacy policy, and terms
AvailabilityCountries or regions where the plugin should be published
Release notesA short note describing what this version includes

The identity and role checks are easy to leave until the end, but they block submission. Confirm them before you schedule a launch date.

Build Against MCP Apps First

OpenAI’s current Apps SDK docs recommend using MCP Apps standard keys and the ui/* bridge when there is a standard equivalent. ChatGPT-specific extensions still exist, but they should be optional layers.

For a portable ChatGPT App, the baseline looks like this:

App concernPortable MCP Apps field or bridgeChatGPT compatibility path
Link tool to UI_meta.ui.resourceUri_meta["openai/outputTemplate"]
Receive tool inputui/initialize and ui/notifications/tool-inputwindow.openai.toolInput
Receive tool resultui/notifications/tool-resultwindow.openai.toolOutput
Let UI call a server tooltools/callwindow.openai.callTool
Ask the host to send a messageui/messagewindow.openai.sendFollowUpMessage
Update model-visible UI stateui/update-model-contextChatGPT-specific state APIs where needed

If your app only needs ChatGPT, you can still use the ChatGPT extensions. If you want the same app to work in other MCP Apps hosts, keep the standard path as the source of truth.

In sunpeak, that usually means the app code uses portable hooks and metadata by default, while host-specific behavior stays explicit. You can test the same resource in a local ChatGPT-style runtime before connecting it to a real ChatGPT development app.

Prepare the MCP Server for the Scan

The portal scan is not just a connectivity check. It validates the server and metadata the submitted plugin will expose.

Run a pre-scan checklist:

  • The production /mcp endpoint is reachable over HTTPS from outside your network.
  • The server uses the same auth behavior reviewers will test.
  • Tool names are stable and specific.
  • Tool descriptions explain what each tool does and when to use it.
  • Input schemas use precise fields, not raw prompt blobs.
  • Tool annotations are set honestly for reads, writes, and external effects.
  • UI tools point at the correct resource URI.
  • Resource metadata includes CSP for every required external domain.
  • Tool outputs do not leak logs, trace IDs, secrets, internal account IDs, or unrelated user data.
  • The privacy policy covers the user data your tools return or process.

If the scan finds metadata issues, fix the server, redeploy, and scan again. Do not work around scan warnings with copy changes in the listing. The reviewed app is the MCP server behavior, not the marketing text around it.

Tool Annotations Matter

Tool annotations affect review and runtime behavior because they tell the host whether a tool reads data, changes data, or reaches outside the user’s local context.

Set these explicitly for every tool:

export const tool = {
  name: 'send_invoice_reminder',
  title: 'Send Invoice Reminder',
  description: 'Send a reminder email for one overdue invoice after the user confirms.',
  annotations: {
    readOnlyHint: false,
    destructiveHint: true,
    openWorldHint: true,
  },
};

A read-only search tool should not look like a write tool. A send, publish, delete, submit, or purchase tool should not look read-only. If a tool affects an external service, mark that honestly.

Add a protocol-level test before submission:

import { expect, test } from 'sunpeak/test';

test('public tools have review-ready annotations', async ({ mcp }) => {
  const { tools } = await mcp.listTools();

  for (const tool of tools) {
    expect(tool.title, `${tool.name} is missing a title`).toBeTruthy();
    expect(tool.description, `${tool.name} is missing a description`).toBeTruthy();
    expect(tool.annotations?.readOnlyHint, `${tool.name} needs readOnlyHint`).toEqual(
      expect.any(Boolean)
    );
    expect(tool.annotations?.destructiveHint, `${tool.name} needs destructiveHint`).toEqual(
      expect.any(Boolean)
    );
    expect(tool.annotations?.openWorldHint, `${tool.name} needs openWorldHint`).toEqual(
      expect.any(Boolean)
    );
  }
});

This catches the common failure where a new tool ships without the metadata reviewers expect.

CSP and Domain Verification

A plugin that contains an app must define a content security policy for the exact domains the app needs. Treat this as part of the app contract, not a last-minute form field.

Check these separately:

  • API domains your iframe calls with fetch() or WebSocket
  • Asset domains for images, fonts, styles, scripts, media, or maps
  • Nested frame domains, if the app embeds another frame
  • OAuth or auth-related domains used during login

Do not add broad domains just to get through review. Broad CSP makes the app harder to reason about, and it can hide accidental dependencies.

Domain verification is separate. If the portal asks for a challenge token, host the exact token at the generated /.well-known/openai-apps-challenge path on the MCP hostname or a valid parent hostname. Return only that token, not JSON and not a page with extra text.

Write the Five Positive Cases

Positive test cases should prove the app works for real user goals, not just that a tool can be called.

A good positive case includes:

  • A user prompt
  • The expected tool or tools
  • The expected arguments
  • The expected UI state, if the app renders UI
  • The expected final outcome
  • Any required test account data

Example:

FieldExample
Prompt“Show overdue invoices for Acme and help me draft reminders.”
Expected toolsearch_overdue_invoices
Expected arguments{ "customer": "Acme" }
Expected UIInvoice review table with unpaid invoice IDs, amounts, due dates, and reminder actions
Expected outcomeUser can review invoices before any reminder is sent

Pick cases that cover the main happy paths, auth state, UI rendering, and at least one write or approval flow if your app has writes.

Write the Three Negative Cases

Negative cases are where many apps reveal unsafe tool design.

Use them to prove the app avoids tool calls when the request is unsafe, unsupported, or under-specified:

Negative caseWhat should happen
User asks to send reminders to every overdue customer without reviewThe app should require explicit review or confirmation before sending
User asks for data outside the account’s permission scopeThe tool should refuse or return a permission-safe error
User gives missing or ambiguous inputThe assistant should ask a clarifying question instead of guessing

Good negative cases are specific. “Bad prompt” is not enough. Reviewers need to see which boundary you expect the app to enforce.

Test in Developer Mode Before Submission

Before you submit, run the same cases in live ChatGPT Developer Mode.

OpenAI’s testing docs recommend creating a developer-mode app that points at your HTTPS /mcp endpoint, toggling it on in a new conversation, and running direct, indirect, and negative prompt sets. Record which tool was selected, what arguments the model passed, and whether confirmation prompts appeared when expected.

Also test mobile. A ChatGPT App that works in a wide desktop iframe can still fail on a phone because of height, safe area, touch target, or display mode behavior.

With sunpeak, keep most of that coverage local and deterministic:

import { expect, test } from 'sunpeak/test';

test('invoice review app renders before reminders can be sent', async ({ inspector }) => {
  const result = await inspector.renderTool('search_overdue_invoices', {
    args: { customer: 'Acme' },
    displayMode: 'inline',
  });

  await expect(result.app().getByRole('button', { name: /send reminder/i })).toBeVisible();
  await expect(result.app().getByText(/review/i)).toBeVisible();
});

Then reserve live ChatGPT tests for the pieces local tests cannot prove: account access, real host tool selection, production auth, portal metadata refresh, and mobile behavior in the actual app.

Submit, Review, Publish

The portal flow is roughly:

  1. Create a plugin draft.
  2. Choose With MCP.
  3. Fill the public listing and publisher fields.
  4. Enter the production MCP server URL.
  5. Configure auth and reviewer credentials.
  6. Define CSP.
  7. Complete domain verification if prompted.
  8. Scan tools.
  9. Fix server or metadata issues, deploy, and scan again.
  10. Add starter prompts, positive cases, negative cases, availability, and release notes.
  11. Submit for review.

Approval does not always mean the plugin appears everywhere immediately. OpenAI documents a separate publish step after approval, and directory placement can depend on the distribution surface. Keep the portal URL and exact publication name handy so you can verify the published listing.

If the plugin is rejected, use the feedback as a failing test case. Fix the MCP server or listing, rerun the relevant local and live tests, then resubmit.

A Short Pre-Submit Checklist

Use this before you click submit:

  • The submitted MCP server is production, public, HTTPS, and reachable.
  • The submitter has Apps Management write access.
  • The publisher identity is verified and matches the listing.
  • The app uses MCP Apps standard fields where possible.
  • ChatGPT-specific extensions are optional and tested.
  • Every tool has accurate name, title, description, schema, and annotations.
  • UI tools link to the right ui:// resources.
  • structuredContent matches the UI contract and any outputSchema.
  • CSP lists exact domains.
  • Domain verification token is hosted at the expected well-known URL.
  • Reviewer credentials work in a clean browser without MFA or private-network access.
  • Five positive cases and three negative cases pass.
  • Mobile, light theme, dark theme, loading state, empty state, error state, and cancellation state have been tested.
  • The privacy policy covers the data your tools process and return.
  • Release notes describe this submitted version.

This is a lot of surface area, but most of it can be tested before the portal is involved. That is the point of building ChatGPT Apps as MCP Apps: the server contract, UI resource, metadata, and test matrix can be checked locally before review.

Where sunpeak Fits

sunpeak is useful for this flow because it gives you a local ChatGPT App inspector, portable MCP App APIs, and tests for the server and rendered resource.

Use it to:

  • Inspect tools and resources before a portal scan.
  • Render the app in ChatGPT-style host modes.
  • Test display modes, themes, safe areas, and mobile widths.
  • Assert tool annotations and metadata.
  • Run E2E and visual tests in CI.
  • Keep ChatGPT-specific code separate from the portable MCP Apps baseline.

If you are starting from scratch, use npx sunpeak new. If you already have an MCP server, use npx sunpeak inspect --server <url> and add the submission checks before the next review pass.

Get Started

Documentation →
npx sunpeak new

Further Reading

Frequently Asked Questions

How do I submit a ChatGPT App for public review?

Submit it as a plugin through the OpenAI plugin submission portal. Choose With MCP, enter the production MCP server URL, configure auth and CSP, scan tools, provide listing details, add starter prompts, add five positive and three negative test cases, select availability, and submit the draft for review.

Do I submit my ChatGPT App ID or MCP server URL?

Submit the MCP server URL. The plugin submission portal scans your production MCP endpoint and stores the discovered metadata with the submitted draft version. Do not submit an existing ChatGPT development app ID or try to reference an already-published app.

What permissions do I need to submit a ChatGPT App plugin?

The submitter needs Apps Management write access in the OpenAI Platform organization that owns the plugin draft. The publisher also needs a verified individual or business identity that matches the public listing, website, support contact, privacy policy, and terms.

What does OpenAI review for a plugin that contains a ChatGPT App?

Review covers the public listing, publisher identity, production MCP reachability, domain verification, tool metadata, tool annotations, content security policy, auth flow, reviewer credentials, privacy disclosures, starter prompts, positive and negative test cases, app behavior, and whether the app follows the published app guidelines.

How many test cases do I need for ChatGPT App submission?

Prepare five positive test cases and three negative test cases. Positive cases should prove the app works for realistic user goals. Negative cases should prove the model refuses or avoids tool calls when the request is unsafe, unsupported, out of scope, missing required data, or likely to change data without approval.

Can I keep a ChatGPT App private instead of publishing it?

Yes. If the app is only for private use or workspace testing, use Developer Mode and do not submit it for public plugin review. Public submission is for apps you intend to publish in the countries or regions selected in the plugin portal.

How do metadata updates work after a ChatGPT App is published?

The plugin portal stores an app metadata snapshot when it scans your MCP server for a draft version. Tool calls and UI resources still use your live MCP server, but published metadata changes require a new reviewed version. The MCP server origin cannot change between versions, so use a stable production origin before the first submission.