Commit graph inspector/README.md
Author SHA1 Message Date
Cliff Hall
d552564966
Network: modern Mcp-* headers, new error codes, cancellation-as-abort (#1628) (#1722)
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
* Network: modern Mcp-* headers, new error codes, cancellation-as-abort (#1628)

Upgrade the Network tab for the modern Streamable HTTP transport
(SEP-2243 / SEP-2575):

- Recognise and highlight the mirrored headers (Mcp-Method, Mcp-Name,
  Mcp-Param-*, MCP-Protocol-Version), decode =?base64?…?= sentinel values
  inline, and cross-check each against the request body so a HeaderMismatch
  is visible before the server rejects it.
- Show the new spec error codes distinctly: -32020 HeaderMismatch,
  -32021 MissingRequiredClientCapability, -32022 UnsupportedProtocolVersion
  (with the server-advertised `supported` versions), and modern -32601
  MethodNotFound on HTTP 404 (distinguished from a legacy bare 404 by its
  JSON-RPC body).
- Reflect cancellation as a connection abort ("Cancelled") rather than a
  hard error, per SEP §7.5.
- Confirm and document browser-path header mirroring: the SDK builds the
  Mcp-* headers client-side and the /api/fetch proxy forwards them verbatim
  (new integration test).

New pure util `mcpNetworkHeaders.ts` holds the recognition/decode/classify
logic; new `McpErrorBadge` element renders the distinct error chips. The
optional raw-request controls (cacheMode bypass/refresh, single-page fetch)
are split to #1721 — they are cross-cutting core + feature-tab work beyond
the Network tab.

Also refreshes the AGENTS.md board Status option-ID table (the Building /
MCP Apps Extension columns were removed).

Closes #1628

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

* Network smoke-test showcase + fix redaction clobbering JSON-RPC error codes

Adds a composable "modern-network" showcase server for exercising the #1628
Network-tab work end-to-end, and fixes a real bug the smoke test surfaced.

Bug fix (core): the fetch-tracking body redaction masked ANY field named
`code` (targeting the OAuth authorization code), including the numeric
JSON-RPC `error.code`. That destroyed the field the Network tab classifies
the modern spec errors on, so -32020/-32021/-32022/-32601 never rendered
against real traffic. Redaction now only masks STRING values of a sensitive
field (secrets are always strings; a numeric code is never one).

Test-server showcase (test-servers): `modern-network-http.json` serves a
`get_weather` tool with an `x-mcp-header: "City"` annotation plus four
`trigger_*` tools. A new `transport.modern.injectSpecErrors` flag installs a
middleware on the modern leg that answers the triggers with real HTTP status
+ JSON-RPC error bodies (400/-32020, 400/-32021, 400/-32022 w/ `supported`,
404/-32601), so the Inspector's error rendering can be driven live.

Mirroring finding (verified live, comment corrected in createRemoteFetch.ts):
the SDK builds the STANDARD mirrored headers (Mcp-Method / Mcp-Name /
MCP-Protocol-Version) unconditionally — they reach the server through the
web proxy — but gates `Mcp-Param-*` on a non-browser environment
(`detectProbeEnvironment() !== "browser"`), so the web client never emits
them. An x-mcp-header tool is therefore uncallable from the web client
against a strict modern server (it answers -32020); it works from the Node
CLI/TUI. The earlier "mirroring works through the proxy" note was corrected.

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

* Network: move the spec-error badge off the crowded compact top row

In the narrow monitoring sidebar (embedded layout) the McpErrorBadge shared
the top row with the timestamp/method/category/duration/status chips, which
truncated the method and category ("PO…", "TRANSP…") and clipped the error
name. Render the badge separately from `metaBadges` and place it on the
second row, to the right of the URL, so the top row stays readable and the
error name shows in full. The full-width layout keeps it in the top-right
cluster (where there's room).

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

* Network: keep the spec error name's casing in McpErrorBadge (no uppercase)

The four spec/SDK error identifiers are PascalCase (`HeaderMismatch`,
`MissingRequiredClientCapability`, `UnsupportedProtocolVersion`,
`MethodNotFound`). Mantine's Badge uppercases label text by default, which
flattened them to a hard-to-read run-together string
("UNSUPPORTEDPROTOCOLVERSION"). Set `tt="none"` so the badge shows the
spec's own casing with word boundaries intact; short badges (POST, status)
keep the default uppercase.

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

* Network: in the full-width layout, move the spec-error badge to the second row

The full-width entry kept the McpErrorBadge in the top-right cluster, where a
long spec name (e.g. "MissingRequiredClientCapability") crowded the top row
and truncated the method chips and URL. Move it to the start of the second
row (the expand-toggle row) so the top row stays readable; the toggle stays
at the end (`space-between` only when the badge is present). The compact
sidebar already places it on its second row beside the URL.

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

* Protocol: show Replay as the right-side icon in the wide layout too

The compact (sidebar) ProtocolEntry already renders Replay as an icon next to
the pin/expand icons; the wide layout used a left-aligned "Replay" text
button instead. Make the wide layout match — Replay as the ReplayButton icon
at the start of the right-side action cluster (Replay, Pin, Expand) — so the
two layouts are consistent. Drops the now-unused SubtleButton constant.

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

* Move the modern spec-error chip + alert from the Network tab to the Protocol tab

The Network tab is focused on the HTTP transaction; the distinct protocol-error
taxonomy (-32020/-32021/-32022/-32601) belongs with the protocol messages. So:

- ProtocolEntry now renders the McpErrorBadge chip on the top line (right after
  the direction badge, both layouts) plus an explanatory alert in the expanded
  detail, classified by JSON-RPC error code via `classifyProtocolSpecError`.
- NetworkEntry drops the chip + alert; it keeps the HTTP-level features (status
  badge, Mcp-* header highlight/sentinel-decode, header/body mismatch marker,
  cancellation-as-abort).
- The SDK delivers -32020/21/22 (HTTP 400) as JSON-RPC error responses, so they
  reach the Protocol log directly. -32601 (HTTP 404) and other non-enveloped
  errors are *thrown* — the request stays pending. `enrichProtocolEntries`
  (App) correlates the Protocol log to the Network log by JSON-RPC id (parsed
  from the transport fetch's requestBody) and folds the thrown error onto its
  pending request, so it too appears in Protocol. This is a derived view; the
  underlying log is not mutated.

Verified live against the modern-network showcase: both a -32020 error response
and a thrown -32601 render distinctly in the Protocol tab.

Follow-up: a "reveal in Network" link on the Protocol alert (cross-tab
scroll-to + expand of the correlated HTTP entry).

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

* Protocol: show the spec-error chip only in the wide layout, after the method

Refine the placement of the spec-error chip added in the previous commit:
- Wide layout: move it from after the direction badge to right after the
  method chip (reads as "tools/call  -32020 HeaderMismatch").
- Compact sidebar row: drop the chip entirely — the two-line row stays
  uncluttered and relies on its ERROR status badge. The explanatory alert
  still appears in the expanded detail in both layouts.

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

* Protocol: "reveal in Network" link — jump from a spec error to its HTTP entry

Adds the cross-tab navigation that was the follow-up to relocating the
spec-error rendering. The expanded Protocol alert now shows a "View the HTTP
request in the Network tab" link when the entry has a correlated Network
request; clicking it:

- correlates the Protocol message to its transport fetch by JSON-RPC id
  (`revealableMessageIds` gates the link; `correlateFetchEntry` finds the
  target on click),
- clears whatever filter would hide the target (network filterText +
  category, and the embedded monitorSearch),
- switches to the Network view in whichever mode is active — the pinned
  column (`pinMonitor`) when the sidebar is open, or the full-screen header
  tab (`onActiveTabChange`) when it's closed,
- scrolls the matching NetworkEntry into view and force-expands it.

The reveal is a one-shot: a new `revealId` threads NetworkScreen →
NetworkStreamPanel → NetworkEntry, where the targeted entry scrolls itself
into view (rAF, after `useScrollMemory`'s restore) + expands, then clears the
signal via `onRevealComplete`. Verified live in both the pinned-sidebar and
closed/full-screen layouts.

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

* Protocol: only show the "reveal in Network" link when it adds value

Gate the link on a new `httpRelevant` flag: show it only when the error is
either thrown by the SDK (its real HTTP response lives only in the Network
log — the Protocol entry is a synthetic fold) OR tied to the HTTP request
headers. A delivered, protocol-only error gets no link, since the raw HTTP
entry adds nothing over the alert.

- -32020 HeaderMismatch  → link (the mirrored headers are the problem)
- -32601 MethodNotFound  → link (thrown; the 404 response is only in Network)
- -32021 MissingRequiredClientCapability → no link (delivered, protocol-only)
- -32022 UnsupportedProtocolVersion      → no link (supported list is in the alert)

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

* Network: cite SEP-2575 for cancellation-as-abort, not the internal doc's §7.5

The cancellation note referenced "(SEP §7.5)", but §7.5 is a section of the
internal v2_new_spec_impact.md doc, not a SEP. The behavior — closing the
Streamable HTTP request stream is the cancellation signal; notifications/cancelled
is stdio-only — is defined in SEP-2575 (stateless-mcp). Cite SEP-2575 in the
user-facing alert and the two related comments.

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

* Network: fix reveal one-shot canceling its own scroll; review doc/comment nits

Address @claude review of #1722:
- Reveal one-shot: move onRevealComplete() inside the rAF (after scrollIntoView)
  so clearing revealId (which flips revealed->false and runs the effect cleanup)
  can no longer race/cancel the pending scroll. Add a flip-back test.
- Comment the intentional double-mount of revealId (pinned monitor + full-screen
  ScreenStage both match; clear is idempotent).
- Doc drift after the Network->Protocol relocation: McpErrorBadge + the
  mcpNetworkHeaders module header now say the spec-error chip lives in Protocol.

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

* Network: clarify redaction array-of-scalars edge + revealableIds superset

Address second @claude review of #1722 (both non-blocking polish notes):
- fetchTracking redaction: note that masking assumes a sensitive value is a
  scalar or object; an array-of-scalars under a sensitive key would slip through
  (no OAuth/token payload has that shape).
- revealableMessageIds: document it's a superset of the linked entries; the
  spec-error/httpRelevant narrowing is applied downstream at render.

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

* Protocol: gate modern -32601 framing to a genuine 404, not in-band 200

Address third @claude review of #1722. classifyProtocolSpecError recognised
-32601 from the code alone, so an ordinary in-band 'method not found' returned
on HTTP 200 was dressed up as the modern MethodNotFound taxonomy: the spec
badge, a 404-asserting alert, and a 'reveal in Network' link that jumped to a
plain 200 (claiming diagnostic value that wasn't there).

Gate the modern -32601 treatment on the correlated fetch actually being a 404
(mirroring classifyMcpSpecError). -32020/-32021/-32022 are SEP-reserved and
unaffected. The correlated HTTP status is threaded from InspectorView (new
correlatedFetchStatusById) through ProtocolScreen/ProtocolListPanel to
ProtocolEntry; when unknown, -32601 falls back to code-only recognition (the
thrown-404 path always carries a 404). Badge, alert copy, and link now resolve
together, so a plain in-band -32601 falls through to ordinary error rendering.

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

* Protocol: require a genuine 404 for modern -32601 (drop unknown-status fallback)

Address fourth @claude review of #1722. The prior fix only rejected -32601 when
the status was *known* and not 404; an unknown status fell back to code-only
recognition. But the thrown-404 path always carries a 404 (folded in from a 404
fetch), so the unknown-status branch is only ever reached by an ordinary in-band
-32601 with no correlated 404 — most notably a stdio connection (no HTTP at all)
— which then wrongly got the modern MethodNotFound badge and 404-asserting copy.

Drop the escape hatch: -32601 is the modern taxonomy only on httpStatus === 404.
Nothing intended is lost (the genuine case always has its 404). Update the tests
(code-only/undefined -32601 now expects null; add a stdio no-correlated-fetch
ProtocolEntry case) and mark the MethodNotFound story with correlatedHttpStatus
404.

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-18 17:32:05 -04:00
Cliff Hall
9b2c188d11
MRTR: drive input_required manually to keep pending-request UX (#1704) (#1719)
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
* MRTR: drive input_required manually to keep pending-request UX (#1704)

Follow-up to #1627/#1705. Instead of letting the SDK auto-fulfil the modern
(2026-07-28) MRTR retry loop, InspectorClient now drives it itself so the
Inspector keeps its pending-request UX, owns the loop, and shows era-accurate
semantics.

Core (core/mcp/inspectorClient.ts):
- Construct the SDK Client with `inputRequired: { autoFulfill: false }`
  (unconditional; a no-op on legacy, which never returns input_required).
- New `requestWithInputRequired()` driver: issues the request with
  `allowInputRequired` + `withInputRequired(schema)`, and while the result is
  `input_required`, fulfils each embedded request through the SAME pending
  sampling/elicitation plumbing, then retries the original call with
  `inputResponses` + the echoed `requestState` on a fresh id, bounded by
  MRTR_MAX_ROUNDS.
- Route tools/call, prompts/get, and resources/read through the driver.
- Embedded roots/list is auto-answered from configured roots (no pending UX);
  decline/cancel is echoed to the server (not treated as an abort); a tool-call
  abort mid-round settles the pending request and surfaces
  ToolCallCancelledError.
- Extract shared `enqueuePendingSample`/`enqueuePendingElicitation` helpers used
  by both the legacy inbound handlers and the driver; add a per-request `origin`
  ("server-request" | "input-required") to the pending wrappers.

Web UI:
- New `MrtrOriginNote` element shows an "input_required — your answer returns as
  a retry" note on a modern MRTR round; legacy panels are unchanged.
- Thread `origin` App -> PendingClientRequestModal -> the sampling/elicitation
  panels (and the inline panels).

Test servers: new MRTR presets (multi-round, roots, sampling, loop, edge-case
param shaping). Integration tests cover the manual round-trip (asserting the
pending UI pauses and the retry carries inputResponses + requestState on a new
id), multi-round, roots auto-answer, sampling, decline-echoed, maxRounds bound,
abort mid-round, and requestState-only/inputRequests-only param shaping.

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

* Review: clarify sampling-reject and between-rounds abort comments (#1704)

Address review nits on PR #1719 (comment-only, no behavior change):
- enqueuePendingSample docstring: sampling has no decline/cancel action; the
  panel either sends a result (resolve) or Rejects (reject → fails the call).
  The shared "declined/cancelled resolves" wording was accurate only for the
  elicitation case.
- callToolWithRetries abort comment: note the between-rounds case — when the
  abort lands while an MRTR round awaits an embedded pending request there is no
  in-flight SDK request, so nothing is sent on the wire; the outcome
  (ToolCallCancelledError, no failed-call record) is identical.

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

* test-servers: add mrtr-showcase config exercising every MRTR preset (#1704)

A single modern-era streamable-HTTP server (port 3111) bundling all six MRTR
presets — mrtr_confirm, mrtr_two_step, mrtr_sample, mrtr_roots, mrtr_edge,
mrtr_loop — for manual testing of the manual-driving pending-request UX across
single-round, multi-round, sampling, roots, param-shaping, and maxRounds paths.
Document it in the README alongside modern-mrtr-http.json.

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-17 23:15:19 -04:00
Cliff Hall
73d9b181fe
History: MRTR conversation grouping + modern message vocabulary (#1705)
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
* History: MRTR conversation grouping + modern message vocabulary

Teach the Protocol/History view the modern (2026-07-28) message vocabulary,
correlating it off the frames the transport already captures — no change to how
MRTR is driven (manual driving is deferred to #1704).

- Correlation helpers in protocolUtils: extractResultType, extractRequestState,
  extractSubscriptionId, isModernFrame, and groupProtocolEntries — clustering the
  contiguous rounds of one logical operation (original call -> input_required ->
  retried call with a new id -> final result) that share a requestState token.
- New MrtrConversation group renders that cluster as one expandable unit with a
  status derived from the final round, reusing ProtocolEntry for each round.
- ProtocolEntry: resultType badge (input required / complete), a "modern" frame
  badge for server/discover, subscriptions/listen, and the acknowledged
  notification, and a copyable subscriptionId.
- ProtocolListPanel folds each section through the grouping pass and shows an era
  badge in the header. Era is threaded from the negotiated protocolEra
  (App -> InspectorView -> ProtocolScreen -> panel) and never inferred from
  frames, respecting the transcript-classification caveat.
- Shared EraBadge element (+ eraUtils); ConnectionInfoContent reuses it.
- test-servers/configs/modern-mrtr-http.json serves an elicitation tool over the
  modern leg for eyeballing a real MRTR round-trip.
- Pre-bundle @modelcontextprotocol/client in optimizeDeps.include so the
  Storybook vitest project doesn't re-optimize mid-run once protocolUtils pulls
  the client barrel into the browser graph.

Closes #1627

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

* test-servers: real MRTR tool + live-verified History grouping

While driving the modern flow in the browser, the elicitation-based config
proved unable to produce MRTR: the modern (2026-07-28) SDK server rejects
`server.elicitInput` — server→client requests aren't allowed on that leg; a
tool must instead return `inputRequired(...)`. So the shipped
`modern-mrtr-http.json` (and its README claim) were wrong.

- Add a genuine MRTR tool preset (`mrtr_confirm` / `createMrtrTool`): its handler
  returns `inputRequired({ inputRequests: { confirm: elicit(...) } })` on the
  first call and the final result once the client echoes the answer back on the
  retry.
- Teach the composable test-server to support it: the tool wrapper forwards an
  `InputRequiredResult` verbatim (instead of coercing it to a CallToolResult),
  and `toHandlerExtra` surfaces `inputResponses`/`requestState` so a handler can
  branch between the original call and the retry.
- Point `modern-mrtr-http.json` at the new preset; correct the README to
  describe the real mechanism.
- Add an integration test that drives the full round-trip through InspectorClient
  (auto-fulfilling the embedded elicitation) and asserts the final result is only
  reachable after the input_required round + retry — regression cover that would
  have caught the original config bug.

Verified live in the browser: the History view renders the round-trip as one
grouped "MRTR conversation" (Round 1 input_required → Round 2 complete), keyed by
the real requestState, under the Modern era badge.

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

* Address review notes: dedupe status badge, unique MRTR token, import alias

Non-blocking review feedback on #1705:

- ProtocolEntry: suppress the redundant green "OK" status badge when a
  `resultType` badge already conveys the outcome (a modern success shows only
  `complete`/`input required`). Errors/pending have no `resultType`, so their
  status badge still shows.
- createMrtrTool: mint a unique `requestState` per original call (closure
  counter) so two same-action invocations don't fold into one conversation
  during manual eyeballing. Real SDK tokens are already unique per operation.
- ProtocolEntry: align the `MessageEntry` import to the `@inspector/core` alias
  used by its siblings (was a deep relative path).

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

* Review round 2: green `complete` badge, lighter embedded MRTR container

Non-blocking observations from the second review:

- ProtocolEntry: `resultType` "complete" now renders green (was gray) so a modern
  success keeps the at-a-glance green success affordance a legacy "OK" had, now
  that the redundant OK badge is suppressed. `input_required` stays yellow.
- MrtrConversation: in the narrow monitoring (embedded) column, drop the outer
  container border and tighten padding — the per-round entries are already
  bordered cards, so this avoids a busy border-in-border nesting. The full-width
  view keeps the bordered surface that sets a conversation apart.

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

* Review round 3: module-scope MRTR counter; revert embedded border change

- test-servers: hoist the MRTR `requestState` counter to module scope. The
  modern (2026-07-28) leg is stateless — `createMcpHandler` rebuilds the server
  (and every tool closure) per request — so the previous per-closure counter
  reset to 0 on every request and every token ended in `:1`, defeating its
  purpose. A module-level counter survives the per-request factory recreation
  and increments monotonically across the process, so same-action invocations
  get distinct tokens and don't fold into one conversation. (Review finding.)
- MrtrConversation: revert the embedded borderless/tighter-padding change — per
  maintainer feedback from manual testing, dropping the border didn't improve
  the narrow-column visual, so the group keeps its border in all layouts.

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-17 20:02:11 -04:00
Cliff Hall
e06c0a45f7
Add modern-era (2026-07-28) test-server preset (#1700) (#1703)
* Add modern-era (2026-07-28) test-server preset (#1700)

The era-aware connection model (#1626, PR #1697) added per-server
`protocolEra` and the era-aware Connection Info UI, but no bundled test
server negotiated the modern era — the modern happy path had only
unit/Storybook fixtures, no live coverage.

Serve the modern (2026-07-28) protocol era from the HTTP test server via
the SDK's `createMcpHandler`. A `modern` flag on `ServerConfig` (and a
`transport.modern` field in the JSON config) mounts the handler with a
dual-era `legacy: "stateless"` fallback by default, or `legacy: "reject"`
for modern-only strict serving. The web-standard `fetch` handler reuses
the existing `toWebRequest`/`writeWebResponse` conversion, so no new
`@modelcontextprotocol/node` dependency is needed.

An SDK v2 client negotiating `protocolEra: "auto" | "modern"` now reaches
the modern leg end-to-end: `getProtocolEra() === "modern"`,
`getNegotiatedProtocolVersion() === "2026-07-28"`, and a populated
`getDiscoverResult()`. Verified live in the Inspector — Connection Info
shows Era Modern / Session Sessionless / the Discovery section.

- Modern serving path + `listen`/`startModernHttp` helpers in
  test-server-http.ts; modern-leg teardown in stop().
- `modern` config field threaded through load-config/resolve-config with
  a streamable-http-only validation guard; modern-http.json demo config.
- Integration test covering auto/modern negotiation, the dual-era legacy
  fallback, a modern tools/call, and the strict `legacy: "reject"`
  rejection.

Closes #1700

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

* Review nit: gate transport.modern guard on truthiness

A falsy `transport.modern` (e.g. `false`) is a no-op that `resolveConfig`
normalizes to `undefined`, so it shouldn't trip the streamable-http-only
guard. Gate on truthiness instead of `!== undefined` so only *enabling*
modern on a non-HTTP transport is rejected.

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-17 11:12:40 -04:00
cliffhall
e6278aea0f docs: address #1674 review — fix broken link + list rendering
- Root README no longer links to docs/mcp-server-configuration.md (not yet
  ported to v2); keep only the mcp-app-review.md pointer (review #3).
- Collapse the Local-fixture parenthetical onto one line so prettier no longer
  renders '- resource preset …' as a mid-sentence bullet (review #5).

Findings #1/#2 (documents unmerged #1576/#1577/#1665 behavior) and #4 (the
web-README #deep-link-auto-connect anchor) resolve in the Wave 4 rollup, which
lands this doc together with those code PRs — the reviewer noted the same.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-11 23:25:20 -04:00
cliffhall
a30a576017 docs: MCP App review recipe + root README pointer (#1578)
Closes #1578

Adds docs/mcp-app-review.md — the CLI-first → one-shot-web recipe for automated
MCP App review: `--app-info` probe → deep-link navigate → rendered widget, plus
OAuth-gated servers (stored-auth handoff, now honoring refresh_token per #1665),
SSH port-forward (both ports), and HTTP proxy support.

Adapted from PR #1510's reference doc to only document what actually merged and
to match the current implementation:
- Correct driver contract: `data-testid="connection-status"` + `data-status`
  /`data-error-message`/`data-deeplink`, `data-testid="open-app"`, and the
  `data-app-status="ready"` app lifecycle (not the reference's stale selectors).
- The zero-click `autoOpen` path (#1577) alongside the click-to-open variant.
- `--use-stored-auth` now refreshes a stored refresh_token (#1665), so the
  "token is not refreshed" caveat is replaced.

The per-client READMEs (web deep-link scheme + data-* contract, CLI handoff +
refresh + proxy) were updated in their own Wave 4 PRs; this adds the root README
pointer to docs/. Proxy/undici is already documented in the CLI README.

Part of the #1579 decomposition (Wave 4, docs — lands last).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-11 23:14:51 -04:00
cliffhall
070f02e85f review: set HOME explicitly + note healthcheck assumes --web
Follow-up on the re-review's two optional notes:

- Set `ENV HOME=/home/node` so the runtime-state dir (core/storage/store-io.ts
  resolves it from HOME) is explicit, rather than relying on the "." → WORKDIR
  fallback that happens to work because Docker doesn't set HOME on USER.
- Document that the HEALTHCHECK assumes the default --web mode; suggest
  `--no-healthcheck` for --cli/--tui runs (no web server to probe).

Re-verified: build ok, HOME=/home/node, runs as node (uid 1000), `/` → 200,
health → healthy.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-11 09:47:25 -04:00
cliffhall
941bf13587 ci(docker): add v2 Dockerfile + GHCR publish job (#1646)
v1 published a container image but v2 arrived with no Dockerfile (the
client/server/cli layout is gone). Add a v2 image for the single-package /
launcher architecture and wire the release-gated GHCR publish job.

- `Dockerfile`: two-stage build — stage 1 installs + `npm pack`s the publishable
  tarball; stage 2 `npm install -g`s that tarball, so the image ships the exact
  same artifact as npm with a clean `mcp-inspector` bin. Defaults to `--web` on
  `0.0.0.0:6274`, browser auto-open off. `.dockerignore` keeps the context lean
  and forces a clean in-container install/build.
- `publish-github-container-registry` job in main.yml (ported from v1):
  release-gated, multi-arch (amd64/arm64) build+push to GHCR with a provenance
  attestation. Independent of the npm `publish` job.
- README: document the image + how to run it; the release section now covers
  both publish jobs.

Verified locally: `docker build` succeeds, the container serves `/` (HTTP 200
with the injected token) and dispatches `--cli` / `--tui`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-11 00:36:39 -04:00
cliffhall
3b7ab5f548 ci(publish): add release-gated npm publish job for the single v2 package (#1640)
Wire the v2 publish pipeline into main.yml. On a published GitHub release, a new
`publish` job runs `npm run pack:verify` (the pre-publish gate — builds, packs
the real tarball, installs it into a clean consumer, and drives the bin) then
`npm publish --access public --provenance`. This is a single `npm publish` (v2
is not an npm workspace, so no v1-style publish-all/--workspaces), with a signed
provenance attestation via GitHub OIDC (id-token: write, environment: release,
NPM_TOKEN). Add the `release: types: [published]` trigger; the existing `build`
job is unchanged and the publish job is gated on the release event.

Document the single-version release/tag flow in the root README (one `npm
version` at the root; no per-client sync / check-version since only the root
package.json carries a version).

Docker/GHCR is deferred: v2 has no Dockerfile (the v1 client/server/cli layout
is gone), and a correct image needs a net-new, docker-build-tested Dockerfile.
Split into #1646.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-10 23:44:44 -04:00
cliffhall
7815548300 refactor(version): single-source the version from the root package.json
The Inspector ships as one package with one version, but four client
package.jsons each carried their own `version` and were read at runtime
(cli.ts, tui App.tsx), and core hardcoded a third, stale value (0.20.0) that
the TUI and web actually reported to servers. Consolidate onto the root
package.json as the single source of truth.

- Add core/node/version.ts: shared, Node-only readInspectorVersion() that walks
  up to the root package.json (always shipped in the tarball). 100% covered by a
  new integration test.
- CLI and TUI read the version via the shared reader instead of their own
  manifest; TUI now also passes a real clientIdentity (was falling through to
  the stale core default).
- Drop the `version` field from all four client package.jsons; the root keeps
  the only version. TUI header name/description become local display constants.
- core default client identity: replace the stale 0.20.0 hardcode with a neutral
  0.0.0 placeholder (real clients pass clientIdentity; web will source its
  version from GET /api/config, see #1639).
- Stop shipping clients/cli/package.json and clients/tui/package.json (no longer
  read at runtime) — supersedes the earlier "ship them" fix; update pack:verify
  and the root README packaging invariants accordingly.
- Add core/node/** to the web coverage include so the new module is gated.
- Add the shared-code architecture diagram to the root README.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-10 17:31:21 -04:00
cliffhall
79b80b459d docs: add root README; move publishing docs out of launcher README
CLAUDE.md included @./README.md, which did not exist. Add a root README
covering project layout, setup, the @inspector/core shared package, the web
"dumb components" + Storybook approach, test-servers, build, the testing/quality
gate, publishing, and how AGENTS.md/CLAUDE.md govern contributions for humans
and agents alike.

Move the publishing/packaging documentation (single-package tarball, "files"
invariants, pack:verify) from the launcher README to the root README, where
whole-repo packaging belongs; the launcher README now points there.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-10 15:46:51 -04:00
cliffhall
442bb68532 Remove all from v2/main 2025-11-28 13:49:31 -05:00
Ola Hungerford
7f6a2768c0
Merge branch 'main' into issue-846 2025-10-19 18:06:36 -07:00
olaservo
60ff2d37af Move Tool Input Validation Guidelines to Readme 2025-10-19 17:31:31 -07:00
Basil Hosmer
d6b179e6a8
Increase default request timeout to 5 minutes
- Changed MCP_SERVER_REQUEST_TIMEOUT from 10s to 5m (300000ms)
- Supports elicitation tools and other long-running operations
- Updated documentation to clarify client-side vs server-side timeouts
- Users can still customize via UI config or URL parameters
2025-10-16 17:13:14 -04:00
Wei He
1207834b7e
docs: add streamable transport examples 2025-09-07 00:27:56 -04:00
Wei He
0caaeed9ce
docs: add streamable transport examples 2025-09-06 23:57:16 -04:00
Ola Hungerford
897d9f07bc
Merge branch 'main' into feature/add-header-support 2025-08-28 19:44:09 -07:00
olaservo
03d48f3787 Add example to Readme of calling a tool with JSON arguments 2025-08-27 20:40:28 -07:00
Ola Hungerford
e5d0f34981
Merge branch 'main' into feature/add-header-support 2025-08-27 20:33:24 -07:00
cliffhall
c2e117a4a9 Add Docker container startup command to README.md 2025-08-25 10:13:32 -04:00
Ola Hungerford
e2fe3da358
Merge pull request #677 from JLLeitschuh/patch-1
Add warning for DANGEROUSLY_OMIT_AUTH usage
2025-08-18 08:19:52 -07:00
Phil Freo
d7927ec8dc
Add documentation for CLI header support
- Document --header flag usage with examples
- Explain header format requirements
- Clarify transport support (HTTP, SSE, not STDIO)
- Add authentication examples with API keys and bearer tokens
2025-08-14 12:20:40 -04:00
Felix Weinberger
e1e702ff14
ignore: ignore mcp.json as a common dev file 2025-08-07 11:42:31 +01:00
Felix Weinberger
1e399b7055
prettier 2025-08-05 14:08:57 +01:00
Felix Weinberger
3a4f3bc510
Document transport types and default-server functionality
- Add examples for stdio, sse, and streamable-http transport types
- Document automatic server selection behavior
- Show how to use default-server in config files
2025-08-05 14:08:57 +01:00
Jonathan Leitschuh
0194e5f62a
Add warning for DANGEROUSLY_OMIT_AUTH usage
Added warning about disabling authentication and its risks.
2025-08-04 14:35:33 -04:00
Ola Hungerford
80aa0c68da
Merge pull request #597 from LazaUK/patch-1
Docs update: Missing mandatory parameter in the Inspector's CLI command
2025-07-13 16:19:52 -07:00
Cliff Hall
5c15e50caa
Merge branch 'main' into patch-1 2025-07-11 14:58:06 -04:00
Olivier Chafik
b2347dbb0e
add npm run dev:sdk to get live sdk changes w/ simple server (#533)
* add `npm run dev:sdk` to get live sdk changes w/ simple server
2025-07-10 11:26:17 +01:00
Laziz Turakulov
7d888fcfa3
Missing mandatory parameter in the Inspector's CLI command
Attempts to execute this command will fail: npx @modelcontextprotocol/inspector --cli http://127.0.0.1:8000 --transport http. The error indicates that --method is a mandatory parameter: "Method is required. Use --method to specify the method to invoke. Failed with exit code: 1". That's why the right command may look like this: npx @modelcontextprotocol/inspector --cli http://127.0.0.1:8000 --transport http --method tools/list.
2025-07-10 00:05:21 +01:00
Kent C. Dodds
7d5f7968d1
Rename MCP_PROXY_TOKEN to MCP_PROXY_AUTH_TOKEN (#1)
Co-authored-by: Cursor Agent <cursoragent@cursor.com>
2025-07-07 15:21:27 -06:00
Kent C. Dodds
15b9b6773c
Update README.md 2025-07-07 13:19:51 -06:00
Kent C. Dodds
7305764d3c
Update README.md 2025-07-07 13:19:38 -06:00
Kent C. Dodds
4f43379ddc
Merge branch 'main' into patch-1 2025-07-07 13:18:28 -06:00
Cliff Hall
b28a10eae5
Merge branch 'main' into fweinberger/client-localhost 2025-06-25 17:57:55 -04:00
Ola Hungerford
f2aaa8d668
Merge pull request #479 from ryanjclark/feat/cli-http
Feat/cli http
2025-06-25 05:02:32 -07:00
Felix Weinberger
fe2c5c767d
refactor: deduplicate URL generation and standardize on localhost
- Extract duplicated URL generation code into getClientUrl() helper function in start.js
- Replace all 127.0.0.1 references with localhost for consistency across codebase
- Update server to respect HOST environment variable for URL generation
- Remove 127.0.0.1 from default allowed origins in CORS configuration
- Update documentation to use localhost instead of 127.0.0.1

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-24 10:43:13 +01:00
Felix Weinberger
9c5c7575c0
fix: Bind client to localhost by default to prevent DNS rebinding attacks
Complete the security hardening started in e8e9909 by also binding the client to localhost only.
Previously only the server was protected while the client remained exposed to the network,
allowing attackers to access the server through the client as a proxy.

Changes:
- Add HOST environment variable support to client (prod mode)
- Configure Vite dev server to bind to localhost by default
- Update browser auto-open URLs to use actual host instead of hardcoded 127.0.0.1
- Fix missing cancelled parameter in startProdClient function

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-24 10:43:13 +01:00
Felix Weinberger
698d2458c0
docs: Update README to reflect auto-open now works with authentication
- Removed outdated statement about auto-open being disabled with authentication
- Clarified that browser automatically opens with token pre-filled in URL
- Updated MCP_AUTO_OPEN_ENABLED description to note it works with authentication

The README now accurately reflects the improved developer experience where
authentication no longer prevents the browser from auto-opening.

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

Co-Authored-By: Claude <noreply@anthropic.com>
2025-06-18 10:23:03 +01:00
Kent C. Dodds
0f3d34bc9a docs: add note in readme 2025-06-16 18:58:51 -06:00
Felix Weinberger
9dbd17343c
feat: disable auto-open when authentication is enabled
- Auto-open browser only works when DANGEROUSLY_OMIT_AUTH=true
- When auth is enabled (default), users must use the secure URL with token
- Server displays clear message that auto-open is disabled with auth
- Updates documentation to reflect this behavior

This improves UX by preventing the browser from opening without auth token,
forcing users to use the secure pre-filled URL instead.
2025-06-10 18:16:38 +01:00
Felix Weinberger
15ecb59a96
feat: add origin validation to prevent DNS rebinding attacks
- Add origin validation middleware to check Origin header
- Default allowed origins respect CLIENT_PORT environment variable
- Support ALLOWED_ORIGINS environment variable for additional origins
- Apply validation to all protected endpoints before auth check
- Return 403 Forbidden with clear message for invalid origins
- Add DNS Rebinding Protection section to README

This completes the security hardening by preventing malicious websites
from making requests to the local proxy server.
2025-06-10 18:16:35 +01:00
Felix Weinberger
111e282386
feat: add session token authentication to proxy server
- Generate random session token on server startup using crypto.randomBytes
- Add authentication middleware requiring Bearer token for all sensitive endpoints
- Apply auth to /mcp, /stdio, /sse, and /message endpoints
- Display session token in console on startup
- Add DANGEROUSLY_OMIT_AUTH environment variable to disable auth for development
- Update README with authentication documentation

This prevents unauthorized access to the proxy server's ability to execute
local processes and connect to MCP servers.
2025-06-10 18:16:28 +01:00
Felix Weinberger
e8e990987c
fix: bind to localhost by default to prevent DNS rebinding attacks
- Change default binding from 0.0.0.0 (all interfaces) to 127.0.0.1 (localhost only)
- Add HOST environment variable to override binding address when needed
- Update console output to show actual binding address
- Document security change in README with warnings about binding to all interfaces

This prevents remote attackers from accessing the MCP Inspector proxy server,
which has the ability to execute local processes. The proxy should only be
accessible from the local machine unless explicitly configured otherwise.
2025-06-10 18:16:24 +01:00
Ryan Clark
5b674cfe35
feat: update README with example for http transport 2025-06-01 13:44:19 -05:00
Cliff Hall
291c59eaf9
Merge branch 'main' into clarify-proxy-role-in-readme 2025-05-30 18:37:40 -04:00
olaservo
11757ef615 Add simple architecture overview 2025-05-30 07:46:28 -07:00
David Sanftenberg
66bb9c896b
Add quick start example 2025-05-28 12:22:22 +01:00
sumeetpardeshi
8ca5a34d12 fix readme after merging main 2025-05-12 20:30:54 -07:00