Commit graph inspector/package-lock.json
Author SHA1 Message Date
cliffhall
eb5bba62ed fix(tui): bundle React-rendering deps so a consumer install can't split React
`ink-form` and `ink-scroll-view` were externalized by the TUI bundler and
both declare a loose `react` peer (">=18"). Installed under a project that
has its own React 18, npm hoists them to that project's root — where the
peer is satisfied — while the Inspector's React 19 nests underneath. The
bundle then renders through React 19 while those two call hooks on React
18, whose dispatcher is null, so opening a tool test form (or any scroll
view) dies with "Cannot read properties of null (reading 'useState')".

Inline them instead: their `import "react"` is emitted into the bundle and
so resolves from the build directory exactly like the bundle's own, with
npm's placement no longer part of the decision. This also pins their
transitive deps to what this repo resolved — notably `ink-select-input@6`
via `overrides`, which npm ignores for a package installed as a dependency,
so consumers were getting the React-18-era v5.

`ink` stays external: it can't be bundled (its CJS `signal-exit@3` fails
ESM interop with `Dynamic require of "assert"`) and doesn't need to be —
its ">=19" peer keeps npm from hoisting it next to an unusable React.

Both packages are dropped from the root `dependencies`, since the tarball
now ships their code rather than having consumers install them.

`clients/tui/__tests__/tsupConfig.test.ts` is the durable guard: the
failure is invisible in this repo and in every smoke (a dev install has
one React) and only appears once the package is installed under another
project, so the invariant is asserted against the config instead — every
dependency declaring a `react` peer must be bundled unless exempted.

