Repository navigation
[Discovery] Publish to the MCP Registry after each stable release - #1132
Conversation
The MCP Registry workflow only ever validated server.json; nothing published it. Release now calls it with the released version: it checks out the release tag, waits for npm to serve @pascal-app/mcp with its mcpName, publishes, and fails unless the registry lists the version.
Codex Review SummaryThis comment shows the latest Codex review activity on this pull request.
ℹ️ About Codex in GitHubYour team has set up Codex to review pull requests in this repo. Reviews are triggered when you
Codex reacts with 👀 while any review is running, comments if it has suggestions, and reacts with 👍 once all reviews finish with no findings. |
Aymericr
left a comment
There was a problem hiding this comment.
Reviewed, and this looks right to me.
releasealready exposesversionandchannel, and every package is tagged in lockstep, sorefs/tags/@pascal-app/mcp@<version>will exist when themcp-registryjob runs (@pascal-app/mcp@1.0.6is there today).- The registry check before publishing makes reruns safe, and the final listing check means a green run actually proves the version is in the registry.
hosted-matches-manifestkeying offinputs.check-hostedkeeps it out of push, PR, andworkflow_callruns, which is what we want.
One small nit, not blocking: a manual dispatch with publish=true and no version publishes whatever server.json is on the dispatched ref without the tag check. Making version required whenever publish is set would close that gap.
Before this is useful, MCP_REGISTRY_PRIVATE_KEY has to be added (repo or npm environment). Until then the next stable release will finish with a red mcp-registry job after npm has already published. After merge, 1.0.6 still needs a one-time dispatch. I'll merge once quality, cli-smoke, and Bugbot are green.
There was a problem hiding this comment.
💡 Codex Review
Here are some automated review suggestions for this pull request.
Reviewed commit: e38db492f2
ℹ️ About Codex in GitHub
Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you
- Open a pull request for review
- Mark a draft as ready
- Comment "@codex review".
If Codex has suggestions, it will comment; otherwise it will react with 👍.
Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".
| # A beta leaves server.json on the last stable version, which is already listed. | ||
| mcp-registry: | ||
| needs: release | ||
| if: inputs.dry-run == false && needs.release.outputs.channel == 'latest' |
There was a problem hiding this comment.
Exclude prerelease recovery runs from registry publishing
When a partial beta release is recovered with the documented bump=none, the release keeps its *-beta.* version but NPM_TAG/the channel output is set to latest because only the literal beta input selects the beta channel. This condition therefore invokes the registry workflow with the prerelease version, while sync-agent-versions.ts intentionally leaves server.json on the previous stable version; the called workflow then fails its VERSION != EXPECTED_VERSION check. A recovery that otherwise successfully publishes or tags the beta is consequently reported failed. Gate this job on the released version containing no prerelease suffix, or expose an explicit stable-release output instead of inferring stability from channel.
Useful? React with 👍 / 👎.
…ublish-after-release-53c2 Co-authored-by: Aymeric Rabot <Aymericr@users.noreply.github.com>
…pe the signing key - Hard-code the server name and package, check the version is a stable semver with no line breaks, and pass it to later steps as a step output. - Run publish in the mcp-registry environment with a concurrency group, and delete the registry token afterwards. - Require version on manual dispatch; pass only MCP_REGISTRY_PRIVATE_KEY from release.yml instead of inheriting every secret. - Document the CI-only DNS key, its environment and its rotation. Co-authored-by: Aymeric Rabot <Aymericr@users.noreply.github.com>
There was a problem hiding this comment.
Cursor Bugbot has reviewed your changes using high effort and found 1 potential issue.
❌ Bugbot Autofix is OFF. To automatically fix reported issues with cloud agents, enable autofix in the Cursor dashboard.
Reviewed by Cursor Bugbot for commit 04c9e4c. Configure here.
- validate resolves the release tag to its commit and outputs it; publish checks out that commit, confirms the tag still points at it, and repeats the full manifest check (name, version, package entry, remote) before login. - Check the version input before any checkout, in both jobs. - Delete ~/.config/mcp-publisher/token.json after publishing, keeping the older token path as a fallback. - Query npm at registry.npmjs.org from outside the checkout. - release.yml: call the registry workflow only for a stable @pascal-app/mcp version. - Docs: the mcp-registry environment as it is set up, the secret's location, and how publish runs queue. Co-authored-by: Aymeric Rabot <Aymericr@users.noreply.github.com>

