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>
* fix(web): give History/Network/Logging list scrollbars a gutter, hide when idle (#1474)
The three main-content list panels rendered their ScrollArea.Autosize with
the default type="hover" and no offsetScrollbars, so the scrollbar popped in
on hover and overlaid the right edge of the cards (occluding the action
icons / status badges).
Add `offsetScrollbars` (reserves a 12px gutter so the bar never overlaps the
cards) and `type="scroll"` (the bar shows only during active scrolling, then
fades — no hover-pop) to HistoryListPanel, NetworkStreamPanel, and
LogStreamPanel. Verified in a real browser; adds a story assertion guarding
the gutter + idle-hidden behavior.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(web): assert scrollbar gutter on all three list panels (#1475 review)
Per review, regression-protect the gutter + idle-hidden scrollbar in every
panel, not just History. Extract the assertion into a shared
`expectScrollbarGutterIdleHidden` helper and use it from the History,
Network, and Logging panel stories.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* test(web): assert a scrollbar exists so idle-hidden check isn't vacuous (#1475 review)
Add `expect(scrollbars.length).toBeGreaterThan(0)` before the loop so the
idle-hidden assertion is meaningful rather than trivially-true if a future
change stops rendering the scrollbar element.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
* feat(sort): add SortToggle for Logs, History, and Network (#1371)
Introduces a reusable SortToggle element and wires it into the Logs,
History, and Network screens so users can flip chronological order.
Selection is persisted per-screen to localStorage under the
inspector.sortDirection.* namespace, with invalid stored values falling
back to the newest-first default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(logs): move auto-scroll checkbox left of the sort selector
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(logs): remove auto-scroll checkbox; ListToggle left of SortToggle
- Drops the Logs auto-scroll checkbox and its plumbing through
LoggingScreen / InspectorView.
- Moves the ListToggle to the left of the SortToggle on the History
and Network panel toolbars.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(sort): address review on #1372
- Hydrate sort direction synchronously (getInitialValueInEffect: false)
to remove the one-frame newest-first flicker on first paint.
- Drop the redundant `[...matched].sort()` copy across the three panels;
`.filter()` already returns a fresh array.
- Document why serializeSortDirection bypasses Mantine's default JSON
stringify (raw enum value in storage).
- MemoryStorage.getItem: collapse `has` + `!` to `?? null`.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(history): default the request list to collapsed for parity with Network
The History list opened expanded while Network opened collapsed; align
History with Network so both panels start in the same compact view.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(history,network): persist list compact state to localStorage
Lifts the per-panel `compact` flag out of HistoryListPanel /
NetworkStreamPanel and into InspectorView's localStorage hooks, mirroring
the sort-direction shape:
inspector.listCompact.history
inspector.listCompact.network
Defaults to collapsed (compact=true) on both, with a corrupted-value
deserializer that clamps unknown strings back to the default.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(servers,resources): persist ListToggle state to localStorage
Lifts the per-screen compact preference out of ServerListScreen and
ResourceControls into InspectorView's localStorage hooks, matching the
History/Network shape:
inspector.listCompact.servers
inspector.listCompact.resources
Servers and Resources both default to expanded (compact=false). For
ResourceControls the displayed compact is still derived from openSections
(so per-section accordion clicks still work locally), and only explicit
ListToggle clicks update the persisted preference — the handler computes
the next compact value from the click outcome rather than blindly
flipping, so a half-open accordion still records the right post-state.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(sort-toggle): switch to icon button matching ListToggle style
Replaces the Mantine Select with a subtle Button (or ActionIcon for the
subtle variant), using TbSortDescending2 for newest-first and
TbSortAscending2 for oldest-first. Clicking the button flips between the
two values.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(sort-toggle): restore Select with sort icon in rightSection
Reverts SortToggle to a Mantine Select with simplified labels ("Newest
First" / "Oldest First") and the corresponding TbSortDescending2 /
TbSortAscending2 icon in the rightSection, replacing the default chevron.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(sort-toggle): swap icons so they match label direction
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(sort-toggle): use down arrow for newest, up arrow for oldest
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* refactor(inspector-view): extract useSortDirection / useListCompact
Collapses seven near-identical useLocalStorage call sites in
InspectorView into two scoped helpers. Each helper carries the shared
namespace, serialize/deserialize adapters, and getInitialValueInEffect:
false; the call sites now read as "this screen has sort persisted to
this scope" rather than re-stating the storage shape every time.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(sort-toggle): apply prettier formatting
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* feat(export): wire History + Logs export JSON handlers (#1354)
Replaces the `todoNoop` placeholders in App.tsx with real handlers that
serialize the current entries and trigger a browser download via the
existing `downloadJsonFile` helper.
- Adds `buildExportFilename(kind, serverId, now?)` next to the download
helper. Produces sortable, filesystem-safe names like
`inspector-history-alpha-2026-03-17T10-00-42.123Z.json` (URL-encodes
server ids that contain slashes / spaces / colons, swaps `:` for `-`
in the ISO timestamp so the file is safe on Windows).
- Wires `onExportHistory` (full MessageEntry list) and `onExportLogs`
(the LogEntryData list the screen renders).
- Routes the existing Network export through the same filename helper
for consistency.
Unit tests cover the filename builder, including the no-server-id
fallback, special-character encoding, and the default-`now` shape.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* fix(export): tighten buildExportFilename signature + docs
- Introduce an `ExportKind = "history" | "logs" | "network"` union and
use it as the type of the `kind` parameter. Typos like "hisotry" now
fail at compile time and the supported export kinds are documented
inline.
- Clarify the JSDoc: the segment is omitted when `serverId` is falsy
(undefined OR empty string), matching the existing `serverId ? ... :
undefined` check. New test pins the empty-string branch.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
* chore(logging): remove Copy All button from LogStreamPanel
Export now produces the same JSON the Copy All button used to dump to
the clipboard, so the button is redundant. History and Network panels
have no equivalent — dropping Copy All also brings Logs into parity.
Removes the prop from LogStreamPanel + LoggingScreen + InspectorView +
App.tsx, and updates the matching tests/stories. Tests now assert the
button is *not* in the document so a future re-introduction would have
to update the assertion deliberately.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
- Extract ToolControls group from ToolsScreen (search, list, list-changed indicator)
- Extract PromptControls and PromptListItem groups for PromptsScreen sidebar
- Refactor PromptsScreen to sidebar + detail layout (matching ResourcesScreen pattern)
- Refactor PromptArgumentsForm to accept a single prompt instead of a dropdown
- Normalize all screen sidebar widths to 340px (Tools, Resources, Prompts, Logs, Tasks, History)
- Convert ToolsScreen from Grid-only to sidebar + Grid layout
- Replace LogControls checkboxes with full-width toggle buttons
- Move Clear/Export buttons from LogControls to LogStreamPanel toolbar
- Make PromptArgumentsForm "Get Prompt" button right-aligned and compact
- Update all affected stories and ConnectedView
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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>