Closes #1952

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01WLYmVPVLdeYpE6aMTUsCks
2026-08-11 23:00:01 -04:00
Cliff Hall
59f5b6d25c
Merge pull request #1971 from modelcontextprotocol/v2/chore/root-only-mcp-sdk-deps
chore: declare the MCP SDK packages only at the repo root (#1970)
2026-08-11 11:12:02 -04:00
cliffhall
31c41d3ad6 chore: declare the MCP SDK packages only at the repo root (#1970)
The four v2 SDK packages (`client`, `core`, `server`, `server-legacy`) plus
`ext-apps` were declared in the root manifest *and* again in clients/web, cli,
and tui, so each client installed its own copy. Node resolution walks up and the
root install is on every client's chain — and the root manifest is already the
documented source of truth for what ships — so the per-client entries were
duplicates of a decision made at the root, and they had already drifted: before
this change the tree carried ext-apps 1.7.4 at the root and 1.7.5 under
clients/web, dragging in two copies of the v1 `@modelcontextprotocol/sdk`
(1.29.0 and 1.30.0) through ext-apps' peer dependency.

Nothing imports the v1 SDK; it is not in any manifest of ours, only a peer of
ext-apps, so consolidating ext-apps is the only lever over it. After a clean
install there is now exactly one copy of each, at the root. `npm update` then
moves ext-apps to 1.7.5 and its SDK peer to 1.30.0, whose widened
`@hono/node-server` range (`^1.19.9 || ^2.0.5`) also drops a nested duplicate of
that package.

`express` needed a real home to make this work. `test-servers/src` imports it,
but no manifest declared it: it was reaching `clients/cli/node_modules` only as
a peer of `express-rate-limit` under `@modelcontextprotocol/server-legacy`, so
removing that entry took express with it and every cli test that spawns a test
server failed to resolve it. It is now a root devDependency — test-servers is
root-owned code with no manifest of its own — and `vitest.shared.mts` resolves
it from the root, joining `yaml`, which was already the precedent for exactly
this case. The clients/web duplicate is dropped; its only reference is a
type-only import covered by `@types/express`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
2026-08-11 09:07:04 -04:00
cliffhall
6c33da38c9 chore(deps): align zod across installs and guard against version skew
Closes #1896

`clients/web`'s `tsc -b` exhausted the 4GB default heap at zod 4.4.x. The
cause is not a zod regression nor a pathological inference site of ours: it
is two copies of zod at *mismatched versions* inside one tsc program.

`clients/web/tsconfig.test.json` compiles `test-servers/src`, whose files
resolve zod (and the SDK's zod) from the **root** install, alongside web's
own sources, which resolve from `clients/web/node_modules`. At 4.3.6 vs
4.4.3 TypeScript must relate two structurally-distinct declarations of every
`@modelcontextprotocol/*` schema type, which is exponential over a recursive
generic surface — `TS2589 Type instantiation is excessively deep`, then OOM.

Bumping root zod to 4.4.3 and changing nothing else returns the build to
baseline: 11.4s and 2,567,882 instantiations, against 11.2s / 2,567,503 at
4.3.6. Two copies at the same version are harmless; skew is what explodes.

So drop the `~4.3.6` hold on `clients/web` and move all four manifests and
lockfiles to zod 4.4.3 together, and add `verify:dep-lockstep` to `validate`
to keep them there. The guard derives its candidate set from what `core/`
and `test-servers/src` import, compares the committed lockfiles' top-level
entries, and fails deny-by-default on any skew not in the annotated
`TOLERATED_SKEW` allowlist. It joins the existing mutual-vouch ring so
dropping it from `validate` is caught by `verify:format-coverage`.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HDdo1rNRVQnVRRdsqSrvG3
2026-08-10 20:29:58 -04:00
Cliff Hall
834fd2d77d
chore(deps): migrate to eslint 10 to clear the brace-expansion DoS advisory (#1900)
* chore(deps): migrate to eslint 10 to clear the brace-expansion DoS advisory (#1838)

eslint 9 pulled `minimatch@3` → `brace-expansion@1.1.16`, which carries
GHSA-mh99-v99m-4gvg (high, DoS via unbounded expansion length). It appeared in
all five lockfiles — the root plus each of the four clients, none of which is an
npm workspace, so all five had to move together.

Deferred from the pre-2.0.0 sweep because it is dev-scope, not attacker-
controlled, and a major bump across five packages was the wrong thing to attempt
immediately before an irreversible publish. This is that work.

## What moved

- `eslint` `^9.39.4/5` → `^10.8.0` and `@eslint/js` → `^10.0.1`, in all five
  manifests.
- `eslint-plugin-react-hooks` `^7.0.1` → `^7.1.1` in web and tui. Required, not
  cosmetic: 7.0.1's peer range stops at `^9.0.0`, so installing it alongside
  eslint 10 fails `ERESOLVE`.
- Everything else already declared eslint 10 support and stayed put:
  `typescript-eslint@^8.56.1` (`^8.57 || ^9 || ^10`),
  `eslint-plugin-react-refresh@^0.5.2` (`^9 || ^10`),
  `eslint-plugin-storybook@^10.2.19` (`>=8`).

## Config migration

None was needed. All five configs were already flat (`eslint.config.js` with
`defineConfig`/`globalIgnores` from `eslint/config`), which is the format v10
keeps and the only one it still supports. The root config's `core/` and shared-
surface blocks — including the `_`-prefix `argsIgnorePattern` /
`varsIgnorePattern` / `caughtErrorsIgnorePattern` — are unchanged and still gate
`lint:core` and `lint:shared`.

No rule was disabled, downgraded, or scoped away to make the new eslint pass.
v10 surfaced 21 real violations across three rules; all 21 are fixed in the code.

### `preserve-caught-error` (new in `eslint:recommended`) — 15 sites

A `throw new Error(msg)` inside a `catch` silently drops the original error.
Each now passes `{ cause }`, so the underlying failure survives into the chain.
Messages are unchanged, so nothing that asserts on them moves. 12 in `core/`,
2 in `clients/cli`, 1 in `test-servers/src`.

### `no-useless-assignment` (new in `eslint:recommended`) — 2 sites

`clients/cli/src/cli.ts`'s `optionArgs` and `test-servers/src/test-server-oauth`
's `valid` were initialized and then unconditionally overwritten on every path.
Both are now declared without the dead initializer; TypeScript's definite-
assignment analysis covers the remaining paths.

### `react-hooks/set-state-in-effect` — 8 sites (web)

These were false negatives the plugin fixed in 7.1.0, not a rule we newly opted
into: `set-state-in-effect` has been `error` in `flat/recommended` since 7.0.1,
and the web config has always extended it. Every one was the same anti-pattern —
resetting or re-syncing local state from a prop inside a `useEffect`, which
paints the stale value for one frame and then renders again to correct it.

All eight now use a new `useValueChange(value, onChange)` hook
(`clients/web/src/hooks/useValueChange.ts`), which is React's documented
"adjusting state during render": compare against the previous render's value
with `Object.is` and call back *during* render, so React discards the
in-progress output before it reaches the DOM.

- `MrtrConversation`, `ProtocolEntry`, `TaskCard` — mirror `isListExpanded`.
- `NetworkEntry` — same mirror, plus the "Reveal in Network" force-open. The
  reveal's rAF scroll stays an effect (real external-system work); only the
  `setIsExpanded(true)` moved. `isExpanded` is now seeded from
  `isListExpanded || revealed`, since a render-time sync fires on change and so
  cannot cover an entry that mounts already revealed.
- `PromptArgumentsForm`, `ResourceTemplatePanel` — reset on template/prompt
  switch.
- `ServerConfigModal` — reset on open. Keyed on `opened ? initial : undefined`,
  which collapses "opened flipped" and "initial changed while open" into one
  value; `initial` is already memoized, so it cannot thrash.

`useValueChange` has its own test file, and `ServerConfigModal` gains an
open→edit→close→reopen test (the close leg was previously untested, which is
what left the new guard uncovered).

## Advisory delta

`npm audit`, per package, before → after:

| Package             | before                     | after                      |
| ------------------- | -------------------------- | -------------------------- |
| root                | 9 (1 low, 6 mod, 2 high)   | 8 (1 low, 6 mod, 1 high)   |
| `clients/web`       | 19 (3 low, 6 mod, 6 high, 4 crit) | 17 (3 low, 6 mod, 4 high, 4 crit) |
| `clients/cli`       | 6 (2 low, 4 high)          | 4 (2 low, 2 high)          |
| `clients/tui`       | 4 (1 low, 3 high)          | 2 (1 low, 1 high)          |
| `clients/launcher`  | 3 (3 high)                 | 1 (1 high)                 |

`brace-expansion` is gone from all five — `eslint@10` resolves `minimatch@10` →
`brace-expansion@5.0.9`. Dropping `@eslint/eslintrc` (removed in v10) also took
`js-yaml` with it, clearing GHSA-52cp-r559-cp3m (high) from all four clients as
a bonus. The remainder are unrelated and tracked separately.

`npm run ci` passes end to end — validate, the per-file ≥90 coverage gate, the
build gate, all five smokes (including `smoke:tui`, run locally on a real TTY),
and 462 Storybook tests.

Also documents the effect rule in AGENTS.md and mirrors it into
`.github/copilot-instructions.md`, since it is now something a reviewer cites.

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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: state useValueChange's referential-stability requirement (#1838)

Copilot review on #1900: the hook compares with `Object.is`, so an object or
array literal rebuilt in the component body compares unequal every render.
Because the callback is what updates state, that is not merely a wasted render —
it is an infinite loop.

Every current caller passes a primitive (`boolean`, `string`) or an already-
memoized value, so nothing changes behaviorally. But the hazard was implicit,
and the next caller is the one it would bite.

Documented on the hook itself, and one clause added to the AGENTS.md rule with
its mirror in `.github/copilot-instructions.md`. No runtime guard: the
requirement is the same one a `useEffect` dependency array carries, and here the
failure is loud and immediate rather than silent.

`npm run ci` passes.

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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* fix(cli): stop asserting the --tool-args-json catch binding is an Error (#1838)

Copilot review round 2 on #1900: the `--tool-args-json` parse handler formatted
its message with `(e as Error).message`. A `catch` binding is `unknown`, so that
cast is an assertion rather than a check — a thrown non-Error would render
`--tool-args-json is not valid JSON: undefined`, which points the user at
nothing.

`JSON.parse` does only throw `SyntaxError` today, so this is not a live bug. But
the cast is the kind AGENTS.md asks us not to leave unjustified, the same
handler was already being touched here to add `{ cause: e }`, and every other
site in this PR uses the guarded form. Now it does too.

`npm run ci` passes.

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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

* docs: require useValueChange's onChange to be render-pure (#1838)

Copilot review round 3 on #1900: `onChange` is called during render, so it is
bound by render-phase purity — and the web client mounts under `StrictMode`,
which deliberately double-renders in development. A callback doing anything
external would run an unpredictable number of times, and concurrent React can
abandon an in-progress render entirely.

Every current caller already passes a callback that only calls `setState`, so
nothing changes. But "runs during render" is the whole point of the hook and the
constraint that follows from it was left implicit.

Stated on the hook, with the same clause in the AGENTS.md rule and its
`.github/copilot-instructions.md` mirror. All three now point at `NetworkEntry`
as the worked example of the split: the reveal's force-open is a state update
and lives in `useValueChange`, while its `requestAnimationFrame` scroll stays a
`useEffect`.

`npm run ci` passes.

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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-08-01 20:13:30 -04:00
Cliff Hall
694d013f26
chore(deps): bring main's rc-series bumps to v2/main (hono 2.x, vite 8.1.5) (#1881)
The 2.0.0-rc series shipped these dependency bumps to `main` only, so
`v2/main` was developing against older versions and would have met them
for the first time at the milestone merge.

- `@hono/node-server` ^1.19.14 -> ^2.0.12 (root, clients/web) — a major.
  v2.0.0's only breaking changes are dropping Node 18 (the repo already
  requires >=22.19.0) and removing the `@hono/node-server/vercel` adapter
  (unused here). The used surface — `serve()`, `serveStatic`, `ServerType`
  — is unchanged, so no source edits were needed.
- `vite` ^8.0.0 -> ^8.1.5 (root, clients/web) and ^8.0.16 -> ^8.1.5
  (clients/tui) — the security bumps from #1841.

`npm install` at the root (postinstall cascades into every client)
regenerated the three touched lockfiles. `npm run ci` passes.


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

Co-authored-by: Claude Opus 5 (1M context) <noreply@anthropic.com>
2026-07-31 23:03:34 -04:00
Cliff Hall
4defa32ac6
chore: close a batch of quality-gate & tidy gaps (#1785 #1787 #1789 #1790 #1767) (#1792)
* chore: close a batch of quality-gate & tidy gaps (#1785 #1787 #1789 #1790 #1767)

Bundles five small, related tidy/gate items into one PR:

- #1785: delete the dead src/types/navigation.ts InspectorTab (0 importers;
  duplicated/drifted from utils/inspectorTabs InspectorTabId). src/types/
  now holds only ambient .d.ts stubs.
- #1787: add src/hooks/** to the web coverage `include` (it was silently
  outside the ≥90 gate) and add tests for the two untested hooks,
  useServerJsonImport and useImportClientConfig, bringing every hook to
  ≥90 on lines/statements/functions/branches.
- #1789: widen clients/{cli,tui,launcher} format/format:check to their
  top-level configs; add root format:shared / format:check:shared covering
  test-servers/src/**, vitest.shared.mts, and the repo-root eslint.config.js.
- #1790: pin prettier to an exact version (3.8.4, dropping the ^3.8.1
  caret) in all five package.json files so the format gate's verdict can't
  shift with an in-range patch bump; lockfiles synced.
- #1767: its root-file format+lint gap is closed here (format:shared +
  a new lint:shared via a second files block in the root eslint.config.js;
  web/server/ was already gated in #1788). Its larger cli/tui __tests__
  typecheck piece (76 type errors) is split out to #1791.

Prettier-normalized the newly-covered files (pure formatting). No runtime
behavior change. `npm run ci` green.

Closes #1785
Closes #1787
Closes #1789
Closes #1790
Closes #1767

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

* chore: address PR #1792 review findings

- #1789 gap: widen clients/web format to include .storybook and clients/
  launcher format to include scripts/; reformat the 3 .storybook files
  (were non-conforming under the pinned prettier 3.8.4). Audited all 885
  tracked source files — now fully prettier-clean.
- Export VALIDATE_DEBOUNCE_MS / COLLAPSE_DELAY_MS / HIGHLIGHT_DURATION_MS
  from useServerJsonImport and use them in the test instead of hardcoded
  300/1000/250 (so a tuned constant can't silently desync the test).
- Assert the symmetric renameErrors["alpha"] alongside ["beta"] so a
  one-directional collision-check regression fails.
- Comment the blank-rename test as exercising a UI-unreachable defensive
  branch (canImport gates submit).
- Un-stale the two specification/v2_ux_interfaces_plan.md references to the
  now-removed navigation.ts InspectorTab.

npm run ci green.

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

* docs: sync AGENTS.md format-surface description with the widened globs (#1792 review r2)

1ce3b64 added .storybook to clients/web's format glob and scripts/ to
clients/launcher's, but AGENTS.md:222 still described the pre-fix scope.
Bring it in line and note the non-recursive top-level glob is why nested
dirs are named explicitly. Docs only.

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

* chore: handle PR #1792 round-3 follow-ups in-place (format-coverage guard, uniform globs, theme gate)

Rather than defer these to tickets, fold them into this PR:

- Obs 2: widen every client + root format glob to a uniform
  *.{ts,tsx,mts,cts,js,jsx,mjs,cjs} so a future .mts/.cjs/.tsx top-level
  file can't silently slip the gate.
- Obs 1: add scripts/verify-format-coverage.mjs (wired into `validate`) —
  it parses the format:check globs out of every package.json, enumerates
  all tracked source files, and fails listing any not covered by a glob.
  The durable, standing version of #1789's one-shot audit; negative-tested.
- Obs 3: bring src/theme/** under the coverage gate (21/23 files were
  already ≥90 transitively; add Paper.test.tsx for the one laggard,
  Paper.ts). src/App.tsx (~4.5k lines, ~42% branches) is converted from a
  silent omission into an explicit, documented exclusion (comment on the
  coverage `include` + AGENTS.md) — gating it is a dedicated effort, not a
  whitelist tweak.

npm run ci green (the new guard reports 885/885 tracked source files gated).

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

* chore: address PR #1792 round-4 findings (guard hardening, docs, exclusion list)

- Harden verify-format-coverage: only harvest format:check globs from
  scripts transitively reachable from `validate` (via npm run refs), so an
  unwired/renamed format:check no longer counts as coverage. Negative-tested
  — renaming clients/web format:check to an unreferenced name now fails the
  guard (627 files flagged) instead of passing.
- Name src/main.tsx and src/index.ts (browser + bin bootstraps) alongside
  src/App.tsx in the coverage-include comment and AGENTS.md, so the
  documented exclusion list is complete rather than claiming App.tsx is the
  only one.
- Doc sync: document verify:format-coverage (AGENTS.md + README script table
  + scripts/ layout line), fix the `validate` ordering (verify:format-
  coverage now runs before validate:core) in AGENTS.md and README, and
  update the quoted globs to the uniform *.{ts,tsx,mts,cts,js,jsx,mjs,cjs} /
  test-servers *.{ts,tsx,mts,cts}.

npm run ci green.

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

* chore: address PR #1792 round-5 findings (airtight guard + doc/config sync)

- verify-format-coverage: assert the root `validate` chain invokes each
  client's `validate` (cd <dir> && npm run validate), closing the last
  reachability edge — a client dropped from the root chain now fails the
  guard even though its own globs still exist. Negative-tested.
- eslint.config.js: widen the shared `files` block to
  test-servers/src/**/*.{ts,tsx,mts,cts} to match the widened lint:shared
  glob, so a future .mts/.cts there is lint-gated, not just prettier-gated.
- AGENTS.md: scope the "uniform extension set" claim to client globs
  (core stays {ts,tsx}, shared {ts,tsx,mts,cts}), with verify:format-coverage
  as the backstop — removing the self-contradiction with the line below.

npm run ci green.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-26 10:43:11 -04:00
Cliff Hall
373f346e5f
chore: close quality-gate coverage gaps (#1689) (#1766)
Some checks failed
Run install, format, lint, build, and test on every push / build (push) Has been cancelled
Run install, format, lint, build, and test on every push / publish (push) Has been cancelled
Run install, format, lint, build, and test on every push / publish-github-container-registry (push) Has been cancelled
Closes #1689. Typecheck cli/tui src, add a core/ format+lint gate, and gate core/json + core/client coverage. See PR #1766 for the full review history.
2026-07-24 17:20:12 -04:00
Cliff Hall
e70a11360c
Tools: x-mcp-header annotation tooling + -32602 unknown-tool rendering (#1632) (#1735)
* Tools: x-mcp-header annotation tooling + -32602 unknown-tool rendering (#1632)

Card 9/10 of the SDK-v2 + New Spec stack. Adds SEP-2243 `x-mcp-header`
tooling to the Tools tab and adjusts result rendering for SDK v2's
unknown-tool behavior, and bumps the SDK to 2.0.0-beta.5.

- **`core/json/xMcpHeader.ts`**: a faithful port of the SDK's (non-public)
  `scanXMcpHeaderDeclarations` — validates every SEP-2243 constraint
  (primitive types incl. `number`, RFC 9110 token, case-insensitive
  uniqueness, statically-reachable `properties`-only chain) and derives the
  mirrored `Mcp-Param-{Name}` params for a kept tool.

- **Excluded tools**: the SDK silently drops tools with invalid annotations
  from `tools/list`. InspectorClient now re-lists the RAW (unfiltered) list
  and scans it to recompute the excluded set with reasons — gated to the
  modern non-stdio era (matching the SDK's gate), recomputed on every
  aggregate refresh, surfaced via a new `excludedToolsChange` event +
  `getExcludedTools()` and `useInspectorClient().excludedTools`. The Tools
  sidebar lists them struck-through under an "Excluded (SEP-2243)" divider
  with the reason on hover.

- **Mirrored args**: ToolDetailPanel shows a "Mirrored request headers"
  section listing each arg → `Mcp-Param-{Name}` (with the browser-omits note).

- **-32602 rendering**: SDK v2 rejects an unknown-tool call with `-32602`
  instead of an `isError` result, which previously rendered nothing. New
  ToolCallErrorPanel renders a thrown tool-call error, with a targeted
  "Unknown Tool" hint for `-32602`.

Adds unit + Storybook + integration coverage (a real modern server with an
invalid-header tool fixture) for every path. `npm run ci`-equivalent checks
green.

Closes #1632

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

* test-servers: x-mcp-header modern showcase config + preset (#1632)

Register the `invalid_header_tool` fixture as a preset and add
`xmcpheader-modern-http.json` — a modern HTTP showcase serving echo, a
valid-header `get_weather`, and the invalid-header tool — so the Tools-tab
x-mcp-header tooling (mirrored headers, excluded-tools surface, -32602
rendering) can be exercised manually. Documented in the README alongside the
other per-SEP showcase configs. Smoke-tested end to end in the web client.

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

* docs: add #1632 proof screenshots to pr-screenshots

End-to-end web-client verification of the x-mcp-header Tools tooling against
the modern showcase server: excluded-tools surface + reason, mirrored-headers
section, and the -32602 unknown-tool error panel.

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

* Respond to review: disambiguate -32602, doc/search nits (#1632)

Addresses the review on PR #1735:

1. -32602 "Unknown Tool" mislabel: `-32602` is generic Invalid params, so a
   known tool called with bad arguments hit the same code and rendered under
   the definitive "Unknown Tool" heading. Replace `isUnknownToolError` with
   `classifyToolCallError(code, message)` → "unknown-tool" only when the
   message names it (not found / unknown tool / …), else "invalid-params"
   (new "Invalid Parameters" heading + schema hint), else "generic".

2. Double pagination walk: expand the `listAllTools` comment to document that
   the excluded-tools recompute is a deliberately un-cached second raw walk
   (doubles list round-trips on modern non-stdio, runs even on a cache-served
   aggregate) — accepted for a debugging tool, no-op on legacy/stdio.

3. Doc nit: `scanXMcpHeaderDeclarations` no longer claims to visit "$ref
   targets" — reworded to note `$ref` is never followed (refs resolve into the
   unreachable `$defs` body).

4. Search nit: the excluded-list filter now matches name AND title, matching
   the main tools filter.

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

* Tighten unknown-tool matcher to be tool-scoped (#1632 re-review nit)

The re-review noted the -32602 disambiguation was symmetric: broad markers
("does not exist", "unrecognized") could inversely mislabel a genuine
invalid-params message (e.g. "property 'region' does not exist") as "Unknown
Tool". Scope the not-found/does-not-exist family to require the word "tool" in
the same clause (matching the SDK's "Tool <name> not found"); keep "unknown
tool" / "no such tool" as standalone unambiguous markers. Adds a
tool-less-"does not exist" test asserting it classifies as invalid-params.

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

* Add trigger_invalid_params showcase tool + Invalid Parameters screenshot (#1632)

Adds a `trigger_invalid_params` spec-error trigger to the modern showcase
server: the injector answers its `tools/call` with a real `-32602 Invalid
params` JSON-RPC error whose message does NOT name an unknown tool. This lets
the "Invalid Parameters" branch of the tool-call error panel (added in the
review response) be exercised live, distinct from the unknown-tool `-32602`.

- `SPEC_ERROR_TRIGGERS` gains the `trigger_invalid_params` entry (HTTP 200,
  in-band -32602) + preset registration.
- Enable `injectSpecErrors` on `xmcpheader-modern-http.json` and add the tool.
- README + pr-screenshots README document it; adds the live "Invalid
  Parameters" proof screenshot.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-22 11:42:54 -04:00
cliffhall
19a6df7ad3 SDK v2 migration (behavior-neutral, versionNegotiation: 'legacy') (#1624)
Migrate all three clients + core + test-servers from
@modelcontextprotocol/sdk@1.29 to SDK v2 (2.0.0-beta.4), keeping behavior
identical against legacy (2025-11-25) servers. Foundation for the SDK V2 +
New Spec stack.

Packages
- Split @modelcontextprotocol/sdk -> @modelcontextprotocol/client (Client,
  transports, OAuth, error taxonomy, protocol types) + core (Zod schemas);
  stdio -> client/stdio, Ajv -> client/validators/ajv. Test-servers ->
  @modelcontextprotocol/server (+ server-legacy/sse). Deterministic import
  rewrite (schemas -> core, everything else -> client); the codemod misroutes
  client code to /server so it was not used for path routing.
- new Client(..., { versionNegotiation: { mode: 'legacy' } }) — nothing
  2026-era goes on the wire.

Runtime (core/mcp/inspectorClient.ts)
- Error taxonomy: McpError -> ProtocolError, ErrorCode -> ProtocolErrorCode;
  RequestTimeout -> SdkError/SdkErrorCode.
- Handlers take method strings; task methods (tasks/*, notifications/tasks/*)
  use the 3-arg custom form (excluded from v2's spec-method set).
- Legacy tasks kept working (per project requirement): requestor tasks re-
  implemented over raw client.request + explicit deprecated schemas;
  experimental.tasks.callToolStream replaced by a local pollTaskToolCall
  async generator (with _progressHandlers revival so task-execution progress
  still reaches the caller). Receiver (server-initiated) tasks: the v2 Client
  validates a sampling/elicit handler result and rejects the legacy { task }
  response with -32602 — installReceiverTaskResponseBypass swaps the wrapped
  entry in the private _requestHandlers map for the task-augmented branch so
  { task } reaches the wire (fixes server-initiated sampling-via-task).
- Pagination: list verbs use raw client.request({method}, List*ResultSchema)
  for single-page behavior (v2's client.listX() auto-aggregate all pages).

Web / auth / apps
- Rename local JsonSchemaType -> InspectorFormSchema + add toFormSchema()
  structural narrowing at the SDK-schema -> form boundary.
- ext-apps still peers on SDK v1: one compat boundary with documented casts
  (remove when ext-apps#702 lands a v2 peer).
- OAuth provider saveClientInformation reconciled with v2's
  OAuthClientInformationContext; InvalidClientMetadataError re-homed as a
  local class (server-legacy/auth is not browser-safe).
- vitest.shared.mts dedupes @modelcontextprotocol/client+core to one copy so
  vi.mock intercepts the SUT's import.

Accepted v2 behavior changes (per #1624 / spec §9.2): unknown-tool call
rejects with -32602; cancellation is stream-abort (no guaranteed
notifications/cancelled frame); output-schema strictness restored via the
Inspector's own Ajv check on the default path.

npm run ci green: validate (web 3087 / cli 212 / tui 274 / launcher 5),
coverage (>=90 per-file, all clients), smoke (launcher/cli/web), storybook
(410).

Closes #1624

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-15 10:44:24 -04:00
cliffhall
32a3610737 Merge remote-tracking branch 'origin/v2/main' into 1579-wave-1
# Conflicts:
#	clients/cli/package-lock.json
#	clients/cli/package.json
#	clients/web/src/App.tsx
#	clients/web/src/lib/environmentFactory.test.ts
#	clients/web/src/lib/environmentFactory.ts
#	clients/web/src/lib/remoteOAuthStorage.test.ts
#	clients/web/src/lib/remoteOAuthStorage.ts
#	clients/web/src/test/core/auth/connection-state.test.ts
#	clients/web/src/test/core/auth/providers.test.ts
#	clients/web/src/test/core/auth/storage-browser.test.ts
#	clients/web/src/test/core/auth/storage-remote.test.ts
#	clients/web/src/test/core/mcp/oauthManager.test.ts
#	clients/web/src/test/integration/auth/node/storage.test.ts
#	clients/web/src/test/integration/storage/adapters.test.ts
#	clients/web/src/utils/clearServerOAuthState.test.ts
#	clients/web/src/utils/clearServerOAuthState.ts
#	core/auth/connection-state.ts
#	core/auth/oauth-storage.ts
#	core/auth/providers.ts
#	core/auth/storage.ts
#	core/auth/store.ts
#	core/storage/adapters/remote-storage.ts
2026-07-09 09:41:08 -04:00
Bob Dickinson
3c65bdb4c6 Removed Zustand storage, cleanup up auth storage IO interfaces, implementations, and usage. 2026-07-04 21:56:45 -07:00
cliffhall
7e931ecfba fix(deps): sync root package-lock engines to Node >=22.19.0 (#1563)
The root package.json engines.node was bumped to >=22.19.0 but the root
package entry in package-lock.json still declared >=22.7.5. Regenerate so
the lockfile matches package.json for npm ci.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
2026-07-01 19:21:39 -04:00
cliffhall
d5be4929e9 feat(core): honor HTTPS_PROXY/HTTP_PROXY/NO_PROXY in the Node transport (closes #1563)
Wrap the Node transport's fetch with an undici EnvHttpProxyAgent dispatcher
when a standard proxy env var is set, so the CLI and web backend can reach
remote MCP servers through corporate proxies with zero new flags. undici is
imported lazily (only when a proxy var is present) and added as a dependency
at the repo root and in the CLI client; a missing undici raises an actionable
error. Documents the behavior and dependency in the CLI README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
2026-07-01 18:25:16 -04:00
Bob Dickinson
5de4e911e3
feat(web,core): enterprise-managed authorization (EMA) (#1509) (#1512)
Adds enterprise-managed authorization (EMA): browser-side IdP login + RFC 8693 token-exchange and JWT-bearer resource-token mint, per-server EMA opt-in in Server Settings, IdP config in Client Settings, and IdP clientSecret keychain storage. Includes the v2/main merge and review follow-ups (400 on malformed client config; normalizeIdpIssuer import consolidation).
2026-06-24 22:15:12 -04:00
Bob Dickinson
2488ca5e38
feat: port CLI, TUI, and launcher from v1.5 into v2 (#1452)
Ports the CLI, TUI, and launcher from v1.5 into v2 behind a single
`mcp-inspector` entrypoint (launcher → web/cli/tui). Includes the
`mcp-inspector --web --dev` launcher fix (Vite alias resolution + prod-server
double-start), the root `postinstall` install cascade with simplified CI, and
spec/AGENTS docs (no-workspaces rationale, Development setup).

Closes #1246.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-06-14 20:27:49 -04:00
Bob Dickinson
62d442b073 Revert "First pass at pulling the v1.5 CLI, TUI, and Launcher clients into v2."
This reverts commit 467b01edd6.
2026-06-09 21:56:38 -07:00
Bob Dickinson
467b01edd6 First pass at pulling the v1.5 CLI, TUI, and Launcher clients into v2. 2026-06-09 17:38:00 -07:00
Cliff Hall
8fa1a0c074
feat(apps): add isAppTool helper in core (#1260) (#1272)
* feat(apps): add isAppTool helper in core (#1260)

Adds @modelcontextprotocol/ext-apps as a dependency (root + clients/web)
and a thin core/mcp/apps.ts wrapper exposing:

- getAppResourceUri(tool): re-export of getToolUiResourceUri so all
  Inspector clients consume the same nested/flat _meta resolution.
- isAppTool(tool): boolean predicate built on top of getAppResourceUri.

Both surface the underlying ext-apps throw on a malformed _meta.ui.resourceUri
(string that does not start with "ui://") rather than swallowing it — server
bugs should be visible.

Tests cover nested format, deprecated flat format, nested-wins precedence,
missing _meta, and the invalid-URI throw.

Refs #1260

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

* test(apps): address PR #1272 review feedback

- core/mcp/apps.ts: convert getAppResourceUri from a typed const re-export to
  a function declaration so its export style matches isAppTool. Behavior is
  unchanged.
- core/mcp/apps.ts: document the filter-iteration implication on isAppTool —
  callers using tools.filter(isAppTool) will halt on a server-malformed URI.
- apps.test.ts: add the symmetric isAppTool false-case for `_meta.ui` present
  without `resourceUri`.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>

---------

Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-01 17:03:11 -04:00
cliffhall
8edff4ea77 Phase 0 foundations for component interfaces refactor
Stands up the type-only prerequisites for the v2 UX component
interfaces refactor per specification/v2_ux_interfaces_plan.md.

Phase 0.1 — MCP SDK dependency:
- Add @modelcontextprotocol/sdk ^1.29.0 and zod ^4.3.6 to both the
  root package.json and clients/web/package.json so component files
  can import Tool / Prompt / Resource / Task / LoggingLevel / etc.
  directly from the SDK.

Phase 0.2 — Inspector wrapper-types module:
- Create core/mcp/types.ts mirroring the type-only subset of v1.5's
  core/mcp/types.ts: MCPServerConfig (discriminated union), ServerType,
  MCPConfig, ConnectionStatus, StderrLogEntry, MessageEntry (Date
  timestamps, not ISO strings), FetchRequestEntry(+Base), ServerState,
  and the *Invocation types. Adds v2-only wrappers
  InspectorResourceSubscription and InspectorServerJsonDraft.
- Add core/mcp/taskNotificationSchemas.ts for the Inspector-owned
  notifications/tasks/list_changed signal (not in the SDK).
- Add core/mcp/elicitationCreateMessage.ts re-exporting ElicitRequest
  and ElicitResult plus an InspectorPendingElicitation interface for
  typing the pending-queue.
- Add core/json/jsonUtils.ts for the shared JsonValue / JsonObject
  types used inside core/mcp/types.ts.
- Copy v1.5's clients/web/src/utils/jsonUtils.ts (JsonSchemaType) and
  schemaUtils.ts (AJV-backed validators) verbatim so SchemaForm can
  switch off its local JsonSchema type.
- Copy v1.5's clients/web/src/lib/types/customHeaders.ts verbatim for
  ServerSettingsForm and the experimental panel.
- Add clients/web/src/types/navigation.ts with InspectorTab (UI-routing
  concept, lives with the web client rather than in core/).
- Wire tsconfig.app.json + vite.config.ts with the @inspector/core/*
  path alias so component files can import from core/ without
  relative-path hell.

Phase 0.3 — Vet (likely) hedges in v2_ux_interfaces.md:
- Follow-up sweep on commit 2ef257da. Replaces the remaining
  useMcpClient references with useInspectorClient, ties pending
  elicitation / sampling callbacks to the in-InspectorClient queue
  rather than a non-existent useElicitation / useSampling hook,
  corrects Task references to the SDK type (not an InspectorTask
  wrapper), corrects log hook wiring to useMessageLog, and strikes
  all remaining v2-only hooks with explicit "no v1.5 analog"
  annotations. grep "likely" specification/v2_ux_interfaces.md now
  returns zero results.

Phase 0.4 — Local-redeclaration inventory:
- specification/v2_ux_interfaces_phase_0_4_inventory.md enumerates
  every ad hoc local type (JsonSchema, LogLevel, TaskStatus,
  PromptItem, ResourceItem, ToolListItemProps, RootEntry, KeyValuePair,
  inline ConnectionStatus / transport unions) that will be replaced
  in Phases 1–4, including the file it lives in, every site that
  re-imports it, and the SDK / wrapper type that replaces it. Intended
  for the Phase 0 PR description and as a running checklist during
  the refactor.

Build state: npm run build / lint / format:check all pass in
clients/web.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 16:06:23 -04:00
cliffhall
a82444be5b update package.json, package-lock.json 2026-03-17 16:54:42 -04:00
cliffhall
442bb68532 Remove all from v2/main 2025-11-28 13:49:31 -05:00
olaservo
54a9e7a8b8 Bump version to 0.17.4
Version 0.17.3 had TypeScript build errors that prevented successful builds. This patch release fixes those errors and bumps the version to 0.17.4.

🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-11-14 06:38:44 -07:00
olaservo
3930bdd700 Bump version to 0.17.3 2025-11-12 18:29:41 -07:00
olaservo
d99a3576b1 chore: bump version to 0.17.2
🤖 Generated with [Claude Code](https://claude.com/claude-code)

Co-Authored-By: Claude <noreply@anthropic.com>
2025-10-22 08:23:24 -07:00
olaservo
aa17bb223f 0.17.2 2025-10-22 07:58:44 -07:00
cliffhall
eeaaeb5707 Bump TS SDK to latest + npm audit fix
* In package.json under cli/, client/, server/ and /
  - bump @modelcontextprotocol/sdk to ^1.20.1
* In client/package.json
  - bump @vitejs/plugin-react to ^5.0.4
  - bump vite to ^7.1.11
* In package-lock.json
  - bump playwright to 1.56.1
  - cascading changes to rollup and other dependencies
2025-10-21 13:29:09 -04:00
cliffhall
742fd8e18d Bump TS SDK to latest + npm audit fix
* In package.json under cli/, client/, server/ and /
  - bump @modelcontextprotocol/sdk to ^1.20.1
* In client/package.json
  - bump @vitejs/plugin-react to ^5.0.4
  - bump vite to ^7.1.11
* In package-lock.json
  - bump playwright to 1.56.1
  - cascading changes to rollup and other dependencies
2025-10-21 12:17:56 -04:00
olaservo
8fab47668d Bump version to 0.17.1 2025-10-12 21:02:52 -07:00
Cliff Hall
95f16e3a3a
Merge branch 'main' into dependabot/npm_and_yarn/npm_and_yarn-73ea615029 2025-10-10 17:25:14 -04:00
cliffhall
0a60c58d4f Bump Inspector version to 0.17.0
- New feature: Connection Type
- Reverts "fix: filter out client_secret when storing OAuth client information"
- Fixes STDIO "Headers is undefined" message when connecting to an SSE server.
2025-09-30 15:41:09 -04:00
Ola Hungerford
801f650faa
Merge branch 'main' into claude/issue-807-20250916-0337 2025-09-17 14:48:01 -07:00
cliffhall
f1e93e0475 * In server/index.ts
- in createCustomFetch function
    - convert finalHeaders to headers object for node-fetch compatibility
    - for SSE streams, convert node stream to response body to web ReadableStream since EventSource polyfill expects web-compatible stream

* In package-lock.json and package.json
  - add node-fetch as a dependency

This fixes #600
2025-09-17 14:45:58 -04:00
claude[bot]
7b338b3ea3 chore: bump version to 0.16.8
Co-authored-by: Ola Hungerford <olaservo@users.noreply.github.com>
2025-09-16 03:39:40 +00:00
Jeetul Samaiya
f36c649bb9
Merge branch 'main' into main 2025-09-15 19:30:18 +05:30
claude[bot]
1c5f5e4839 chore: bump version to 0.16.7
Co-authored-by: Ola Hungerford <olaservo@users.noreply.github.com>
2025-09-12 14:30:54 +00:00
cliffhall
ca26a4f7a9 Bumped TS SDK in all the packages to ^1.18.0 2025-09-11 16:41:03 -04:00
dependabot[bot]
a1e04c72fe
Bump vite in the npm_and_yarn group across 1 directory
Bumps the npm_and_yarn group with 1 update in the / directory: [vite](https://github.com/vitejs/vite/tree/HEAD/packages/vite).


Updates `vite` from 6.3.5 to 6.3.6
- [Release notes](https://github.com/vitejs/vite/releases)
- [Changelog](https://github.com/vitejs/vite/blob/v6.3.6/packages/vite/CHANGELOG.md)
- [Commits](https://github.com/vitejs/vite/commits/v6.3.6/packages/vite)

---
updated-dependencies:
- dependency-name: vite
  dependency-version: 6.3.6
  dependency-type: direct:development
  dependency-group: npm_and_yarn
...

Signed-off-by: dependabot[bot] <support@github.com>
2025-09-10 02:14:39 +00:00
jeetul.samaiya
c5acc63129 Add support for custom headers and migrate from legacy auth
- Introduced CustomHeaders component for managing custom headers.
- Updated Sidebar to include CustomHeaders component.
- Enhanced useConnection hook to handle custom headers and legacy auth migration.
- Added tests for custom headers functionality.
- Updated package.json and package-lock.json for new dependencies.
2025-09-09 13:44:57 +05:30
cliffhall
31dd05634e Bump version to 0.16.6 2025-09-03 10:21:29 -04:00
cliffhall
6db41c19d5 * In all package.json and package-lock.json,
- set TS SDK to 1.17.5, which has the automatic log level handling
* In useConnection.ts
  - push the logging/setLevel message onto the message history stack so it is clear in the Inspector that it has been sent.
2025-09-02 16:17:04 -04:00
Ola Hungerford
5598cff694
Merge branch 'main' into update-dependencies 2025-08-20 07:35:50 -07:00
olaservo
610cbddd35 Merge remote-tracking branch 'upstream/main' into setup-prettier-precommit 2025-08-18 08:45:53 -07:00
olaservo
f7b1cd6f43 Setup prettier pre-commit formatting with husky and lint-staged
- Install husky and lint-staged dev dependencies
- Configure pre-commit hook to run prettier on staged files
- Only format changed files to improve performance
- Prevents CI formatting failures by formatting locally
2025-08-18 08:43:09 -07:00
cliffhall
2f93d70364 bump version to 0.16.5 2025-08-18 11:37:41 -04:00
cliffhall
28c0bf176a Update Typescript SDK version to latest 2025-08-18 11:13:49 -04:00
olaservo
b6af0c7f67 bump @modelcontextprotocol/sdk version to ^1.17.2 in all package.json files 2025-08-13 11:06:13 -07:00
Minh-Phuc Tran
b7b62af282
Merge branch 'main' into update-dependencies 2025-08-13 11:33:05 +07:00
Ola Hungerford
8da07c83f6
Merge branch 'main' into bump-version 2025-08-12 20:17:03 -07:00
olaservo
ca913eb06f Bump to 0.16.4 2025-08-12 20:16:06 -07:00