modelcontextprotocol / modelcontextprotocol/inspector

v1: automate npm deprecate in the main.yml publish job so v1 releases ship deprecated

Open
#2,104 0 comments 0 reactions 0 assignees View on GitHub

Nobody has claimed this yet.

chore CI v1
Dominant language
TypeScript
Stars
10.9k
Forks
1.5k
Avg merge
6h 17m
Merged PRs (30d)
151

Description

Problem

npm deprecate is a point-in-time write to the versions matching the range when you run it — not a standing rule. Nothing carries it forward to versions published later, and there is no deprecation metadata in package.json.

Result: every v1 release lands on npm un-deprecated. This is not hypothetical — 1.0.2 shipped that way and had to be fixed by hand after the fact. The repo-side notices from #1827 (READMEs, runtime stderr banners) are a separate mechanism and do not touch registry metadata; #1816 tracked the npm deprecate run as a one-off manual step, and there is nothing to make it repeat.

The same trap already bit once before, silently: 2.0.0-rc.1/2/3 were published after #1816's run and are un-deprecated to this day. That one is benign (they are v2 prereleases and should not carry a v1 notice) but it is the same root cause.

Proposal

Add a deprecate step to the publish job in .github/workflows/main.yml, after npm run publish-all, so a v1 release is deprecated the moment it is published.

Deprecate the exact version, not a range

The range recorded in #1816 is @"<2.0.0". Do not automate that range. In semver a prerelease sorts below its release, so <2.0.0 matches 2.0.0-rc.1, 2.0.0-rc.2 and 2.0.0-rc.3. Verified against the live registry — a <2.0.0 deprecate today would stamp all three v2 release candidates with "v1 is deprecated. Upgrade to v2."

Automate on the version being published instead. It is exact, idempotent, and immune to this class of range rot:

- name: Deprecate the published v1 packages
  run: |
    set -euo pipefail
    VERSION="$(node -p 'require("./package.json").version')"
    MSG="v1 is deprecated. Upgrade to v2: npm i @modelcontextprotocol/inspector@latest. v1 gets security fixes only, published under the v1-latest tag."
    # An empty message LIFTS deprecation — fail loudly rather than silently un-deprecating.
    [ -n "$MSG" ] || { echo "refusing to run with an empty message"; exit 1; }
    for pkg in inspector inspector-cli inspector-client inspector-server; do
      npm deprecate "@modelcontextprotocol/${pkg}@${VERSION}" "$MSG"
    done
  env:
    NODE_AUTH_TOKEN: ${{ secrets.NPM_DEPRECATE_TOKEN }}

If the range form is ever wanted again, the correct spelling is @"<2.0.0-0" — the -0 excludes prereleases of 2.0.0.

The message must stay in sync

The wording above is byte-identical to what is already on 1.0.0 / 1.0.1 / 1.0.2 across all four names, and to the runtime banners added in #1827. #1827 called out deliberately that the banner and the npm warning use identical wording. If this step is added, that string now lives in a third place — worth a comment pointing at cli/src/cli.ts, server/src/index.ts and client/bin/start.js.

The catch: this needs a stored npm token

Publishing currently uses OIDC trusted publishing (id-token: write, NPM_CONFIG_PROVENANCE: "true", no NODE_AUTH_TOKEN anywhere). The repo has no Actions secrets at all — neither repo-level nor on the release environment. That is a genuinely good posture.

OIDC covers publish. It does not cover npm deprecate, which is an ordinary authenticated registry write. Per the npm trusted publishers docs: "OIDC authentication supports the npm publish and npm stage publish commands. [...] Other npm commands such as install, view, or access still require traditional authentication methods." Confirmed empirically both ways during the 1.0.2 release: the publish job succeeded carrying only id-token: write and no NODE_AUTH_TOKEN, while an interactive npm deprecate authenticated as a logged-in user and then failed the registry write with HttpErrorAuthOTP: OTP required (EOTP). So automating this means introducing the first long-lived npm credential to a repo that currently has none, and that is a real security trade-off, not a formality — it should be a deliberate decision rather than a side effect of convenience.

If accepted, limit the blast radius:

  • Use a granular access token scoped to only the four @modelcontextprotocol/inspector* packages, with write permission and an expiry.
  • Store it as an environment secret on release, not repo-wide, so it inherits the existing required-reviewer gate (dsp-ant, pcarleton, olaservo, cliffhall).
  • A granular/automation token also sidesteps the 2FA prompt. The publishing account is set to 2FA for authorization and writes, so an interactive npm deprecate fails with EOTP — which is exactly why this cannot simply be scripted locally without a human present.

Alternative if the token is unacceptable

Keep it manual but stop relying on memory: add the four commands to the v1 release checklist so they run immediately after the release is approved. Cheaper and introduces no credential, but it will be forgotten again — it already has been, twice.

Acceptance criteria

  • A v1 release published from v1/main ends with all four packages deprecated at that exact version, with no manual step
  • The step cannot deprecate anything outside the version just published (in particular, no v2 prerelease is ever touched)
  • An empty or unset message fails the job rather than lifting deprecation
  • The credential decision above is made explicitly, and the token (if any) is scoped and stored on the release environment

Context

  • #1816 — original dist-tag lock + deprecate phase, where the manual step and the <2.0.0 range originate
  • #1827 — repo-side deprecation notices and the shared wording
  • #1829 — why the dist-tag is v1-latest and not v1
  • #2080 — the 1.0.2 release that surfaced this

Contributor guide

Open the contributing guide

First steps

  1. Read the whole issue, then the project's contributing guide.
  2. Comment on the issue to say you are picking it up — it saves two people doing the same work.
  3. Fork the repository and make your change on a branch.
  4. Open a pull request that references the issue number.

Research direction

Start with the publish job in .github/workflows/main.yml, then review package.json and the existing wording in cli/src/cli.ts, server/src/index.ts, and client/bin/start.js. Confirm how the published version is obtained and how the release environment is configured before deciding whether the credential trade-off is acceptable. Done means the acceptance criteria pass without affecting v2 prereleases, and the token decision is explicit.

Written by the indexing model from the issue text.

Assessment

Tech stack
github-actions, node.js
Domain
ci-cd, devops, release
Issue type
Feature
Difficulty
4/5
Estimated time
3-5 days
Activity status
Active
Clarity
Mostly clear
Newbie friendliness
52/100

Get new issues in your inbox

A short digest of beginner-friendly GitHub issues.