Skip to content

feat: make the plugin submittable to the OpenAI directory - #12

Merged
stefanjudis merged 9 commits into
mainfrom
stefanjudis/openai-mcp-auth-declaration
Nov 4, 2026
Merged

stefanjudis merged 9 commits into
mainfrom
stefanjudis/openai-mcp-auth-declaration

Conversation

@stefanjudis

@stefanjudis stefanjudis commented Nov 4, 2026 •

Copy link
Copy Markdown
Collaborator

Gets the Checkly plugin to the point where it can be submitted to the OpenAI plugin directory, and adds the review material the submission form asks for.

Why the previous submission was stuck

The earlier listing showed its MCP server as "Not connected" with Connect, Reconnect and Rescan all disabled, and no error anywhere in the UI. The dashboard API explained it:

"mcp_servers": [{ "key": "checkly", "can_configure": false, ... }]
"app_id": null, "associated_apps": [], "app_ids": []
"submission": { "issues": [{
  "field": "mcpServers.checkly",
  "description": "No app is attached to this MCP server. Apps are created for the first supported remote MCP in a new plugin upload."
}]}

No app was attached, so there was nothing for those buttons to act on — and apps are only attached on the first upload of a new plugin, which is why no amount of re-uploading fixed it. The package it was created from used the Codex compatibility layout with "type": "http", which isn't a transport the Agent Plugins schema knows (stdio, streamable-http, sse).

A fresh plugin created from the portable package in this PR attached an app immediately, connected over OAuth, and scanned 46 tools.

What changed

Packaging (scripts/package.ts) — the ZIP is now a portable Agent Plugins bundle: root plugin.json and mcp.json generated from the repo's compatibility manifests, with skills/, assets/, LICENSE and README.md. The compatibility manifests and .app.json are no longer shipped. The repo's own files are untouched, so Claude Code, Cursor and Codex marketplace installs are unaffected.

  • mcp.json declares streamable-http, and extensions["com.openai"].auth with client.mode: "cimd", baseScopes and defaultScopes: [].
  • plugin.json keeps portable identity at the root and moves interface under extensions["com.openai"].
  • The package name ships as checkly again; the app-6a7f1be5… override is gone. That value was a ChatGPT app ID reused as a package name, and the dashboard reports can_edit_package_name: true with its own plugins_… identifier.

CIMD instead of a registered OAuth client — auth.checklyhq.com advertises no registration_endpoint, so DCR isn't available. A Client Identifier Metadata Document works and needs no client registration: the authorize request goes out with client_id=https://chatgpt.com/oauth/client.json and PKCE. No Auth0 work needed.

defaultScopes: [] — this is the one with teeth. ChatGPT's connection form defaults to requesting every scope the server advertises, including checkly:environment-variables:write. Without the override, the scope narrowing from c3042bd was undone at connect time and the two environment-variable tools OpenAI flagged came back. With it, the authorize request carries exactly our 14 scopes, and the tool scan shows only list-account-environment-variables and get-account-environment-variable.

Review material (.codex-plugin/plugin.json) — the submission form shows test cases read-only ("managed by your plugin ZIP"), so five positive and three negative cases live in the manifest under extensions["com.openai"].review, along with commerce and release notes. The positive prompts name checks that exist in the review demo account.

On the negative test cases

A negative case here can't rest on "the MCP server has no tool for it". Diffing the server's advertised tools against the scan shows only two are withheld from the plugin:

create-account-environment-variable
update-account-environment-variable

Everything else is exposed, and the checkly skill's generic checkly api pass-through covers most account operations besides. So the three negatives are:

  1. Secrets - setting CHECKLY_API_KEY as an account environment variable. The only case grounded in a withheld tool, and it answers the October rejection about tools accepting raw secret values.
  2. Actions outside monitoring - rolling back a deploy after checks start failing. This replaced "deploy the Playwright Check Suite", which was wrong: the checkly skill lists Playwright Check Suites among the things it handles, so the plugin should engage with that prompt rather than stay out.
  3. Billing - upgrading the subscription and changing the payment method.

Incidentally, get-error-group-root-cause-analyses is exposed even though the package never requests checkly:error-groups:read; the server gates it on rca:read. Positive case 5 depends on that tool, so it's worth knowing the scope list doesn't need widening for it.

Not in this PR

Reviewer credentials, the demo walkthrough URL and country availability are portal-side and already filled in the dashboard. Still outstanding before submission: readOnlyHint / openWorldHint / destructiveHint with a justification on every tool, which has to come from the MCP server itself.

The plugin detail page shows the checkly MCP server as "Not connected"
with Connect, Reconnect and Rescan all disabled, so the submission can't
proceed. OpenAI reads a server's OAuth settings from
`extensions["com.openai"].auth`, which the repo's .mcp.json never
declares - so the portal has no auth configuration to connect with.

