Review findings: RELEASING.md claimed publishing happens exclusively from release.yml with no registry tokens, but typescript.yml and python.yml still carried release:[published] publish jobs — never fired (releases are created with GITHUB_TOKEN, which suppresses workflow triggers) and unable to succeed under OIDC (typescript.yml's still referenced the deleted NPM_TOKEN). Remove both jobs and their release triggers; the workflows are now pure CI. Also: - add skip-existing to the PyPI publish action so re-runs tolerate already-uploaded files, and describe the per-registry guard accurately in RELEASING.md (npm aborts; PyPI skips) - correct the fresh-dispatch note: same-day dispatches collide on the date-granular tag rather than minting a new version Co-Authored-By: Claude Fable 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01AViME1R9ES2UXT1uQDx1TG
4 KiB
Releasing
How the packages in this repository are published, and what to do when a publish fails.
How publishing works
All packages publish exclusively from the release.yml GitHub Actions workflow, gated by the release environment (a required reviewer must approve each deployment). Runs are triggered on a daily schedule or manually via workflow_dispatch.
Authentication is OIDC trusted publishing on both registries — there are no registry tokens.
- npm (TypeScript servers): each
@modelcontextprotocol/*package is registered on npmjs.com with a trusted publisher bound to this repository, workflow filenamerelease.yml, and environmentrelease(the binding is case-sensitive). Packages publish with provenance attestations. - PyPI (Python servers): published via PyPI trusted publishing using
pypa/gh-action-pypi-publish.
A release run:
- Detects changed packages since the last release tag — a package counts as changed if any
.py,.ts, or.mdfile in its directory changed (READMEs ship inside the published artifacts). - Stamps versions and pushes the release tag — versions are date-based (CalVer, e.g.
2026.7.4). - Publishes each changed package as an independent matrix job (
fail-fast: false— one package's failure never blocks another). Each job: checkout at the release tag → install → double-publish guard → run the package's tests (pluspyrightfor Python) → build → publish. The guard differs by registry: the npm job aborts if the version already exists; the PyPI action skips files already uploaded (skip-existing). - Creates the GitHub release with generated notes.
When a publish fails
A failed matrix leg means that one package didn't publish; everything that succeeded stays published.
Preferred: re-run the failed jobs on the same run.
gh run rerun <run-id> --failed --repo modelcontextprotocol/servers
- A re-run is still a
release.ymlrun in thereleaseenvironment, so it satisfies the trusted-publisher binding. - It re-runs only the failed legs, checked out at the original release tag — it publishes exactly the tagged code, and the double-publish guard keeps already-published packages safe.
- It needs a fresh
releaseenvironment approval, and the run must be complete first (approve or reject any pending deployments). - GitHub's re-run window is ~30 days from the original run, and re-runs execute the original workflow snapshot — workflow fixes on
maindon't apply to a re-run.
Otherwise: let the next release pick it up. If the re-run window has closed (or the fix required a workflow change), the failed version simply never exists on that registry — that's benign; npm and PyPI version histories don't need to match. The package publishes at the next version, provided it has a qualifying change (.py, .ts, or .md) since the last release tag.
Never:
- Publish manually with an npm token or from a laptop — there are no registry tokens, and manual publishes would break the provenance/trust chain.
- Dispatch a fresh
release.ymlrun expecting it to retry a failed version — versions are date-granular, so a same-day dispatch collides with the existing tag, and a later dispatch mints a new version. Neither retries the failed one.
Environment approvals
The release environment's required-reviewer list is configured in the repository settings (Settings → Environments → release). Reviewer rights come only from that list — repository admin does not confer deployment approval.
Planned changes to this process — semver via changesets for the TypeScript packages, publishing triggered by manually-created GitHub Releases — are tracked in #4463. This document will be updated when that work merges.