Commit graph inspector/clients/web/src/components/groups/ProtocolEntry
Author SHA1 Message Date
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
8d33e76f97
web: server-screen, footer, monitoring-sidebar, and responsive-header polish (#1683)
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 #1682

Server-screen, footer, monitoring-sidebar, and responsive-header polish: connected-card highlight/scroll + Failed status, footer row + MCP-docs tooltip, Servers box, card-surface refresh (gray-0 entry cards + white code), monitoring-sidebar layout, a 1280px responsive floor honored by the header (always-icon Disconnect, always-SegmentedControl tabs, 1500px status text, always-available sidebar), a horizontally-scrollable long resource URI in the compact Protocol entry, and copy buttons beside resource URIs / network URLs.

🤖 Generated with [Claude Code](https://claude.com/claude-code)
2026-07-14 08:55:39 -04:00
Cliff Hall
3d75ef5f8e
web: Accessibility — accessible palette + WCAG AA contrast, labels, names, roles (#1679)
* web: accessible light/dark color palette + dark-mode legibility fixes (#1678)

Ports the design system's accessible palette. Colors are defined once as Mantine
color tuples in theme.ts (shade 0 lightest → 9 darkest); every `--inspector-*`
token in App.css references `--mantine-color-*`, so improving the tuples cascades
through the whole UI.

- theme.ts: full accessible tuple set (inspector/gray/dark/green/red/yellow/blue/
  teal/violet), WCAG-AA at the primary shades. Tuples are the one place literal
  hex belongs — the token source. Primary stays inspector-7 (light) / -8 (dark).
- theme/ActionIcon.ts: default `color: "gray"` so chrome icons (settings, theme
  toggle, sidebar, pin, sort, replay) render neutral grey instead of inheriting
  the blue primary. Icons that set their own color/variant still override.
- App.css: fix light surfaces (`surface-body` gray-0, `surface-card` white — the
  body token pointed at near-black dark-9); add direction/method badge tokens;
  lift the semantic status/log/task/history colors 1–2 steps in the dark scheme
  (the shared `*-6` shade sat too dark on charcoal). Light `:root` keeps `*-6`.
- Badges: MessageDirectionBadge + new MethodBadge element driven by
  `--inspector-badge-*` tokens (deep fill + light text in dark, tinted in light)
  via Mantine `bg`/`c` style props with `autoContrast={false}`; swaps
  `<Badge color="dark">{method}</Badge>` in ProtocolEntry/NetworkEntry (their
  Badge import stays — both still use it for status/category chips). Stories +
  test added for MethodBadge (≥90 coverage gate).

Domain badge semantics live in the element components, not theme files. No raw
hex outside the tuples; no inline styles. Supersedes the reverted #1677.

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

* web: WCAG AA accessibility pass — contrast, labels, names, roles (#1678)

Drives the axe-core ruleset (the engine behind Storybook's a11y addon) to
zero violations across all stories in light and dark mode.

Contrast (805 → 0 hits):
- Darken Mantine `*-light-color` + `dimmed` in light mode (and lift dark
  `dimmed`) via `!important` App.css overrides — Mantine injects these at
  runtime at matching specificity, so specificity alone loses the tie.
- `filledBadgeColor` pins amber (orange/yellow) filled badges to shade 5 so
  `autoContrast` (JS-computed from the theme shade, not CSS vars) picks black.
- `accessibleTextColor` routes colored log/filter text through the scheme-aware
  `-light-color` token so it clears AA on light and dark surfaces.
- `status` Badge variant: amber statuses use black-on-amber (orange text can't
  reach AA on light at any Mantine shade).
- Resource URIs use `-light-color` (blue-4 was 4.38:1 in dark).

Names / roles:
- `aria-dialog-name`: custom-header modals use `Modal.Title` (wires the
  dialog's `aria-labelledby`) instead of a bare `Title` + a root `aria-label`
  (which also tripped `aria-prohibited-attr`).
- `button-name`: a `Modal` theme default labels every built-in close button.
- `label`: `aria-label` on the previously-unlabelled Textarea/Select inputs.
- `scrollable-region-focusable`: keyboard-focusable content Code/ScrollArea
  viewports; long OAuth values wrap instead of scrolling.
- `heading-order`: correct heading levels (keeping visual size via `size`) so
  outlines don't skip a level under the h2 modal/section titles.

Scrollbars: `type="scroll"` ScrollArea theme default — scrollbars auto-hide and
show only while scrolling (a deliberate product choice over the always-visible
a11y default).

`region` is disabled in the Storybook a11y config: isolated component/screen
stories render outside the app's `AppShell` landmarks, so it's a page-level
concern the components can't own.

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

* web: let the resource template form fill the main-area width (#1678)

Drop the maw=40% cap on the template-form pane so the URI preview and
variable inputs use the full width beside the sidebar, matching the
resource preview pane.

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

* web: left-align + reverse the input-panel action rows (#1678)

Move the footer action buttons to the left (justify flex-start) and put
the primary action first on the Tool / Prompt / Resource-preview /
Resource-template panels — Subscribe, Refresh, View Source on the
preview panel; Execute before Cancel on the tool panel. Shortens the
pointer travel from the sidebar controls / the form fields above to the
actions, which matters most when the monitoring sidebar is hidden and
the form is wide.

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

* web: match the dark-mode body background to the header tab bar (#1678)

Set the dark `--inspector-surface-body` to `dark-8` (the Mantine
SegmentedControl fill used by the header tab bar) instead of `dark-9`,
mirroring light mode where the body and the tab bar share `gray-1`.
Cards (`gray-9`) still sit a step above the body, and the contrast audit
stays at zero AA violations on the slightly lighter background.

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

* web: enforce the a11y gate + fix modal-overlay heading-order (review #1679)

Address the code review:
- Flip the Storybook a11y addon from test: 'todo' to 'error' so axe
  violations fail the play-function tests in CI — the zero-violation
  state is now gated, not point-in-time.
- Enforcing it surfaced a real heading-order skip: the Tools screen's
  h4 'Tools'/'Results' sit under an h2 Modal.Title when a sampling/
  elicitation request modal opens over them. Lift both to h3 (size h4
  preserves the look) so h2→h3 is valid in any DOM order.
- Comment the intentional color+c pairing on the Remove/Disconnect
  danger buttons.

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

* web: brighter warning alerts + pin the elicitation form's actions (#1678)

- Add a 'warning' Alert variant: a bright light-yellow fill (yellow-1)
  with crisp near-black text and a bright yellow-5 left accent, instead
  of the muddy brown a tinted yellow alert produced once
  yellow-light-color was darkened to yellow-9 for contrast. Dark mode
  keeps the light-on-dark-tint treatment. Applied to all five yellow
  warning/notice alerts.
- Elicitation form: scroll only the form fields (capped ScrollArea),
  keeping the quoted message, warning, and Cancel/Decline/Submit buttons
  in view. The modal has no other close affordance, so the actions must
  never scroll out of reach.

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

* web: robustly pin the elicitation form's actions (#1678)

The previous approach capped only the fields' ScrollArea with a fixed
rem reserve for the pinned chrome, which underestimated it on some
viewports — the whole modal then overflowed and the buttons clipped
half-off-screen. Instead cap the whole panel below the modal height and
lay it out as a flex column: the message and the warning + buttons are
pinned (flex: 0 0 auto) and only the fields (flex: 1) shrink and scroll.
Verified the buttons stay within the panel and the modal fits the
viewport down to very short heights.

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

* web: fix elicitation form scrolling — fields scroll, actions pinned (#1678)

The prior fix capped the panel with a bare max-height, but a flex:1
ScrollArea's viewport won't bound against a max-height (not fixed-height)
parent, so a tall form's fields overflowed unscrollably (clipped) rather
than scrolling. Adopt ToolDetailPanel's pattern instead: the modal gives
its body a bounded flex column, the panel fills it, and the fields use a
flex: "0 1 auto" ScrollArea — which sizes to content when the form is
short (modal stays compact, nothing scrolls) and shrinks + scrolls once
the panel is capped at the modal height, keeping the message, warning,
and Cancel/Decline/Submit pinned in view. Add a ManyFields story (with a
bounded-height decorator mirroring the modal) to cover the scroll case.

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

* web: fix elicitation scrolling in the real modal (cap fields, not flex)

The flex-column approach (panel flex:1 in a flex-column modal body, fields
flex:"0 1 auto") worked in a fixed-height Storybook decorator but NOT in the
real modal: the modal body is bounded by max-height, not an explicit height,
so the fields' ScrollArea viewport still couldn't bound — a tall form's
fields overflowed and pushed the warning + Cancel/Decline/Submit off-screen.

Drop the flex plumbing (and the modal-body styles) and cap the fields
directly with ScrollArea.Autosize mah=calc(90dvh - 22rem): it sizes to
content when the form is short (modal compact, nothing scrolls) and scrolls
once the fields hit the cap. The 22rem reserve = the measured non-field
chrome (modal header/padding + message + warning + buttons + gaps), so the
whole modal stays within 90dvh and the actions are always in view. Verified
in the real PendingClientRequestModal (new ElicitationFormTall story) for
both short and tall forms.

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

* web: pin the sampling modal's actions too (#1678)

Apply the elicitation-form treatment to SamplingRequestPanel: wrap the
request/response content in a ScrollArea.Autosize capped at
calc(90dvh - 10rem) so it scrolls while the Reject / Send Response buttons
stay pinned in view (the modal has no other close affordance). The 10rem
reserve is the pinned chrome (modal header/padding + the actions + gaps) —
smaller than the elicitation form's 22rem since there's no warning banner.
Add a SamplingTall story (8 messages + preferences) to cover the scroll
case in the real PendingClientRequestModal.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-13 18:20:55 -04:00
Cliff Hall
dbda70e83b
Monitoring sidebar toggle, Prompts-style Tools results, and icon-button UI polish (#1661) (#1662)
* feat(web): single header MonitoringToggle + Prompts-style Tools results (#1661)

Consolidate the monitoring column's open/close affordances into one header
toggle, and align the Tools/Prompts result panes and the sidebar log layout.

- Add a single `MonitoringToggle` in the header, to the right of the theme
  icon. It opens/closes the monitoring column and only shows when a server is
  connected OR a connect attempt failed (wide viewport + a monitor tab
  available) — the same availability rule the column uses.
- Remove the scattered pin/close icons: delete `PinColumnButton`; drop `onPin`
  from the Logs/Protocol/Network/Console screens and their stream panels; drop
  `onOpenMonitor` from the server list; remove the close button from
  `MonitoringControls`/`MonitoringScreen`.
- Tools screen: remove the "Results will appear here" placeholder pane. Results
  now replace the input form in a single content pane (the Prompts pattern),
  with a top-left X (ToolResultPanel) that dismisses them back to the form;
  form values are preserved for a re-run.
- Prompts screen: the argument form fills the content pane width (drop the 40%
  cap), matching the Tools input.
- LogEntry: add a `compact` two-line layout (time/level/logger on line one,
  message wrapping below) used by the embedded monitoring-column Logs.

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

* polish(web): tooltips on icon buttons + rename "monitoring column" → "monitoring sidebar" (#1661)

- Add Mantine `Tooltip`s to the icon-only controls: Client settings, Theme
  (dynamic light/dark label), the monitoring-sidebar toggle, the list-level and
  per-entry Expand/Collapse toggles, the Pin/Unpin toggle, and the Replay
  button. Labels mirror each control's existing aria-label.
- Rename the feature term "monitoring column" → "monitoring sidebar" across the
  user-facing labels (Open/Close/Resize), comments, and tests, so the UI calls
  it a sidebar consistently.

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

* review: consistent sidebar wording + reuse MonitoringToggleProps (#1661)

Address the two optional notes from the PR #1662 review:
- Make the MonitoringToggle JSDoc voice consistent ("sidebar" throughout,
  no lingering "column").
- Drop ViewHeader's duplicate `MonitorToggleProps` and reuse the element's
  exported `MonitoringToggleProps` as the single source of truth.

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

* feat(web): slide-in/out animation for the monitoring-sidebar toggle (#1661)

The header MonitoringToggle now animates in/out via a Mantine Transition whose
in/out styles drive width + margin-left (plus opacity/translateX): as its slot
grows from 0 → the 36px icon, the flex-end header row reflows and pushes the
theme + client-settings buttons one slot to the left; the exit reverses and they
slide back over. A negative margin-left while collapsed cancels the row gap so
the theme icon stays flush-right when the toggle is absent. Only appearance/
disappearance (connect / disconnect / narrow viewport) animates — opening or
closing the sidebar leaves the toggle in place.

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

---------

Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
2026-07-11 17:30:16 -04:00
cliffhall
9824055425 refactor(web): unify message-direction arrows and rename Pinned section (#1623)
- Direction labels now both point right so the arrow reads clearly at
  badge size: incoming "client ← server" becomes "server → client"
  (from → to), in MessageDirectionBadge and MessageDirectionFilter
  (shared by the Protocol and Network views).
- The Protocol list's pinned section header now reads "Pinned Messages"
  (was "Pinned Requests"), matching the "Messages" panel title.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-09 19:45:52 -04:00
cliffhall
ace4250d31 refactor(web): rename "History" tab/feature to "Protocol" (#1623)
Rename the web client's History tab and its supporting code to Protocol
so the feature reads as the MCP "Protocol" view.

- Tab renders as "Protocol"; INSPECTOR_TAB_IDS, InspectorView tab
  constants/MonitorTab, TAB_UI_REGISTRY key, and navigation.ts tab id all
  updated to Protocol.
- Rename components + folders: HistoryScreen -> ProtocolScreen,
  HistoryListPanel -> ProtocolListPanel, HistoryEntry -> ProtocolEntry,
  HistoryControls -> ProtocolControls, historyUtils -> protocolUtils
  (plus their tests and stories, and exported types).
- Rename App/InspectorView plumbing: historyUi, pinnedHistoryIds,
  on*History* handlers/props, replayHistoryRequest,
  isReplayableHistoryMethod, sort/compact scopes, etc. -> Protocol names.
- Export filenames use the "protocol" slug (buildExportFilename +
  ExportKind).

Preserved (per issue #1623 decisions):
- The in-panel section discriminator keeps its "pinned" | "history"
  values, and the sub-sections stay labeled Pinned / History.
- window.history and core message-log/replay concepts are untouched.

npm run ci passes (format, lint, build, unit tests, >=90 per-file
coverage gate, smoke, Storybook).

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-09 19:04:54 -04:00