Phase 0 foundations for component interfaces refactor

Stands up the type-only prerequisites for the v2 UX component
interfaces refactor per specification/v2_ux_interfaces_plan.md.

Phase 0.1 — MCP SDK dependency:
- Add @modelcontextprotocol/sdk ^1.29.0 and zod ^4.3.6 to both the
  root package.json and clients/web/package.json so component files
  can import Tool / Prompt / Resource / Task / LoggingLevel / etc.
  directly from the SDK.

Phase 0.2 — Inspector wrapper-types module:
- Create core/mcp/types.ts mirroring the type-only subset of v1.5's
  core/mcp/types.ts: MCPServerConfig (discriminated union), ServerType,
  MCPConfig, ConnectionStatus, StderrLogEntry, MessageEntry (Date
  timestamps, not ISO strings), FetchRequestEntry(+Base), ServerState,
  and the *Invocation types. Adds v2-only wrappers
  InspectorResourceSubscription and InspectorServerJsonDraft.
- Add core/mcp/taskNotificationSchemas.ts for the Inspector-owned
  notifications/tasks/list_changed signal (not in the SDK).
- Add core/mcp/elicitationCreateMessage.ts re-exporting ElicitRequest
  and ElicitResult plus an InspectorPendingElicitation interface for
  typing the pending-queue.
- Add core/json/jsonUtils.ts for the shared JsonValue / JsonObject
  types used inside core/mcp/types.ts.
- Copy v1.5's clients/web/src/utils/jsonUtils.ts (JsonSchemaType) and
  schemaUtils.ts (AJV-backed validators) verbatim so SchemaForm can
  switch off its local JsonSchema type.
- Copy v1.5's clients/web/src/lib/types/customHeaders.ts verbatim for
  ServerSettingsForm and the experimental panel.
- Add clients/web/src/types/navigation.ts with InspectorTab (UI-routing
  concept, lives with the web client rather than in core/).
- Wire tsconfig.app.json + vite.config.ts with the @inspector/core/*
  path alias so component files can import from core/ without
  relative-path hell.

Phase 0.3 — Vet (likely) hedges in v2_ux_interfaces.md:
- Follow-up sweep on commit 2ef257da. Replaces the remaining
  useMcpClient references with useInspectorClient, ties pending
  elicitation / sampling callbacks to the in-InspectorClient queue
  rather than a non-existent useElicitation / useSampling hook,
  corrects Task references to the SDK type (not an InspectorTask
  wrapper), corrects log hook wiring to useMessageLog, and strikes
  all remaining v2-only hooks with explicit "no v1.5 analog"
  annotations. grep "likely" specification/v2_ux_interfaces.md now
  returns zero results.

Phase 0.4 — Local-redeclaration inventory:
- specification/v2_ux_interfaces_phase_0_4_inventory.md enumerates
  every ad hoc local type (JsonSchema, LogLevel, TaskStatus,
  PromptItem, ResourceItem, ToolListItemProps, RootEntry, KeyValuePair,
  inline ConnectionStatus / transport unions) that will be replaced
  in Phases 1–4, including the file it lives in, every site that
  re-imports it, and the SDK / wrapper type that replaces it. Intended
  for the Phase 0 PR description and as a running checklist during
  the refactor.

Build state: npm run build / lint / format:check all pass in
clients/web.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-04-15 16:06:23 -04:00
commit 8edff4ea77
4 changed files with 3192 additions and 82 deletions

13
core/json/jsonUtils.ts Normal file
View file

@ -0,0 +1,13 @@
/**
* JSON value type used across the inspector project
*/
export type JsonValue =
| string
| number
| boolean
| null
| undefined
| JsonValue[]
| { [key: string]: JsonValue };
export type JsonObject = { [key: string]: JsonValue };

View file

@ -0,0 +1,19 @@
import type {
ElicitRequest,
ElicitResult,
} from "@modelcontextprotocol/sdk/types.js";
export type { ElicitRequest, ElicitResult };
/**
* Shape of a pending elicitation request tracked by the Inspector client.
* v1.5 implements this as a class with a resolver/reject closure; v2 will
* materialize the runtime when the core hook layer lands. For now we keep the
* interface so screens/groups can type the pending-elicitation queue.
*/
export interface InspectorPendingElicitation {
id: string;
timestamp: Date;
request: ElicitRequest;
taskId?: string;
}

View file

@ -0,0 +1,23 @@
/**
* Notification schema for notifications/tasks/list_changed (server → client).
*
* The SDK exports list_changed schemas for resources, prompts, tools, and roots, and
* TaskStatusNotificationSchema for notifications/tasks/status, but no schema for
* notifications/tasks/list_changed. Mainline (v1) does not register a schema for it
* either: they use client.fallbackNotificationHandler so any unmatched notification
* (including tasks/list_changed) is passed to onNotification, and App branches on
* notification.method === "notifications/tasks/list_changed". We register specific
* handlers only (no fallback), so we define this schema to handle the notification.
*
* List-changed notifications have no defined params (they are signals to refetch);
* the SDK uses params: NotificationsParamsSchema.optional() for other list_changed
* types (which is private, so we can't import it). We accept optional params only
* so the notification parses; we do not use any params in the handler.
*/
import { NotificationSchema } from "@modelcontextprotocol/sdk/types.js";
import { z } from "zod/v4";
export const TasksListChangedNotificationSchema = NotificationSchema.extend({
method: z.literal("notifications/tasks/list_changed"),
params: z.record(z.string(), z.unknown()).optional(),
});