What does this PR do?
Root cause: no workflow in this repo has ever published
server.jsonto the MCP Registry.mcp-registry.ymlonly validates the manifest, and publishing was a manual owner step (wiki/npm-release.md). The green run after the 1.0.6 release (run 39880562788) was just thepushtrigger'svalidatejob, so a green result said nothing about the registry.Changes:
mcp-registry.ymlalso runs as a reusable workflow (workflow_call) and by manual dispatch, withversionandpublishinputs.versionis required for dispatch, so every publish run is tied to a release tag.app.pascal/editor, package@pascal-app/mcpand remotehttps://editor.pascal.app/api/mcpare hard-coded. The version check and the full manifest check are defined once at workflow level, and both jobs run them.validatechecks the version input before any checkout. The version must be a stableX.Y.Z(anchored regex, no CR or LF). It then checks out the@pascal-app/mcp@<version>tag, resolves the tag to its commit, and outputs that commit SHA. It also runs the full manifest check.publish(needsvalidate, environmentmcp-registry, concurrency groupmcp-registry-publishwithout cancel-in-progress):checks the version input, and that validate reported a 40-hex commit, before any checkout;
checks out that exact commit, not the tag name. It fails if HEAD differs or the tag no longer points at that commit;
repeats the full check before login, and fails on any mismatch:
server.json: name, version, exactly one package entry (registry type, registry base URL, identifier, version, runtime hint, transport) and exactly the one remote URL;packages/mcp/package.json: name,mcpNameand version.Later steps get the version only from this step's output, and nothing goes to
$GITHUB_ENV;waits up to 10 minutes for
npm view --registry https://registry.npmjs.org @pascal-app/mcp@<version> mcpNameto returnapp.pascal/editor, run from outside the checkout;skips the publish if the registry already lists the version. Otherwise it runs
mcp-publisher login dns --domain pascal.appandmcp-publisher publish;deletes
~/.config/mcp-publisher/token.json(and~/.mcpregistry_registry_tokenas a fallback) in anif: always()step;fails unless
/v0/servers/app.pascal%2Feditor/versionsthen lists the version.hosted-matches-manifestruns only when thecheck-hosteddispatch input is set, and it doesn't gate the publish.contents: readpermissions, checkout SHA pin andmcp-publishersha256 check.release.yml: a newmcp-registryjob runs afterreleasefor non-dry-runlatestreleases. It runs only when the released@pascal-app/mcpversion, taken frompackages/mcp/package.jsonas a newmcp-versionoutput, contains no-, so abump=nonererun after a beta doesn't publish. It passes onlyMCP_REGISTRY_PRIVATE_KEY, through the declaredworkflow_call.secrets, instead ofsecrets: inherit. GitHub needs that pass-through even though the secret lives only in the environment.wiki/npm-release.md, README): the automated flow, the CI-only signing key and themcp-registryenvironment, key rotation, publish queueing, and recovering a missed version.Version sourcing: this was already in place.
scripts/sync-agent-versions.tsrewritesserver.jsonfrompackages/mcp/package.json, andskills:validatefails on drift. This PR doesn't change it.Server card: it is served by the hosted deployment, not this repo. Top-level
"version": "1.0"is the card's format version.serverInfo.versionand the API catalog follow whatever@pascal-app/mcpthe deployment runs, so fixing them is a hosted-deployment change.hosted-matches-manifestflags the mismatch when dispatched.Maintainer action needed (repository admin)
The
mcp-registryenvironment exists, limited tomain, with no required reviewer.pascal.appTXT record (v=MCPv1; k=ed25519; p=<public key>). Neither of the two existingv=MCPv1records onpascal.appis CI's. Record CI's public key inwiki/npm-release.md#the-ci-signing-key.MCP_REGISTRY_PRIVATE_KEYin themcp-registryenvironment only. It must never be a repository secret or a secret of another environment.@pascal-app/mcp@*tags is recommended. Setting it up needs a repository admin.This PR adds no secrets and changes no environments or rulesets. Until the secret exists, the
publishjob fails with an error naming it. 1.0.6 is already in the registry, so no manual publish is needed after merge.How to test
actionlint .github/workflows/mcp-registry.yml(actionlint 1.7.7) reports nothing, and shellcheck is clean on the two shared check scripts.release.ymlpasses plain actionlint; its only shellcheck findings are in lines this PR doesn't touch.1.0.6passes. These fail before checkout:1.0.6-beta.1,v1.0.6,01.0.6or an empty valueversion=1.0.6, and so do the real@pascal-app/mcp@1.0.6files. Each of the following fails and writes no output:server.jsonname, version, or a multi-line versionpackage.jsonname,mcpNameor versionvalidateresolves the tag and outputs its commit.publishpasses when it is handed that commit, and refuses when it is handed a different commit, when the checkout differs from the commit it was handed, or when the tag is moved after validation. The commit check also passes against the live@pascal-app/mcp@1.0.6tag.MCP Registryrunsvalidateonly.publishandhosted-matches-manifestare skipped.I did not run a registry publish.
Screenshots / screen recording
N/A — non-visual change
Checklist
bun devbun checkto verify)mainbranchNote
Medium Risk
Introduces automated registry publishing with a DNS signing secret and strict tag/commit pinning; misconfiguration or key issues could block releases or publish wrong manifests until admins complete environment setup.
Reviewed by Cursor Bugbot for commit d2e96ca. Bugbot is set up for automated code reviews on this repo. Configure here.