/** * InspectorClientProtocol: the minimal surface that state managers (core/mcp/state/) * and React hooks (core/react/) consume. * * v1.5 has a single concrete `InspectorClient` class (~2k LOC) bundling the transport, * SDK client, OAuth flow, pending-request queues, and fetch tracking. Porting the full * class requires first porting the auth subsystem (core/auth/, ~12 files) — that's * deferred to a follow-up issue. * * This interface captures just what the state+hooks layer needs to function, * so the layer is decoupled from how (or whether) the runtime is wired up. * The real `InspectorClient` will implement this interface in the follow-up port. */ import type { ConnectionStatus, InspectorServerSettings, ResourceReadInvocation, ResourceTemplateReadInvocation, PromptGetInvocation, ToolCallInvocation, ResourceSubscriptionStreamState, ExcludedTool, RequestMetadata, AppRendererClient, } from "./types.js"; import type { CacheMode, ClientCapabilities, DiscoverResult, Implementation, LoggingLevel, Prompt, ProtocolEra, Resource, ResourceTemplateType as ResourceTemplate, ServerCapabilities, Task, Tool, } from "@modelcontextprotocol/client"; import type { JsonValue } from "../json/jsonUtils.js"; import type { MalformedListItem } from "./listSalvage.js"; import type { ConnectionDiagnostics } from "./connectionDiagnostics.js"; import type { InspectorClientEventTarget } from "./inspectorClientEventTarget.js"; import type { SkillEntry } from "./skillsSchemas.js"; import type { SkillsExtensionSupport } from "./skills.js"; import type { DirectoryReadResult } from "./skillsSchemas.js"; import type { SamplingCreateMessage } from "./samplingCreateMessage.js"; import type { ElicitationCreateMessage } from "./elicitationCreateMessage.js"; /** * Re-exported from `./types.js`, where it is defined and documented, so hook * code that types against this protocol (`core/react/useInspectorClient.ts`) * can import it here without a second, drift-prone definition (#1745). */ export type { AppRendererClient } from "./types.js"; /** * The contract every state manager and hook depends on. Anything that holds * a reference to "an Inspector client" — including tests and fixtures — * should depend on this protocol rather than the concrete class. */ export interface InspectorClientProtocol extends InspectorClientEventTarget { // Connection state accessors getStatus(): ConnectionStatus; getCapabilities(): ServerCapabilities | undefined; getClientCapabilities(): ClientCapabilities; getServerInfo(): Implementation | undefined; getInstructions(): string | undefined; getProtocolVersion(): string | undefined; getProtocolEra(): ProtocolEra | undefined; /** Tools excluded from `tools/list` for invalid `x-mcp-header` annotations * (SEP-2243); empty on legacy/stdio connections (#1632). */ getExcludedTools(): ExcludedTool[]; /** Entries dropped from a list result as malformed, across every list method; * empty against a conforming server (#1909). */ getMalformedListItems(): MalformedListItem[]; getResourceSubscriptionStreamState(): ResourceSubscriptionStreamState; getDiscoverResult(): DiscoverResult | undefined; getServerSettings(): InspectorServerSettings | undefined; setServerSettings(settings: InspectorServerSettings): void; /** * What the client is still waiting on, when it last heard back, and the * state of the notification stream — the snapshot a request timeout is * annotated with (#2318). Optional so existing test doubles satisfy the * interface without implementing it; `connectionDiagnosticsChange` fires * whenever it changes. */ getConnectionDiagnostics?(): ConnectionDiagnostics; getAppRendererClient(): AppRendererClient | null; // Connection control connect(): Promise; disconnect(safeDisconnectTimeout?: number): Promise; // Paginated list methods used by managed/paged state stores listTools( cursor?: string, metadata?: RequestMetadata, ): Promise<{ tools: Tool[]; nextCursor?: string }>; listPrompts( cursor?: string, metadata?: RequestMetadata, ): Promise<{ prompts: Prompt[]; nextCursor?: string }>; listResources( cursor?: string, metadata?: RequestMetadata, ): Promise<{ resources: Resource[]; nextCursor?: string }>; listResourceTemplates( cursor?: string, metadata?: RequestMetadata, ): Promise<{ resourceTemplates: ResourceTemplate[]; nextCursor?: string }>; listRequestorTasks( cursor?: string, ): Promise<{ tasks: Task[]; nextCursor?: string }>; /** Poll one requestor task's current status (era-aware: modern `DetailedTask` * via `tasks/get`, or the legacy flattened task). Dispatches * `requestorTaskUpdated`. Used by the modern task store's refresh (no * `tasks/list`). */ getRequestorTask(taskId: string): Promise; /** True when a modern (2026-07-28) connection negotiated the * `io.modelcontextprotocol/tasks` extension (SEP-2663). Gates the Tasks tab * and the modern task store's poll-based refresh. */ isTasksExtensionNegotiated(): boolean; /** The Skills extension (SEP-2640) the server declared, or `undefined`. * Gates the Skills tab and the managed skills store (#2234). */ getSkillsExtension(): SkillsExtensionSupport | undefined; /** One page of `skills/list`; the managed skills store walks the cursor. */ listSkills( cursor?: string, metadata?: RequestMetadata, ): Promise<{ skills: SkillEntry[]; nextCursor?: string }>; /** One skill entry by URI (`skills/get`). */ getSkill(uri: string, metadata?: RequestMetadata): Promise; /** * One page of `resources/directory/read` — the direct children of a directory * resource (#2248). Optional on this interface, unlike the two methods above: * declaring the extension commits a server to `skills/list` and `skills/get`, * while this one is separately gated on `directoryRead`, so a caller has to * check for it anyway and the many test doubles that satisfy this interface * should not all have to grow a method most of them never reach. */ readResourceDirectory?( uri: string, cursor?: string, metadata?: RequestMetadata, ): Promise; /** * Mark the response that most recently answered `method` as rejected by the * client, so its Protocol entry shows the reason instead of rendering as a * clean success (#1953). Optional so existing test doubles satisfy the * interface without implementing it; see `InspectorClient` for how the id is * correlated and why that correlation is exact. */ markResponseRejected?(method: string, reason: string): void; // Aggregate (all-page) list methods used by the managed state stores on // refresh. Unlike the single-page methods above, these route through the // SDK's cache-aware high-level verbs so `cacheMode` ('use' | 'refresh' | // 'bypass') is honored — a manual refresh forces a cache-bypassing round trip // on modern servers that send `ttlMs` hints. Options are optional; omitting // `cacheMode` uses the SDK default ('use'). listAllTools(options?: { cacheMode?: CacheMode; metadata?: RequestMetadata; }): Promise<{ tools: Tool[] }>; listAllPrompts(options?: { cacheMode?: CacheMode; metadata?: RequestMetadata; }): Promise<{ prompts: Prompt[] }>; listAllResources(options?: { cacheMode?: CacheMode; metadata?: RequestMetadata; }): Promise<{ resources: Resource[] }>; listAllResourceTemplates(options?: { cacheMode?: CacheMode; metadata?: RequestMetadata; }): Promise<{ resourceTemplates: ResourceTemplate[] }>; // Invocation methods used by paged result panels callTool( tool: Tool, args: Record, generalMetadata?: RequestMetadata, toolSpecificMetadata?: RequestMetadata, ): Promise; readResource( uri: string, metadata?: RequestMetadata, ): Promise; readResourceFromTemplate( uriTemplate: string, params: Record, metadata?: RequestMetadata, ): Promise; getPrompt( name: string, params?: Record, metadata?: RequestMetadata, ): Promise; // Misc surface required by hooks/state setLoggingLevel(level: LoggingLevel): Promise; getSessionId(): string | undefined; // Server-initiated request queues. Each entry exposes `respond()` / `reject()` // (and `remove()`), which resolve the handler Promise that left the originating // call (e.g. a tool execution) in flight. The `pendingSamplesChange` / // `pendingElicitationsChange` events fire whenever these arrays mutate. getPendingSamples(): SamplingCreateMessage[]; getPendingElicitations(): ElicitationCreateMessage[]; // Completions (resource templates / prompt arguments) getCompletions( ref: | { type: "ref/resource"; uri: string } | { type: "ref/prompt"; name: string }, argumentName: string, argumentValue: string, context?: Record, metadata?: RequestMetadata, ): Promise<{ values: string[]; total?: number; hasMore?: boolean }>; }