Skip to content

ci: publish server.json to the Official MCP Registry from GitHub Actions - #7

Merged
okwasniewski merged 1 commit into
tester-army:mainfrom
lil-bsz:ci/publish-mcp-registry
Sep 21, 2026
Merged

okwasniewski merged 1 commit into
tester-army:mainfrom
lil-bsz:ci/publish-mcp-registry

Conversation

@lil-bsz

@lil-bsz lil-bsz commented Sep 21, 2026 •

Copy link
Copy Markdown
Member

Why

Publishing io.github.tester-army/testerarmy to the Official MCP Registry from a laptop only works for an org Owner: the registry grants an organisation's io.github.<org>/* namespace to GitHub users whose org role is Owner (its github_at.go and authentication docs), and refuses ordinary members with "only io.github.lil-bsz/* allowed". Public membership makes no difference.

The route that needs nobody's personal login is GitHub Actions OIDC: the registry grants io.github.<repository owner>/* to any workflow running in this organisation's repositories. So the publish moves into CI. Tracked in Linear GTM-86 (Listings project).

What changes

.github/workflows/publish-mcp-registry.yml runs when server.json (or the workflow file) lands on main, and on demand from the Actions tab (main only). Steps:

  1. install mcp-publisher 1.8.1, verifying the download against the release checksums
  2. mcp-publisher validate server.json (the registry's own validator, schema plus semantic rules)
  3. skip cleanly if this version is already in the registry, so re-runs and later workflow edits stay green
  4. login github-oidc, publish
  5. confirm the exact version entry exists (the step fails on a 404) and print it plus the Glama page link in the job summary

Permissions are contents: read plus id-token: write on the publish job only. No pull_request trigger, no secrets, no npm code runs in the job, so fork PRs cannot reach anything. The job declares the mcp-registry-publish environment so an Owner can add a deployment branch rule and a required reviewer later without a code change; until then the if: github.ref == 'refs/heads/main' guard is the gate, because the registry authorises on the repository owner alone and never checks the branch.

Merging this PR triggers the first publish of version 1.0.0 (the workflow file is in its own path filter and server.json is already on main).

scripts/check-plugin-manifests.sh and validate-plugin.yml now cover server.json: valid JSON, version equal to plugin.json, remotes[0].url equal to the MCP files. It runs on pull requests that touch server.json, so a version bump that forgets the registry manifest fails before merge. actions/checkout goes to v5 in both workflows ahead of GitHub removing Node 20 from runners on September 23.

glama.json names the maintainers (Oskar, Szymon, Bartek) who may claim the Glama pages that the registry entry will generate. Glama, PulseMCP and the other aggregators ingest the official registry.

Verification

  • actionlint clean on both workflows; YAML parses
  • mcp-publisher validate server.json against the live registry: valid
  • consistency script passes on the tree and fails when server.json is bumped alone (tested)
  • exact version endpoint checked live: 200 with the expected shape for a published server, 404 for ours today
  • checksum file format and tarball layout confirmed against the v1.8.1 release assets
  • not exercised: the OIDC exchange itself, which only runs inside Actions on this repo; if the first run fails, the Actions log names the step

After merge

  1. Watch the "Publish to MCP Registry" run; the job summary shows the entry. Manual check: https://registry.modelcontextprotocol.io/v0/servers?search=io.github.tester-army/testerarmy
  2. Claim the Glama pages once they appear (usually within days): https://glama.ai/mcp/connectors/io.github.tester-army/testerarmy and https://glama.ai/mcp/servers/tester-army/cli, signed in to glama.ai with GitHub as one of the maintainers in glama.json.
  3. Optional hardening for an Owner: Settings > Environments > mcp-registry-publish, deployment branches limited to main, add a required reviewer.

🤖 Generated with Claude Code

Review in cubic

The registry grants an organisation's io.github.<org>/* namespace only to
org Owners on a personal login; ordinary members are refused, which is what
blocked the manual publish. Workflows in this organisation's repositories get
the namespace through GitHub Actions OIDC, so the publish moves into CI.

- .github/workflows/publish-mcp-registry.yml: runs when server.json or the
  workflow lands on main, and on demand (main only). Validates with the
  registry's own validator, skips versions that are already published,
  publishes with login github-oidc, then confirms the exact version entry
  and links the Glama page in the job summary. Publisher pinned to 1.8.1
  with the release checksum verified; no npm code runs next to the OIDC
  token.
- scripts/check-plugin-manifests.sh + validate-plugin.yml: server.json joins
  the consistency check (version equals plugin.json, remote URL equals the
  MCP files) and runs on pull requests. actions/checkout bumped to v5 ahead
  of the Node 20 removal from runners on 2026-09-23.
- glama.json: maintainers who can claim the Glama pages.

Refs Linear GTM-86.

Co-Authored-By: Claude Fable 5.1 <noreply@anthropic.com>

@cubic-dev-ai cubic-dev-ai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

1 issue found across 4 files

Prompt for AI agents (unresolved issues)

Check if these issues are valid — if so, understand the root cause of each and fix them. If appropriate, use sub-agents to investigate and fix each issue separately.


<file name=".github/workflows/publish-mcp-registry.yml">

<violation number="1" location=".github/workflows/publish-mcp-registry.yml:70">
P2: When the registry lookup gets a transient 5xx, rate limit, or network error, this conditional treats the version as unpublished and runs the publish path. Distinguish HTTP 404 from lookup errors and retry or fail before publishing.</violation>
</file>

Reply with feedback, questions, or to request a fix.

Re-trigger cubic

name=$(jq -r .name server.json)
version=$(jq -r .version server.json)
encoded=$(jq -rn --arg n "$name" '$n | @uri')
if curl -fsS -o /dev/null "$REGISTRY_URL/v0/servers/$encoded/versions/$version"; then

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P2: When the registry lookup gets a transient 5xx, rate limit, or network error, this conditional treats the version as unpublished and runs the publish path. Distinguish HTTP 404 from lookup errors and retry or fail before publishing.

Prompt for AI agents
Check if this issue is valid — if so, understand the root cause and fix it. At .github/workflows/publish-mcp-registry.yml, line 70:

<comment>When the registry lookup gets a transient 5xx, rate limit, or network error, this conditional treats the version as unpublished and runs the publish path. Distinguish HTTP 404 from lookup errors and retry or fail before publishing.</comment>

<file context>
@@ -0,0 +1,99 @@
+          name=$(jq -r .name server.json)
+          version=$(jq -r .version server.json)
+          encoded=$(jq -rn --arg n "$name" '$n | @uri')
+          if curl -fsS -o /dev/null "$REGISTRY_URL/v0/servers/$encoded/versions/$version"; then
+            echo "::notice::$name@$version is already in the registry; bump version in server.json to publish again"
+            echo "skip=true" >> "$GITHUB_OUTPUT"
</file context>

@okwasniewski
okwasniewski merged commit edb5110 into tester-army:main Sep 21, 2026
2 checks passed
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.

2 participants