Add that block inside the submission ZIP the same way the manifest name
is already swapped, leaving the repo file alone for marketplace
installs. The rewrite is additive only, because the portal rejects two
things we tried first:

  `$schema` is invalid: Extra inputs are not permitted.
  Adding, removing, or replacing an MCP server isn't supported for
  existing plugins.

The second one fires when `type` moves to the Agent Plugins transport
name `streamable-http`, so the transport, URL and scopes stay as the
repo declares them.
Two additions to the OAuth block in the submission ZIP.

`client.mode: "cimd"` gives the portal a registration method. The repo
declared none, DCR is unavailable on auth.checklyhq.com (no
registration_endpoint) and a `provided` client would need a client ID we
don't have. A Client Identifier Metadata Document needs no registration
at all, and a connector created with it completed OAuth against the
tenant, so it demonstrably works.

`defaultScopes: []` stops the fallback. ChatGPT's connection form shows
default scopes pre-selected to all 17 scopes the server advertises,
including checkly:environment-variables:write, and documents that they
are requested in addition to the base scopes. Without the override the
scope narrowing from c3042bd is undone at connect time and the two
environment variable tools OpenAI flagged come back.
The ZIP replaced the manifest `name` with app-6a7f1be5a9e48191b23a7b6ae73cf976
on the belief that the directory pins the name to an existing listing ID and
rejects anything else. The portal's own API contradicts that: the plugin
record reports can_edit_package_name true and app_id null, and its identifier
is plugins_6ae9d4ac3ddc81919bedf3fef36760da, assigned on creation. The name we
were sending is just echoed back as the package name.

The hex came from asdk_app_6a7f1be5a9e48191b23a7b6ae73cf976, a ChatGPT app
reference added in 57dbc8e and reverted in 131b6c4, re-prefixed and reused as
a package name.

Ship `checkly` instead, as the repo manifest declares it. The .app.json
stripping and the OpenAI auth block are unaffected.
The listing uploaded from the compatibility layout came back with
transport "http" and no app attached, and the portal's own diagnostic
says apps are created "for the first supported remote MCP in a new
plugin upload". `http` isn't a transport the Agent Plugins schema knows
- it allows stdio, streamable-http and sse - so the compatibility
manifest validated while very likely not registering as a supported
remote MCP.

Build the ZIP as the documented portable package instead: a root
plugin.json carrying portable identity plus extensions["com.openai"],
and a root mcp.json declaring streamable-http with the OAuth block.
Both are generated from the repo's compatibility manifests, which stay
as they are for marketplace installs.

This also resolves the earlier "`$schema` is invalid" rejection: that
was the compatibility .mcp.json, which is validated against a closed
model. The portable mcp.json is specified to carry $schema.
The portal's review wizard shows test cases as read-only: "Review test
cases are managed by your plugin ZIP". It wants exactly five positive
cases and exactly three negative ones, so they live in the manifest
under extensions["com.openai"].review and travel with the package.

The positive cases cover the flows the listing advertises - check
status, inspecting a failing result, creating an API check, running a
check on demand, and root cause analysis - and name the tools the
scan actually discovered. The negative cases are the documented
limitations: environment variable writes, Playwright Check Suites, and
billing.

toPortableManifest now merges the manifest's own extensions["com.openai"]
with the interface it moves there, so review and publication reach the
portable plugin.json.

demo_recording_url is left out: there's no recording yet, and the portal
accepts the walkthrough URL and release notes directly.
The demo account reviewers use has a check called Login, so the prompt
names it instead of a 'homepage browser check' that doesn't exist there.
Case 2 now asks about the account's API checks, and case 5 names the
Products API check instead of a checkout check that doesn't exist there.
Case 2's expected behaviour follows the plural prompt.
api.example.com has no DNS record, so a check created against it would
error instead of demonstrating the non-200 assertion. example.com is
reserved for documentation and answers 200.
"Deploy the Playwright Check Suite in this repository to Checkly" isn't
a case where the plugin should stay out: the bundled checkly skill lists
Playwright Check Suites among the things it handles, and its generic
`checkly api` pass-through covers most account operations besides.

Comparing the server's tool list against the scan confirms only
create-account-environment-variable and update-account-environment-variable
are withheld from the plugin, so a negative case can't rest on a missing
tool. Use an action outside monitoring instead: rolling back a deploy
follows naturally from the plugin reporting failing checks, which is
exactly where a mistaken trigger would happen.
@stefanjudis
stefanjudis merged commit 666ab65 into main Nov 4, 2026
1 check passed
@stefanjudis
stefanjudis deleted the stefanjudis/openai-mcp-auth-declaration branch November 4, 2026 10:19
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant