Add a specification/v2_auth.md landing page summarizing the four auth specs (EMA / XAA, Hardening, Mid-session, Smoke testing) with a section submenu, mirroring the V2 UX index pattern. Add a V2 Auth link (after V2 UX) and a V2 New Spec Impact link to every spec page's top menu so the top-level nav is uniform, and restructure the four v2_auth_*.md pages onto the shared two-tier menu (top menu + auth submenu). Docs only. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01S3fTN8H3R8YV4yUGvZjYnX
46 KiB
Inspector V2 UX - Storybook Component Plan
Brief | V1 Problems | V2 Scope | V2 Tech Stack | V2 UX | V2 Auth | V2 New Spec Impact
Overview | Features | Handlers | Screenshots | Components | Interfaces | Storybook
This document defines every presentational component to be built in Storybook using Mantine. Components are organized bottom-up: elements first, then groups, then screens. Each component receives all data and callbacks via props — no store access.
Table of Contents
- Conventions
- Tier 1 - Elements
- Tier 2 - Groups
- Tier 3 - Screens
- Tier 4 - Page Views
- Build Order
- Storybook Configuration
Conventions
File Structure
src/components/
elements/
StatusIndicator/
StatusIndicator.tsx
StatusIndicator.stories.tsx
...
groups/
ServerCard/
ServerCard.tsx
ServerCard.stories.tsx
...
screens/
ToolsScreen/
ToolsScreen.tsx
ToolsScreen.stories.tsx
...
views/
AppShell/
AppShell.tsx
AppShell.stories.tsx
...
Props Pattern
Every component exports a Props interface. Callbacks use the onVerb convention. Boolean flags use canVerb or isState naming.
// Example
export interface StatusIndicatorProps {
status: 'connected' | 'connecting' | 'disconnected' | 'failed';
latencyMs?: number;
retryCount?: number;
}
Story Pattern
Each component has at minimum a Default story plus stories for each meaningful state variation.
const meta: Meta<typeof StatusIndicator> = {
component: StatusIndicator,
decorators: [MantineDecorator],
};
export const Connected: Story = { args: { status: 'connected', latencyMs: 23 } };
export const Failed: Story = { args: { status: 'failed', retryCount: 3 } };
Tier 1 - Elements
Small, single-purpose components. Most map directly to a Mantine component with specific props/styling.
1.1 StatusIndicator
Connection status dot with label.
| Prop | Type | Description |
|---|---|---|
status |
'connected' | 'connecting' | 'disconnected' | 'failed' |
Current state |
latencyMs |
number? |
Ping latency (shown when connected) |
retryCount |
number? |
Retry attempts (shown when failed) |
Mantine: Custom component using Box for the dot + Text. Pulsing animation via CSS for connecting.
Stories: Connected, Connecting, Disconnected, Failed, FailedWithRetries
1.2 AnnotationBadge
Tag-style badge for tool/resource annotations.
| Prop | Type | Description |
|---|---|---|
label |
string |
Badge text (e.g., "user", "read-only", "destructive") |
variant |
'audience' | 'readOnly' | 'destructive' | 'longRun' | 'priority' | 'default' |
Visual style |
Mantine: Badge with variant and color mapped from annotation type. Destructive = red outline, long-run = yellow, read-only = dark filled, audience = gray outline, priority = orange/yellow/green by level.
Stories: Audience, ReadOnly, Destructive, LongRun, PriorityHigh, PriorityMedium, PriorityLow, Custom
1.3 CapabilityItem
Single capability row with check/cross icon and label.
| Prop | Type | Description |
|---|---|---|
name |
string |
Capability name |
supported |
boolean |
Whether capability is available |
count |
number? |
Optional count (e.g., "Tools (4)") |
Mantine: Group with ThemeIcon (check or X) + Text.
Stories: Supported, SupportedWithCount, NotSupported
1.4 LogEntry
Single log line with timestamp, level badge, and message.
| Prop | Type | Description |
|---|---|---|
timestamp |
string |
Formatted time (e.g., "14:32:01") |
level |
'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency' |
RFC 5424 level |
message |
string |
Log message text |
logger |
string? |
Logger name |
Mantine: Group with Text (timestamp) + Badge (level, color-coded) + Text (message). Level colors: debug=gray, info=blue, notice=teal, warning=yellow, error=red, critical=red+filled, alert=red+bold, emergency=red+filled+bold.
Stories: Debug, Info, Notice, Warning, Error, Critical, Alert, Emergency, WithLogger
1.5 ProgressDisplay
Progress bar with percentage and optional step description.
| Prop | Type | Description |
|---|---|---|
progress |
number |
0-100 percentage |
description |
string? |
Current step text |
elapsed |
string? |
Elapsed time display |
Mantine: Progress bar + Text for description and percentage.
Stories: ZeroPercent, HalfComplete, NearComplete, Complete, WithDescription, WithElapsed
1.6 ContentViewer
Renders text, JSON, image, or audio content with syntax highlighting.
| Prop | Type | Description |
|---|---|---|
type |
'text' | 'json' | 'image' | 'audio' |
Content type |
content |
string |
The content (text, JSON string, base64, or URL) |
mimeType |
string? |
MIME type for images/audio |
onCopy |
() => void |
Copy callback |
Mantine: Code block for JSON/text (with language prop), Image for images, native <audio> for audio. Wrapped in Paper with light background.
Stories: PlainText, JsonContent, ImagePreview, AudioPlayer, LongContent
1.7 ListChangedIndicator
Yellow dot + "List updated" text + Refresh button.
| Prop | Type | Description |
|---|---|---|
visible |
boolean |
Whether to show the indicator |
onRefresh |
() => void |
Refresh callback |
Mantine: Group with animated Box (yellow dot) + Text + Button with refresh icon.
Stories: Visible, Hidden
1.8 CopyButton
Icon button that copies text and shows brief confirmation.
| Prop | Type | Description |
|---|---|---|
value |
string |
Text to copy |
Mantine: CopyButton (built-in) with ActionIcon and Tooltip.
Stories: Default, LongValue
1.9 ConnectionToggle
Switch control for connecting/disconnecting a server.
| Prop | Type | Description |
|---|---|---|
checked |
boolean |
On/off state |
loading |
boolean |
Show loading state during connection |
disabled |
boolean |
Disable interaction |
onChange |
(checked: boolean) => void |
Toggle callback |
Mantine: Switch with size="lg".
Stories: Connected, Disconnected, Loading, Disabled
1.10 TransportBadge
Shows transport type (STDIO, HTTP) as a styled badge.
| Prop | Type | Description |
|---|---|---|
transport |
'stdio' | 'http' |
Transport type |
Mantine: Badge with variant="outline".
Stories: Stdio, Http
1.11 InlineError
Expandable error display with retry count and doc links.
| Prop | Type | Description |
|---|---|---|
message |
string |
Short error message |
details |
string? |
Full error text (shown on expand) |
retryCount |
number? |
Current retry attempt |
maxRetries |
number? |
Max retry attempts |
onShowMore |
() => void |
Expand callback |
docLink |
string? |
URL to troubleshooting guide |
Mantine: Alert with color="red", Spoiler for expandable details.
Stories: ShortError, LongErrorCollapsed, LongErrorExpanded, WithRetryCount, WithDocLink
1.12 MessageBubble
Single chat message with role label and content (text, image, or audio).
| Prop | Type | Description |
|---|---|---|
index |
number |
Message index |
role |
'user' | 'assistant' |
Message role |
content |
string |
Text content |
imageContent |
{ data: string; mimeType: string }? |
Optional image |
audioContent |
{ data: string; mimeType: string }? |
Optional audio |
Mantine: Paper with subtle background, Text for role label, content display.
Stories: UserText, AssistantText, UserWithImage, UserWithAudio, LongMessage
Tier 2 - Groups
Composed from elements. Represent distinct functional units within a screen.
2.1 ServerCard
Server connection card with status, transport, command, and action buttons.
| Prop | Type | Description |
|---|---|---|
name |
string |
Server name |
version |
string? |
Server version |
transport |
'stdio' | 'http' |
Transport type |
command |
string |
Command or URL |
status |
'connected' | 'connecting' | 'disconnected' | 'failed' |
Connection state |
retryCount |
number? |
Failed retry count |
error |
{ message: string; details?: string }? |
Error info |
onToggleConnection |
(connect: boolean) => void |
Connect/disconnect |
onCopyCommand |
() => void |
Copy command |
onServerInfo |
() => void |
View server info |
onSettings |
() => void |
Open settings |
onEdit |
() => void |
Edit server |
onClone |
() => void |
Clone server |
onRemove |
() => void |
Remove server |
Mantine: Card with Card.Section dividers. Uses StatusIndicator, TransportBadge, ConnectionToggle, CopyButton, InlineError elements.
Stories: Connected, Disconnected, Connecting, Failed, FailedWithError, HttpDirect, LongCommand
2.2 AddServerMenu
Dropdown button with options: Add manually, Import config, Import server.json.
| Prop | Type | Description |
|---|---|---|
onAddManually |
() => void |
Add manually callback |
onImportConfig |
() => void |
Import config callback |
onImportServerJson |
() => void |
Import server.json callback |
Mantine: Menu with Button trigger ("+ Add Server" with chevron).
Stories: Default (closed), Open
2.3 ToolListItem
Single tool entry in the sidebar list with name and annotation badges.
| Prop | Type | Description |
|---|---|---|
name |
string |
Tool name |
annotations |
AnnotationBadge[] |
Array of annotation badges |
selected |
boolean |
Whether this tool is currently selected |
onClick |
() => void |
Selection callback |
Mantine: NavLink or UnstyledButton with Group of AnnotationBadge children. Active state uses background highlight.
Stories: Default, Selected, WithAnnotations, MultipleAnnotations, LongName
2.3a AppListItem
Single MCP App entry in the sidebar list. Mirrors ToolListItem but renders the app's tool.icons (real MCP Tool.icons) when present and surfaces a chevron affordance.
| Prop | Type | Description |
|---|---|---|
tool |
Tool |
App tool (real MCP Tool type) |
selected |
boolean |
Whether this app is currently selected |
onClick |
() => void |
Selection callback |
Mantine: UnstyledButton with Group (Icon + label/description + ChevronRight). Reuses the same active-state background highlight pattern as ToolListItem.
Stories: Default, Selected, WithIcon, NoDescription, LongName
Alternative: fold icon support into
ToolListItemand reuse it on the Apps screen. Decide during implementation — see issue "Tool icon support on tool list items".
2.4 ToolDetailPanel
Tool name, description, annotations display, and generated parameter form.
| Prop | Type | Description |
|---|---|---|
name |
string |
Tool name |
description |
string? |
Tool description |
annotations |
object? |
Annotations object (audience, readOnly, destructive, etc.) |
schema |
object |
JSON Schema for tool input |
formValues |
Record<string, unknown> |
Current form values |
isExecuting |
boolean |
Whether tool is currently executing |
progress |
{ percent: number; description?: string }? |
Execution progress |
onFormChange |
(values: Record<string, unknown>) => void |
Form value change |
onExecute |
() => void |
Execute tool callback |
onCancel |
() => void |
Cancel execution callback |
Mantine: Stack with Title, Text, AnnotationBadge group, SchemaForm (see 2.12), Button for execute, ProgressDisplay.
Stories: SimpleStringParam, MultipleParams, WithAnnotations, Executing, WithProgress, ComplexSchema
2.5 ResultPanel
Displays tool execution results with copy/clear actions.
| Prop | Type | Description |
|---|---|---|
content |
Array<{ type: string; text?: string; data?: string; mimeType?: string }> |
Result content items |
onCopy |
() => void |
Copy result |
onClear |
() => void |
Clear result |
Mantine: Paper with ContentViewer for each content item, Group with action buttons.
Stories: TextResult, JsonResult, ImageResult, AudioResult, MixedContent, Empty
2.5a AppDetailPanel
App name, description, optional icon, and generated App Input form. Mirrors ToolDetailPanel but the action button is Open App (no annotations, no progress display, no cancel — execution is delegated to the embedded app).
| Prop | Type | Description |
|---|---|---|
tool |
Tool |
App tool (real MCP Tool type) |
formValues |
Record<string, unknown> |
Current form values |
isOpening |
boolean |
Whether the app is currently being launched |
onFormChange |
(values: Record<string, unknown>) => void |
Form value change |
onOpenApp |
() => void |
Open App callback |
Mantine: Stack with icon + Title (tool.title ?? tool.name), description Text, SchemaForm (see 2.12), full-width Button with IconPlayerPlay for open. Disabled while isOpening or while the form has validation errors.
Stories: NoFields, SimpleStringParam, MultipleParams, WithIcon, Opening, ComplexSchema
2.5b AppRenderer
Embeds an MCP App in a sandboxed iframe and exposes an imperative ref so the wiring layer can drive the bridge. The component is a thin shell — it owns the iframe element and the AppBridge lifecycle but does not know about the active MCP Client. The wiring layer constructs the bridge (real MCP Client + Implementation + McpUiHostCapabilities) and passes it in via the bridgeFactory callback when the iframe mounts.
| Prop | Type | Description |
|---|---|---|
sandboxPath |
string |
URL/path of the sandbox proxy iframe |
tool |
Tool |
The selected app tool (for resource URI lookup) |
bridgeFactory |
(iframe: HTMLIFrameElement) => AppBridge |
Wiring layer creates the bridge from the active MCP client |
onError |
(err: Error) => void |
Error notification callback |
Imperative ref (AppRendererHandle):
| Method | Description |
|---|---|
sendToolInput(args) |
Push complete tool arguments to the app |
sendToolResult(result) |
Push the CallToolResult from the server |
sendToolCancelled(reason) |
Notify the app that execution was cancelled |
teardown() |
Request graceful shutdown before unmounting |
Mantine: Box with iframe element. No Mantine controls — display logic only. The renderer treats the bridge handle and iframe element as runtime state, not props, but exposes the bridge actions through useImperativeHandle.
Stories: Loading, Loaded (with mock bridge), Error, Maximized
2.6 ResourceListItem
Single resource entry with name and annotation badges.
| Prop | Type | Description |
|---|---|---|
name |
string |
Resource display name |
uri |
string |
Resource URI |
annotations |
object? |
Audience and priority annotations |
selected |
boolean |
Selection state |
onClick |
() => void |
Selection callback |
Mantine: Similar to ToolListItem with AnnotationBadge.
Stories: Default, Selected, WithAnnotations, WithHighPriority
2.7 ResourcePreviewPanel
Resource content display with URI, MIME type, annotations, and subscribe/copy actions.
| Prop | Type | Description |
|---|---|---|
uri |
string |
Resource URI |
mimeType |
string |
MIME type |
annotations |
object? |
Annotations |
content |
string |
Resource content |
lastUpdated |
string? |
Last update timestamp |
isSubscribed |
boolean |
Subscription state |
onCopy |
() => void |
Copy content |
onSubscribe |
() => void |
Subscribe callback |
onUnsubscribe |
() => void |
Unsubscribe callback |
Mantine: Stack with metadata Text items, ContentViewer, action Button group.
Stories: JsonResource, TextResource, ImageResource, Subscribed, NotSubscribed, WithAnnotations
2.8 ResourceTemplateInput
Template with inline variable inputs and Go button.
| Prop | Type | Description |
|---|---|---|
template |
string |
URI template (e.g., "user/{id}") |
variables |
Record<string, string> |
Current variable values |
onVariableChange |
(name: string, value: string) => void |
Variable change |
onSubmit |
() => void |
Navigate/fetch callback |
Mantine: Group with Text for template prefix, TextInput for each variable, Button for Go.
Stories: SingleVariable, MultipleVariables, FilledIn
2.9 PromptArgumentsForm
Prompt selection dropdown, description, argument form, and Get Prompt button.
| Prop | Type | Description |
|---|---|---|
prompts |
Array<{ name: string; description?: string }> |
Available prompts |
selectedPrompt |
string? |
Currently selected prompt name |
arguments |
Array<{ name: string; required: boolean; description?: string }> |
Prompt arguments |
argumentValues |
Record<string, string> |
Current argument values |
onSelectPrompt |
(name: string) => void |
Prompt selection |
onArgumentChange |
(name: string, value: string) => void |
Argument value change |
onGetPrompt |
() => void |
Execute prompt |
Mantine: Select for prompt dropdown, TextInput for each argument with required indicator, Button for Get Prompt. Description shown as Text with dimmed color.
Stories: NoSelection, Selected, WithRequiredArgs, AllFilled, ManyArguments
2.10 PromptMessagesDisplay
Rendered prompt result showing role-labeled messages.
| Prop | Type | Description |
|---|---|---|
messages |
Array<{ role: string; content: string; imageContent?: object; audioContent?: object }> |
Prompt messages |
onCopy |
() => void |
Copy messages |
Mantine: Stack of MessageBubble elements, Button for copy.
Stories: SingleMessage, Conversation, WithImage, WithAudio, LongConversation
2.11 LogControls
Log level selector, text filter, level checkboxes, clear/export buttons.
| Prop | Type | Description |
|---|---|---|
currentLevel |
string |
Active log level |
filterText |
string |
Text filter value |
visibleLevels |
Record<string, boolean> |
Which levels are checked |
onSetLevel |
(level: string) => void |
Set log level callback |
onFilterChange |
(text: string) => void |
Filter text change |
onToggleLevel |
(level: string, visible: boolean) => void |
Toggle level visibility |
onClear |
() => void |
Clear logs |
onExport |
() => void |
Export logs |
Mantine: Select for level + Button "Set Level", TextInput for filter, Checkbox.Group for levels (each color-coded), Group with action buttons.
Stories: AllLevelsVisible, FilteredLevels, WithFilterText, DebugLevel, ErrorLevel
2.12 SchemaForm
Dynamic form generated from JSON Schema. Core reusable component for tool params, elicitation forms, and prompt arguments.
| Prop | Type | Description |
|---|---|---|
schema |
object |
JSON Schema definition |
values |
Record<string, unknown> |
Current form values |
onChange |
(values: Record<string, unknown>) => void |
Value change callback |
disabled |
boolean? |
Disable all fields |
Mantine: Maps schema types to Mantine inputs: TextInput (string), NumberInput (number/integer), Checkbox (boolean), Select (enum/oneOf), MultiSelect (array+anyOf), JsonInput (complex fallback). Uses Stack layout. Required fields marked with withAsterisk.
Stories: StringFields, NumberFields, BooleanFields, EnumDropdown, TitledEnum, MultiSelectEnum, MixedTypes, NestedObject, ArrayField, ComplexFallback, AllRequired, WithDefaults, WithDescriptions
2.13 TaskCard
Single task card with status, progress, method info, and action buttons.
| Prop | Type | Description |
|---|---|---|
taskId |
string |
Task identifier |
status |
'waiting' | 'running' | 'completed' | 'failed' | 'cancelled' |
Task state |
method |
string |
MCP method (e.g., "tools/call") |
target |
string? |
Tool name or resource URI |
progress |
number? |
0-100 progress percentage |
progressDescription |
string? |
Current step text |
startedAt |
string? |
Start timestamp |
completedAt |
string? |
Completion timestamp |
elapsed |
string? |
Elapsed or duration string |
error |
string? |
Error message (for failed tasks) |
onViewDetails |
() => void |
View details callback |
onViewResult |
() => void |
View result callback |
onCancel |
() => void |
Cancel task callback |
onDismiss |
() => void |
Dismiss from list |
Mantine: Card with Group for header (task ID + status Badge + progress), Text for metadata, Group for action buttons. Status badge colors: waiting=gray, running=blue, completed=green, failed=red, cancelled=yellow.
Stories: Running, RunningWithProgress, Waiting, Completed, Failed, FailedWithError, Cancelled
2.14 HistoryEntry
Single request history entry with expand/collapse, replay, and pin.
| Prop | Type | Description |
|---|---|---|
timestamp |
string |
Request timestamp |
method |
string |
MCP method |
target |
string? |
Tool/resource/prompt name |
status |
'success' | 'error' |
Result status |
durationMs |
number |
Response time |
parameters |
object? |
Request parameters |
response |
object? |
Response data |
children |
HistoryEntry[]? |
Nested child requests (sampling/elicitation) |
isPinned |
boolean |
Pin state |
isExpanded |
boolean |
Expanded state |
onToggleExpand |
() => void |
Expand/collapse |
onReplay |
() => void |
Replay request |
onTogglePin |
() => void |
Pin/unpin |
Mantine: Card with collapsible Collapse section. Header Group with timestamp, method Badge, target, status Badge (green/red), duration. Children rendered indented with connector lines via CSS.
Stories: SuccessCollapsed, SuccessExpanded, Error, WithChildren, Pinned, PinnedWithLabel, DeepNesting
2.15 SamplingRequestPanel
Displays a sampling request with messages, model preferences, parameters, and response input.
| Prop | Type | Description |
|---|---|---|
messages |
Array<{ role: string; content: string; imageContent?: object }> |
Request messages |
modelHints |
string[]? |
Model hint strings |
costPriority |
number? |
0-1 cost priority |
speedPriority |
number? |
0-1 speed priority |
intelligencePriority |
number? |
0-1 intelligence priority |
maxTokens |
number? |
Max tokens |
stopSequences |
string[]? |
Stop sequences |
temperature |
number? |
Temperature |
includeContext |
string? |
Context inclusion |
tools |
Array<{ name: string; description?: string; inputSchema: object }>? |
Available tools for tool-enabled sampling |
toolChoice |
string? |
Tool choice mode |
responseText |
string |
Current response text |
modelUsed |
string |
Model used field |
stopReason |
string |
Stop reason |
onResponseChange |
(text: string) => void |
Response text change |
onModelChange |
(model: string) => void |
Model field change |
onStopReasonChange |
(reason: string) => void |
Stop reason change |
onAutoRespond |
() => void |
Auto-respond with profile |
onSend |
() => void |
Send response |
onReject |
() => void |
Reject request |
Mantine: Stack with MessageBubble list, Paper for model preferences (Badge chips + Slider displays), parameter Text items, Textarea for response, TextInput for model, Select for stop reason, action Button group.
Stories: SimpleRequest, WithModelHints, WithPriorities, WithAllParams, WithImage, WithTools, WithToolChoice, PrefilledResponse, InlineCompact
2.16 ElicitationFormPanel
Form-based elicitation with JSON Schema-generated fields and security warning.
| Prop | Type | Description |
|---|---|---|
message |
string |
Server's message/prompt |
schema |
object |
JSON Schema for form fields |
values |
Record<string, unknown> |
Current form values |
serverName |
string |
Requesting server name (for warning) |
onChange |
(values: Record<string, unknown>) => void |
Form change |
onSubmit |
() => void |
Submit form |
onCancel |
() => void |
Cancel/decline |
Mantine: Stack with message Text, Divider, SchemaForm, Alert with warning icon and server name, action Button group.
Stories: SimpleForm, ComplexForm, WithEnums, AllRequired, WithDefaults, LongMessage
2.17 ElicitationUrlPanel
URL-based elicitation with copy, open, and waiting indicator.
| Prop | Type | Description |
|---|---|---|
message |
string |
Server's message |
url |
string |
URL to visit |
elicitationId |
string |
Elicitation ID |
isWaiting |
boolean |
Waiting for completion |
onCopyUrl |
() => void |
Copy URL |
onOpenInBrowser |
() => void |
Open URL |
onCancel |
() => void |
Cancel |
Mantine: Stack with message Text, Code block for URL, Button group (Copy, Open in Browser), Loader + status text, Alert with domain warning.
Stories: Waiting, WithLongUrl, Completed
2.18 RootsTable
Table of configured roots with remove actions and add form.
| Prop | Type | Description |
|---|---|---|
roots |
Array<{ name: string; uri: string }> |
Configured roots |
newRootName |
string |
New root name field |
newRootPath |
string |
New root path field |
onRemoveRoot |
(uri: string) => void |
Remove root |
onNewRootNameChange |
(name: string) => void |
Name field change |
onNewRootPathChange |
(path: string) => void |
Path field change |
onAddRoot |
() => void |
Add root |
onBrowse |
() => void |
Browse for path |
Mantine: Table for roots list with ActionIcon delete buttons, Divider, TextInput fields for name/path, Group with Browse + Add buttons, Alert with security warning.
Stories: WithRoots, Empty, AddingNew, ManyRoots
2.19 ServerInfoContent
Server metadata, capabilities, and instructions display (modal body content).
| Prop | Type | Description |
|---|---|---|
name |
string |
Server name |
version |
string |
Server version |
protocolVersion |
string |
Protocol version |
transport |
string |
Transport type |
serverCapabilities |
Array<{ name: string; supported: boolean; count?: number }> |
Server caps |
clientCapabilities |
Array<{ name: string; supported: boolean; count?: number }> |
Client caps |
instructions |
string? |
Server instructions text |
oauthDetails |
{ authUrl?: string; scopes?: string[]; accessToken?: string }? |
OAuth info |
Mantine: SimpleGrid cols={2} for metadata pairs, two-column CapabilityItem lists, Blockquote for instructions, Paper for OAuth details.
Stories: FullCapabilities, MinimalCapabilities, WithInstructions, WithOAuth, NoExperimental
2.20 ServerSettingsForm
Server settings form with headers, metadata, timeouts, and OAuth.
| Prop | Type | Description |
|---|---|---|
headers |
Array<{ key: string; value: string }> |
Custom headers |
metadata |
Array<{ key: string; value: string }> |
Request metadata |
connectionTimeout |
number |
Connection timeout ms |
requestTimeout |
number |
Request timeout ms |
oauthClientId |
string? |
OAuth client ID |
oauthClientSecret |
string? |
OAuth client secret |
oauthScopes |
string? |
OAuth scopes |
onConnectionModeChange |
(mode: string) => void |
Mode change |
onAddHeader |
() => void |
Add header |
onRemoveHeader |
(index: number) => void |
Remove header |
onHeaderChange |
(index: number, key: string, value: string) => void |
Edit header |
onAddMetadata |
() => void |
Add metadata |
onRemoveMetadata |
(index: number) => void |
Remove metadata |
onMetadataChange |
(index: number, key: string, value: string) => void |
Edit metadata |
onTimeoutChange |
(field: string, value: number) => void |
Timeout change |
onOAuthChange |
(field: string, value: string) => void |
OAuth field change |
Mantine: Stack with Select for connection mode, key-value pair sections using Group of TextInput pairs with ActionIcon remove buttons, NumberInput for timeouts, TextInput for OAuth fields, Divider between sections.
Stories: DefaultSettings, WithHeaders, WithMetadata, WithOAuth, AllConfigured
2.21 ImportServerJsonPanel
JSON editor with validation results, package selection, env var form, and name override.
| Prop | Type | Description |
|---|---|---|
jsonContent |
string |
Current JSON content |
validationResults |
Array<{ type: 'success' | 'warning' | 'info' | 'error'; message: string }> |
Validation items |
packages |
Array<{ registryType: string; identifier: string; runtimeHint: string }>? |
Available packages |
selectedPackageIndex |
number |
Selected package |
envVars |
Array<{ name: string; description?: string; required: boolean; value: string }> |
Environment variables |
serverName |
string |
Override name |
onJsonChange |
(content: string) => void |
JSON content change |
onValidate |
() => void |
Re-validate |
onSelectPackage |
(index: number) => void |
Select package |
onEnvVarChange |
(name: string, value: string) => void |
Env var change |
onServerNameChange |
(name: string) => void |
Name change |
onAddServer |
() => void |
Add server |
onCancel |
() => void |
Cancel |
Mantine: Textarea or JsonInput for JSON, List with icons for validation results, Radio.Group for package selection, TextInput fields for env vars (required marked), TextInput for name, action Button group.
Stories: Empty, ValidJson, InvalidJson, MultiplePackages, WithEnvVars, FullyConfigured
2.22 ExperimentalFeaturesPanel
Server/client experimental capabilities + raw JSON-RPC tester.
| Prop | Type | Description |
|---|---|---|
serverCapabilities |
Array<{ name: string; description?: string; methods?: string[] }> |
Server experimental caps |
clientCapabilities |
Array<{ name: string; enabled: boolean }> |
Client experimental caps |
requestJson |
string |
JSON-RPC request editor content |
responseJson |
string? |
JSON-RPC response |
customHeaders |
Array<{ key: string; value: string }> |
Custom headers for request |
requestHistory |
Array<{ timestamp: string; method: string; status: string; durationMs: number }> |
Request history |
onToggleClientCapability |
(name: string, enabled: boolean) => void |
Toggle cap |
onRequestChange |
(json: string) => void |
Request JSON change |
onSendRequest |
() => void |
Send request |
onAddHeader |
() => void |
Add header |
onRemoveHeader |
(index: number) => void |
Remove header |
onHeaderChange |
(index: number, key: string, value: string) => void |
Edit header |
onCopyResponse |
() => void |
Copy response |
Mantine: Stack with Alert warning, server caps as Card list with Test buttons, Checkbox group for client caps, Divider, JsonInput for request/response, header key-value pairs, action Button, history Table.
Stories: WithServerCaps, NoServerCaps, WithResponse, WithHistory, WithCustomHeaders
2.23 InlineSamplingRequest
Compact inline sampling request shown during tool execution.
| Prop | Type | Description |
|---|---|---|
queuePosition |
string |
e.g., "1 of 2" |
modelHints |
string[]? |
Model hints |
messagePreview |
string |
Truncated first message |
responseText |
string |
Response text |
onAutoRespond |
() => void |
Auto-respond |
onEditAndSend |
() => void |
Edit & send |
onReject |
() => void |
Reject |
onViewDetails |
() => void |
Expand to full view |
Mantine: Paper with Group header (type badge + queue position), compact content, Textarea for response, action Button group.
Stories: Default, WithModelHints, PrefilledResponse, InQueue
2.24 InlineElicitationRequest
Compact inline elicitation (form or URL) shown during tool execution.
| Prop | Type | Description |
|---|---|---|
mode |
'form' | 'url' |
Elicitation mode |
message |
string |
Server message |
queuePosition |
string |
e.g., "2 of 2" |
schema |
object? |
JSON Schema (form mode) |
values |
Record<string, unknown>? |
Form values |
url |
string? |
URL (url mode) |
isWaiting |
boolean? |
Waiting state (url mode) |
onChange |
(values: Record<string, unknown>) => void |
Form change |
onSubmit |
() => void |
Submit/confirm |
onCancel |
() => void |
Cancel |
Mantine: Paper with header badge, compact SchemaForm or URL display, action buttons.
Stories: FormMode, UrlMode, UrlWaiting
2.25 PendingClientRequests
Container for inline sampling/elicitation requests during tool execution.
| Prop | Type | Description |
|---|---|---|
count |
number |
Total pending requests |
children |
ReactNode |
InlineSamplingRequest and/or InlineElicitationRequest |
Mantine: Alert with count header, Stack of children.
Stories: SingleSampling, SingleElicitation, MultipleMixed
Tier 3 - Screens
Full screen sections composed of groups. These are the main content areas.
3.1 ServerListScreen
Home screen with server card grid and add button.
| Prop | Type | Description |
|---|---|---|
servers |
ServerCard props[] |
Array of server data |
onAddManually |
() => void |
Add manually |
onImportConfig |
() => void |
Import config |
onImportServerJson |
() => void |
Import server.json |
Mantine: Container with Title "MCP Inspector" header, Group with AddServerMenu, SimpleGrid cols={{ base: 1, md: 2 }} of ServerCard components.
Stories: MultipleServers, SingleServer, Empty, MixedStates
3.2 ToolsScreen
Three-panel layout: tool list, detail/form, and results.
| Prop | Type | Description |
|---|---|---|
tools |
ToolListItem props[] |
Tool list data |
selectedTool |
ToolDetailPanel props? |
Selected tool details |
result |
ResultPanel props? |
Execution result |
listChanged |
boolean |
List changed indicator |
searchText |
string |
Search filter |
pendingRequests |
PendingClientRequests props? |
Inline client requests |
onSearchChange |
(text: string) => void |
Search change |
onRefreshList |
() => void |
Refresh tool list |
onSelectTool |
(name: string) => void |
Select tool |
| (tool detail & result callbacks forwarded) |
Mantine: Three-column layout using Grid (col spans ~3/5/4 of 12) or CSS Grid. Left panel: ListChangedIndicator + TextInput search + ToolListItem stack. Center: ToolDetailPanel with PendingClientRequests below. Right: ResultPanel.
Stories: NoSelection, ToolSelected, WithResult, Executing, WithProgress, WithListChanged, WithPendingRequests, WithSearch
3.2a AppsScreen
Two-panel layout: app list on the left, dynamic right pane that toggles between an input form (before launch) and the embedded AppRenderer (after launch). Receives the already-filtered subset of tools that are MCP Apps; filtering happens in the wiring layer via the shared isAppTool helper in core/mcp/apps.
| Prop | Type | Description |
|---|---|---|
tools |
Tool[] |
Apps (already filtered to those with _meta.ui.resourceUri) |
listChanged |
boolean |
List changed indicator |
sandboxPath |
string |
Sandbox proxy path passed through to AppRenderer |
bridgeFactory |
(iframe) => AppBridge |
Wiring layer creates the bridge from the active MCP client |
rendererRef |
Ref<AppRendererHandle> |
Imperative ref forwarded to the embedded AppRenderer |
onRefreshList |
() => void |
Refresh apps list |
onSelectApp |
(name: string) => void |
App selection callback |
onOpenApp |
(name: string, args: Record<string, unknown>) => void |
Open App callback (wiring runs tools/call and pushes result via the ref) |
onCloseApp |
() => void |
Close currently-open app |
Mantine: Two-column layout. Left: Card with ListChangedIndicator, search TextInput, AppListItem stack, "Refresh Apps" Button. Right: Card whose content swaps between AppDetailPanel (form state) and AppRenderer (running state) plus a "Back to Input" / Maximize / Close header. Maximize hides the sidebar by widening the right card to full width.
Stories: NoSelection, AppSelected, AppRunning, AppRunningMaximized, NoFieldsApp (auto-launches on select), WithListChanged, Empty (no apps available)
3.3 ResourcesScreen
Two-panel layout: resource list (with accordion sections) and content preview.
| Prop | Type | Description |
|---|---|---|
resources |
ResourceListItem props[] |
Resources |
templates |
ResourceTemplateInput props[] |
Templates |
subscriptions |
Array<{ name: string; lastUpdated?: string }> |
Active subscriptions |
selectedResource |
ResourcePreviewPanel props? |
Selected resource preview |
listChanged |
boolean |
List changed indicator |
searchText |
string |
Search filter |
onSearchChange |
(text: string) => void |
Search change |
onRefreshList |
() => void |
Refresh |
onSelectResource |
(uri: string) => void |
Select resource |
| (preview callbacks forwarded) |
Mantine: Two-column Grid. Left: ListChangedIndicator, TextInput search, Accordion with Resources/Templates/Subscriptions sections. Right: ResourcePreviewPanel.
Stories: WithResources, WithTemplates, WithSubscriptions, ResourceSelected, Empty, WithListChanged
3.4 PromptsScreen
Two-panel layout: prompt selection/form and message results.
| Prop | Type | Description |
|---|---|---|
promptForm |
PromptArgumentsForm props |
Left panel props |
messages |
PromptMessagesDisplay props? |
Right panel props |
listChanged |
boolean |
List changed indicator |
onRefreshList |
() => void |
Refresh |
Mantine: Two-column Grid. Left: ListChangedIndicator + PromptArgumentsForm. Right: PromptMessagesDisplay.
Stories: NoSelection, PromptSelected, WithResult, WithListChanged, ManyArguments
3.5 LoggingScreen
Two-panel layout: controls and log stream.
| Prop | Type | Description |
|---|---|---|
controls |
LogControls props |
Left panel controls |
entries |
LogEntry props[] |
Log entries |
autoScroll |
boolean |
Auto-scroll state |
onToggleAutoScroll |
() => void |
Toggle auto-scroll |
onCopyAll |
() => void |
Copy all logs |
Mantine: Two-column Grid (~3/9 split). Left: LogControls. Right: Paper with header (Auto-scroll Checkbox + Copy All Button), ScrollArea with LogEntry stack.
Stories: Empty, WithEntries, MixedLevels, FilteredView, ManyEntries
3.6 TasksScreen
Grouped task cards: active and completed sections.
| Prop | Type | Description |
|---|---|---|
activeTasks |
TaskCard props[] |
Active/waiting tasks |
completedTasks |
TaskCard props[] |
Completed/failed/cancelled tasks |
onRefresh |
() => void |
Refresh tasks |
onClearHistory |
() => void |
Clear completed tasks |
Mantine: Stack with Title + Refresh Button, "Active Tasks" section header + TaskCard list, "Completed Tasks" section header with Clear History Button + TaskCard list.
Stories: ActiveOnly, CompletedOnly, Mixed, Empty, ManyTasks
3.7 HistoryScreen
Request history list with search, filter, pinned section.
| Prop | Type | Description |
|---|---|---|
entries |
HistoryEntry props[] |
History entries |
pinnedEntries |
HistoryEntry props[] |
Pinned entries |
searchText |
string |
Search filter |
methodFilter |
string? |
Method type filter |
totalCount |
number |
Total entries |
displayedCount |
number |
Currently shown |
onSearchChange |
(text: string) => void |
Search change |
onMethodFilterChange |
(method: string) => void |
Filter change |
onClearAll |
() => void |
Clear history |
onExport |
() => void |
Export JSON |
| (entry callbacks forwarded) |
Mantine: Paper with header Group (Title + TextInput search + Select filter + Export Button + Clear Button), Stack of HistoryEntry, "Pinned Requests" Divider + pinned entries.
Stories: WithEntries, WithPinned, WithNestedChildren, Filtered, Empty, ManyEntries
Tier 4 - Page Views
4.1 HomeLayout
Layout for the disconnected state (server list page).
| Prop | Type | Description |
|---|---|---|
children |
ReactNode |
Page content (ServerListScreen) |
colorScheme |
'light' | 'dark' |
Theme |
onToggleTheme |
() => void |
Theme toggle |
Mantine: AppShell with AppShell.Header containing "MCP Inspector" title + theme toggle ActionIcon. Body renders children.
Stories: Light, Dark
4.2 ConnectedLayout
Layout for connected state with navigation bar.
| Prop | Type | Description |
|---|---|---|
serverName |
string |
Server name in header |
status |
'connected' | 'connecting' | 'failed' |
Connection status |
latencyMs |
number? |
Ping latency |
activeTab |
string |
Currently active nav tab |
availableTabs |
string[] |
Which tabs to show (based on capabilities) |
colorScheme |
'light' | 'dark' |
Theme |
onTabChange |
(tab: string) => void |
Tab navigation |
onDisconnect |
() => void |
Disconnect callback |
onToggleTheme |
() => void |
Theme toggle |
children |
ReactNode |
Screen content |
Mantine: AppShell with AppShell.Header containing server name Menu (dropdown), StatusIndicator, navigation Tabs (Tools, Resources, Prompts, Logs, Tasks, History), theme toggle, Disconnect Button (red outline). Body renders children.
Stories: ToolsActive, ResourcesActive, AllTabs, LimitedTabs, LongServerName
Build Order
Components should be built bottom-up. Within each tier, components can be built in parallel. Dependencies are listed to show what must exist before a component can be composed.
Phase 1: Foundation (Elements)
All Tier 1 elements can be built in parallel. No dependencies on each other.
- StatusIndicator
- AnnotationBadge
- CapabilityItem
- LogEntry
- ProgressDisplay
- ContentViewer
- ListChangedIndicator
- CopyButton
- ConnectionToggle
- TransportBadge
- InlineError
- MessageBubble
Phase 2: Core Groups
Depends on Phase 1 elements being complete.
- SchemaForm (2.12) - Critical path, used by many other groups
- ServerCard (2.1) - Uses StatusIndicator, TransportBadge, ConnectionToggle, CopyButton, InlineError
- AddServerMenu (2.2)
- ToolListItem (2.3) - Uses AnnotationBadge
- AppListItem (2.3a) - Renders
tool.icons(or fold into ToolListItem) - ResourceListItem (2.6) - Uses AnnotationBadge
- LogControls (2.11)
- ResultPanel (2.5) - Uses ContentViewer, CopyButton
- PromptMessagesDisplay (2.10) - Uses MessageBubble
- RootsTable (2.18)
- ServerInfoContent (2.19) - Uses CapabilityItem
Phase 3: Dependent Groups
Depends on SchemaForm and Phase 2.
- ToolDetailPanel (2.4) - Uses SchemaForm, AnnotationBadge, ProgressDisplay
- AppDetailPanel (2.5a) - Uses SchemaForm
- AppRenderer (2.5b) - Imperative ref shell for embedded MCP App; depends on
@modelcontextprotocol/ext-apps - ResourcePreviewPanel (2.7) - Uses ContentViewer
- ResourceTemplateInput (2.8)
- PromptArgumentsForm (2.9)
- TaskCard (2.13) - Uses ProgressDisplay
- HistoryEntry (2.14)
- SamplingRequestPanel (2.15) - Uses MessageBubble
- ElicitationFormPanel (2.16) - Uses SchemaForm
- ElicitationUrlPanel (2.17)
- ServerSettingsForm (2.20)
- ImportServerJsonPanel (2.21)
- ExperimentalFeaturesPanel (2.22)
Phase 4: Inline Handlers
Depends on Phase 3 panels.
- InlineSamplingRequest (2.23)
- InlineElicitationRequest (2.24) - Uses SchemaForm
- PendingClientRequests (2.25) - Contains inline requests
Phase 5: Screens (Screens)
Depends on all groups.
- ServerListScreen (3.1)
- ToolsScreen (3.2)
- AppsScreen (3.2a)
- ResourcesScreen (3.3)
- PromptsScreen (3.4)
- LoggingScreen (3.5)
- TasksScreen (3.6)
- HistoryScreen (3.7)
Phase 6: Views
Depends on screens.
- HomeLayout (4.1)
- ConnectedLayout (4.2)
Storybook Configuration
Global Setup
// .storybook/preview.tsx
import { MantineProvider } from '@mantine/core';
import '@mantine/core/styles.css';
import { theme } from '../src/theme';
export const decorators = [
(Story) => (
<MantineProvider theme={theme}>
<Story />
</MantineProvider>
),
];
export const parameters = {
layout: 'centered', // elements/groups
// layout: 'fullscreen', // screens/views
};
Addon Recommendations
| Addon | Purpose |
|---|---|
storybook-addon-mantine |
Theme switching, color scheme toggle |
@storybook/addon-actions |
Log callback invocations |
@storybook/addon-controls |
Interactive prop editing |
@storybook/addon-viewport |
Responsive testing |
@storybook/addon-a11y |
Accessibility checks |
Theme Configuration
The Mantine theme should be defined once and shared between Storybook and the application:
// src/theme.ts
import { createTheme } from '@mantine/core';
export const theme = createTheme({
primaryColor: 'dark',
// Inspector-specific overrides
});
Dark Mode Stories
Every component should be verified in both light and dark mode. Use storybook-addon-mantine to provide a global toggle rather than duplicating stories.