Skip to content

go-live 5/9: publish 2.0.0 (rc under next first; rollback plan written first) #1818

Description

@cliffhall

Phase 5 of 9 in the v2 go-live runbook — see #1804 (§6, §7). Depends on phase 4.

⚠️ IRREVERSIBLE — this publishes to npm. npm unpublish is effectively unavailable (72h window, disallowed once others depend on it).

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:

  1. npm dist-tag add @modelcontextprotocol/inspector@1.0.1 latest
  2. npm deprecate the bad 2.0.0
  3. ship 2.0.1

Confirm whoever cuts the release holds the npm rights to do all three.

Tasks

  • Publish 2.0.0-rc.1 under --tag next to the real registry and install it on a clean machine via npx @modelcontextprotocol/inspector@next. This is the only way to validate what only exists against live npm: provenance/OIDC minting, the files allowlist 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:verify green (the publish job already runs it as a gate).
  • Cut the release: tag 2.0.0 or v2.0.0 (the assert tolerates the leading v) targeting the post-merge main commit.
  • Confirm Docker latest moves to 2.0.0 and a 1-pinned tag is preserved for v1 users.
  • Verify npm view @modelcontextprotocol/inspector dist-tags → latest: 2.0.0, v1: 1.0.1.

Activity

  1. self-assigned this
    on Jul 27, 2026
  2. 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
  3. cliffhall commented on Jul 28, 2026

    @cliffhall
    MemberAuthor

    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 — for npm deprecate, put the message in a script file rather than pasting it.

    State to restore to

    latest      1.0.1
    v1-latest   1.0.1
    

    If 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 latest back — do this first, it stops the bleeding.

    npm dist-tag add @modelcontextprotocol/inspector@1.0.1 latest --otp=<code>

    Every npx @modelcontextprotocol/inspector immediately resolves to 1.0.1 again. Note this re-points latest at 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 latest automatically (the version is not a prerelease, so #1834's tag derivation gives it latest).

    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 publish of one package, so the multi-package partial failure that made v1's publish-all risky (#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.1

    The v1 tag is v1-latest, not v1 — npm rejects any dist-tag that parses as a SemVer range, and v1 parses as 1.x (found in #1816, fixed in #1829). The expected end state is:

    latest      2.0.0
    v1-latest   1.0.1
    
  4. cliffhall commented on Jul 28, 2026

    @cliffhall
    MemberAuthor

    2.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.3
    

    npx @modelcontextprotocol/inspector now resolves to v2 worldwide.

    Verification

    Check Result
    npm latest → 2.0.0 ✅
    npm v1-latest → 1.0.1 (escape hatch intact) ✅
    Cold node:22 container install from latest ✅ 2.0.0; --help and a real --cli tools/list against a live stdio server both work
    Docker latest moved ✅ digest identical to 2.0.0
    Docker image runs ✅ contains @modelcontextprotocol/inspector@2.0.0
    Open Dependabot alerts ✅ 0
    Open CodeQL alerts ✅ 0

    The 1-pinned Docker tag — closing as satisfied by 1.0.1

    This issue asked to confirm "a 1-pinned tag is preserved for v1 users". There is no 1 tag, and there never was — the workflow tags images by exact version only. What exists is ghcr.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 1 would 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 floating 1 is ever wanted, it needs adding to v1/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:

    1. ENEEDAUTH — NODE_AUTH_TOKEN pointed at a non-existent NPM_TOKEN secret, writing an empty _authToken into setup-node's .npmrc and 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)
    2. 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)
    3. No --tag on publish — npm publish defaults to latest regardless of prerelease status, so cutting 2.0.0-rc.1 would have pointed latest at 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:22 container and driving it end to end. latest stayed 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

    Both dev-scope, neither shipped. Their alerts are dismissed with per-alert reasoning — tolerable_risk for vitest, not_used for 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).

  5. added this to the v2.0.0 milestone on Jul 28, 2026
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Metadata

Metadata

Assignees

Labels

v2Issues and PRs for v2

Type

No type

Projects

No projects

    Milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions