Skip to content

fix(ci): publish via OIDC trusted publishing, not a non-existent NPM_TOKEN - #1836

Merged
cliffhall merged 2 commits into
mainfrom
fix/publish-oidc-auth
Jul 28, 2026
Merged

fix(ci): publish via OIDC trusted publishing, not a non-existent NPM_TOKEN#1836
cliffhall merged 2 commits into
mainfrom
fix/publish-oidc-auth

Conversation

@cliffhall

Copy link
Copy Markdown
Member

The 2.0.0-rc.1 publish failed with npm error code ENEEDAUTH. Part of #1818.

Nothing was published and no version was consumed — the run failed before npm publish wrote anything. latest is still 1.0.1 and 2.0.0-rc.1 does not exist on npm.

Root cause — two problems, both in a job that had never run

Every previous release was cut from the v1 workflow. The v2 publish job has existed since the v2 branch was created but was executed for the first time tonight.

1. NODE_AUTH_TOKEN pointed at a secret that does not exist.

env:
  NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

The repo has no secrets at either repo or environment level — publishing moved to npm OIDC trusted publishing, which needs no token. With the secret absent, setup-node still writes its .npmrc with an empty _authToken, and npm fails ENEEDAUTH before OIDC is ever attempted.

2. The job never upgraded the npm CLI.

Trusted publishing requires npm >= 11.5.1; Node 22 bundles 10.x. So this would have failed even with the token line removed. The v1 workflow carries this step — v2 was missing it.

The fix

+      - name: Ensure npm CLI supports OIDC trusted publishing
+        run: npm install -g npm@^11.5.1
+
       - name: Install dependencies (root + all clients)

           npm publish --access public --provenance --tag "$NPM_TAG"
-        env:
-          NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

Both now match the v1 publish job, which did successfully publish 1.0.1 from v1/main in #1815.

The publish step carries a comment explaining why there is deliberately no NODE_AUTH_TOKEN, since re-adding it looks like an obvious fix and would silently break publishing again.

Bonus: #1834 is confirmed working

Before failing on auth, the run logged:

Publishing 2.0.0-rc.1 under dist-tag 'next'

So the dist-tag derivation is correct — latest was never at risk.

Why the RC was worth doing

This is exactly what §6 wanted an RC for. Both defects are invisible to pack:verify and to every local check: they only exist in the interaction between the workflow, GitHub's OIDC, and the live registry. Catching them on a throwaway prerelease number instead of on 2.0.0 is the whole point.

Also

README.md documented NPM_TOKEN as part of the publish setup — corrected to describe trusted publishing, including the npm version requirement and a warning that adding the token back breaks it.

🤖 Generated with Claude Code

https://claude.ai/code/session_01Txmv2qqv3yeKgRzoqXytzD

…TOKEN

The 2.0.0-rc.1 publish failed with `npm error code ENEEDAUTH`. Two causes, both
in the v2 publish job, which had never actually executed before — every prior
release was cut from the v1 workflow.

1. `NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}` pointed at a secret that does
   not exist (the repo has no secrets at repo or environment level — publishing
   moved to OIDC trusted publishing). `setup-node` therefore wrote an *empty*
   `_authToken` into its generated `.npmrc`, and npm failed ENEEDAUTH before
   OIDC was ever attempted. Removed, with a comment saying why it must not be
   "restored".

2. The job never upgraded the npm CLI. Trusted publishing requires npm
   >= 11.5.1 and Node 22 bundles 10.x, so this would have failed even with the
   token line gone. The v1 workflow has this step; v2 was missing it.

The RC did its job: it caught both against the real registry, and consumed
nothing — the run failed before `npm publish` wrote anything, so no version was
burned and the dist-tags are untouched.

Also confirms #1834 works: the log shows `Publishing 2.0.0-rc.1 under dist-tag
'next'` before the auth failure.

README corrected — it documented `NPM_TOKEN` as part of the publish setup.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Txmv2qqv3yeKgRzoqXytzD
@cliffhall cliffhall added the v2 Issues and PRs for v2 label Jul 28, 2026
The section had grown into eight undifferentiated paragraphs, the longest 1,965
characters, each describing one or two showcase configs. The content is good
reference material — the problem was purely that you could not find anything in
it or tell where one config's description ended and the next began.

Restructured, not rewritten. No facts removed:

- A summary table of all nine showcase configs, with what each demonstrates and
  its tracking issue, so the set is visible at a glance.
- One `####` subsection per config family, so each is linkable and skimmable.
- Enumerations that were buried in prose are now tables: the six MRTR presets,
  the four `trigger_*` spec-error tools and their status/code pairs.
- Step sequences (advertised extensions) and era contrasts (logging,
  subscriptions, tasks) are now bullets rather than run-on sentences.
- Two caveats that were parenthetical asides — browser `Mcp-Param-*` skipping,
  and `collect_elicitation` erroring on the modern leg — are now blockquotes,
  since both are things you hit and then have to go hunting for.

Longest line drops 1,965 → 620 characters. Verified every previously-documented
config and all technical identifiers survive.

