Repository navigation
go-live 5/9: publish 2.0.0 (rc under next first; rollback plan written first) #1818
Description
Activity
- changed the title
[-]go-live 4/9: replace main's tree with v2[/-][+]go-live 5/9: publish 2.0.0 (rc under next first; rollback plan written first)[/+]on Jul 27, 2026 Rollback plan (the blocking prerequisite) — written, verified, ready
Verified 2026-07-28, before any 2.0.0 publish.
npm rights confirmed
npm access list collaborators @modelcontextprotocol/inspector— the person cutting the release (cliffhall) holds read-write, which covers all three rollback operations. Four other maintainers also hold read-write (ashwin-ant,fweinberger,jspahrsummers,ochafik-ant), so recovery is not single-person dependent.⚠️ All registry writes require a 2FA OTP. Under rollback pressure that means having an authenticator to hand. Learned in #1816: a long pasted command gets hard-wrapped by the terminal and the newline is stored verbatim — fornpm deprecate, put the message in a script file rather than pasting it.State to restore to
latest 1.0.1 v1-latest 1.0.1If 2.0.0 is broken
npm unpublish is effectively unavailable (72h window, disallowed once anything depends on it). Rollback is dist-tag surgery, in this order:
1. Move
latestback — do this first, it stops the bleeding.npm dist-tag add @modelcontextprotocol/inspector@1.0.1 latest --otp=<code>
Every
npx @modelcontextprotocol/inspectorimmediately resolves to 1.0.1 again. Note this re-pointslatestat a deprecated version, so installs will warn — correct and intended: a deprecation warning beats a broken tool.2. Deprecate the bad version, so anyone pinned to it is told why:
npm deprecate "@modelcontextprotocol/inspector@2.0.0" "2.0.0 was withdrawn: <reason>. Use @modelcontextprotocol/inspector@2.0.1 or later." --otp=<code>
Deprecation is retroactive but mutable — liftable later with an empty message.
3. Ship 2.0.1 with the fix, which reclaims
latestautomatically (the version is not a prerelease, so #1834's tag derivation gives itlatest).Docker rollback
The container image is a separate artifact on the same release trigger. If the image is bad but npm is fine, re-point the registry tag rather than re-cutting a release; if both are bad, the release-level rollback covers each.
What is NOT a valid rollback
npm unpublish— unavailable in practice; do not plan around it.- Deleting the GitHub release/tag — does nothing to npm. The package stays published. The
1.*tag ruleset (19859230) also blocks deleting or moving v1 tags outright; the 2.x tags are unprotected today, but deleting one still has no registry effect. - Force-pushing
main— does not unpublish, and breaks the merge-parent structure established in go-live 4/9: replace main's tree with v2 #1817.
Rollback is not needed for
A failed publish (workflow errors before or during
npm publish) consumes nothing and needs no rollback — fix and re-cut. The dangerous state is a successful publish of a broken artifact, which is what the above addresses.The narrow bad case is a partial publish, but v2 is not an npm workspace — it is a single
npm publishof one package, so the multi-package partial failure that made v1'spublish-allrisky (#1815) does not exist here.
Correction to this issue's last task
It reads:
Verify
npm view @modelcontextprotocol/inspector dist-tags→latest: 2.0.0,v1: 1.0.1The v1 tag is
v1-latest, notv1— npm rejects any dist-tag that parses as a SemVer range, andv1parses as1.x(found in #1816, fixed in #1829). The expected end state is:latest 2.0.0 v1-latest 1.0.12.0.0 is published. Phase 5 complete.
latest 2.0.0 ← was 1.0.1 v1-latest 1.0.1 next 2.0.0-rc.3npx @modelcontextprotocol/inspectornow resolves to v2 worldwide.Verification
Check Result npm latest→ 2.0.0✅ npm v1-latest→ 1.0.1 (escape hatch intact)✅ Cold node:22container install fromlatest✅ 2.0.0; --helpand a real--cli tools/listagainst a live stdio server both workDocker latestmoved✅ digest identical to 2.0.0Docker image runs ✅ contains @modelcontextprotocol/inspector@2.0.0Open Dependabot alerts ✅ 0 Open CodeQL alerts ✅ 0 The
1-pinned Docker tag — closing as satisfied by1.0.1This issue asked to confirm "a
1-pinned tag is preserved for v1 users". There is no1tag, and there never was — the workflow tags images by exact version only. What exists isghcr.io/modelcontextprotocol/inspector:1.0.1, multi-arch and intact at its own digest.That serves the purpose: v1 users have a working, stable pin. A floating
1would add a maintenance obligation (something must move it on every future v1 release) for little benefit on a line that is deprecated and takes security fixes only. Closing on that basis rather than creating the tag; if a floating1is ever wanted, it needs adding tov1/main's workflow, not just pushing once.What the RC process actually caught
Three release candidates, and they were not ceremony — the v2 publish job had never executed before, so every defect below was waiting for whoever cut the first v2 release:
ENEEDAUTH—NODE_AUTH_TOKENpointed at a non-existentNPM_TOKENsecret, writing an empty_authTokeninto setup-node's.npmrcand failing before OIDC was ever attempted. Would have failed 2.0.0 outright. (fix(ci): publish via OIDC trusted publishing, not a non-existent NPM_TOKEN #1836)- Missing npm CLI upgrade — trusted publishing needs npm >= 11.5.1; Node 22 bundles 10.x. Would have failed even after fixing Progress notifications #1. (fix(ci): publish via OIDC trusted publishing, not a non-existent NPM_TOKEN #1836)
- No
--tagon publish —npm publishdefaults tolatestregardless of prerelease status, so cutting2.0.0-rc.1would have pointedlatestat a release candidate. Caught before the first RC ran; the RCs then proved the fix. (ci: derive the npm dist-tag from the version instead of defaulting to latest #1834)
The RCs also prompted a dependency audit that cleared every prod-scope advisory (#1837) and three high-severity vite dev-server file-read advisories (#1841).
Each RC was validated by installing the published tarball into a cold
node:22container and driving it end to end.lateststayed on 1.0.1 through all three — the proof that the tag derivation worked.Rollback
Not needed. The plan remains valid if 2.0.0 turns out to be broken: dist-tag surgery, not unpublish.
Deferred, tracked
- chore(deps): migrate to eslint 10 to clear the brace-expansion DoS advisory #1838 — eslint 10 migration (blocked:
eslint-plugin-react-hooksdoes not accept eslint 10) - chore(deps): untangle the vitest/Storybook exact-pin knot to clear GHSA-p63j-vcc4-9vmv #1839 — vitest/Storybook exact-pin knot (blocked: circular exact peer pins,
npm ifails ERESOLVE)
Both dev-scope, neither shipped. Their alerts are dismissed with per-alert reasoning —
tolerable_riskfor vitest,not_usedfor esbuild (its dev-server mode is never invoked here; esbuild is reached only as a bundler via tsup and vite).Next: #1819 (triage and bulk-close the v1 backlog — note it is 125 PRs, not ~30).
Phase 5 of 9 in the v2 go-live runbook — see #1804 (§6, §7). Depends on phase 4.
Publish 2.0.0 from the post-swap
main.Write the rollback plan first
This is a blocking prerequisite, not a follow-up. Rollback for a broken 2.0.0 is dist-tag surgery, not unpublish:
npm dist-tag add @modelcontextprotocol/inspector@1.0.1 latestnpm deprecatethe bad 2.0.0Confirm whoever cuts the release holds the npm rights to do all three.
Tasks
2.0.0-rc.1under--tag nextto the real registry and install it on a clean machine vianpx @modelcontextprotocol/inspector@next. This is the only way to validate what only exists against live npm: provenance/OIDC minting, thefilesallowlist as npm actually packs it, the postinstall cascade's early-exit under a real dependency install, and the Docker job. A wasted prerelease number is cheap next to a broken 2.0.0.npm run pack:verifygreen (the publish job already runs it as a gate).2.0.0orv2.0.0(the assert tolerates the leadingv) targeting the post-mergemaincommit.latestmoves to 2.0.0 and a1-pinned tag is preserved for v1 users.npm view @modelcontextprotocol/inspector dist-tags→latest: 2.0.0,v1: 1.0.1.