Skip to content

MCP Registry publish: release: published can never fire, and workflow_dispatch publishes the ref's version (found by v0.76.0) #841

Closed

Description

@ffroliva

Two defects in the MCP Registry publish (#829), both found by v0.76.0 — the first release that could exercise it

#829 shipped in v0.76.0 and its live behaviour was recorded as "cannot be verified until this tag reaches main" in docs/LIVE_VERIFICATION_v0.76.0.md. It is verifiable now, and it did not work.


1. release: published can never fire — the Release is created with GITHUB_TOKEN

mcp-registry.yml triggers on:

on:
  release:
    types: [published]

v0.76.0 published a real GitHub Release (published=2026-09-16T06:41:28Z, draft=false, prerelease=false), the workflow file is on the default branch (develop), and gh run list --workflow=mcp-registry.yml returned zero runs, ever.

Mechanism — release.yml creates the Release with the default token:

- name: Create GitHub Release
  uses: softprops/action-gh-release@efb3536...  # v3

GitHub does not start new workflow runs from events created with the repository's GITHUB_TOKEN. So this trigger is structurally dead, not merely mistimed. #829's own caveat anticipated a default-branch resolution problem and was correct that something would block the first run — but the cause is the token, and that does not resolve itself at the next release.

Fix options: trigger on the tag push (on: push: tags: ['v*']) and gate on PyPI availability; or create the Release with a PAT / GitHub App token. The tag-push route is simpler, but note the ordering constraint the workflow documents — mcp-publisher reads the mcp-name: token out of the published PyPI README, so it must run after the PyPI upload, not merely after the tag.

2. workflow_dispatch publishes whatever server.json says on the dispatched ref

I hit this immediately, and it is worth recording because the run goes green while publishing the wrong thing.

Having found (1), I used the documented re-run path: gh workflow run mcp-registry.yml --ref develop. It completed successfully. The registry then read:

name:    io.github.ffroliva/gflow-cli
version: 0.75.0          <- the PREVIOUS release
status:  active

Because at that moment origin/develop:server.json still said 0.75.0 — the v0.76.0 bump was on main via the release, and the back-merge (#840) had not landed. The dispatch did exactly what it was told and published a listing pointing at a superseded version, with no failure anywhere.

This is #839's hazard realised: "the registry never runs the command it describes — so a bump that forgets it publishes a listing pointing at a version that is no longer current, and nothing downstream notices." Here nothing "forgot" the bump; the dispatch simply ran against a ref that predated it.

Fix: have the job assert that server.json's version matches the release being published (and/or the latest on PyPI) and fail if not, rather than publishing whatever the ref happens to carry. A workflow_dispatch input for the intended version, checked against the file, would make the mismatch loud.


Immediate state

Why this is the useful kind of failure

The row was recorded as not verified, with a reason rather than omitted, precisely so it would get checked the moment it became checkable. It was, and it caught two defects in one pass — one structural, one self-inflicted. A release note that had simply claimed the channel worked would have left a stale listing in the registry indefinitely, and nothing downstream compares it to PyPI.

Activity

  1. ffroliva commented on Sep 16, 2026

    @ffroliva
    OwnerAuthor

    Resolved for v0.76.0 — defect 2 corrected, defect 1 still open

    After #840 (the back-merge) landed, develop carried the bump and a second workflow_dispatch published correctly:

    origin/develop:server.json  ->  0.76.0  (lines 6 and 17)
    gh workflow run mcp-registry.yml --ref develop  ->  completed success
    
    registry.modelcontextprotocol.io/v0/servers?search=gflow
      io.github.ffroliva/gflow-cli  0.75.0  active
      io.github.ffroliva/gflow-cli  0.76.0  active
    

    The registry retains version history, so both entries are active and 0.76.0 is published. PyPI was correct throughout.

    Defect 1 is unaffected and remains open. The release: published trigger still cannot fire, because release.yml still creates the Release with the default GITHUB_TOKEN. Every release from here needs the manual dispatch until that is fixed — which makes defect 2 a recurring trap rather than a one-off, since the dispatch has to happen after the back-merge or it republishes the previous version.

    That ordering is the thing to encode: today the correct sequence is tag → release → back-merge → dispatch, and nothing enforces it. A version assertion in the job (compare server.json against the latest on PyPI and fail on mismatch) would make the wrong order loud instead of green.

  2. added a commit that references this issue on Sep 16, 2026
  3. ffroliva commented on Sep 16, 2026

    @ffroliva
    OwnerAuthor

    Fixed in #843, merged to develop as f86bdec4. Leaving this open deliberately — the wiring has not run yet.

    What changed. release.yml now calls mcp-registry.yml (uses: + needs: build-and-publish) instead of relying on on: release: published. With no event there is nothing for the GITHUB_TOKEN rule to block. A user PAT was built first, reviewed, and then dropped: a fine-grained token expires within 366 days, which schedules a failure on a path that runs a few times a year — the same shape as this bug. needs: also enforces the PyPI-before-registry ordering structurally rather than by two files agreeing about step order.

    Defect 2 is fixed too: both triggers now require a version input, asserted against every version field in server.json, so a wrong-ref dispatch fails instead of publishing a superseded listing green.

    A third defect surfaced during review, and it was the most valuable find. release.yml fires on every v*.*.* tag including rc, and release: published fires for pre-releases (GitHub documents this explicitly). While the publish was dead this was latent — repairing it would have armed it, and a v0.77.0rc1 would have become the registry's active listing, which is what PulseMCP and GitHub's MCP gallery ingest. The call is now skipped for pre-releases, and the workflow refuses a pre-release version that reaches it by hand.

    What closes this issue: the first real release, v0.77.0. Unlike the PAT route, this design cannot be rehearsed with a throwaway Release — the trade-off is recorded in the PR rather than glossed. Five regression tests lock the wiring in the meantime, each verified to fail against a broken variant.

  4. ffroliva commented on Sep 16, 2026

    @ffroliva
    OwnerAuthor

    Proven by the v0.77.0 release

    Left open deliberately until now: the repaired wiring is a call from release.yml, not an event, so it could not be rehearsed before a real tag push. v0.77.0 was its first exercise.

    run 35137150483  (tag v0.77.0)
      build-and-publish       completed/success
      mcp-registry / publish  completed/success   <- had NEVER run once before
    

    Verified downstream rather than trusting a green job:

    channel result
    PyPI gflow_cli-0.77.0-py3-none-any.whl + sdist live
    MCP Registry io.github.ffroliva/gflow-cli — 0.77.0 active

    Both defects closed: the trigger fired automatically from the tag with no manual dispatch, and the registry carries the released version rather than a superseded listing.

    The pre-release filter found during council review was not exercised (v0.77.0 is a stable tag) and remains pinned by test_the_release_skips_the_registry_for_a_prerelease, verified to fail against its removal.

    docs/LIVE_VERIFICATION_v0.77.0.md § 4 named the command to check after the tag and said to reopen this issue if the job did not appear. It appeared.

  5. added a commit that references this issue on Sep 16, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions