* chore: close a batch of quality-gate & tidy gaps (#1785 #1787 #1789 #1790 #1767) Bundles five small, related tidy/gate items into one PR: - #1785: delete the dead src/types/navigation.ts InspectorTab (0 importers; duplicated/drifted from utils/inspectorTabs InspectorTabId). src/types/ now holds only ambient .d.ts stubs. - #1787: add src/hooks/** to the web coverage `include` (it was silently outside the ≥90 gate) and add tests for the two untested hooks, useServerJsonImport and useImportClientConfig, bringing every hook to ≥90 on lines/statements/functions/branches. - #1789: widen clients/{cli,tui,launcher} format/format:check to their top-level configs; add root format:shared / format:check:shared covering test-servers/src/**, vitest.shared.mts, and the repo-root eslint.config.js. - #1790: pin prettier to an exact version (3.8.4, dropping the ^3.8.1 caret) in all five package.json files so the format gate's verdict can't shift with an in-range patch bump; lockfiles synced. - #1767: its root-file format+lint gap is closed here (format:shared + a new lint:shared via a second files block in the root eslint.config.js; web/server/ was already gated in #1788). Its larger cli/tui __tests__ typecheck piece (76 type errors) is split out to #1791. Prettier-normalized the newly-covered files (pure formatting). No runtime behavior change. `npm run ci` green. Closes #1785 Closes #1787 Closes #1789 Closes #1790 Closes #1767 Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * chore: address PR #1792 review findings - #1789 gap: widen clients/web format to include .storybook and clients/ launcher format to include scripts/; reformat the 3 .storybook files (were non-conforming under the pinned prettier 3.8.4). Audited all 885 tracked source files — now fully prettier-clean. - Export VALIDATE_DEBOUNCE_MS / COLLAPSE_DELAY_MS / HIGHLIGHT_DURATION_MS from useServerJsonImport and use them in the test instead of hardcoded 300/1000/250 (so a tuned constant can't silently desync the test). - Assert the symmetric renameErrors["alpha"] alongside ["beta"] so a one-directional collision-check regression fails. - Comment the blank-rename test as exercising a UI-unreachable defensive branch (canImport gates submit). - Un-stale the two specification/v2_ux_interfaces_plan.md references to the now-removed navigation.ts InspectorTab. npm run ci green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * docs: sync AGENTS.md format-surface description with the widened globs (#1792 review r2) 1ce3b64 added .storybook to clients/web's format glob and scripts/ to clients/launcher's, but AGENTS.md:222 still described the pre-fix scope. Bring it in line and note the non-recursive top-level glob is why nested dirs are named explicitly. Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * chore: handle PR #1792 round-3 follow-ups in-place (format-coverage guard, uniform globs, theme gate) Rather than defer these to tickets, fold them into this PR: - Obs 2: widen every client + root format glob to a uniform *.{ts,tsx,mts,cts,js,jsx,mjs,cjs} so a future .mts/.cjs/.tsx top-level file can't silently slip the gate. - Obs 1: add scripts/verify-format-coverage.mjs (wired into `validate`) — it parses the format:check globs out of every package.json, enumerates all tracked source files, and fails listing any not covered by a glob. The durable, standing version of #1789's one-shot audit; negative-tested. - Obs 3: bring src/theme/** under the coverage gate (21/23 files were already ≥90 transitively; add Paper.test.tsx for the one laggard, Paper.ts). src/App.tsx (~4.5k lines, ~42% branches) is converted from a silent omission into an explicit, documented exclusion (comment on the coverage `include` + AGENTS.md) — gating it is a dedicated effort, not a whitelist tweak. npm run ci green (the new guard reports 885/885 tracked source files gated). Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * chore: address PR #1792 round-4 findings (guard hardening, docs, exclusion list) - Harden verify-format-coverage: only harvest format:check globs from scripts transitively reachable from `validate` (via npm run refs), so an unwired/renamed format:check no longer counts as coverage. Negative-tested — renaming clients/web format:check to an unreferenced name now fails the guard (627 files flagged) instead of passing. - Name src/main.tsx and src/index.ts (browser + bin bootstraps) alongside src/App.tsx in the coverage-include comment and AGENTS.md, so the documented exclusion list is complete rather than claiming App.tsx is the only one. - Doc sync: document verify:format-coverage (AGENTS.md + README script table + scripts/ layout line), fix the `validate` ordering (verify:format- coverage now runs before validate:core) in AGENTS.md and README, and update the quoted globs to the uniform *.{ts,tsx,mts,cts,js,jsx,mjs,cjs} / test-servers *.{ts,tsx,mts,cts}. npm run ci green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 * chore: address PR #1792 round-5 findings (airtight guard + doc/config sync) - verify-format-coverage: assert the root `validate` chain invokes each client's `validate` (cd <dir> && npm run validate), closing the last reachability edge — a client dropped from the root chain now fails the guard even though its own globs still exist. Negative-tested. - eslint.config.js: widen the shared `files` block to test-servers/src/**/*.{ts,tsx,mts,cts} to match the widened lint:shared glob, so a future .mts/.cts there is lint-gated, not just prettier-gated. - AGENTS.md: scope the "uniform extension set" claim to client globs (core stays {ts,tsx}, shared {ts,tsx,mts,cts}), with verify:format-coverage as the backstop — removing the self-contradiction with the line below. npm run ci green. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01XNdjEPKLG637X8YmhDiEk5 --------- Co-authored-by: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
721 lines
41 KiB
Markdown
721 lines
41 KiB
Markdown
# Inspector V2 UX - Component Interfaces Implementation Plan
|
||
|
||
### [Brief](README.md) | [V1 Problems](v1_problems.md) | [V2 Scope](v2_scope.md) | [V2 Tech Stack](v2_web_client.md) | V2 UX | [V2 Auth](v2_auth.md) | [V2 New Spec Impact](v2_new_spec_impact.md)
|
||
#### [Overview](v2_ux.md) | [Features](v2_ux_features.md) | [Handlers](v2_ux_handlers.md) | [Screenshots](v2_screenshots.md) | [Components](v2_ux_components.md) | [Interfaces](v2_ux_interfaces.md) | Plan
|
||
|
||
## Goal
|
||
|
||
Refactor every component under `clients/web/src/components/` so its prop
|
||
interface aligns with the [V2 UX Component Interfaces](v2_ux_interfaces.md)
|
||
spec. After this work, every dumb component consumes either:
|
||
|
||
1. A **raw MCP schema type** imported from `@modelcontextprotocol/sdk/types.js`
|
||
(`Tool`, `Prompt`, `Resource`, `ServerCapabilities`, `Implementation`,
|
||
`LoggingMessageNotification`, `Task`, etc.), or
|
||
2. An **Inspector-owned wrapper type** for a UI concept that has no MCP
|
||
equivalent (`MCPServerConfig`, `ConnectionStatus`, `MessageEntry`,
|
||
`StderrLogEntry`, `FetchRequestEntry`, …), or
|
||
3. A **callback** with a signature shaped by the MCP request/result types
|
||
the wiring layer will eventually pass through, or
|
||
4. **Local UI state** (search text, compact toggle, selected name, etc.)
|
||
that is screen-owned and not derived from any MCP object.
|
||
|
||
### Sources of truth
|
||
|
||
- **MCP schema types** — `@modelcontextprotocol/sdk` (TypeScript SDK), pinned
|
||
at the latest `1.x` line (currently `^1.29.0`). The SDK exports every type
|
||
this plan references, including `Task` and `LoggingLevel`.
|
||
- **Inspector-owned wrapper types** — the `v1.5/main` branch of *this* repo
|
||
is the canonical reference. v1.5 already has a complete `core/mcp/types.ts`
|
||
(416 lines) defining every wrapper type the v2 components need:
|
||
- `core/mcp/types.ts` — `MCPServerConfig` discriminated union
|
||
(`StdioServerConfig`/`SseServerConfig`/`StreamableHttpServerConfig`),
|
||
`ServerType`, `MCPConfig`, `ConnectionStatus`, `StderrLogEntry`,
|
||
`MessageEntry`, `FetchRequestEntry`, `FetchRequestEntryBase`,
|
||
`ServerState`, `ResourceReadInvocation`,
|
||
`ResourceTemplateReadInvocation`, `PromptGetInvocation`,
|
||
`ToolCallInvocation`, `InspectorClientEnvironment`, `InspectorClientOptions`,
|
||
`CreateTransport`, `AppRendererClient`.
|
||
- `core/mcp/taskNotificationSchemas.ts` — Inspector's
|
||
`notifications/tasks/list_changed` zod extension (the SDK exports
|
||
`Task` and `TaskStatusNotificationSchema` natively, but not the
|
||
list-changed signal).
|
||
- `core/react/useInspectorClient.ts` — `UseInspectorClientResult` shape
|
||
(`status`, `capabilities`, `serverInfo`, `instructions`,
|
||
`appRendererClient`, `connect`, `disconnect`).
|
||
- `core/react/useManaged{Tools,Prompts,Resources,ResourceTemplates,RequestorTasks}.ts`
|
||
— confirmed return shapes the screen layer will eventually consume:
|
||
`Tool[]`, `Prompt[]`, `Resource[]`, `ResourceTemplate[]`, `Task[]`.
|
||
- `core/react/{useMessageLog,useStderrLog,useFetchRequestLog}.ts` — log
|
||
wrapper consumers.
|
||
- `clients/web/src/utils/customHeaders.ts` — `CustomHeader` /
|
||
`CustomHeaders` shape, used by `ServerSettingsForm`.
|
||
- `clients/web/src/utils/schemaUtils.ts` + `clients/web/src/utils/jsonUtils.ts`
|
||
— JSON Schema typing (`JsonValue`, `JsonSchemaType`, `JsonObject`) and
|
||
AJV-backed validation for tool input/output schemas.
|
||
|
||
**Rule**: when a wrapper type is needed in v2, copy the v1.5 definition
|
||
verbatim (including field names, optionality, and `Date` vs `string`
|
||
choices). Do not invent new names. The interfaces doc's `Inspector*`
|
||
prefixes (e.g. `InspectorHistoryEntry`, `InspectorTask`,
|
||
`InspectorServerConfig`, `InspectorTaskStatus`) are placeholders — they
|
||
resolve to v1.5's actual names below.
|
||
|
||
### Name-mapping correction (interfaces doc → v1.5 source of truth)
|
||
|
||
Several names invented in `v2_ux_interfaces.md` are already defined in v1.5
|
||
and should be replaced verbatim during the refactor:
|
||
|
||
| `v2_ux_interfaces.md` placeholder | v1.5 actual | Source |
|
||
| --- | --- | --- |
|
||
| `InspectorServerConfig` | `MCPServerConfig` (discriminated union) | `core/mcp/types.ts` |
|
||
| `InspectorTransportType` | `ServerType` (`"stdio" \| "sse" \| "streamable-http"`) | `core/mcp/types.ts` |
|
||
| `ConnectionStatus` | `ConnectionStatus` (already named that — `"disconnected" \| "connecting" \| "connected" \| "error"`) | `core/mcp/types.ts` |
|
||
| `InspectorHistoryEntry` | `MessageEntry` (note `timestamp: Date`, not `string`) | `core/mcp/types.ts` |
|
||
| `InspectorTask` | `Task` (raw SDK type — exported by MCP SDK 1.x at `@modelcontextprotocol/sdk/types.js`) | SDK |
|
||
| `InspectorTaskStatus` | Status field on the SDK `Task` type — no separate wrapper needed | SDK |
|
||
| `InspectorLogEntry` | (consume `LoggingMessageNotification['params']` directly, plus a v2-side `{ id, receivedAt }` lift) | SDK + light wrapper |
|
||
| `InspectorPendingRequest` | No first-class wrapper in v1.5 — pending sampling/elicitation flows are coordinated as raw SDK request handlers inside `InspectorClient` with UI state living locally. v2 may need a small `{ id, request }` wrapper if the dumb component needs an id key — keep it minimal. | n/a |
|
||
| `InspectorResourceSubscription` | No dedicated wrapper in v1.5; v2 should add `{ resource: Resource; lastUpdated?: Date }` per the interfaces spec, placed in `core/mcp/types.ts` alongside the others. | new (v2-only) |
|
||
| `InspectorOAuthDetails` | `InspectorClientOptions['oauth']` shape from `core/mcp/types.ts` (`clientId`, `clientSecret`, `clientMetadataUrl`, `scope`) | `core/mcp/types.ts` |
|
||
| `InspectorServerJsonDraft` | No v1.5 equivalent — v2-only. Keep the wrapper but place it in `core/mcp/types.ts` next to `MCPConfig`. | new (v2-only) |
|
||
| `InspectorServerSettings` | Closest v1.5 analog is `InspectorClientOptions` (timeouts, OAuth, sampling/elicit/roots flags). Settings form should accept the relevant subset. | `core/mcp/types.ts` |
|
||
| `InspectorRequestHistoryItem` | Subset of `MessageEntry` filtered to outbound requests. No new type needed. | `core/mcp/types.ts` |
|
||
| `InspectorUrlElicitRequest` | URL-mode elicitation IS supported by v1.5 via `InspectorClientOptions.elicit: { url: true }`; the request/response shape lives in `core/mcp/elicitationCreateMessage.ts`. v2 should mirror that file's types rather than invent. | `core/mcp/elicitationCreateMessage.ts` |
|
||
| `InspectorTab` | No v1.5 analog — v2-only enum. Superseded by `InspectorTabId` in `clients/web/src/utils/inspectorTabs.ts` (#1785). | new (v2-only) |
|
||
|
||
## Non-goals
|
||
|
||
- **Building the v2 `core/` hook layer.** This plan only refactors component
|
||
prop interfaces and their Storybook fixtures. The hooks (`useTools`,
|
||
`usePrompts`, `useConnection`, etc.) that will eventually populate those
|
||
props are out of scope here. They'll arrive in a separate effort, and the
|
||
contracts produced by this work will be the input spec for that effort.
|
||
- **Wiring `App.tsx` to the MCP transport.** The current `App.tsx` is a
|
||
42-line theme-toggle shell. It stays that way until the core hook layer
|
||
exists.
|
||
- **Updating CLI / TUI clients.** Those don't exist yet in this repo.
|
||
- **Changing the visual design** of any component. Refactors are interface-only;
|
||
Storybook output should look identical (same screenshots) unless a new field
|
||
becomes visible because it was previously dropped (e.g. `Annotations` on
|
||
`ResourceListItem`).
|
||
- **Adding tests** beyond what Storybook gives us. Vitest unit tests for the
|
||
refactored components are a follow-up — the AGENTS.md 90% coverage rule will
|
||
be addressed in its own pass once the interface dust settles.
|
||
|
||
## Current state (snapshot taken on this branch)
|
||
|
||
- `clients/web/` is the only client. No `core/`, no `cli/`, no `tui/`, no
|
||
shared `clients/launcher/`.
|
||
- `clients/web/src/App.tsx` is a 42-line `MantineProvider` shell — no wiring.
|
||
- `clients/web/package.json` has **no** `@modelcontextprotocol/sdk` dependency.
|
||
- Components live under `clients/web/src/components/{elements,groups,screens,views}`,
|
||
each in its own folder with `<Name>.tsx` + `<Name>.stories.tsx`.
|
||
- Component count: **16 elements**, **37 groups**, **7 screens**, **2 views**
|
||
= 62 components.
|
||
- Components use ad hoc local types and flat scalar props (`name: string`,
|
||
`version: string`, `transport: "stdio" | "http"`, `level: LogLevel`, …)
|
||
invented during the visual-design phase. Several local re-declarations
|
||
exist for what should be schema types: `JsonSchema`, `LogLevel`,
|
||
`PromptItem`, `ResourceItem`, `ToolListItemProps`, `TaskStatus`,
|
||
`RootEntry`, etc.
|
||
|
||
## Phase 0 — Foundations
|
||
|
||
Before any component is touched, decide and stand up the things every later
|
||
phase depends on.
|
||
|
||
### 0.1 Add the MCP SDK as a dependency
|
||
|
||
- Add `@modelcontextprotocol/sdk` to `clients/web/package.json` so component
|
||
files can `import type { Tool, Prompt, Resource, Task, LoggingLevel, ... }
|
||
from "@modelcontextprotocol/sdk/types.js";`.
|
||
- Pin to **`^1.29.0`** (latest 1.x line as of writing). v1.5 is on `^1.25.2`,
|
||
but the 1.29 schema bundle is a strict superset and includes the latest
|
||
task / progress refinements.
|
||
- Verify TypeScript path resolution by importing one type into a throwaway
|
||
test file and running `npm run build`.
|
||
- If 1.29 introduces a breaking change vs 1.25 that affects a wrapper type
|
||
copied from v1.5, prefer adapting the v2 wrapper to 1.29 — do not roll the
|
||
SDK back.
|
||
|
||
### 0.2 Stand up the Inspector wrapper-types module
|
||
|
||
The interfaces doc invents `Inspector*` wrapper names; the v1.5 branch of
|
||
this repo has the canonical definitions under different (cleaner) names. Use
|
||
v1.5 as the source of truth — see the **Sources of truth** and
|
||
**Name-mapping correction** sections above.
|
||
|
||
**Decision: create `core/mcp/types.ts` in v2** at the same path v1.5 uses,
|
||
copying the types-only subset of v1.5's `core/mcp/types.ts` verbatim. This
|
||
sets up the directory structure for the eventual v2 core hook layer (which
|
||
is out of scope for this plan but will arrive in a follow-up effort) and
|
||
avoids two consecutive renames.
|
||
|
||
**Subset to copy from v1.5 `core/mcp/types.ts`** (types only — no runtime
|
||
helpers, no `InspectorClient` constructor types):
|
||
|
||
- `StdioServerConfig`, `SseServerConfig`, `StreamableHttpServerConfig`,
|
||
`MCPServerConfig`, `ServerType`, `MCPConfig`
|
||
- `ConnectionStatus`
|
||
- `StderrLogEntry`
|
||
- `MessageEntry` (note: `timestamp: Date`, not `string`)
|
||
- `FetchRequestEntry`, `FetchRequestEntryBase`, `FetchRequestCategory`
|
||
- `ServerState`
|
||
- `ResourceReadInvocation`, `ResourceTemplateReadInvocation`,
|
||
`PromptGetInvocation`, `ToolCallInvocation`
|
||
|
||
**Skip from v1.5** (transport / OAuth / lifecycle types — these belong in
|
||
the eventual v2 core hook layer, not this plan):
|
||
|
||
- `CreateTransport`, `CreateTransportOptions`, `CreateTransportResult`
|
||
- `InspectorClientEnvironment`, `InspectorClientOptions`, `AppRendererClient`
|
||
|
||
**Add new in v2** (v2-only wrappers needed by the interfaces refactor that
|
||
have no v1.5 equivalent):
|
||
|
||
- `InspectorResourceSubscription` = `{ resource: Resource; lastUpdated?: Date }`
|
||
- `InspectorServerJsonDraft` = `{ rawText: string; parsed?: RegistryServerJson; selectedPackageIndex?: number; envOverrides: Record<string, string>; nameOverride?: string }`
|
||
- `InspectorTab` enum (`"tools" | "prompts" | "resources" | "logs" | "tasks" | "history"`)
|
||
|
||
**Tab enum placement**: `InspectorTab` is a UI-routing concept, not an MCP
|
||
or transport concept. Place it under `clients/web/src/types/navigation.ts`
|
||
rather than in `core/mcp/types.ts`. Everything else lives in `core/mcp/types.ts`.
|
||
_(Superseded by [#1785](https://github.com/modelcontextprotocol/inspector/issues/1785): the live tab-id type is `InspectorTabId` in `clients/web/src/utils/inspectorTabs.ts`, and the dead `navigation.ts` `InspectorTab` was removed — a pure UI domain type belongs in `utils/`, not `src/types/`, per the `src/lib` vs `src/utils` rule in AGENTS.md.)_
|
||
|
||
**Custom headers**: `clients/web/src/utils/customHeaders.ts` is a verbatim
|
||
copy of the v1.5 module. It owns `CustomHeader` / `CustomHeaders` shape used by
|
||
`ServerSettingsForm` and the experimental panel.
|
||
|
||
**JSON Schema typing for `SchemaForm`**: copy v1.5's
|
||
`clients/web/src/utils/jsonUtils.ts` (defines `JsonValue`, `JsonObject`,
|
||
`JsonSchemaType`) and `clients/web/src/utils/schemaUtils.ts` (AJV-backed
|
||
validators). This resolves the JSON Schema typing risk and keeps
|
||
`SchemaForm` aligned with how v1.5 validates `Tool.inputSchema` and
|
||
`Tool.outputSchema`.
|
||
|
||
**Elicitation URL request shape**: copy the URL-mode elicitation
|
||
request/result types from v1.5 `core/mcp/elicitationCreateMessage.ts` into
|
||
the new `core/mcp/types.ts` (or a peer file). The interfaces doc's
|
||
`InspectorUrlElicitRequest` placeholder maps to whatever v1.5 calls them.
|
||
|
||
### 0.3 Vet every "likely" hedge in `v2_ux_interfaces.md`
|
||
|
||
`v2_ux_interfaces.md` was produced by parallel research subagents that I
|
||
instructed to prefix uncertain hook names with `(likely)` rather than fail
|
||
to fill in a section. The result: **52 instances of "likely"** survived to
|
||
the doc, almost all of them attached to hook names or hook-name-to-callback
|
||
mappings that were never cross-checked against v1.5. Several are actively
|
||
wrong. Before any component is touched, walk through every `likely` and
|
||
either resolve it against v1.5 / the SDK or strike it.
|
||
|
||
Use these source-of-truth lookups, not guessing:
|
||
|
||
- **v1.5 React hooks** — `git show origin/v1.5/main:core/react/<hook>.ts`.
|
||
The complete v1.5 hook surface is:
|
||
- `useInspectorClient.ts` — `status`, `capabilities`, `serverInfo`,
|
||
`instructions`, `appRendererClient`, `connect`, `disconnect`.
|
||
- `useManagedTools.ts` — `{ tools: Tool[]; refresh }`
|
||
- `useManagedPrompts.ts` — `{ prompts: Prompt[]; refresh }`
|
||
- `useManagedResources.ts` — `{ resources: Resource[]; refresh }`
|
||
- `useManagedResourceTemplates.ts` — `{ resourceTemplates: ResourceTemplate[]; refresh }`
|
||
- `useManagedRequestorTasks.ts` — `{ tasks: Task[]; refresh }`
|
||
- `useMessageLog.ts` — JSON-RPC message buffer (`MessageEntry[]`)
|
||
- `useStderrLog.ts` — stdio stderr buffer (`StderrLogEntry[]`)
|
||
- `useFetchRequestLog.ts` — auth/transport HTTP fetch buffer (`FetchRequestEntry[]`)
|
||
- `usePagedTools.ts`, `usePagedPrompts.ts`, `usePagedResources.ts`,
|
||
`usePagedResourceTemplates.ts`, `usePagedRequestorTasks.ts` — paged
|
||
siblings of the managed hooks above.
|
||
- **MCP SDK types** — `import type { ... } from "@modelcontextprotocol/sdk/types.js"`.
|
||
- **v1.5 core types** — `git show origin/v1.5/main:core/mcp/types.ts`.
|
||
|
||
#### Confirmed hook-name corrections
|
||
|
||
| `v2_ux_interfaces.md` `(likely)` claim | Actual v1.5 surface |
|
||
| --- | --- |
|
||
| `useTools` | **`useManagedTools`** (`core/react/useManagedTools.ts`) |
|
||
| `usePrompts` | **`useManagedPrompts`** |
|
||
| `useResources` | **`useManagedResources`** |
|
||
| `useResourceTemplates` (implied) | **`useManagedResourceTemplates`** |
|
||
| `useTasks` | **`useManagedRequestorTasks`** |
|
||
| `useMcpClient` | **`useInspectorClient`** |
|
||
| `useServerCapabilities` | Not a separate hook — `capabilities: ServerCapabilities` is a field on `useInspectorClient`'s result. Strike. |
|
||
| `useLogs` | **Three** v1.5 hooks: `useMessageLog`, `useStderrLog`, `useFetchRequestLog`. The interfaces doc conflates them. Split per use site: log stream → `useMessageLog`; stdio stderr → `useStderrLog`; HTTP fetch (auth/transport) → `useFetchRequestLog`. |
|
||
| `useHistory` | **`useMessageLog`** — v1.5's message log IS the history; `MessageEntry` is the wrapper type. Strike `useHistory`. |
|
||
| `useElicitation` | **No discrete hook in v1.5.** Elicitation is handled inside `InspectorClient` (`core/mcp/elicitationCreateMessage.ts`) via SDK request handlers. UI state for pending elicitations lives wherever the consuming screen puts it. Strike `useElicitation` and replace with "handled inside `InspectorClient` request handlers; v2 will need a small UI-state hook for the pending queue but it doesn't exist yet". |
|
||
| `useSampling` | **No discrete hook in v1.5.** Same shape as elicitation: `core/mcp/samplingCreateMessage.ts` plus SDK request handlers. Strike. |
|
||
| `useRoots` | **No discrete hook in v1.5.** Roots are configured via `InspectorClientOptions.roots: Root[]` at construction time and answered by an SDK request handler inside `InspectorClient`. There is no React hook. Strike or mark as "v2 to introduce". |
|
||
| `useServers` | **Not in v1.5.** v2-only — keep, but mark explicitly as "v2 to introduce, no v1.5 analog". |
|
||
| `useServerRegistry` | **Not in v1.5.** v2-only — same treatment. |
|
||
| `useClientCapabilities` | **Not in v1.5.** v2-only — same treatment. |
|
||
| `useInspectorNavigation` | **Not in v1.5.** Pure speculation in the interfaces doc. v2 may not need a hook at all (tab state can be `useState` in the wiring layer). Strike or mark "may not exist". |
|
||
|
||
#### Confirmed wrapper / type corrections
|
||
|
||
These are also `(likely)`-flavored claims that slipped past the spec:
|
||
|
||
- The interfaces doc says **"MCP 2025-11-25 base schema does not define a
|
||
first-class task primitive"**. This is **wrong** — the MCP TS SDK 1.x
|
||
exports `Task` from `@modelcontextprotocol/sdk/types.js`, and v1.5's
|
||
`useManagedRequestorTasks` already returns `Task[]`. Update every entry
|
||
in the interfaces doc that proposes an `InspectorTask` wrapper to use
|
||
`Task` directly. The list-changed signal **is** an Inspector extension
|
||
(no SDK schema for `notifications/tasks/list_changed`); v1.5 defines it
|
||
in `core/mcp/taskNotificationSchemas.ts`. Cite that file rather than
|
||
inventing.
|
||
- **Appendix A line 854** asserts: "v1.5 monorepo bundles most
|
||
primitive-specific logic inside `client/src/App.tsx`; hooks marked
|
||
'likely' will be extracted cleanly in v2 core". This is **wrong about
|
||
v1.5/main of THIS repo**. It's true of the upstream
|
||
`modelcontextprotocol/inspector` v1.x mainline, which an earlier subagent
|
||
conflated with v1.5. v1.5 in this repo already has the full
|
||
`core/react/*` hook split-out. Rewrite Appendix A to cite the actual
|
||
v1.5 file paths under `core/react/` and remove the "will be extracted
|
||
cleanly" framing.
|
||
- **Appendix A's pointer to `client/src/lib/hooks`** in v1.5 is also
|
||
wrong-source: that path is the upstream repo, not this repo. The path in
|
||
this repo's v1.5 branch is `core/react/`. Update the link.
|
||
- Anywhere the doc references a `LogEntry` shape that mixes in a `string`
|
||
timestamp: v1.5's `MessageEntry` and `StderrLogEntry` use `Date`, not
|
||
`string`. Reconcile.
|
||
|
||
#### Procedure
|
||
|
||
1. Open `v2_ux_interfaces.md` and walk every `likely` instance in document
|
||
order (`grep -n "likely" specification/v2_ux_interfaces.md`).
|
||
2. For each, look up the claim against v1.5 (`git show origin/v1.5/main:...`)
|
||
or the SDK (`grep` in `node_modules/@modelcontextprotocol/sdk/dist/types.d.ts`
|
||
once 0.1 lands).
|
||
3. Either replace `likely useFoo` with the verified name or, if there is
|
||
no v1.5 analog, replace it with an explicit `v2 to introduce — no v1.5
|
||
analog` annotation. **Do not leave `(likely)` in the doc.**
|
||
4. While doing this, fix Appendix A's structural errors above.
|
||
5. Land the corrected `v2_ux_interfaces.md` as the **second commit** of
|
||
Phase 0, immediately after the `core/mcp/types.ts` setup commit. The
|
||
commit message should call out the rename table from this section so
|
||
reviewers can audit it.
|
||
6. After 0.3 ships, **`grep "likely" specification/v2_ux_interfaces.md`
|
||
must return zero results**. This is the definition-of-done for Phase 0.3.
|
||
|
||
(See Risks #11 below for the underlying root cause and the
|
||
"vet weasel words" rule that should now apply to every planning doc in
|
||
this repo.)
|
||
|
||
### 0.4 Inventory current local re-declarations to delete
|
||
|
||
Run a single pass to find every local type that will be replaced by an MCP
|
||
schema type or a wrapper. Add a checklist to the PR description so reviewers
|
||
can confirm all of them are gone by the end:
|
||
|
||
- `JsonSchema` (in `groups/SchemaForm/SchemaForm.tsx`) → `JsonSchemaType`
|
||
from the new `clients/web/src/utils/jsonUtils.ts` (copied from v1.5).
|
||
- `LogLevel` (in `elements/LogEntry/LogEntry.tsx`) → `LoggingLevel` from the
|
||
SDK.
|
||
- `TaskStatus` (in `groups/TaskCard/TaskCard.tsx`) → status field on the SDK
|
||
`Task` type. **Do not introduce a separate `InspectorTaskStatus`.**
|
||
- `TaskCardProps`-as-data-shape (in `groups/TaskListPanel/TaskListPanel.tsx`)
|
||
→ SDK `Task[]`.
|
||
- `PromptItem`, `SelectedPrompt` → `Prompt`.
|
||
- `ResourceItem`, `TemplateListItem`, `SubscriptionItem` → `Resource`,
|
||
`ResourceTemplate`, `InspectorResourceSubscription`.
|
||
- `ToolListItemProps`-as-data-shape → `Tool`.
|
||
- `RootEntry` → `Root`.
|
||
- `KeyValuePair` (in `ServerSettingsForm`) → keep, but reconcile with
|
||
`CustomHeader` from v1.5; if the form is editing custom headers
|
||
specifically, prefer `CustomHeader`.
|
||
- Inline `ConnectionStatus` literal unions in multiple components
|
||
(e.g. `ServerStatusIndicator`, `ServerCard`, `ConnectedView`) → import
|
||
`ConnectionStatus` from `core/mcp/types.ts`.
|
||
- Inline `"stdio" | "http"` transport unions (e.g. `TransportBadge`) →
|
||
`ServerType` from `core/mcp/types.ts`.
|
||
|
||
Output of 0.3 lives in the PR description, not in this file.
|
||
|
||
## Phase 1 — Elements (16 components)
|
||
|
||
> Smallest blast radius, fewest dependencies, and the type changes here
|
||
> propagate up. Do this first so groups can immediately consume the new
|
||
> element shapes.
|
||
|
||
### Strategy
|
||
|
||
- One commit per element (or one commit per logical batch of 2–3 closely
|
||
related elements). Each commit refactors `<Name>.tsx`, updates
|
||
`<Name>.stories.tsx`, and runs format/lint/build.
|
||
- For each element, the steps are mechanical:
|
||
1. Read `v2_ux_interfaces.md` Section 1's entry for the component.
|
||
2. Replace the current props interface with the **Target props** from the spec.
|
||
3. Update the body to read fields off the new prop shape.
|
||
4. Update the story `args` to construct realistic schema fixtures (use real
|
||
`LoggingLevel` strings, real `Annotations` objects, etc.).
|
||
5. Run `npm run format && npm run lint && npm run build`.
|
||
|
||
### Order (rough dependency order)
|
||
|
||
1. **Pure presentational, no behavior change** (warm-up):
|
||
`CopyButton`, `ListToggle`, `ListChangedIndicator`, `SubscribeButton` —
|
||
spec says "unchanged" or trivial.
|
||
2. **Schema-typed primitives** that other elements depend on:
|
||
`LogLevelBadge` (introduces `LoggingLevel`), `TransportBadge` (introduces
|
||
`InspectorTransportType`), `TaskStatusBadge` (introduces
|
||
`InspectorTaskStatus`), `ServerStatusIndicator` (introduces
|
||
`ConnectionStatus`).
|
||
3. **Components consuming MCP wrapper objects**:
|
||
`AnnotationBadge` (consumes `Annotations`/`ToolAnnotations`),
|
||
`CapabilityItem` (consumes `keyof ServerCapabilities`),
|
||
`LogEntry` (consumes `LoggingMessageNotification['params']` + receivedAt),
|
||
`MessageBubble` (consumes `SamplingMessage`/`PromptMessage`),
|
||
`ProgressDisplay` (consumes `ProgressNotification['params']`).
|
||
4. **Composite-input elements**:
|
||
`ContentViewer` (consumes the full `ContentBlock` discriminated union),
|
||
`InlineError` (consumes `JSONRPCError['error']`),
|
||
`ConnectionToggle` (split single `onChange` into `onConnect`/`onDisconnect`).
|
||
|
||
### Validation per element
|
||
|
||
- `npm run format && npm run lint && npm run build` (this is the project rule
|
||
in `AGENTS.md`).
|
||
- Open the element's story in Storybook locally; visual output should be
|
||
unchanged unless the spec calls out a new visible field.
|
||
|
||
### Phase 1 checkpoint
|
||
|
||
- Single commit on the branch summarizing the elements pass, OR a stack of
|
||
smaller commits — preference: small commits.
|
||
- Before moving to Phase 2, scan `clients/web/src/components/elements/` for
|
||
any remaining local-union literal types that should have been replaced.
|
||
Delete the dead local types.
|
||
|
||
## Phase 2 — Groups (37 components)
|
||
|
||
> The bulk of the schema surface flows through this layer. Most groups embed
|
||
> 1–3 elements that already changed in Phase 1, so doing groups *after* Phase
|
||
> 1 means we only touch each file once.
|
||
|
||
### Strategy
|
||
|
||
- **One PR per logical group cluster** (not one PR per component) — clusters
|
||
below. Each PR refactors all components in the cluster, updates their
|
||
stories, and runs format/lint/build.
|
||
- For groups whose props are mostly a flat copy of an MCP schema object
|
||
(`ToolListItem`, `ResourceListItem`, `PromptListItem`, `ToolControls`,
|
||
`PromptControls`, `ResourceControls`, `RootsTable`, `LogStreamPanel`,
|
||
`LogControls`), the refactor is mechanical: import the schema type, swap
|
||
the local prop name, update story fixtures.
|
||
- For groups that wrap a single full panel of state (`ToolDetailPanel`,
|
||
`ResourcePreviewPanel`, `ResourceTemplatePanel`, `SamplingRequestPanel`,
|
||
`ElicitationFormPanel`, `ElicitationUrlPanel`, `PromptArgumentsForm`),
|
||
the refactor moves multiple flat scalar props into a single schema or
|
||
wrapper object and updates internal destructuring.
|
||
- For groups that flag a **dumb-component principle violation** (notably
|
||
`ServerCard`'s auto-connect `useEffect`), the violation is fixed in the
|
||
same commit that refactors the props — do not let it linger.
|
||
|
||
### Cluster order (within Phase 2)
|
||
|
||
1. **Tools cluster** — `ToolListItem`, `ToolControls`, `ToolDetailPanel`,
|
||
`ToolResultPanel`. All consume `Tool` / `CallToolRequest` /
|
||
`CallToolResult` / `ToolAnnotations`.
|
||
2. **Prompts cluster** — `PromptListItem`, `PromptControls`,
|
||
`PromptArgumentsForm`, `PromptMessagesDisplay`. All consume `Prompt` /
|
||
`PromptArgument` / `GetPromptResult` / `PromptMessage`.
|
||
3. **Resources cluster** — `ResourceListItem`, `ResourceControls`,
|
||
`ResourcePreviewPanel`, `ResourceTemplatePanel`, `ResourceSubscribedItem`.
|
||
Consume `Resource` / `ResourceTemplate` / `ReadResourceResult` /
|
||
`Text|BlobResourceContents` / `Annotations`.
|
||
4. **Logs cluster** — `LogControls`, `LogStreamPanel`. Consume `LoggingLevel`,
|
||
`LoggingMessageNotification['params']`.
|
||
5. **Tasks cluster** — `TaskCard`, `TaskControls`, `TaskListPanel`. Introduce
|
||
`InspectorTask` wrapper.
|
||
6. **History cluster** — `HistoryControls`, `HistoryEntry`, `HistoryListPanel`.
|
||
Introduce `InspectorHistoryEntry` wrapper.
|
||
7. **Sampling / elicitation cluster** — `SamplingRequestPanel`,
|
||
`InlineSamplingRequest`, `ElicitationFormPanel`, `ElicitationUrlPanel`,
|
||
`InlineElicitationRequest`, `PendingClientRequests`. Consume
|
||
`CreateMessageRequest`/`Result`, `ElicitRequest`/`Result`,
|
||
`InspectorUrlElicitRequest`, `InspectorPendingRequest`.
|
||
8. **Roots / Schema / Experimental cluster** — `RootsTable` (consumes `Root`),
|
||
`SchemaForm` (consumes shared JSON Schema type),
|
||
`ExperimentalFeaturesPanel` (consumes
|
||
`ServerCapabilities['experimental']`, `ClientCapabilities['experimental']`,
|
||
`JSONRPCRequest`/`Response`/`Error`).
|
||
9. **Server / settings / view-shell cluster** — `ServerCard` (**also** lifts
|
||
the auto-connect `useEffect`), `ServerInfoContent`, `ServerSettingsForm`,
|
||
`ServerListControls`, `ServerAddMenu`, `ImportServerJsonPanel`,
|
||
`ViewHeader`. Consume `Implementation`, `InitializeResult`,
|
||
`ServerCapabilities`, `ClientCapabilities`, `InspectorServerConfig`,
|
||
`InspectorServerSettings`, `InspectorOAuthDetails`,
|
||
`InspectorServerJsonDraft`, `InspectorTransportType`.
|
||
|
||
### Validation per cluster
|
||
|
||
- `npm run format && npm run lint && npm run build` after each cluster.
|
||
- Spot-check the corresponding Storybook stories visually.
|
||
- After every cluster, run a `tsc --noEmit` pass over the whole web client to
|
||
catch consumers in screens/views that are still passing the old shape.
|
||
Those will be temporarily broken — that's expected; screens/views are
|
||
Phase 3 / Phase 4. To keep the build green during the transition, screens
|
||
pass props through `as unknown as ...` adapter casts that get deleted in
|
||
Phase 3. Alternative: do Phase 3 alongside each cluster — see below.
|
||
|
||
### Inter-phase coupling decision
|
||
|
||
There are two ways to keep the build green while groups are mid-refactor:
|
||
|
||
1. **Adapter shims** in the consuming screen for the duration of the cluster,
|
||
deleted in Phase 3. Pro: clean per-cluster commits. Con: noisy diffs in
|
||
screens that get rewritten anyway.
|
||
2. **Refactor each group cluster's parent screen in the same PR.** Pro: no
|
||
shims, no temporarily-broken builds. Con: bigger PRs, but those PRs are
|
||
the natural unit of work anyway because each cluster maps almost 1:1 to
|
||
one screen.
|
||
|
||
**Proposed: option 2.** When the Tools group cluster is refactored, the
|
||
`ToolsScreen` is refactored in the same PR. This collapses Phase 2 and Phase
|
||
3 into a single per-feature pass and keeps `npm run build` green at every
|
||
commit.
|
||
|
||
## Phase 3 — Screens (7 components, folded into Phase 2)
|
||
|
||
Per the decision above, each screen is refactored in the same PR as its
|
||
group cluster:
|
||
|
||
- `ToolsScreen` ← Tools cluster
|
||
- `PromptsScreen` ← Prompts cluster
|
||
- `ResourcesScreen` ← Resources cluster
|
||
- `LoggingScreen` ← Logs cluster
|
||
- `TasksScreen` ← Tasks cluster
|
||
- `HistoryScreen` ← History cluster
|
||
- `ServerListScreen` ← Server / settings / view-shell cluster
|
||
|
||
For each screen:
|
||
|
||
1. Replace ad hoc list-of-`*Props` props with `<SchemaType>[]` arrays.
|
||
2. Move selection from a pre-built selected-detail prop (`selectedTool:
|
||
ToolDetailPanelProps`) to a selected name/uri/id (`selectedToolName:
|
||
string`) and derive the detail object internally via `tools.find(...)`.
|
||
3. Replace the separate `result` / `messages` props with a unified
|
||
`callState` / `getPromptState` / `readState` discriminated union so the
|
||
detail panel can render pending and error states uniformly.
|
||
4. Local UI state (search text, filter toggles, compact mode, selected name)
|
||
stays in `useState` inside the screen.
|
||
5. Update the screen's stories — the existing stories carry the burden of
|
||
exercising every prop combination. They will need new fixtures because
|
||
they currently pass a flattened shape that no longer compiles.
|
||
|
||
### Sampling / elicitation cluster note
|
||
|
||
Sampling and elicitation flows do not have a dedicated screen — they appear
|
||
inline (`InlineSamplingRequest`, `InlineElicitationRequest`) or as modal
|
||
panels invoked from other screens (`SamplingRequestPanel`,
|
||
`ElicitationFormPanel`, `ElicitationUrlPanel`). Their cluster ends with
|
||
updates to the *stories* that mount them in modals (`UnconnectedView ⇒
|
||
WithSettingsModal`-style and `ConnectedView ⇒ ToolsWithElicitation*Modal`-style
|
||
existing stories) rather than a screen refactor.
|
||
|
||
## Phase 4 — Views (2 components)
|
||
|
||
> Trivial after groups + screens. Finishes the refactor.
|
||
|
||
### `ConnectedView`
|
||
|
||
- Replace flat scalars `serverName`, `status`, `latencyMs`, `activeTab`,
|
||
`availableTabs` with: `serverInfo: Implementation`, `capabilities:
|
||
ServerCapabilities`, `connectionStatus: ConnectionStatus`, `latencyMs?:
|
||
number`, `activeTab: InspectorTab`, `availableTabs: InspectorTab[]`.
|
||
- Compute `availableTabs` against `capabilities` rather than accepting an
|
||
arbitrary string list.
|
||
- Forward `serverInfo` to the now-refactored `ViewHeader`.
|
||
|
||
### `UnconnectedView`
|
||
|
||
- Rename internal `HomeLayoutProps` interface to `UnconnectedViewProps`.
|
||
- No prop changes needed.
|
||
- Optional: add `connectionStatus` if the embedded header needs it.
|
||
|
||
### 4.3 Move selection state into the screens
|
||
|
||
The Phase 2 cluster work threaded `selectedXxxName` / `onSelectXxx` props
|
||
through every list-and-detail screen, leaving selection as caller-owned
|
||
state. That choice contradicts Phase 3's "selected name stays in `useState`
|
||
inside the screen" rule and creates a real wiring gap: nothing above the
|
||
screen has any reason to know which tool/prompt/resource the user clicked
|
||
on, so under `InspectorView` clicks would have to be round-tripped through
|
||
`App.tsx` purely to get the detail panel to render.
|
||
|
||
Selection is screen-local UI state. MCP-derived data (`callState` / fetched
|
||
prompt messages / resource read results) keeps flowing in from
|
||
`InspectorView` as props. Refactor each screen accordingly.
|
||
|
||
Screens to fix:
|
||
|
||
- **`ToolsScreen`** — drop `selectedToolName` and `onSelectTool` from the
|
||
prop interface; track the selected tool name in `useState` inside the
|
||
screen. `ToolControls` continues to receive `selectedName` and
|
||
`onSelectTool` as props from the screen. `callState` (and any future
|
||
result/progress payload) keeps coming in from `InspectorView`.
|
||
`InspectorView` only needs to know the selected tool name when it
|
||
triggers `onCallTool` — which the screen already owns, so it can pass
|
||
the name up at call time rather than maintaining it in the parent.
|
||
- **`PromptsScreen`** — same pattern for `selectedPromptName`. Fetched
|
||
`GetPromptResult` continues to arrive as a prop from `InspectorView`.
|
||
- **`ResourcesScreen`** — same pattern for the selected resource URI and
|
||
selected resource template URI. `ReadResourceResult` continues to
|
||
arrive as a prop.
|
||
|
||
For each screen:
|
||
|
||
1. Remove the `selectedXxxName` / `onSelectXxx` props from the screen's
|
||
prop interface.
|
||
2. Add a `useState` for the selection inside the screen and pass it down
|
||
to the controls group.
|
||
3. When the user triggers a request (`onCallTool` / `onGetPrompt` /
|
||
`onReadResource`), pass the currently selected name/uri up alongside
|
||
any other arguments — the parent does not need to track selection
|
||
between calls.
|
||
4. Update each screen's stories to remove the now-deleted props. The
|
||
stories will start working without `useArgs` selection wiring, because
|
||
selection is now genuinely internal.
|
||
5. Audit the controls groups (`ToolControls`, `PromptControls`,
|
||
`ResourceControls`) — their `selectedName` / `onSelectXxx` props are
|
||
correct (they ARE caller-owned, because the screen owns them) and
|
||
should not change.
|
||
6. Update `InspectorView` and any wiring fixtures to drop the now-removed
|
||
selection props they were passing to each screen.
|
||
|
||
## Phase 5 — Cleanup pass
|
||
|
||
After all four phases, sweep:
|
||
|
||
1. Delete every local type listed in Phase 0.3 that should have disappeared.
|
||
2. Run `npm run format && npm run lint && npm run build` one last time.
|
||
3. `git grep` for `JsonSchema`, `LogLevel`, `TaskStatus`, `RootEntry`,
|
||
`ToolListItemProps`, `PromptItem`, `ResourceItem`, `SubscriptionItem`,
|
||
`TemplateListItem`, `ServerCardProps as a data shape` — confirm zero
|
||
matches outside of intentional kept types.
|
||
4. Update `clients/web/README.md` if there's anything in it that refers to
|
||
the now-deleted shapes.
|
||
5. Create a follow-up issue (or PR placeholder) for the v2 `core/` package
|
||
migration: lift `clients/web/src/types/inspector/` into `core/types/`,
|
||
update all import paths.
|
||
|
||
## Validation strategy (whole-project)
|
||
|
||
After each phase:
|
||
|
||
- `cd clients/web && npm run format && npm run lint && npm run build`
|
||
- Visually open Storybook (`npm run storybook`) and click through the
|
||
refactored components.
|
||
- For groups/screens that introduced or substantially changed a wrapper
|
||
type, write at least one Storybook story that uses a fixture clearly
|
||
derived from a real MCP SDK type (e.g. import `Tool` from the SDK in the
|
||
story file and construct one — this catches type drift instantly).
|
||
|
||
After the whole effort:
|
||
|
||
- Read `specification/v2_ux_interfaces.md` end to end and confirm every
|
||
component's "Internal refactors" bullet is satisfied. Mark done.
|
||
- The follow-up "build the v2 core hook layer" effort can begin with this
|
||
branch's component contracts as its input spec.
|
||
|
||
## Risks and open questions
|
||
|
||
1. ~~**JSON Schema typing.**~~ **Resolved.** Copy v1.5's
|
||
`clients/web/src/utils/jsonUtils.ts` (`JsonValue`, `JsonObject`,
|
||
`JsonSchemaType`) and `clients/web/src/utils/schemaUtils.ts` (AJV-backed
|
||
`Tool.outputSchema` validation) verbatim. `SchemaForm` switches from its
|
||
local `JsonSchema` type to `JsonSchemaType`.
|
||
2. **`PrimitiveSchemaDefinition` for elicitation.** `ElicitRequest`'s
|
||
`requestedSchema` is constrained to primitives. The current `SchemaForm`
|
||
handles arbitrary schemas. The fix is either two form components or one
|
||
form component with a primitive-only mode flag — decide during the
|
||
sampling/elicitation cluster. Check what v1.5 does first; v1.5 already
|
||
supports both form-mode and URL-mode elicitation per
|
||
`InspectorClientOptions.elicit` so the constraint is already represented.
|
||
3. **`tools` / `toolChoice` on `SamplingRequestPanel`.** These fields are not
|
||
in `CreateMessageRequest` in MCP 2025-11-25. Per the spec, the choice is
|
||
"Inspector extension or drop". Default proposal: **drop** them; revisit if
|
||
a future MCP revision adds them. Verify v1.5's
|
||
`core/mcp/samplingCreateMessage.ts` does or does not surface these fields
|
||
before deciding.
|
||
4. **`Task` is in the SDK 1.x — interfaces doc is partially stale.** The
|
||
`v2_ux_interfaces.md` text says "MCP 2025-11-25 base schema doesn't have
|
||
a first-class task primitive". This is no longer correct: MCP TS SDK 1.x
|
||
exports `Task` from `@modelcontextprotocol/sdk/types.js` and v1.5 already
|
||
uses it as a raw type via `useManagedRequestorTasks`. **Action**: during
|
||
Phase 0, post a follow-up commit that updates the `TaskStatusBadge`,
|
||
`TaskCard`, `TaskListPanel`, and `TasksScreen` entries in
|
||
`v2_ux_interfaces.md` to reference `Task` directly instead of the
|
||
`InspectorTask` placeholder. Do not silently diverge.
|
||
5. **`InspectorClientOptions` boundary.** v1.5's `InspectorClientOptions` is a
|
||
construction-time options bag for `InspectorClient`, not a settings object
|
||
the user edits at runtime. The `ServerSettingsForm` props need a runtime
|
||
*subset* (timeouts, OAuth client id/secret/scope, headers, metadata,
|
||
custom instructions, feature flags). Create a small `InspectorServerSettings`
|
||
wrapper in `core/mcp/types.ts` that picks exactly those fields off
|
||
`InspectorClientOptions` — do not invent a parallel hierarchy.
|
||
6. **Story bundle size.** The existing `ConnectedView.stories.tsx` is already
|
||
long because each story inlines a full screen of fixture data. Refactoring
|
||
each story to use real schema types will make them longer. Consider
|
||
factoring fixtures into shared `*.fixtures.ts` files alongside the
|
||
component, especially for tools / prompts / resources where the same
|
||
object set is reused across multiple stories. Decide per cluster.
|
||
7. **Wrapper type names.** Use **v1.5's names verbatim**, not the
|
||
`Inspector*` placeholders from `v2_ux_interfaces.md`. The
|
||
"Name-mapping correction" table at the top of this plan is the
|
||
authoritative rename list. Update `v2_ux_interfaces.md` after Phase 0 so
|
||
the spec and the implementation agree on names.
|
||
8. **Date vs. string for timestamps.** v1.5's `MessageEntry`,
|
||
`StderrLogEntry`, `FetchRequestEntry`, and the `*Invocation` types use
|
||
`Date`, not ISO strings. The current Storybook stories pass ISO strings.
|
||
When refactoring, convert fixtures to `new Date(...)`. Stories' visible
|
||
output should not change because `Date.toISOString()` / `toLocaleString()`
|
||
is invoked at render time anyway.
|
||
9. **Visual regressions.** The refactor is meant to be visually invisible,
|
||
but several elements get *new* visible fields (`ResourceListItem` will
|
||
start rendering `Annotations` it currently ignores; `LogEntry` will
|
||
render `params.data` directly). Capture before/after Storybook
|
||
screenshots for each cluster's PR description so reviewers can sign off.
|
||
10. **v1.5 has more state machinery than this plan touches.** v1.5 ships
|
||
`core/mcp/state/managed{Tools,Prompts,Resources,ResourceTemplates,RequestorTasks}State.ts`
|
||
plus `paged*State.ts` siblings, plus `core/react/useManaged*.ts` hooks
|
||
that subscribe to them via `EventTarget`. None of that is in scope for
|
||
this plan — only the *types* are. The hook layer build-out is the
|
||
follow-up effort that will consume the contracts produced here.
|
||
11. **`v2_ux_interfaces.md` was produced with unverified hedges.** The doc
|
||
contains 52 instances of `(likely)` because the parallel research
|
||
subagents that filled in each section were instructed to prefix
|
||
uncertain hook names with the hedge rather than fail to fill in a
|
||
section. Several of those hedges turned out to be wrong (see Phase 0.3
|
||
rename table). The **rule going forward** for any planning / spec doc
|
||
in this repo: hedging words like `likely`, `probably`, `presumably`,
|
||
`should be` next to a type name, hook name, or file path are an
|
||
unverified claim. Either resolve them against the source of truth
|
||
(v1.5 branch of this repo for Inspector-owned types/hooks/wrappers;
|
||
`@modelcontextprotocol/sdk/types.js` for protocol schema types) before
|
||
the doc ships, or mark them explicitly as `not present in v1.5 — needs
|
||
design`. Do not leave a hedge in a planning doc and treat it as
|
||
research. The Phase 0.3 step is the immediate cleanup; the rule is the
|
||
durable preference.
|
||
|
||
## Out of this plan, but adjacent
|
||
|
||
- **Vitest unit tests** for each refactored component (90% per AGENTS.md).
|
||
- **Building the v2 `core/` hook layer.** v1.5 already has the full hook
|
||
surface (`useInspectorClient`, `useManagedTools`, `useManagedPrompts`,
|
||
`useManagedResources`, `useManagedResourceTemplates`,
|
||
`useManagedRequestorTasks`, `useMessageLog`, `useStderrLog`,
|
||
`useFetchRequestLog`, plus `usePaged*` siblings) and the state machinery
|
||
it subscribes to (`core/mcp/state/managed*State.ts`,
|
||
`core/mcp/state/paged*State.ts`). Porting that layer wholesale to v2 is a
|
||
separate effort that will consume the component contracts produced here as
|
||
its input spec.
|
||
- **Wiring `App.tsx`** to the v2 core hook layer.
|
||
- **CLI / TUI clients.** v1.5 has both (`clients/cli/`, `clients/tui/`); v2
|
||
will reuse the wrapper types from `core/mcp/types.ts` once that file exists
|
||
in v2.
|
||
- **Updating `v2_ux_interfaces.md`** to use v1.5's actual wrapper-type names
|
||
(`MCPServerConfig`, `MessageEntry`, `Task`, etc.) and v1.5's actual hook
|
||
names (`useManagedTools`, `useInspectorClient`, `useMessageLog`, etc.)
|
||
instead of the `Inspector*` and `(likely)` placeholders. **Done as part
|
||
of Phase 0.3 in this plan**, not a follow-up.
|