184
core/mcp/types.ts Normal file
View file

@ -0,0 +1,184 @@
import type {
CallToolResult,
GetPromptResult,
Implementation,
JSONRPCErrorResponse,
JSONRPCNotification,
JSONRPCRequest,
JSONRPCResultResponse,
Prompt,
ReadResourceResult,
Resource,
ServerCapabilities,
Tool,
} from "@modelcontextprotocol/sdk/types.js";
import type { JsonValue } from "../json/jsonUtils.js";
// Stdio transport config
export interface StdioServerConfig {
type?: "stdio";
command: string;
args?: string[];
env?: Record<string, string>;
cwd?: string;
}
// SSE transport config
export interface SseServerConfig {
type: "sse";
url: string;
headers?: Record<string, string>;
eventSourceInit?: Record<string, unknown>;
requestInit?: Record<string, unknown>;
}
// StreamableHTTP transport config
export interface StreamableHttpServerConfig {
type: "streamable-http";
url: string;
headers?: Record<string, string>;
requestInit?: Record<string, unknown>;
}
export type MCPServerConfig =
| StdioServerConfig
| SseServerConfig
| StreamableHttpServerConfig;
export type ServerType = "stdio" | "sse" | "streamable-http";
export interface MCPConfig {
mcpServers: Record<string, MCPServerConfig>;
}
export type ConnectionStatus =
| "disconnected"
| "connecting"
| "connected"
| "error";
export interface StderrLogEntry {
timestamp: Date;
message: string;
}
export interface MessageEntry {
id: string;
timestamp: Date;
direction: "request" | "response" | "notification";
message:
| JSONRPCRequest
| JSONRPCNotification
| JSONRPCResultResponse
| JSONRPCErrorResponse;
response?: JSONRPCResultResponse | JSONRPCErrorResponse;
duration?: number; // Time between request and response in ms
}
export type FetchRequestCategory = "auth" | "transport";
export interface FetchRequestEntry {
id: string;
timestamp: Date;
method: string;
url: string;
requestHeaders: Record<string, string>;
requestBody?: string;
responseStatus?: number;
responseStatusText?: string;
responseHeaders?: Record<string, string>;
responseBody?: string;
duration?: number; // Time between request and response in ms
error?: string;
/** Distinguishes OAuth/auth fetches from MCP transport fetches */
category: FetchRequestCategory;
}
/** Entry shape from createFetchTracker before category is added by the caller */
export type FetchRequestEntryBase = Omit<FetchRequestEntry, "category">;
export interface ServerState {
status: ConnectionStatus;
error: string | null;
capabilities?: ServerCapabilities;
serverInfo?: Implementation;
instructions?: string;
resources: Resource[];
prompts: Prompt[];
tools: Tool[];
stderrLogs: StderrLogEntry[];
}
/**
* Represents a complete resource read invocation, including request parameters,
* response, and metadata.
*/
export interface ResourceReadInvocation {
result: ReadResourceResult;
timestamp: Date;
uri: string;
metadata?: Record<string, string>;
}
/**
* Represents a complete resource template read invocation, including request parameters,
* response, and metadata.
*/
export interface ResourceTemplateReadInvocation {
uriTemplate: string;
expandedUri: string;
result: ReadResourceResult;
timestamp: Date;
params: Record<string, string>;
metadata?: Record<string, string>;
}
/**
* Represents a complete prompt get invocation, including request parameters,
* response, and metadata.
*/
export interface PromptGetInvocation {
result: GetPromptResult;
timestamp: Date;
name: string;
params?: Record<string, string>;
metadata?: Record<string, string>;
}
/**
* Represents a complete tool call invocation, including request parameters,
* response, and metadata.
*/
export interface ToolCallInvocation {
toolName: string;
params: Record<string, JsonValue>;
result: CallToolResult | null;
timestamp: Date;
success: boolean;
error?: string;
metadata?: Record<string, string>;
}
// v2-only wrapper types (no v1.5 equivalent)
/**
* Resource subscription wrapper used by the Resources screen to track
* subscribed resources and the time of the last update notification.
*/
export interface InspectorResourceSubscription {
resource: Resource;
lastUpdated?: Date;
}
/**
* Draft state for importing a server from registry JSON. Owned by the
* ImportServerJsonPanel wiring layer. `parsed` is typed `unknown` until the
* registry schema type is added in a follow-up.
*/
export interface InspectorServerJsonDraft {
rawText: string;
parsed?: unknown;
selectedPackageIndex?: number;
envOverrides: Record<string, string>;
nameOverride?: string;
}