Merge branch 'v2/main' into v2/feat/2144-oauth-revocation
This commit is contained in:
commit
5ba2fadd3b
4 changed files with 3326 additions and 1516 deletions
|
|
@ -18,6 +18,20 @@ import type { Meta, StoryObj } from "@storybook/react-vite";
|
|||
import { useState } from "react";
|
||||
import { expect, fn } from "storybook/test";
|
||||
import { InspectorView } from "./InspectorView";
|
||||
import type {
|
||||
AppsPanelProps,
|
||||
ConnectionProps,
|
||||
ConsolePanelProps,
|
||||
LogsPanelProps,
|
||||
NetworkPanelProps,
|
||||
PromptsPanelProps,
|
||||
ProtocolPanelProps,
|
||||
ResourcesPanelProps,
|
||||
ServerListProps,
|
||||
ShellProps,
|
||||
TasksPanelProps,
|
||||
ToolsPanelProps,
|
||||
} from "./types";
|
||||
import { noopPagination } from "../../../test/fixtures/pagination";
|
||||
import {
|
||||
EMPTY_TOOLS_UI,
|
||||
|
|
@ -324,116 +338,164 @@ const demoInitializeResult: InitializeResult = {
|
|||
serverInfo: { name: "Local Dev Server", version: "1.2.0" },
|
||||
};
|
||||
|
||||
// One constant per domain bundle, so a story can override a single field
|
||||
// without replacing its whole bundle — Storybook merges args only at the top
|
||||
// level, and each of these IS one top-level arg now (#2130).
|
||||
const shellArgs: ShellProps = {
|
||||
activeTab: "Servers",
|
||||
onActiveTabChange: fn(),
|
||||
onToggleTheme: fn(),
|
||||
onOpenClientSettings: fn(),
|
||||
};
|
||||
|
||||
// Stories default to "disconnected"; per-story overrides drive the connected /
|
||||
// error narratives.
|
||||
const connectionArgs: ConnectionProps = {
|
||||
activeServer: undefined,
|
||||
connectionStatus: "disconnected",
|
||||
initializeResult: undefined,
|
||||
latencyMs: undefined,
|
||||
onToggleConnection: fn(),
|
||||
onDisconnect: fn(),
|
||||
};
|
||||
|
||||
// Callbacks are wired to storybook spies so play functions can assert on
|
||||
// dispatch. Real wiring routes these to InspectorClient methods (the app shell
|
||||
// at clients/web/src/App.tsx).
|
||||
const serversArgs: ServerListProps = {
|
||||
servers: demoServers,
|
||||
onServerAdd: fn(),
|
||||
onServerImportConfig: fn(),
|
||||
onServerImportJson: fn(),
|
||||
onServerExport: fn(),
|
||||
onConnectionInfo: fn(),
|
||||
onServerSettings: fn(),
|
||||
onServerEdit: fn(),
|
||||
onServerClone: fn(),
|
||||
onServerRemove: fn(),
|
||||
onServerReorder: fn(),
|
||||
};
|
||||
|
||||
const toolsArgs: ToolsPanelProps = {
|
||||
tools: demoTools,
|
||||
toolsListChanged: false,
|
||||
toolsUi: EMPTY_TOOLS_UI,
|
||||
toolsPagination: noopPagination,
|
||||
serverSupportsTaskToolCalls: false,
|
||||
onToolsUiChange: fn(),
|
||||
onCallTool: fn(),
|
||||
onRefreshTools: fn(),
|
||||
};
|
||||
|
||||
const promptsArgs: PromptsPanelProps = {
|
||||
prompts: demoPrompts,
|
||||
promptsListChanged: false,
|
||||
promptsUi: EMPTY_PROMPTS_UI,
|
||||
promptsPagination: noopPagination,
|
||||
onPromptsUiChange: fn(),
|
||||
onGetPrompt: fn(),
|
||||
onRefreshPrompts: fn(),
|
||||
};
|
||||
|
||||
const resourcesArgs: ResourcesPanelProps = {
|
||||
resources: demoResources,
|
||||
resourceTemplates: demoResourceTemplates,
|
||||
subscriptions: demoSubscriptions,
|
||||
resourcesListChanged: false,
|
||||
resourcesUi: EMPTY_RESOURCES_UI,
|
||||
resourcesPagination: noopPagination,
|
||||
onResourcesUiChange: fn(),
|
||||
onReadResource: fn(),
|
||||
onSubscribeResource: fn(),
|
||||
onUnsubscribeResource: fn(),
|
||||
onRefreshResources: fn(),
|
||||
};
|
||||
|
||||
const appsArgs: AppsPanelProps = {
|
||||
appsUi: EMPTY_APPS_UI,
|
||||
sandboxPath: "about:blank",
|
||||
bridgeFactory: noopBridgeFactory,
|
||||
appRendererRef: { current: null },
|
||||
onAppsUiChange: fn(),
|
||||
onSelectApp: fn(),
|
||||
onOpenApp: fn(),
|
||||
onCloseApp: fn(),
|
||||
onAppError: fn(),
|
||||
onRefreshApps: fn(),
|
||||
};
|
||||
|
||||
const tasksArgs: TasksPanelProps = {
|
||||
tasks: demoTasks,
|
||||
progressByTaskId: demoProgressByTaskId,
|
||||
tasksUi: EMPTY_TASKS_UI,
|
||||
onTasksUiChange: fn(),
|
||||
onCancelTask: fn(),
|
||||
onClearCompletedTasks: fn(),
|
||||
onRefreshTasks: fn(),
|
||||
};
|
||||
|
||||
const logsArgs: LogsPanelProps = {
|
||||
logs: demoLogs,
|
||||
logsUi: EMPTY_LOGS_UI,
|
||||
currentLogLevel: "info",
|
||||
onSetLogLevel: fn(),
|
||||
onLogsUiChange: fn(),
|
||||
onClearLogs: fn(),
|
||||
onExportLogs: fn(),
|
||||
};
|
||||
|
||||
const protocolArgs: ProtocolPanelProps = {
|
||||
protocol: demoHistory,
|
||||
protocolUi: EMPTY_PROTOCOL_UI,
|
||||
onProtocolUiChange: fn(),
|
||||
onClearProtocol: fn(),
|
||||
onExportProtocol: fn(),
|
||||
onClearProtocolSection: fn(),
|
||||
onExportProtocolSection: fn(),
|
||||
onReplayProtocol: fn(),
|
||||
onTogglePinProtocol: fn(),
|
||||
};
|
||||
|
||||
const networkArgs: NetworkPanelProps = {
|
||||
network: demoNetwork,
|
||||
networkUi: EMPTY_NETWORK_UI,
|
||||
onNetworkUiChange: fn(),
|
||||
onClearNetwork: fn(),
|
||||
onExportNetwork: fn(),
|
||||
};
|
||||
|
||||
const consoleArgs: ConsolePanelProps = {
|
||||
stderrLogs: demoStderr,
|
||||
consoleUi: EMPTY_CONSOLE_UI,
|
||||
onConsoleUiChange: fn(),
|
||||
onClearConsole: fn(),
|
||||
onExportConsole: fn(),
|
||||
};
|
||||
|
||||
const meta: Meta<typeof InspectorView> = {
|
||||
title: "Views/InspectorView",
|
||||
component: InspectorView,
|
||||
parameters: { layout: "fullscreen" },
|
||||
args: {
|
||||
// Data
|
||||
servers: demoServers,
|
||||
tools: demoTools,
|
||||
prompts: demoPrompts,
|
||||
resources: demoResources,
|
||||
resourceTemplates: demoResourceTemplates,
|
||||
subscriptions: demoSubscriptions,
|
||||
logs: demoLogs,
|
||||
tasks: demoTasks,
|
||||
progressByTaskId: demoProgressByTaskId,
|
||||
protocol: demoHistory,
|
||||
network: demoNetwork,
|
||||
stderrLogs: demoStderr,
|
||||
|
||||
// Connection state — stories default to "disconnected"; per-story
|
||||
// overrides drive the connected / error narratives.
|
||||
activeServer: undefined,
|
||||
connectionStatus: "disconnected",
|
||||
initializeResult: undefined,
|
||||
latencyMs: undefined,
|
||||
|
||||
// Misc state
|
||||
currentLogLevel: "info",
|
||||
sandboxPath: "about:blank",
|
||||
bridgeFactory: noopBridgeFactory,
|
||||
appRendererRef: { current: null },
|
||||
|
||||
// Per-screen UI state (search / filter / selection), one object per screen.
|
||||
toolsUi: EMPTY_TOOLS_UI,
|
||||
promptsUi: EMPTY_PROMPTS_UI,
|
||||
resourcesUi: EMPTY_RESOURCES_UI,
|
||||
appsUi: EMPTY_APPS_UI,
|
||||
tasksUi: EMPTY_TASKS_UI,
|
||||
logsUi: EMPTY_LOGS_UI,
|
||||
protocolUi: EMPTY_PROTOCOL_UI,
|
||||
networkUi: EMPTY_NETWORK_UI,
|
||||
consoleUi: EMPTY_CONSOLE_UI,
|
||||
|
||||
// Callbacks — all wired to storybook spies so play functions can assert
|
||||
// on dispatch. Real wiring routes these to InspectorClient methods (the
|
||||
// app shell at clients/web/src/App.tsx).
|
||||
onToggleTheme: fn(),
|
||||
onOpenClientSettings: fn(),
|
||||
onToggleConnection: fn(),
|
||||
onDisconnect: fn(),
|
||||
onServerAdd: fn(),
|
||||
onServerImportConfig: fn(),
|
||||
onServerImportJson: fn(),
|
||||
onServerExport: fn(),
|
||||
onConnectionInfo: fn(),
|
||||
onServerSettings: fn(),
|
||||
onServerEdit: fn(),
|
||||
onServerClone: fn(),
|
||||
onServerRemove: fn(),
|
||||
onServerReorder: fn(),
|
||||
serverSupportsTaskToolCalls: false,
|
||||
onToolsUiChange: fn(),
|
||||
onCallTool: fn(),
|
||||
onRefreshTools: fn(),
|
||||
toolsPagination: noopPagination,
|
||||
promptsPagination: noopPagination,
|
||||
resourcesPagination: noopPagination,
|
||||
onPromptsUiChange: fn(),
|
||||
onGetPrompt: fn(),
|
||||
onRefreshPrompts: fn(),
|
||||
onResourcesUiChange: fn(),
|
||||
onReadResource: fn(),
|
||||
onSubscribeResource: fn(),
|
||||
onUnsubscribeResource: fn(),
|
||||
onRefreshResources: fn(),
|
||||
onTasksUiChange: fn(),
|
||||
onCancelTask: fn(),
|
||||
onClearCompletedTasks: fn(),
|
||||
onRefreshTasks: fn(),
|
||||
onSetLogLevel: fn(),
|
||||
onLogsUiChange: fn(),
|
||||
onClearLogs: fn(),
|
||||
onExportLogs: fn(),
|
||||
onProtocolUiChange: fn(),
|
||||
onClearProtocol: fn(),
|
||||
onExportProtocol: fn(),
|
||||
onReplayProtocol: fn(),
|
||||
onTogglePinProtocol: fn(),
|
||||
onNetworkUiChange: fn(),
|
||||
onClearNetwork: fn(),
|
||||
onExportNetwork: fn(),
|
||||
onConsoleUiChange: fn(),
|
||||
onClearConsole: fn(),
|
||||
onExportConsole: fn(),
|
||||
onAppsUiChange: fn(),
|
||||
onSelectApp: fn(),
|
||||
onOpenApp: fn(),
|
||||
onCloseApp: fn(),
|
||||
onAppError: fn(),
|
||||
onRefreshApps: fn(),
|
||||
activeTab: "Servers",
|
||||
onActiveTabChange: fn(),
|
||||
shell: shellArgs,
|
||||
connection: connectionArgs,
|
||||
servers: serversArgs,
|
||||
tools: toolsArgs,
|
||||
prompts: promptsArgs,
|
||||
resources: resourcesArgs,
|
||||
apps: appsArgs,
|
||||
tasks: tasksArgs,
|
||||
logs: logsArgs,
|
||||
protocol: protocolArgs,
|
||||
network: networkArgs,
|
||||
console: consoleArgs,
|
||||
},
|
||||
render: (args) => {
|
||||
const [activeTab, setActiveTab] = useState(args.activeTab ?? "Servers");
|
||||
const [activeTab, setActiveTab] = useState(args.shell.activeTab);
|
||||
return (
|
||||
<InspectorView
|
||||
{...args}
|
||||
activeTab={activeTab}
|
||||
onActiveTabChange={setActiveTab}
|
||||
shell={{ ...args.shell, activeTab, onActiveTabChange: setActiveTab }}
|
||||
/>
|
||||
);
|
||||
},
|
||||
|
|
@ -463,7 +525,7 @@ export const Default: Story = {
|
|||
|
||||
export const NoServers: Story = {
|
||||
args: {
|
||||
servers: [],
|
||||
servers: { ...serversArgs, servers: [] },
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -480,7 +542,7 @@ const manyServers: ServerEntry[] = Array.from({ length: 24 }, (_, i) => ({
|
|||
|
||||
export const ManyServers: Story = {
|
||||
args: {
|
||||
servers: manyServers,
|
||||
servers: { ...serversArgs, servers: manyServers },
|
||||
},
|
||||
play: async ({ canvasElement }) => {
|
||||
// Even under enough content to overflow, the shell stays viewport-clamped
|
||||
|
|
@ -509,10 +571,13 @@ export const ManyServers: Story = {
|
|||
// regression / storybook play function coverage.
|
||||
export const Connected: Story = {
|
||||
args: {
|
||||
activeServer: demoServers[0]!.id,
|
||||
connectionStatus: "connected",
|
||||
initializeResult: demoInitializeResult,
|
||||
latencyMs: 142,
|
||||
connection: {
|
||||
...connectionArgs,
|
||||
activeServer: demoServers[0]!.id,
|
||||
connectionStatus: "connected",
|
||||
initializeResult: demoInitializeResult,
|
||||
latencyMs: 142,
|
||||
},
|
||||
},
|
||||
};
|
||||
|
||||
|
|
@ -524,10 +589,13 @@ export const ConnectionError: Story = {
|
|||
// connect-attempt-failure signal that gates the failure column; `network: []`
|
||||
// (and empty Protocol) keep it a pure stdio failure so only Console is offered.
|
||||
args: {
|
||||
activeServer: demoServers[0]!.id,
|
||||
erroredServerId: demoServers[0]!.id,
|
||||
connectionStatus: "error",
|
||||
network: [],
|
||||
stderrLogs: demoStderr,
|
||||
connection: {
|
||||
...connectionArgs,
|
||||
activeServer: demoServers[0]!.id,
|
||||
erroredServerId: demoServers[0]!.id,
|
||||
connectionStatus: "error",
|
||||
},
|
||||
network: { ...networkArgs, network: [] },
|
||||
console: { ...consoleArgs, stderrLogs: demoStderr },
|
||||
},
|
||||
};
|
||||
|
|
|
|||
File diff suppressed because it is too large
Load diff
|
|
@ -1,14 +1,4 @@
|
|||
import {
|
||||
useEffect,
|
||||
useMemo,
|
||||
useRef,
|
||||
useState,
|
||||
type ReactNode,
|
||||
type Ref,
|
||||
} from "react";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import type { ReplayParamsOverride } from "../../../lib/protocolReplay";
|
||||
import type { DeepLink, DeepLinkParseStatus } from "../../../utils/deepLink";
|
||||
import { useEffect, useMemo, useRef, useState, type ReactNode } from "react";
|
||||
import {
|
||||
AppShell,
|
||||
Flex,
|
||||
|
|
@ -18,81 +8,38 @@ import {
|
|||
type MantineTransition,
|
||||
} from "@mantine/core";
|
||||
import { useLocalStorage } from "@mantine/hooks";
|
||||
import type {
|
||||
Implementation,
|
||||
InitializeResult,
|
||||
LoggingLevel,
|
||||
Prompt,
|
||||
ProtocolEra,
|
||||
ReadResourceResult,
|
||||
Resource,
|
||||
ResourceTemplateType as ResourceTemplate,
|
||||
Task,
|
||||
Tool,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type {
|
||||
ConnectionStatus,
|
||||
ExcludedTool,
|
||||
FetchRequestEntry,
|
||||
InspectorResourceSubscription,
|
||||
MessageEntry,
|
||||
ResourceSubscriptionStreamState,
|
||||
ServerEntry,
|
||||
StderrLogEntry,
|
||||
} from "@inspector/core/mcp/types.js";
|
||||
import type { Implementation, Tool } from "@modelcontextprotocol/client";
|
||||
import type { ServerEntry } from "@inspector/core/mcp/types.js";
|
||||
import { isTerminalStatus } from "@inspector/core/mcp/types.js";
|
||||
import { isAppTool } from "@inspector/core/mcp/apps.js";
|
||||
import { TASKS_EXTENSION_KEY } from "@inspector/core/mcp/modernTaskSchemas.js";
|
||||
import { ViewHeader } from "../../groups/ViewHeader/ViewHeader";
|
||||
import { VersionBadge } from "../../elements/VersionBadge/VersionBadge";
|
||||
import { CopyrightBadge } from "../../elements/CopyrightBadge/CopyrightBadge";
|
||||
import type { ListPaginationControlsProps } from "../../elements/ListPaginationControls/ListPaginationControls";
|
||||
import { ServerListScreen } from "../../screens/ServerListScreen/ServerListScreen";
|
||||
import {
|
||||
ToolsScreen,
|
||||
type ToolCallState,
|
||||
type ToolsUiState,
|
||||
} from "../../screens/ToolsScreen/ToolsScreen";
|
||||
import {
|
||||
AppsScreen,
|
||||
type AppsUiState,
|
||||
} from "../../screens/AppsScreen/AppsScreen";
|
||||
import { ToolsScreen } from "../../screens/ToolsScreen/ToolsScreen";
|
||||
import { AppsScreen } from "../../screens/AppsScreen/AppsScreen";
|
||||
import { PromptsScreen } from "../../screens/PromptsScreen/PromptsScreen";
|
||||
import { ResourcesScreen } from "../../screens/ResourcesScreen/ResourcesScreen";
|
||||
import { LoggingScreen } from "../../screens/LoggingScreen/LoggingScreen";
|
||||
import { TasksScreen } from "../../screens/TasksScreen/TasksScreen";
|
||||
import { ProtocolScreen } from "../../screens/ProtocolScreen/ProtocolScreen";
|
||||
import { NetworkScreen } from "../../screens/NetworkScreen/NetworkScreen";
|
||||
import { ConsoleScreen } from "../../screens/ConsoleScreen/ConsoleScreen";
|
||||
import type {
|
||||
AppRendererHandle,
|
||||
BridgeFactory,
|
||||
} from "../../elements/AppRenderer/AppRenderer";
|
||||
import {
|
||||
PromptsScreen,
|
||||
type GetPromptState,
|
||||
type PromptsUiState,
|
||||
} from "../../screens/PromptsScreen/PromptsScreen";
|
||||
import {
|
||||
ResourcesScreen,
|
||||
type ReadResourceState,
|
||||
type ResourcesUiState,
|
||||
} from "../../screens/ResourcesScreen/ResourcesScreen";
|
||||
import {
|
||||
LoggingScreen,
|
||||
type LogsUiState,
|
||||
} from "../../screens/LoggingScreen/LoggingScreen";
|
||||
import type { LogEntryData } from "../../elements/LogEntry/LogEntry";
|
||||
import {
|
||||
TasksScreen,
|
||||
type TasksUiState,
|
||||
} from "../../screens/TasksScreen/TasksScreen";
|
||||
import type { TaskProgress } from "../../groups/TaskCard/TaskCard";
|
||||
import {
|
||||
ProtocolScreen,
|
||||
type ProtocolUiState,
|
||||
} from "../../screens/ProtocolScreen/ProtocolScreen";
|
||||
import {
|
||||
NetworkScreen,
|
||||
type NetworkUiState,
|
||||
} from "../../screens/NetworkScreen/NetworkScreen";
|
||||
import {
|
||||
ConsoleScreen,
|
||||
type ConsoleUiState,
|
||||
} from "../../screens/ConsoleScreen/ConsoleScreen";
|
||||
AppsPanelProps,
|
||||
ConnectionProps,
|
||||
ConsolePanelProps,
|
||||
LogsPanelProps,
|
||||
NetworkPanelProps,
|
||||
PromptsPanelProps,
|
||||
ProtocolPanelProps,
|
||||
ResourcesPanelProps,
|
||||
ServerListProps,
|
||||
ShellProps,
|
||||
TasksPanelProps,
|
||||
ToolsPanelProps,
|
||||
} from "./types";
|
||||
import type { SortDirection } from "../../elements/SortToggle/SortToggle";
|
||||
import { ScreenStage } from "../../elements/ScreenStage/ScreenStage";
|
||||
import { MonitoringScreen } from "../../groups/MonitoringScreen/MonitoringScreen";
|
||||
|
|
@ -360,402 +307,193 @@ const MonitorColumnGroup = Group.withProps({
|
|||
flex: "0 0 auto",
|
||||
});
|
||||
|
||||
/**
|
||||
* `InspectorView` takes one prop per domain rather than a wall of ~130 flat
|
||||
* ones (#2130). Each bundle's shape lives in `./types`; the fields inside keep
|
||||
* the names they had as flat props, so this is a regrouping rather than a
|
||||
* rename.
|
||||
*
|
||||
* The bundles stop here: the function below destructures each one back into
|
||||
* the same locals it already used, and the screens beneath receive the same
|
||||
* individual props they always did.
|
||||
*/
|
||||
export interface InspectorViewProps {
|
||||
/**
|
||||
* Validated deep-link parameters from the page URL. When present and
|
||||
* `openApp` is set, the parent switches to the Apps tab and pre-selects that
|
||||
* app (with `appArgs` as the form values) once the connection is up and the
|
||||
* app list contains it. The connect itself is driven by the parent.
|
||||
*/
|
||||
deepLink?: DeepLink;
|
||||
/**
|
||||
* Outcome of parsing the initial-URL deep link, surfaced as `data-deeplink`
|
||||
* on the `connection-status` testid. Distinguishes "no deep link" from
|
||||
* "rejected" (token mismatch / bad serverUrl) — both leave `data-status`
|
||||
* idle, so an automated driver otherwise cannot tell them apart.
|
||||
*/
|
||||
deepLinkStatus?: DeepLinkParseStatus;
|
||||
|
||||
// Server list (static config; runtime connection state comes from the
|
||||
// separate fields below and is merged into each card by this component).
|
||||
servers: ServerEntry[];
|
||||
/**
|
||||
* Whether the server list is writable (catalog) or read-only (a `--config`
|
||||
* session file / ad-hoc launch). When false, the Servers screen hides all
|
||||
* catalog mutation controls. Defaults to true.
|
||||
*/
|
||||
serverListWritable?: boolean;
|
||||
|
||||
// Connection state — driven by the parent via `useInspectorClient`.
|
||||
activeServer?: string;
|
||||
/**
|
||||
* Id of the server whose last connection attempt failed (#1621). Its card in
|
||||
* the Servers screen draws a red border until another server is connected or
|
||||
* a new connection is attempted. Independent of `activeServer`, which the
|
||||
* parent clears on the failure's `disconnect` event.
|
||||
*
|
||||
* It is also the sole signal that opens the monitoring sidebar onto the
|
||||
* failure's diagnostics (#2108). That used to be gated on the `"error"`
|
||||
* connection status, which an OAuth failure never reaches — the parent drives
|
||||
* that leg and tears the client down itself, settling the session at
|
||||
* `"disconnected"`. So the parent must set this for *every* connect-attempt
|
||||
* failure, auth legs included, and clear it as each new attempt starts.
|
||||
*/
|
||||
erroredServerId?: string;
|
||||
/**
|
||||
* Id of the server that just connected successfully (#1682). Its card draws
|
||||
* the green highlight border and scrolls into view once the monitoring
|
||||
* sidebar has opened — the success mirror of `erroredServerId`.
|
||||
*/
|
||||
connectedServerId?: string;
|
||||
/**
|
||||
* The Inspector build version (root `package.json`), shown at the left of the
|
||||
* footer row (#1682). Absent on a legacy backend that omits it — the version
|
||||
* label then renders nothing.
|
||||
*/
|
||||
version?: string;
|
||||
connectionStatus: ConnectionStatus;
|
||||
/**
|
||||
* Last connection-level error message (handshake failure, OAuth start
|
||||
* failure, deep-link automation failure). Surfaced as `data-error-message`
|
||||
* on the header's `connection-status` testid so an automated driver can read
|
||||
* *why* a connect failed without scraping a transient toast.
|
||||
*/
|
||||
connectErrorMessage?: string;
|
||||
initializeResult?: InitializeResult;
|
||||
latencyMs?: number;
|
||||
|
||||
// Primitive lists, log streams, task state — all sourced from the
|
||||
// per-primitive `useManaged*` / `useMessageLog` hooks in the parent.
|
||||
tools: Tool[];
|
||||
/** Tools excluded from `tools/list` for invalid `x-mcp-header` annotations
|
||||
* (SEP-2243); shown in the Tools sidebar with the reason (#1632). */
|
||||
excludedTools?: ExcludedTool[];
|
||||
/**
|
||||
* Entries dropped from a list result as malformed, across every list method.
|
||||
* Each list panel filters for its own and warns about them (#1909).
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
prompts: Prompt[];
|
||||
resources: Resource[];
|
||||
resourceTemplates: ResourceTemplate[];
|
||||
subscriptions: InspectorResourceSubscription[];
|
||||
/**
|
||||
* Modern-era `subscriptions/listen` stream state (#1630). Drives the
|
||||
* Resources screen's stream badge/dot; `active: false` (or omitted) on the
|
||||
* legacy era.
|
||||
*/
|
||||
subscriptionStreamState?: ResourceSubscriptionStreamState;
|
||||
|
||||
// "List changed since last refresh" flags, sourced from the managed-state
|
||||
// layer (#1402). They light the per-screen list-changed indicator. Apps is a
|
||||
// filtered view of tools, so it shares the tools flag.
|
||||
toolsListChanged: boolean;
|
||||
promptsListChanged: boolean;
|
||||
resourcesListChanged: boolean;
|
||||
|
||||
// Last list-load failure per screen, sourced from the same managed-state
|
||||
// layer. Rendered above the sidebar list so a failed load (including the
|
||||
// connect-time one) can't read as "this server has none" (#1953).
|
||||
toolsLoadError?: Error | null;
|
||||
promptsLoadError?: Error | null;
|
||||
/**
|
||||
* The Resources sidebar lists resources AND templates behind a single
|
||||
* Refresh, so App passes whichever of the two loads failed.
|
||||
*/
|
||||
resourcesLoadError?: Error | null;
|
||||
logs: LogEntryData[];
|
||||
tasks: Task[];
|
||||
progressByTaskId?: Record<string, TaskProgress>;
|
||||
protocol: MessageEntry[];
|
||||
/** Negotiated protocol era (SEP §7.8), for the Protocol view's era badge. */
|
||||
protocolEra?: ProtocolEra;
|
||||
network: FetchRequestEntry[];
|
||||
/** Captured stdio stderr (the Console screen). Empty for HTTP servers. (#1621) */
|
||||
stderrLogs: StderrLogEntry[];
|
||||
|
||||
// Per-screen "operation in flight" states (panel-level; optional because
|
||||
// the underlying screens accept them as optional).
|
||||
toolCallState?: ToolCallState;
|
||||
getPromptState?: GetPromptState;
|
||||
readResourceState?: ReadResourceState;
|
||||
|
||||
// Per-screen selection / search / filter state, one object per screen. Owned
|
||||
// by the parent (App) so it persists across tab navigation within a live
|
||||
// session — the screens unmount on tab switch, so screen-local state would be
|
||||
// lost (#1417). Each is paired with an `on{Screen}UiChange` setter below.
|
||||
toolsUi: ToolsUiState;
|
||||
promptsUi: PromptsUiState;
|
||||
resourcesUi: ResourcesUiState;
|
||||
appsUi: AppsUiState;
|
||||
tasksUi: TasksUiState;
|
||||
logsUi: LogsUiState;
|
||||
protocolUi: ProtocolUiState;
|
||||
networkUi: NetworkUiState;
|
||||
consoleUi: ConsoleUiState;
|
||||
|
||||
/** Active inspector tab (lifted to App for OAuth resume). */
|
||||
activeTab: string;
|
||||
onActiveTabChange: (tab: string) => void;
|
||||
|
||||
// Logging level. The MCP `logging/setLevel` request has no echo
|
||||
// notification, so the parent keeps the optimistic current value.
|
||||
currentLogLevel: LoggingLevel;
|
||||
|
||||
// MCP Apps sandbox. The parent's web environment provides the sandbox iframe
|
||||
// URL (undefined when the sandbox controller is unavailable), the per-app
|
||||
// bridge factory, and the renderer handle the parent uses to push tool
|
||||
// input/result into the running app and tear it down.
|
||||
sandboxPath?: string;
|
||||
bridgeFactory: BridgeFactory;
|
||||
appRendererRef: Ref<AppRendererHandle>;
|
||||
|
||||
// Protocol pinning. Optional because pin state isn't persisted yet (#1244
|
||||
// is single-PR; persistence is a separate concern).
|
||||
pinnedProtocolIds?: Set<string>;
|
||||
|
||||
// Theme toggle (lives in the parent so the color scheme can also flow
|
||||
// into other top-level UI later).
|
||||
onToggleTheme: () => void;
|
||||
/** Open install-level client settings (client.json / EMA IdP). */
|
||||
onOpenClientSettings: () => void;
|
||||
|
||||
// Connection lifecycle (dispatched to `useInspectorClient.connect/disconnect`).
|
||||
onToggleConnection: (id: string) => void;
|
||||
onDisconnect: () => void;
|
||||
|
||||
// Server list actions.
|
||||
onServerAdd: () => void;
|
||||
onServerImportConfig: () => void;
|
||||
onServerImportJson: () => void;
|
||||
/** Download the current server list as a canonical `mcp.json` file. */
|
||||
onServerExport: () => void;
|
||||
onConnectionInfo: (id: string) => void;
|
||||
onServerSettings: (id: string) => void;
|
||||
onServerEdit: (id: string) => void;
|
||||
onServerClone: (id: string) => void;
|
||||
onServerRemove: (id: string) => void;
|
||||
/** Persist a new server ordering (drag-and-drop / keyboard reorder). */
|
||||
onServerReorder: (orderedIds: string[]) => void;
|
||||
/** Ids of freshly-added servers to highlight on the list (first is scrolled to). */
|
||||
highlightedServerIds?: string[];
|
||||
/** Clears the freshly-added highlight for a server (on click of its card). */
|
||||
onClearHighlight?: (id: string) => void;
|
||||
|
||||
// Per-primitive actions (route to `inspectorClient` methods / hook refresh).
|
||||
// Each `on{Screen}UiChange` persists that screen's lifted UI state (#1417).
|
||||
/** Whether the connected server advertises task-augmented tool calls. */
|
||||
serverSupportsTaskToolCalls: boolean;
|
||||
onToolsUiChange: (next: ToolsUiState) => void;
|
||||
onCallTool: (
|
||||
name: string,
|
||||
args: Record<string, unknown>,
|
||||
runAsTask?: boolean,
|
||||
) => void;
|
||||
onCancelToolCall?: () => void;
|
||||
onClearToolResult?: () => void;
|
||||
onRefreshTools: () => void;
|
||||
/** Pagination controls for the Tools list (#1721). */
|
||||
toolsPagination: ListPaginationControlsProps;
|
||||
/** Pagination controls for the Prompts list (#1721). */
|
||||
promptsPagination: ListPaginationControlsProps;
|
||||
/** Pagination controls for the Resources list (#1721). */
|
||||
resourcesPagination: ListPaginationControlsProps;
|
||||
/**
|
||||
* Read-on-demand handler for `resource_link` blocks in a tool result.
|
||||
* Returns the linked resource's contents so the result panel can inline them.
|
||||
*/
|
||||
onReadResourceContents?: (uri: string) => Promise<ReadResourceResult>;
|
||||
|
||||
onPromptsUiChange: (next: PromptsUiState) => void;
|
||||
onGetPrompt: (name: string, args: Record<string, string>) => void;
|
||||
onCopyPromptMessages?: () => void;
|
||||
onRefreshPrompts: () => void;
|
||||
|
||||
onResourcesUiChange: (next: ResourcesUiState) => void;
|
||||
onReadResource: (uri: string) => void;
|
||||
onSubscribeResource: (uri: string) => void;
|
||||
onUnsubscribeResource: (uri: string) => void;
|
||||
onRefreshResources: () => void;
|
||||
onCompleteArgument?: (
|
||||
ref:
|
||||
| { type: "ref/resource"; uri: string }
|
||||
| { type: "ref/prompt"; name: string },
|
||||
argumentName: string,
|
||||
argumentValue: string,
|
||||
context: Record<string, string>,
|
||||
) => Promise<string[]>;
|
||||
completionsSupported?: boolean;
|
||||
/**
|
||||
* Whether the connected server advertises the `resources.subscribe`
|
||||
* capability. When false, the Resources screen hides the Subscribe/
|
||||
* Unsubscribe button and the Subscriptions accordion section.
|
||||
*/
|
||||
subscriptionsSupported?: boolean;
|
||||
|
||||
onTasksUiChange: (next: TasksUiState) => void;
|
||||
onCancelTask: (taskId: string) => void;
|
||||
onClearCompletedTasks: () => void;
|
||||
onRefreshTasks: () => void;
|
||||
|
||||
onSetLogLevel: (level: LoggingLevel) => void;
|
||||
/**
|
||||
* Modern-era per-request log level currently stamped, or `null` when opted
|
||||
* out (#1629). On modern connections the Logs sidebar shows a per-request
|
||||
* opt-in control instead of the legacy `logging/setLevel` selector.
|
||||
*/
|
||||
modernLogLevel?: LoggingLevel | null;
|
||||
/** Set (or clear, with `null`) the modern per-request log level. */
|
||||
onSetModernLogLevel?: (level: LoggingLevel | null) => void;
|
||||
onLogsUiChange: (next: LogsUiState) => void;
|
||||
onClearLogs: () => void;
|
||||
onExportLogs: () => void;
|
||||
|
||||
onProtocolUiChange: (next: ProtocolUiState) => void;
|
||||
onClearProtocol: () => void;
|
||||
onExportProtocol: () => void;
|
||||
onClearProtocolSection: (section: "pinned" | "history") => void;
|
||||
onExportProtocolSection: (section: "pinned" | "history") => void;
|
||||
onReplayProtocol: (id: string, overrideParams?: ReplayParamsOverride) => void;
|
||||
onTogglePinProtocol: (id: string) => void;
|
||||
|
||||
onNetworkUiChange: (next: NetworkUiState) => void;
|
||||
onClearNetwork: () => void;
|
||||
onExportNetwork: () => void;
|
||||
|
||||
onConsoleUiChange: (next: ConsoleUiState) => void;
|
||||
onClearConsole: () => void;
|
||||
onExportConsole: () => void;
|
||||
|
||||
onAppsUiChange: (next: AppsUiState) => void;
|
||||
onSelectApp: (name: string) => void;
|
||||
onOpenApp: (name: string, args: Record<string, unknown>) => void;
|
||||
onCloseApp: () => void;
|
||||
onAppError: (err: Error) => void;
|
||||
onRefreshApps: () => void;
|
||||
/** App chrome: active tab, theme/settings entry points, footer, deep link. */
|
||||
shell: ShellProps;
|
||||
/** Live connection state plus the capability facts several screens read. */
|
||||
connection: ConnectionProps;
|
||||
/** The server catalog and every action on it. */
|
||||
servers: ServerListProps;
|
||||
tools: ToolsPanelProps;
|
||||
prompts: PromptsPanelProps;
|
||||
resources: ResourcesPanelProps;
|
||||
apps: AppsPanelProps;
|
||||
tasks: TasksPanelProps;
|
||||
logs: LogsPanelProps;
|
||||
protocol: ProtocolPanelProps;
|
||||
network: NetworkPanelProps;
|
||||
console: ConsolePanelProps;
|
||||
}
|
||||
|
||||
export function InspectorView({
|
||||
deepLink,
|
||||
deepLinkStatus,
|
||||
servers: serversInput,
|
||||
serverListWritable = true,
|
||||
activeServer,
|
||||
erroredServerId,
|
||||
connectedServerId,
|
||||
version,
|
||||
connectionStatus,
|
||||
connectErrorMessage,
|
||||
initializeResult,
|
||||
latencyMs,
|
||||
tools,
|
||||
excludedTools = [],
|
||||
malformedListItems = [],
|
||||
prompts,
|
||||
resources,
|
||||
resourceTemplates,
|
||||
toolsListChanged,
|
||||
promptsListChanged,
|
||||
resourcesListChanged,
|
||||
toolsLoadError,
|
||||
promptsLoadError,
|
||||
resourcesLoadError,
|
||||
subscriptions,
|
||||
subscriptionStreamState,
|
||||
logs,
|
||||
tasks,
|
||||
progressByTaskId,
|
||||
protocol,
|
||||
protocolEra,
|
||||
network,
|
||||
stderrLogs,
|
||||
toolCallState,
|
||||
getPromptState,
|
||||
readResourceState,
|
||||
toolsUi,
|
||||
promptsUi,
|
||||
resourcesUi,
|
||||
appsUi,
|
||||
tasksUi,
|
||||
logsUi,
|
||||
protocolUi,
|
||||
networkUi,
|
||||
consoleUi,
|
||||
currentLogLevel,
|
||||
sandboxPath,
|
||||
bridgeFactory,
|
||||
appRendererRef,
|
||||
pinnedProtocolIds,
|
||||
onToggleTheme,
|
||||
onOpenClientSettings,
|
||||
onToggleConnection,
|
||||
onDisconnect,
|
||||
onServerAdd,
|
||||
onServerImportConfig,
|
||||
onServerImportJson,
|
||||
onServerExport,
|
||||
onConnectionInfo,
|
||||
onServerSettings,
|
||||
onServerEdit,
|
||||
onServerClone,
|
||||
onServerRemove,
|
||||
onServerReorder,
|
||||
highlightedServerIds,
|
||||
onClearHighlight,
|
||||
serverSupportsTaskToolCalls,
|
||||
onToolsUiChange,
|
||||
onCallTool,
|
||||
onCancelToolCall,
|
||||
onClearToolResult,
|
||||
onReadResourceContents,
|
||||
onRefreshTools,
|
||||
toolsPagination,
|
||||
promptsPagination,
|
||||
resourcesPagination,
|
||||
onPromptsUiChange,
|
||||
onGetPrompt,
|
||||
onCopyPromptMessages,
|
||||
onRefreshPrompts,
|
||||
onResourcesUiChange,
|
||||
onReadResource,
|
||||
onSubscribeResource,
|
||||
onUnsubscribeResource,
|
||||
onRefreshResources,
|
||||
onCompleteArgument,
|
||||
completionsSupported,
|
||||
subscriptionsSupported,
|
||||
onTasksUiChange,
|
||||
onCancelTask,
|
||||
onClearCompletedTasks,
|
||||
onRefreshTasks,
|
||||
onSetLogLevel,
|
||||
modernLogLevel = null,
|
||||
onSetModernLogLevel,
|
||||
onLogsUiChange,
|
||||
onClearLogs,
|
||||
onExportLogs,
|
||||
onProtocolUiChange,
|
||||
onClearProtocol,
|
||||
onExportProtocol,
|
||||
onClearProtocolSection,
|
||||
onExportProtocolSection,
|
||||
onReplayProtocol,
|
||||
onTogglePinProtocol,
|
||||
onNetworkUiChange,
|
||||
onClearNetwork,
|
||||
onExportNetwork,
|
||||
onConsoleUiChange,
|
||||
onClearConsole,
|
||||
onExportConsole,
|
||||
onAppsUiChange,
|
||||
onSelectApp,
|
||||
onOpenApp,
|
||||
onCloseApp,
|
||||
onAppError,
|
||||
onRefreshApps,
|
||||
activeTab: activeTabProp,
|
||||
onActiveTabChange,
|
||||
shell,
|
||||
connection,
|
||||
servers: serverList,
|
||||
tools: toolsPanel,
|
||||
prompts: promptsPanel,
|
||||
resources: resourcesPanel,
|
||||
apps: appsPanel,
|
||||
tasks: tasksPanel,
|
||||
logs: logsPanel,
|
||||
protocol: protocolPanel,
|
||||
network: networkPanel,
|
||||
console: consolePanel,
|
||||
}: InspectorViewProps) {
|
||||
// Unpack the bundles back into the locals the body already uses, so the
|
||||
// grouping is confined to the prop boundary.
|
||||
const {
|
||||
deepLink,
|
||||
deepLinkStatus,
|
||||
version,
|
||||
malformedListItems = [],
|
||||
activeTab: activeTabProp,
|
||||
onActiveTabChange,
|
||||
onToggleTheme,
|
||||
onOpenClientSettings,
|
||||
} = shell;
|
||||
const {
|
||||
activeServer,
|
||||
erroredServerId,
|
||||
connectedServerId,
|
||||
connectionStatus,
|
||||
connectErrorMessage,
|
||||
initializeResult,
|
||||
latencyMs,
|
||||
protocolEra,
|
||||
onCompleteArgument,
|
||||
completionsSupported,
|
||||
onToggleConnection,
|
||||
onDisconnect,
|
||||
} = connection;
|
||||
const {
|
||||
servers: serversInput,
|
||||
serverListWritable = true,
|
||||
highlightedServerIds,
|
||||
onClearHighlight,
|
||||
onServerAdd,
|
||||
onServerImportConfig,
|
||||
onServerImportJson,
|
||||
onServerExport,
|
||||
onConnectionInfo,
|
||||
onServerSettings,
|
||||
onServerEdit,
|
||||
onServerClone,
|
||||
onServerRemove,
|
||||
onServerReorder,
|
||||
} = serverList;
|
||||
const {
|
||||
tools,
|
||||
excludedTools = [],
|
||||
toolsListChanged,
|
||||
toolsLoadError,
|
||||
toolsUi,
|
||||
toolCallState,
|
||||
toolsPagination,
|
||||
serverSupportsTaskToolCalls,
|
||||
onToolsUiChange,
|
||||
onCallTool,
|
||||
onCancelToolCall,
|
||||
onClearToolResult,
|
||||
onRefreshTools,
|
||||
onReadResourceContents,
|
||||
} = toolsPanel;
|
||||
const {
|
||||
prompts,
|
||||
promptsListChanged,
|
||||
promptsLoadError,
|
||||
promptsUi,
|
||||
getPromptState,
|
||||
promptsPagination,
|
||||
onPromptsUiChange,
|
||||
onGetPrompt,
|
||||
onCopyPromptMessages,
|
||||
onRefreshPrompts,
|
||||
} = promptsPanel;
|
||||
const {
|
||||
resources,
|
||||
resourceTemplates,
|
||||
subscriptions,
|
||||
subscriptionStreamState,
|
||||
subscriptionsSupported,
|
||||
resourcesListChanged,
|
||||
resourcesLoadError,
|
||||
resourcesUi,
|
||||
readResourceState,
|
||||
resourcesPagination,
|
||||
onResourcesUiChange,
|
||||
onReadResource,
|
||||
onSubscribeResource,
|
||||
onUnsubscribeResource,
|
||||
onRefreshResources,
|
||||
} = resourcesPanel;
|
||||
const {
|
||||
appsUi,
|
||||
sandboxPath,
|
||||
bridgeFactory,
|
||||
appRendererRef,
|
||||
onAppsUiChange,
|
||||
onSelectApp,
|
||||
onOpenApp,
|
||||
onCloseApp,
|
||||
onAppError,
|
||||
onRefreshApps,
|
||||
} = appsPanel;
|
||||
const {
|
||||
tasks,
|
||||
progressByTaskId,
|
||||
tasksUi,
|
||||
onTasksUiChange,
|
||||
onCancelTask,
|
||||
onClearCompletedTasks,
|
||||
onRefreshTasks,
|
||||
} = tasksPanel;
|
||||
const {
|
||||
logs,
|
||||
logsUi,
|
||||
currentLogLevel,
|
||||
modernLogLevel = null,
|
||||
onSetLogLevel,
|
||||
onSetModernLogLevel,
|
||||
onLogsUiChange,
|
||||
onClearLogs,
|
||||
onExportLogs,
|
||||
} = logsPanel;
|
||||
const {
|
||||
protocol,
|
||||
protocolUi,
|
||||
pinnedProtocolIds,
|
||||
onProtocolUiChange,
|
||||
onClearProtocol,
|
||||
onExportProtocol,
|
||||
onClearProtocolSection,
|
||||
onExportProtocolSection,
|
||||
onReplayProtocol,
|
||||
onTogglePinProtocol,
|
||||
} = protocolPanel;
|
||||
const {
|
||||
network,
|
||||
networkUi,
|
||||
onNetworkUiChange,
|
||||
onClearNetwork,
|
||||
onExportNetwork,
|
||||
} = networkPanel;
|
||||
const {
|
||||
stderrLogs,
|
||||
consoleUi,
|
||||
onConsoleUiChange,
|
||||
onClearConsole,
|
||||
onExportConsole,
|
||||
} = consolePanel;
|
||||
// UI-only state. Connection state, primitive lists, and all action
|
||||
// dispatching live in the parent; this component only owns view-local
|
||||
// toggles (sort direction, list compact). Tab selection is lifted (#1417).
|
||||
|
|
|
|||
379
clients/web/src/components/views/InspectorView/types.ts
Normal file
379
clients/web/src/components/views/InspectorView/types.ts
Normal file
|
|
@ -0,0 +1,379 @@
|
|||
// Domain prop bundles for `InspectorView` (#2130).
|
||||
//
|
||||
// The view used to take ~130 flat props, which made App.tsx's JSX a prop wall
|
||||
// rather than a component tree. Each bundle below groups one domain's data,
|
||||
// UI state, and actions into a single object, so the call site reads as the
|
||||
// tree it is and a hook can hand over exactly one bundle.
|
||||
//
|
||||
// Two conventions keep the change reviewable and keep it from rippling
|
||||
// downward:
|
||||
//
|
||||
// - **Field names are unchanged.** Every field carries the exact name it had
|
||||
// as a flat prop, so the bundles are a regrouping, not a rename.
|
||||
// - **The bundles stop here.** `InspectorView` destructures them back into
|
||||
// the same locals and passes them down to the screens as before, so
|
||||
// nothing below the view knows they exist.
|
||||
import type { Ref } from "react";
|
||||
import type {
|
||||
InitializeResult,
|
||||
LoggingLevel,
|
||||
Prompt,
|
||||
ProtocolEra,
|
||||
ReadResourceResult,
|
||||
Resource,
|
||||
ResourceTemplateType as ResourceTemplate,
|
||||
Task,
|
||||
Tool,
|
||||
} from "@modelcontextprotocol/client";
|
||||
import type { MalformedListItem } from "@inspector/core/mcp";
|
||||
import type {
|
||||
ConnectionStatus,
|
||||
ExcludedTool,
|
||||
FetchRequestEntry,
|
||||
InspectorResourceSubscription,
|
||||
MessageEntry,
|
||||
ResourceSubscriptionStreamState,
|
||||
ServerEntry,
|
||||
StderrLogEntry,
|
||||
} from "@inspector/core/mcp/types.js";
|
||||
import type { ReplayParamsOverride } from "../../../lib/protocolReplay";
|
||||
import type { DeepLink, DeepLinkParseStatus } from "../../../utils/deepLink";
|
||||
import type { ListPaginationControlsProps } from "../../elements/ListPaginationControls/ListPaginationControls";
|
||||
import type {
|
||||
AppRendererHandle,
|
||||
BridgeFactory,
|
||||
} from "../../elements/AppRenderer/AppRenderer";
|
||||
import type { LogEntryData } from "../../elements/LogEntry/LogEntry";
|
||||
import type { TaskProgress } from "../../groups/TaskCard/TaskCard";
|
||||
import type {
|
||||
ToolCallState,
|
||||
ToolsUiState,
|
||||
} from "../../screens/ToolsScreen/ToolsScreen";
|
||||
import type { AppsUiState } from "../../screens/AppsScreen/AppsScreen";
|
||||
import type {
|
||||
GetPromptState,
|
||||
PromptsUiState,
|
||||
} from "../../screens/PromptsScreen/PromptsScreen";
|
||||
import type {
|
||||
ReadResourceState,
|
||||
ResourcesUiState,
|
||||
} from "../../screens/ResourcesScreen/ResourcesScreen";
|
||||
import type { LogsUiState } from "../../screens/LoggingScreen/LoggingScreen";
|
||||
import type { TasksUiState } from "../../screens/TasksScreen/TasksScreen";
|
||||
import type { ProtocolUiState } from "../../screens/ProtocolScreen/ProtocolScreen";
|
||||
import type { NetworkUiState } from "../../screens/NetworkScreen/NetworkScreen";
|
||||
import type { ConsoleUiState } from "../../screens/ConsoleScreen/ConsoleScreen";
|
||||
|
||||
/**
|
||||
* App chrome and the few things that belong to no single screen: the active
|
||||
* tab, the theme/client-settings entry points, the footer version, the
|
||||
* deep-link parameters, and the malformed-list-item log that three separate
|
||||
* screens each filter for their own entries.
|
||||
*/
|
||||
export interface ShellProps {
|
||||
/**
|
||||
* Validated deep-link parameters from the page URL. When present and
|
||||
* `openApp` is set, the parent switches to the Apps tab and pre-selects that
|
||||
* app (with `appArgs` as the form values) once the connection is up and the
|
||||
* app list contains it. The connect itself is driven by the parent.
|
||||
*/
|
||||
deepLink?: DeepLink;
|
||||
/**
|
||||
* Outcome of parsing the initial-URL deep link, surfaced as `data-deeplink`
|
||||
* on the `connection-status` testid. Distinguishes "no deep link" from
|
||||
* "rejected" (token mismatch / bad serverUrl) — both leave `data-status`
|
||||
* idle, so an automated driver otherwise cannot tell them apart.
|
||||
*/
|
||||
deepLinkStatus?: DeepLinkParseStatus;
|
||||
/**
|
||||
* The Inspector build version (root `package.json`), shown at the left of the
|
||||
* footer row (#1682). Absent on a legacy backend that omits it — the version
|
||||
* label then renders nothing.
|
||||
*/
|
||||
version?: string;
|
||||
/**
|
||||
* Entries dropped from a list result as malformed, across every list method.
|
||||
* Each list panel filters for its own and warns about them (#1909). It spans
|
||||
* three screens, so it has no single domain owner.
|
||||
*/
|
||||
malformedListItems?: MalformedListItem[];
|
||||
/** Active inspector tab (lifted to App for OAuth resume). */
|
||||
activeTab: string;
|
||||
onActiveTabChange: (tab: string) => void;
|
||||
/**
|
||||
* Theme toggle (lives in the parent so the color scheme can also flow into
|
||||
* other top-level UI later).
|
||||
*/
|
||||
onToggleTheme: () => void;
|
||||
/** Open install-level client settings (client.json / EMA IdP). */
|
||||
onOpenClientSettings: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* Live connection state and the capability facts more than one screen reads.
|
||||
* Driven by the parent via `useInspectorClient`.
|
||||
*/
|
||||
export interface ConnectionProps {
|
||||
activeServer?: string;
|
||||
/**
|
||||
* Id of the server whose last connection attempt failed (#1621). Its card in
|
||||
* the Servers screen draws a red border until another server is connected or
|
||||
* a new connection is attempted. Independent of `activeServer`, which the
|
||||
* parent clears on the failure's `disconnect` event.
|
||||
*
|
||||
* It is also the sole signal that opens the monitoring sidebar onto the
|
||||
* failure's diagnostics (#2108). That used to be gated on the `"error"`
|
||||
* connection status, which an OAuth failure never reaches — the parent drives
|
||||
* that leg and tears the client down itself, settling the session at
|
||||
* `"disconnected"`. So the parent must set this for *every* connect-attempt
|
||||
* failure, auth legs included, and clear it as each new attempt starts.
|
||||
*/
|
||||
erroredServerId?: string;
|
||||
/**
|
||||
* Id of the server that just connected successfully (#1682). Its card draws
|
||||
* the green highlight border and scrolls into view once the monitoring
|
||||
* sidebar has opened — the success mirror of `erroredServerId`.
|
||||
*/
|
||||
connectedServerId?: string;
|
||||
connectionStatus: ConnectionStatus;
|
||||
/**
|
||||
* Last connection-level error message (handshake failure, OAuth start
|
||||
* failure, deep-link automation failure). Surfaced as `data-error-message`
|
||||
* on the header's `connection-status` testid so an automated driver can read
|
||||
* *why* a connect failed without scraping a transient toast.
|
||||
*/
|
||||
connectErrorMessage?: string;
|
||||
initializeResult?: InitializeResult;
|
||||
latencyMs?: number;
|
||||
/** Negotiated protocol era (SEP §7.8), for the Protocol view's era badge. */
|
||||
protocolEra?: ProtocolEra;
|
||||
/**
|
||||
* Argument completion, shared by the Prompts and Resources screens — hence
|
||||
* here rather than in either one.
|
||||
*/
|
||||
onCompleteArgument?: (
|
||||
ref:
|
||||
| { type: "ref/resource"; uri: string }
|
||||
| { type: "ref/prompt"; name: string },
|
||||
argumentName: string,
|
||||
argumentValue: string,
|
||||
context: Record<string, string>,
|
||||
) => Promise<string[]>;
|
||||
completionsSupported?: boolean;
|
||||
// Connection lifecycle (dispatched to `useInspectorClient.connect/disconnect`).
|
||||
onToggleConnection: (id: string) => void;
|
||||
onDisconnect: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The server catalog and every action on it. Static config only — the runtime
|
||||
* connection state comes from {@link ConnectionProps} and is merged into each
|
||||
* card by the view.
|
||||
*/
|
||||
export interface ServerListProps {
|
||||
servers: ServerEntry[];
|
||||
/**
|
||||
* Whether the server list is writable (catalog) or read-only (a `--config`
|
||||
* session file / ad-hoc launch). When false, the Servers screen hides all
|
||||
* catalog mutation controls. Defaults to true.
|
||||
*/
|
||||
serverListWritable?: boolean;
|
||||
/** Ids of freshly-added servers to highlight on the list (first is scrolled to). */
|
||||
highlightedServerIds?: string[];
|
||||
/** Clears the freshly-added highlight for a server (on click of its card). */
|
||||
onClearHighlight?: (id: string) => void;
|
||||
onServerAdd: () => void;
|
||||
onServerImportConfig: () => void;
|
||||
onServerImportJson: () => void;
|
||||
/** Download the current server list as a canonical `mcp.json` file. */
|
||||
onServerExport: () => void;
|
||||
onConnectionInfo: (id: string) => void;
|
||||
onServerSettings: (id: string) => void;
|
||||
onServerEdit: (id: string) => void;
|
||||
onServerClone: (id: string) => void;
|
||||
onServerRemove: (id: string) => void;
|
||||
/** Persist a new server ordering (drag-and-drop / keyboard reorder). */
|
||||
onServerReorder: (orderedIds: string[]) => void;
|
||||
}
|
||||
|
||||
/** The Tools screen: its list, lifted UI state, and actions. */
|
||||
export interface ToolsPanelProps {
|
||||
tools: Tool[];
|
||||
/**
|
||||
* Tools excluded from `tools/list` for invalid `x-mcp-header` annotations
|
||||
* (SEP-2243); shown in the Tools sidebar with the reason (#1632).
|
||||
*/
|
||||
excludedTools?: ExcludedTool[];
|
||||
/** "List changed since last refresh", from the managed-state layer (#1402). */
|
||||
toolsListChanged: boolean;
|
||||
/**
|
||||
* Last list-load failure, rendered above the sidebar list so a failed load
|
||||
* (including the connect-time one) can't read as "this server has none"
|
||||
* (#1953).
|
||||
*/
|
||||
toolsLoadError?: Error | null;
|
||||
/**
|
||||
* Lifted selection / search / filter state. Owned by the parent so it
|
||||
* persists across tab navigation within a live session — the screens unmount
|
||||
* on tab switch, so screen-local state would be lost (#1417).
|
||||
*/
|
||||
toolsUi: ToolsUiState;
|
||||
/** Panel-level "operation in flight" state. */
|
||||
toolCallState?: ToolCallState;
|
||||
/** Pagination controls for the Tools list (#1721). */
|
||||
toolsPagination: ListPaginationControlsProps;
|
||||
/** Whether the connected server advertises task-augmented tool calls. */
|
||||
serverSupportsTaskToolCalls: boolean;
|
||||
onToolsUiChange: (next: ToolsUiState) => void;
|
||||
onCallTool: (
|
||||
name: string,
|
||||
args: Record<string, unknown>,
|
||||
runAsTask?: boolean,
|
||||
) => void;
|
||||
onCancelToolCall?: () => void;
|
||||
onClearToolResult?: () => void;
|
||||
onRefreshTools: () => void;
|
||||
/**
|
||||
* Read-on-demand handler for `resource_link` blocks in a tool result.
|
||||
* Returns the linked resource's contents so the result panel can inline them.
|
||||
*/
|
||||
onReadResourceContents?: (uri: string) => Promise<ReadResourceResult>;
|
||||
}
|
||||
|
||||
/** The Prompts screen: its list, lifted UI state, and actions. */
|
||||
export interface PromptsPanelProps {
|
||||
prompts: Prompt[];
|
||||
promptsListChanged: boolean;
|
||||
promptsLoadError?: Error | null;
|
||||
promptsUi: PromptsUiState;
|
||||
getPromptState?: GetPromptState;
|
||||
/** Pagination controls for the Prompts list (#1721). */
|
||||
promptsPagination: ListPaginationControlsProps;
|
||||
onPromptsUiChange: (next: PromptsUiState) => void;
|
||||
onGetPrompt: (name: string, args: Record<string, string>) => void;
|
||||
onCopyPromptMessages?: () => void;
|
||||
onRefreshPrompts: () => void;
|
||||
}
|
||||
|
||||
/** The Resources screen: resources, templates, subscriptions, and actions. */
|
||||
export interface ResourcesPanelProps {
|
||||
resources: Resource[];
|
||||
resourceTemplates: ResourceTemplate[];
|
||||
subscriptions: InspectorResourceSubscription[];
|
||||
/**
|
||||
* Modern-era `subscriptions/listen` stream state (#1630). Drives the
|
||||
* Resources screen's stream badge/dot; `active: false` (or omitted) on the
|
||||
* legacy era.
|
||||
*/
|
||||
subscriptionStreamState?: ResourceSubscriptionStreamState;
|
||||
/**
|
||||
* Whether the connected server advertises the `resources.subscribe`
|
||||
* capability. When false, the Resources screen hides the Subscribe/
|
||||
* Unsubscribe button and the Subscriptions accordion section.
|
||||
*/
|
||||
subscriptionsSupported?: boolean;
|
||||
resourcesListChanged: boolean;
|
||||
/**
|
||||
* The Resources sidebar lists resources AND templates behind a single
|
||||
* Refresh, so App passes whichever of the two loads failed.
|
||||
*/
|
||||
resourcesLoadError?: Error | null;
|
||||
resourcesUi: ResourcesUiState;
|
||||
readResourceState?: ReadResourceState;
|
||||
/** Pagination controls for the Resources list (#1721). */
|
||||
resourcesPagination: ListPaginationControlsProps;
|
||||
onResourcesUiChange: (next: ResourcesUiState) => void;
|
||||
onReadResource: (uri: string) => void;
|
||||
onSubscribeResource: (uri: string) => void;
|
||||
onUnsubscribeResource: (uri: string) => void;
|
||||
onRefreshResources: () => void;
|
||||
}
|
||||
|
||||
/**
|
||||
* The Apps screen and its sandbox wiring. The parent's web environment
|
||||
* provides the sandbox iframe URL (undefined when the sandbox controller is
|
||||
* unavailable), the per-app bridge factory, and the renderer handle the parent
|
||||
* uses to push tool input/result into the running app and tear it down.
|
||||
*/
|
||||
export interface AppsPanelProps {
|
||||
appsUi: AppsUiState;
|
||||
sandboxPath?: string;
|
||||
bridgeFactory: BridgeFactory;
|
||||
appRendererRef: Ref<AppRendererHandle>;
|
||||
onAppsUiChange: (next: AppsUiState) => void;
|
||||
onSelectApp: (name: string) => void;
|
||||
onOpenApp: (name: string, args: Record<string, unknown>) => void;
|
||||
onCloseApp: () => void;
|
||||
onAppError: (err: Error) => void;
|
||||
onRefreshApps: () => void;
|
||||
}
|
||||
|
||||
/** The Tasks monitor: the task list, its progress map, and actions. */
|
||||
export interface TasksPanelProps {
|
||||
tasks: Task[];
|
||||
progressByTaskId?: Record<string, TaskProgress>;
|
||||
tasksUi: TasksUiState;
|
||||
onTasksUiChange: (next: TasksUiState) => void;
|
||||
onCancelTask: (taskId: string) => void;
|
||||
onClearCompletedTasks: () => void;
|
||||
onRefreshTasks: () => void;
|
||||
}
|
||||
|
||||
/** The Logs monitor, including both eras' log-level controls. */
|
||||
export interface LogsPanelProps {
|
||||
logs: LogEntryData[];
|
||||
logsUi: LogsUiState;
|
||||
/**
|
||||
* The MCP `logging/setLevel` request has no echo notification, so the parent
|
||||
* keeps the optimistic current value.
|
||||
*/
|
||||
currentLogLevel: LoggingLevel;
|
||||
/**
|
||||
* Modern-era per-request log level currently stamped, or `null` when opted
|
||||
* out (#1629). On modern connections the Logs sidebar shows a per-request
|
||||
* opt-in control instead of the legacy `logging/setLevel` selector.
|
||||
*/
|
||||
modernLogLevel?: LoggingLevel | null;
|
||||
onSetLogLevel: (level: LoggingLevel) => void;
|
||||
/** Set (or clear, with `null`) the modern per-request log level. */
|
||||
onSetModernLogLevel?: (level: LoggingLevel | null) => void;
|
||||
onLogsUiChange: (next: LogsUiState) => void;
|
||||
onClearLogs: () => void;
|
||||
onExportLogs: () => void;
|
||||
}
|
||||
|
||||
/** The Protocol monitor: the message log, pinning, replay, and exports. */
|
||||
export interface ProtocolPanelProps {
|
||||
protocol: MessageEntry[];
|
||||
protocolUi: ProtocolUiState;
|
||||
/**
|
||||
* Pinned message ids. Optional because pin state isn't persisted yet (#1244
|
||||
* is single-PR; persistence is a separate concern).
|
||||
*/
|
||||
pinnedProtocolIds?: Set<string>;
|
||||
onProtocolUiChange: (next: ProtocolUiState) => void;
|
||||
onClearProtocol: () => void;
|
||||
onExportProtocol: () => void;
|
||||
onClearProtocolSection: (section: "pinned" | "history") => void;
|
||||
onExportProtocolSection: (section: "pinned" | "history") => void;
|
||||
onReplayProtocol: (id: string, overrideParams?: ReplayParamsOverride) => void;
|
||||
onTogglePinProtocol: (id: string) => void;
|
||||
}
|
||||
|
||||
/** The Network monitor: captured HTTP traffic and its actions. */
|
||||
export interface NetworkPanelProps {
|
||||
network: FetchRequestEntry[];
|
||||
networkUi: NetworkUiState;
|
||||
onNetworkUiChange: (next: NetworkUiState) => void;
|
||||
onClearNetwork: () => void;
|
||||
onExportNetwork: () => void;
|
||||
}
|
||||
|
||||
/** The Console monitor: captured stdio stderr. Empty for HTTP servers (#1621). */
|
||||
export interface ConsolePanelProps {
|
||||
stderrLogs: StderrLogEntry[];
|
||||
consoleUi: ConsoleUiState;
|
||||
onConsoleUiChange: (next: ConsoleUiState) => void;
|
||||
onClearConsole: () => void;
|
||||
onExportConsole: () => void;
|
||||
}
|
||||
Loading…
Reference in a new issue