Commit graph inspector/clients/web/src/components/groups/ServerListControls
Author SHA1 Message Date
Cliff Hall
da37692485
refactor(web): sweep AGENTS.md TypeScript + Mantine rule violations (#1771)
Codebase-wide sweep bringing the web client into strict compliance with the AGENTS.md TypeScript and Mantine/React conventions: ~170 inline 2+-static-prop elements extracted to named .withProps() constants across ~77 files, the AGENTS.md rule wording tightened to match (static-prop boundary + Accordion/Transition/data-*/Box exemptions), a real ScreenStage Box->Flex layout regression fixed (direction: column) with a Storybook regression guard, and an App.tsx double-cast tightened. Closes #1762.
2026-07-25 11:38:49 -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
ea8715c9c4 style(web): size + space the toolbar icon buttons to match the header
Make the server-list toolbar's expand/collapse toggle and open/close-monitoring
icons the same width and spacing as the header's theme / client-settings icons:

- ListToggle (default variant), PinColumnButton, and the MonitoringControls
  close button now render as `ActionIcon variant="subtle" size={36}` (glyph 20),
  matching the header's 36px ActionIcons, instead of wider `Button`s.
- ServerListControls uses `gap="sm"` so its buttons sit the same 12px apart as
  the header's RightSection controls.

Update a TaskListPanel test that found the toggle by a brittle
`mantine-Button-root` class to select it by its accessible name instead.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-10 20:49:18 -04:00
cliffhall
0c6fafbf60 feat(web): add an open-monitoring-column button to the server list
When a server is connected, has monitor screens to show, and the monitoring
column isn't open, show an "open monitoring column" button to the right of the
list expand/collapse toggle on the server list. It reuses PinColumnButton (the
same right-sidebar affordance the monitor screens use, now with an optional
label), so opening the column is reachable from the server list — not only from
a monitor screen's pin button. InspectorView gates it on
`canPin && monitorAvailable.length > 0 && !effectivePinned`.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-10 20:23:27 -04:00
cliffhall
5d024ab54f feat(web): add copyright notice + footer/toolbar polish (#1639)
- Add a grey CopyrightBadge ("Copyright © Model Context Protocol a Series of LF
  Projects, LLC.") to the footer. Version (bottom-left) and copyright
  (bottom-right) share the bottom `xl` margin band: vertically centered in it
  and inset by `xl` to line up with the content's left/right margins.
- Move the list expand/collapse toggle to the right of the "Add Servers" button
  in ServerListControls.
- Make the monitoring column's close-sidebar control match the open-sidebar
  PinColumnButton theme (Button size sm / variant subtle) instead of a gray
  ActionIcon, so the open/close affordances read as a pair.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01BrihGWcrM9JGRyu41nzZYw
2026-07-10 20:11:52 -04:00
Cliff Hall
91f02fd6db
feat(web): catalog vs read-only session launch model + apply --header (#1481, #1483) (#1496)
* feat(web): catalog vs read-only session launch model + apply --header (#1481, #1483)

Per specification/v2_catalog_launch_config.md, `mcp-inspector --web` now selects
the backend's server-list source and a session-wide writability, instead of
seeding an inert `initialMcpConfig` the UI never read:

- `--catalog <path>` (and `MCP_CATALOG_PATH`) → active WRITABLE catalog
  (seed/CRUD/watch), like the default catalog.
- `--config <path>` → READ-ONLY session file: served in the UI but never
  written, seeded, or plaintext-secret-migrated — safe to point at a foreign
  config (e.g. claude_desktop_config.json).
- ad-hoc `--server-url`/command (+ `--header`) → one server held IN MEMORY
  (no file written); `--header` is lifted onto the entry so the seeded
  connection actually uses it (closes the #1483 warn-only no-op).
- nothing → default catalog (writable), unchanged.

Backend (`createRemoteApp`): new `writable` + `initialServers` options. GET
/api/servers branches three ways (in-memory / read-only file with no
seed+no migrate / writable catalog); the four mutating routes return 403 when
read-only; `/api/config` carries `writable`; the file watcher is skipped for
in-memory sessions. `--catalog`/`--config` are mutually exclusive and neither
combines with an ad-hoc target or `--header`; `--header` requires an ad-hoc
HTTP/SSE server (clear errors otherwise — no silent no-op).

UI: `useServerListWritable` reads `writable` from /api/config; the Servers
screen hides add/edit/clone/remove/reorder/settings and shows a read-only
banner when false. All new props default to writable=true (back-compatible).

Docs: spec gaps G2/G3/G4 updated; launcher README documents the flags.

Verified end-to-end against the built launcher: --config (writable:false,
file byte-stable, mutations 403), ad-hoc --header (in-memory, header lifted),
and --catalog (writable:true, POST persists).

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

* refactor(web): address review — Mantine <Code> in read-only banner, clarify watcher comment

- Replace raw <code> with Mantine <Code> in the read-only-session Alert
  (repo convention: prefer Mantine primitives over raw HTML).
- Add a comment near `watchable` clarifying that a read-only `--config` file
  is still watched on purpose (read-only blocks UI writes, not external
  observation; `writable` does not gate watching).

Deferred (per reviewer): consolidating useSandboxUrl + useServerListWritable
into one useConfig hook to avoid the duplicate /api/config fetch — optional
follow-up. The redundant ad-hoc header type-guard is kept as defensive.

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-06-16 14:24:58 -04:00
Cliff Hall
420da2e415
feat(servers): export server list as mcp.json download (#1346) (#1351)
* feat(servers): export server list as mcp.json download (#1346)

Adds an Export button to the Servers screen, next to "Add Servers".
Clicking it downloads the current server list as a canonical mcp.json
file (2-space indent, same shape serializeStore writes), so the user
can hand it off, sync it to another machine, or symlink into Claude
Desktop / Cursor / Cline.

Wiring:
- ServerListControls: new Export Button (variant=default), disabled
  when serverCount === 0 (nothing to download).
- ServerListScreen / InspectorView: thread `onExport` / `onServerExport`
  through.
- App.tsx: onServerExport runs serverEntriesToMcpConfig on the in-memory
  servers list, JSON.stringify with 2-space indent, and triggers a Blob
  download via a temporary anchor (appended to body for Firefox
  compatibility, then removed; object URL revoked).

Tests:
- ServerListControls unit tests cover button-count (toggle hidden vs
  shown, Export always present), disabled-on-empty, click fires onExport.
- ServerListControls.stories grows play functions for both stories —
  WithServers asserts Export is enabled + click fires the spy in real
  Chromium, WithoutServers asserts Export is disabled.
- Existing screen + view tests / stories updated for the new prop.

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

* fix(servers): address PR #1351 review (extract helpers + exception safety + guard)

Substantive items from the review:

1. Serialization format parity (review #1): added
   `serializeMcpConfig(entries)` to core/mcp/serverList.ts — a
   browser-safe helper that runs `serverEntriesToMcpConfig` then
   stringifies with 2-space indent. Single source of truth for export
   formatting; can't drift from serializeStore's on-disk shape.

2. Exception safety on the anchor cleanup (review #2) + a direct test
   for the App.tsx download wiring (review #6): extracted
   `downloadJsonFile(filename, json)` to clients/web/src/lib/
   downloadFile.ts. removeChild + revokeObjectURL run in a `finally`
   so a thrown click() doesn't leak the DOM node or the object URL.
   Unit-tested under happy-dom with URL.createObjectURL mocked
   (happy-dom doesn't ship it).

3. Empty-list guard (review #5): App.tsx onServerExport bails when
   `servers.length === 0`. The button's disabled prop already prevents
   the UI path, but the handler is now locally correct against any
   future programmatic caller (storybook, keyboard shortcut, etc.).

Acknowledged but not changed:
- Tooltip on the disabled Export button (review #3) — Mantine's
  disabled-button + Tooltip wart wants a span wrapper + custom events
  config. Optional per the reviewer; deferred.
- Repeated exports → `mcp (1).json` browser suffix (review #4) — a
  deliberate trade-off since `mcp.json` is the conventional name a user
  would expect to symlink into Claude Desktop / Cursor / etc.

Tests: 1647 unit (+6) + 410 integration + 306 storybook = 2363 green.

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-23 15:15:56 -04:00
Cliff Hall
75f74d64c1
test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245) (#1269)
* test(coverage): bootstrap vitest unit infra and add tests at 90%+ per file (#1245)

Establishes the unit-test runtime AGENTS.md mandates: a vitest "unit"
project alongside the existing storybook project, RTL + jest-dom +
happy-dom, a Mantine-aware render helper, and a 90%-line per-file
coverage gate enforced by CI. Adds 66 test files covering all 62
components under elements/groups/screens/views, plus core/mcp and
clients/web/src/utils. Total: 577 tests, 98.7% line coverage globally.

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

* test(coverage): address PR #1269 review feedback

- AGENTS.md: correct coverage gate description (lines ≥ 90, statements ≥ 85,
  functions ≥ 80, branches ≥ 50) with rationale for the relaxed branch
  threshold
- InspectorView.test.tsx: document what the Math.random() mock values
  (0.1 / 0.99) control vs. STUB_SUCCESS_RATE in InspectorView.tsx
- vite.config.ts / tsconfig.test.json: drop unused `globals: true` and the
  `vitest/globals` type now that every test file imports describe/it/expect/vi
  explicitly; manual `afterEach(cleanup)` in setup.ts remains required because
  testing-library auto-cleanup relies on globalThis.afterEach

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 15:15:32 -04:00
cliffhall
961dc283fc Add new components, typography, badge contrast, and screen layout refactors
New element components:
- ListToggle: expand/collapse button using react-icons (RiExpandVerticalLine/RiCollapseVerticalLine)
- LogLevelBadge: extracted from LogEntry, handles all 8 log levels with filled variant and dark/light text contrast

New group components:
- ViewHeader: shared header bar for ConnectedView and UnconnectedView with discriminated union on `connected` prop; MCP logo links to modelcontextprotocol.io
- ServerListControls: combines ListToggle and AddServerMenu, hides toggle when no servers
- LogStreamPanel: extracted from LoggingScreen with scroll containment, filtering by visible levels and search text
- HistoryControls: vertical sidebar with search, method filter, export, and clear
- HistoryListPanel: extracted from HistoryScreen with ListToggle compact mode, filtering by method and search text

Badge contrast improvements:
- AnnotationBadge: filled variant for both dark/light modes; black text in dark, white text in light
- LogLevelBadge: same contrast approach; bold weight for alert/emergency levels
- Badge theme default weight reduced from 700 to 600

Typography:
- Added Fredoka (300-700) as body and heading font
- Added Roboto Mono as monospace font
- Google Fonts loaded in index.html and Storybook previewHead
- Heading weight set to 600

Screen layout refactors:
- LoggingScreen: owns filterText and visibleLevels state; passes to LogControls and LogStreamPanel; removed controls prop in favor of flat props
- HistoryScreen: owns searchText and methodFilter state; passes to HistoryControls and HistoryListPanel for filtering
- Both screens use new ScreenLayout (Flex variant="screen") and Sidebar subcomponents
- Added Flex theme with "screen" variant (overflow: hidden)
- Added Paper theme "panel" variant (flex column, overflow hidden)

Component enhancements:
- ServerCard: added compact prop showing only header row
- HistoryEntry: renamed isExpanded to isListExpanded; internal useState syncs via useEffect; individual entries toggle independently
- LogControls: Select All / Deselect All toggle for level checkboxes; nowrap on level selector group
- ConnectionToggle: LoadingOverlay with rounded corners for connecting state
- ConnectedView: ThemeToggle ActionIcon matched to DisconnectButton height (36px) and border color (outline variant)
- Storybook preview: fullscreen layout wrapper with 100vh overflow hidden

New stories:
- UnconnectedView: Connecting and ManyServers (12 servers for scroll testing)
- ServerCard: CompactConnected and CompactDisconnected
- All new components have corresponding stories

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-28 20:38:04 -04:00