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).
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. npm rejects publishes from any other workflow or environment — the binding is case-sensitive. Packages are published with provenance attestations (NPM_CONFIG_PROVENANCE). - 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 tags — versions are currently date-based (CalVer, e.g.
2026.7.4) for all packages. (Planned: the TypeScript packages move to semver managed by changesets, while Python stays CalVer — see #4463.) - Publishes each package as an independent matrix job (
fail-fast: false— one package's failure never blocks another): checkout at the release tag → install → skip if this version already exists on the registry → run the package's tests → build → publish. - Creates the GitHub release with generated notes.
Runs are triggered by a daily schedule or manually via workflow_dispatch on release.yml.
When a publish fails
A failed matrix leg means that one package didn't publish; everything else that succeeded stays published (the version-exists guard makes retries idempotent).
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.
- 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 main don't apply to a re-run (secrets and registry-side registrations are read fresh).
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 since the last 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.
- Re-dispatch
release.ymlexpecting it to retry a failed version — a fresh run mints a new version.
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.
The release process is evolving — see #4463 for planned changes (manual GitHub-release triggering, semver via changesets for the TypeScript packages). This document describes the process as it works today and will be updated when that lands.