Noted while auditing: `demo.json`, `oauth-step-up-demo.json`,
`url-elicitation-form.json` and `xaa-ema-http.json` exist in
`test-servers/configs/` but have never been documented here. Left alone —
that is a pre-existing gap, not a regression from this change.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01Txmv2qqv3yeKgRzoqXytzD
@cliffhall
cliffhall merged commit 954f9e8 into main Jul 28, 2026
6 checks passed
@cliffhall
cliffhall deleted the fix/publish-oidc-auth branch July 28, 2026 05:09
@cliffhall cliffhall mentioned this pull request Jul 28, 2026
cliffhall added a commit that referenced this pull request Jul 28, 2026
The v2 go-live release (#1818, phase 5 of #1804).

This is byte-identical to 2.0.0-rc.3 apart from dropping the prerelease suffix,
which is what makes rc.3's validation meaningful: the tree that ships is the
tree that was validated against the live registry.

`2.0.0` has no hyphen, so the derivation added in #1834 resolves it to `latest`
— this is the release that takes the tag from 1.0.1.

Validated across three release candidates:

- rc.1 caught two publish-path defects invisible to every local check —
  `NODE_AUTH_TOKEN` pointing at a non-existent secret (shadowing OIDC trusted
  publishing) and a missing npm CLI upgrade (trusted publishing needs
  >= 11.5.1; Node 22 bundles 10.x). Both fixed in #1836.
- rc.2 validated the dependency sweep (#1837) that cleared every prod-scope
  advisory using lockfile-only changes.
- rc.3 validated the vite 8.1.5 bump (#1841) closing three high-severity
  dev-server file-read advisories.

Each RC was verified with a cold `node:22` container install driving the
published tarball end to end — install, `--help`, and a real `--cli tools/list`
against a live stdio server. `latest` remained on 1.0.1 throughout, proving the
`--tag` derivation.

Zero open Dependabot alerts at time of release.


Claude-Session: https://claude.ai/code/session_01Txmv2qqv3yeKgRzoqXytzD

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
cliffhall added a commit that referenced this pull request Aug 1, 2026
…1880)

* docs: converge README Test servers section with main's restructure

Transplants main's restructured "Test servers" section (#1836) onto
v2/main — the "Serving the modern protocol era" and "Showcase configs"
headings, the config table, and the per-config subsections — replacing
the eight long paragraphs v2/main still carried.

Folds in v2/main's #1846 correction: main's copy still claims
Mcp-Param-* mirroring is skipped in the browser, which stopped being
true when the Inspector took over building the mirrored headers itself.
The blockquote callout keeps main's shape with the corrected wording.

Scoped to that section only — the whole file is deliberately NOT copied
from main, so #1866's repo-status callout and release-section edits (and
every other v2/main-only line) are untouched.

Shrinks the first v2/main -> main milestone merge from a whole-section
conflict to a single 3-line hunk.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAt8rqxysNbhYWLhoRm3fU

* docs: point the #1846 reference at the issue, not a nonexistent PR URL

#1846 is the issue; the fix shipped as PR #1847. The callout's link used
/pull/1846, which 404s. Per Copilot review.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAt8rqxysNbhYWLhoRm3fU

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
cliffhall added a commit that referenced this pull request Aug 2, 2026
…#1904)

main's copy of .github/workflows/main.yml carried three release fixes
v2/main never took -- #1831 (least-privilege default GITHUB_TOKEN
scope), #1834 (derive the npm dist-tag from the version) and #1836
(publish via npm OIDC trusted publishing).

Diffing the file both directions shows v2/main has NOTHING main lacks:
its only unique content is the superseded publish step,

    run: npm publish --access public --provenance
    env:
      NODE_AUTH_TOKEN: ${{ secrets.NPM_TOKEN }}

which is exactly what #1836 replaced. Every other line already matched.
So this is a verbatim copy of main's file rather than a hunk-by-hunk
forward-port -- fewer ways to get it wrong, and it leaves the two
branches byte-identical (`git diff origin/main -- .github/workflows`
is now empty).

Not a back-merge: only this one file is taken, so none of main's
pre-swap v1 lineage enters v2/main's ancestry (see #1868).


Claude-Session: https://claude.ai/code/session_01YAt8rqxysNbhYWLhoRm3fU

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
pull Bot pushed a commit to Bang2985/inspector that referenced this pull request Aug 5, 2026
Resolves the eight conflicts from the first v2/main -> main milestone
merge on a branch cut from main, so v2/main's ancestry stays clean --
resolving them on the PR head would have back-merged main into v2/main,
which is exactly what modelcontextprotocol#1868 was closed to avoid.

All eight took v2/main's side, each verifiably the newer content:

- README.md          stale Mcp-Param-* callout; v2/main's is corrected
                     by modelcontextprotocol#1847 (the Inspector builds the headers itself)
- SECURITY.md        add/add (modelcontextprotocol#1843 on main, modelcontextprotocol#1867 on v2/main); differ
                     by one line, the CONTRIBUTORS.md -> CONTRIBUTING.md
                     rename from modelcontextprotocol#1884
- clients/web/package.json   vitest ^4.1.0 -> ^4.1.10, the modelcontextprotocol#1899 fix
- the five package-lock.json files, then regenerated from a clean
  install rather than hand-merged

main's release-workflow commits (modelcontextprotocol#1831 least-privilege token, modelcontextprotocol#1834
dist-tag derivation, modelcontextprotocol#1836 OIDC trusted publishing) auto-merged and are
preserved -- .github/workflows/main.yml is the only file where the
merged tree differs from v2/main.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01YAt8rqxysNbhYWLhoRm3fU
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

v2 Issues and PRs for v2

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant