Wire App.tsx to v2 core/ hook layer; remove demo stub from InspectorView#1322
Wire App.tsx to v2 core/ hook layer; remove demo stub from InspectorView#1322cliffhall wants to merge 4 commits into
Conversation
… InspectorView
App.tsx mounts InspectorView with live data from the v2 hook layer instead
of the title-page placeholder. InspectorView becomes pure prop-driven; the
demo handshake stub and connection state machinery are removed.
Connection lifecycle:
- App.tsx lazily instantiates `InspectorClient` on the connect edge,
rebuilding it (and its state managers) when the user picks a different
server. State managers (Managed{Tools,Prompts,Resources,ResourceTemplates,
RequestorTasks}State + MessageLog/FetchRequestLog/StderrLog) are
destroyed on switch.
- `useInspectorClient` + the per-primitive `useManaged*` hooks drive the
view's data. `latencyMs` is captured at the connecting → connected edge
via a ref so the intermediate rerenders don't lose the start timestamp.
- `errorMessage` is captured from `client.connect()` rejection; status-
machine-driven errors that don't surface through the promise are a known
follow-up (no `error` event in the v2 InspectorClientEventMap yet).
Action handlers:
- `onCallTool` / `onGetPrompt` / `onReadResource` await the corresponding
InspectorClient method and reflect pending → ok/error in `ToolCallState`
/ `GetPromptState` / `ReadResourceState` panel props. Manually verified
end-to-end against `npx @modelcontextprotocol/server-filesystem` —
the Tools result panel renders the real "Allowed directories: /private/tmp"
output rather than a stub.
- `onSetLogLevel` calls `client.setLoggingLevel(level)` and optimistically
bumps `currentLogLevel` locally (the request has no echo notification).
- `onSubscribeResource` / `onUnsubscribeResource` / `onCancelTask` route to
their respective client methods. Server CRUD (`onServerAdd`/`onServerEdit`
/…) and history pinning are intentionally `todoNoop` for now; the seed
server list is hardcoded per the agreed scope (a `useServers` v2-only
hook is a separate effort).
- `notifications/message` notifications flowing through `MessageLogState`
are filtered into `LogEntryData[]` and passed to the Logs screen, so
`logging/setLevel` and the resulting log stream round-trip.
InspectorView (`InspectorView.tsx`):
- Removed: `STUB_*` constants, `handshakeTimer` ref, `handleToggleConnection`
/ `disconnect` functions, stub `bridgeFactory`/`sandboxPath`, internal
connection state, `logLevel` state.
- Added 38 props covering connection state, panel states, log level, and
action callbacks. The view is pure presentational.
- `availableTabs` is derived from `connectionStatus`; `activeTab` is
clamped to whatever's currently available (avoiding the `set-state-in-effect`
lint and keeping the previously-selected tab on reconnect).
Stories + tests:
- `InspectorView.stories.tsx` adds spy callbacks for every action prop and
new `Connected` / `ConnectionError` stories for the connected/error
narratives.
- `InspectorView.test.tsx` is rewritten around the prop-driven contract
(10 tests covering the empty list, server cards, toggle dispatch,
connected header, error banner, tab snap-back on disconnect, app
filtering, log-level dispatch, autoScroll local toggle).
Dev backend port wiring (`vite.config.ts`):
- Vite's dev server is now pinned to `CLIENT_PORT` (default 6274) with
`strictPort: true`, matching the dev backend's `allowedOrigins`. Without
this, Vite would fall back to 5173 while the Hono origin check still
required 6274 — breaking every browser `/api/*` fetch out of the box.
- Top-level `resolve.alias` now spreads the same bare-module aliases the
vitest projects use. Rolldown can't reach `pino/browser.js` /
`zustand/middleware` from `core/`'s parent (no node_modules there); the
wired App pulls those modules into the browser dep graph for the first
time, so the aliases need to be visible at build time too.
createWebEnvironment helper (`src/lib/environmentFactory.ts`):
- Mirrors v1.5's `lib/adapters/environmentFactory.ts`: builds
`InspectorClientEnvironment` from `createRemoteTransport` +
`createRemoteFetch` + `createRemoteLogger` + `BrowserOAuthStorage` +
`BrowserNavigation`. Returns the assembled environment plus the logger
for direct app-level use.
Closes #1244.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@claude review |
|
Claude finished @cliffhall's task in 2m 32s —— View job Claude PR Review
Overall this is clean, well-commented, and the scope (pure prop-driven view + parent-owned orchestration) is exactly right. The manual e2e capture is convincing and the storybook/integration suites pass. A few things worth flagging before merge — most are minor; one is a latent resource issue that'll bite as soon as server CRUD lands. High-signal1. Misleading const invocation = await inspectorClient.callTool(
tool,
args as Record<string, never>,
);
2. Previous
Recommendation: at the top of 3. No unmount cleanup
Medium-signal4. Dead state: These two state managers are instantiated and destroyed but never read. The trailing
5. Already called out in the PR description as a known limitation. The Promise rejection path captures failures during the initial handshake, but a transport dying mid-session transitions the client's 6. The callback depends on all eight state managers, each of which changes twice per switch. That means the callback identity changes ~16 times during a single server flip, and every handler closing over it ( Low-signal / nits7. Comment acknowledges it; fine for now. Consider adding 8. The Resources screen will always render "no subscriptions" even after 9. Small pure helper; one or two tests covering (a) non-notification skip, (b) wrong-method skip, (c) correct extraction would keep per-file coverage safe if more log-entry sources land later. No blocker. 10. The same Things that look good
RecommendationHappy for this to merge once #1 (the |
…w full stdio command in ServerCard Two issues surfaced after the #1244 wiring landed locally: 1. **401 on `/api/mcp/connect`** when running `npm run dev` with the default auth (no `DANGEROUSLY_OMIT_AUTH`). The dev backend generates a random token and prints `http://localhost:6274?MCP_INSPECTOR_API_TOKEN=…` in the banner, but App.tsx was calling `createWebEnvironment(undefined, …)` so the browser never sent `x-mcp-remote-auth`. The Hono middleware correctly rejected with "Authentication required. Use the x-mcp-remote-auth header with Bearer token." Adds a small `getAuthToken()` helper in App.tsx that reads `MCP_INSPECTOR_API_TOKEN` off `window.location.search`, falls back to `sessionStorage`, and persists fresh values into sessionStorage so client-side navigation / OAuth round-trips don't drop the token. The resolved value is passed to `createWebEnvironment` whenever a new InspectorClient is built. 2. **Misleading ServerCard display** — the seed server's `config` is `{ command: "npx", args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"] }` but the card only rendered `"npx"`, so it looked like the command was incomplete. `getCommandOrUrl` now joins `command + args` for stdio configs, so the card shows the same string that gets spawned. SSE / streamable-http paths unchanged. Manual end-to-end re-verified against `npm run dev` (no DANGEROUSLY_OMIT_AUTH): - Banner prints the auth-token URL. - Browser navigates to that URL → `getAuthToken()` picks the token off the query string, persists to sessionStorage, threads it through to every `/api/*` request. - Server card shows `npx -y @modelcontextprotocol/server-filesystem /tmp`. - Toggle connect → connection succeeds; ViewHeader shows `Connected (~1.3s)`; Tools tab populates with the 14 filesystem tools. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
`main.tsx` overrode `--mantine-color-body` to `--mantine-color-blue-9` in dark mode, while `.storybook/preview.tsx` used `--mantine-color-dark-9`. The dev app then rendered with a blue background while every story (and the design intent visible in storybook) used dark grey. Switch main.tsx to `--mantine-color-dark-9` so the dev app matches the storybook reference. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
…rver Adds `@modelcontextprotocol/server-everything` to the hardcoded seed list alongside the filesystem server. The everything server exposes tools, prompts, resources, sampling, and completion — covering most of the surface a developer might want to exercise against the wired-up InspectorView without configuring anything. Comment updated to explain the two-seed shape. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
App.tsxnow mountsInspectorViewwith live data from the v2 hook layer (useInspectorClient,useManaged{Tools,Prompts,Resources,ResourceTemplates,RequestorTasks},useMessageLog). Lazy-instantiatesInspectorClienton connect via a newcreateWebEnvironmenthelper; state managers are destroyed on server switch.InspectorViewbecomes pure prop-driven: allSTUB_*/handshakeTimer/handle{Toggle,Disconnect}machinery removed. Connection state (activeServer,connectionStatus,initializeResult,latencyMs,errorMessage) and panel states (ToolCallState/GetPromptState/ReadResourceState) flow in as props.onCallTool,onGetPrompt,onReadResource,onSetLogLevel,onSubscribeResource,onUnsubscribeResource,onCancelTask,onRefresh*) route to InspectorClient methods.npx @modelcontextprotocol/server-filesystem /tmpentry per the agreed scope;useServers-style persistence is a separate effort.vite.config.tspins the dev server toCLIENT_PORT(default 6274) withstrictPort: trueso it matches the dev backend'sallowedOrigins. Top-levelresolve.aliasnow spreads the same bare-module aliases the vitest projects use (the wired App pullspino/browser.js,zustand/middlewareetc. into the browser dep graph for the first time).Manual end-to-end verification
Driven via Playwright against
npx @modelcontextprotocol/server-filesystem /tmp:npm run dev→ browser opens athttp://localhost:6274/, InspectorView renders with the seed server card.secure-filesystem-serveridentified, ViewHeader showsConnected (~1.3s).read_file,list_directory,list_allowed_directories, …).list_allowed_directoriesand Execute Tool → result panel shows the real output:Allowed directories: /private/tmp(no stub).What's intentionally noop in this PR
Server CRUD (
onServerAdd/onServerEdit/ etc.), history pinning, app sandbox round-trip, log export — these are wired astodoNoopcallbacks. Each requires additional UI/state that's separate from "wire the hook layer." The prop interface is in place for them.Test plan
npm run validate— 1489 / 1489 (lint + per-file coverage gate clean)npm run test:integration— 379 / 379npm run test:storybook— 300 / 300npx @modelcontextprotocol/server-filesystem /tmpend-to-end (Playwright-driven, captured above)logging/setLevelround-trip — defer to a server that emits log notifications (filesystem-server only emits "Method not found" for logging/setLevel; the wiring works but there's nothing visible to verify against)resources/readround-trip — same caveat; filesystem-server has tools but no resources catalogKnown limitations
set-state-in-effect. Reasonable UX either way.connect()errors are captured from the Promise rejection only — the InspectorClient state machine can transitionstatus → errorwithout a Promise rejection (e.g. transport dies mid-session) and we'd miss the message. Follow-up needs anerrorevent inInspectorClientEventMap.Closes #1244.
🤖 Generated with Claude Code