Refactor ServerCard with active server logic, rename StatusIndicator, and polish UI panels
- Rename StatusIndicator to ServerStatusIndicator across all files - ServerCard: add activeServer/onSetActiveServer props for coordinated connection flow — when a card's toggle is switched on, it sets itself as the active server, shows "Connecting" status, and dims all other cards (opacity + pointerEvents none + inert for full keyboard blocking) - ServerListScreen: manage activeServer state internally, pass to cards; switch to full-width layout with 3-column grid and scrollable card area - ServerCard: move "Test Client Features" dropdown above ContentViewer with min-height row for consistent card heights - ToolResultPanel: move Clear button to top-right header row - ToolDetailPanel: right-align Execute Tool button, size md, not full-width - ListChangedIndicator: bump Refresh button to size sm - ToolsActive story: expand to 30 tools with titles - UnconnectedView stories: all servers disconnected, remove Connecting story Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
parent
ad2f3b7efd
commit
8e73bc6ffb
12 changed files with 295 additions and 117 deletions
|
|
@ -16,7 +16,7 @@ export function ConnectionToggle({
|
||||||
return (
|
return (
|
||||||
<Switch
|
<Switch
|
||||||
size="lg"
|
size="lg"
|
||||||
checked={checked}
|
checked={checked || loading}
|
||||||
disabled={disabled || loading}
|
disabled={disabled || loading}
|
||||||
onChange={(event) => onChange(event.currentTarget.checked)}
|
onChange={(event) => onChange(event.currentTarget.checked)}
|
||||||
/>
|
/>
|
||||||
|
|
|
||||||
|
|
@ -18,7 +18,7 @@ const UpdateLabel = Text.withProps({
|
||||||
});
|
});
|
||||||
|
|
||||||
const RefreshButton = Button.withProps({
|
const RefreshButton = Button.withProps({
|
||||||
size: "xs",
|
size: "sm",
|
||||||
variant: "light",
|
variant: "light",
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,42 @@
|
||||||
|
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||||
|
import { ServerStatusIndicator } from "./ServerStatusIndicator";
|
||||||
|
|
||||||
|
const meta: Meta<typeof ServerStatusIndicator> = {
|
||||||
|
title: "Elements/ServerStatusIndicator",
|
||||||
|
component: ServerStatusIndicator,
|
||||||
|
};
|
||||||
|
|
||||||
|
export default meta;
|
||||||
|
type Story = StoryObj<typeof ServerStatusIndicator>;
|
||||||
|
|
||||||
|
export const Connected: Story = {
|
||||||
|
args: {
|
||||||
|
status: "connected",
|
||||||
|
latencyMs: 23,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Connecting: Story = {
|
||||||
|
args: {
|
||||||
|
status: "connecting",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Disconnected: Story = {
|
||||||
|
args: {
|
||||||
|
status: "disconnected",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const Failed: Story = {
|
||||||
|
args: {
|
||||||
|
status: "failed",
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
||||||
|
export const FailedWithRetries: Story = {
|
||||||
|
args: {
|
||||||
|
status: "failed",
|
||||||
|
retryCount: 3,
|
||||||
|
},
|
||||||
|
};
|
||||||
|
|
@ -0,0 +1,53 @@
|
||||||
|
import { Group, Paper, Text } from "@mantine/core";
|
||||||
|
|
||||||
|
export interface ServerStatusIndicatorProps {
|
||||||
|
status: "connected" | "connecting" | "disconnected" | "failed";
|
||||||
|
latencyMs?: number;
|
||||||
|
retryCount?: number;
|
||||||
|
}
|
||||||
|
|
||||||
|
const statusColorVar: Record<ServerStatusIndicatorProps["status"], string> = {
|
||||||
|
connected: "var(--inspector-status-connected)",
|
||||||
|
connecting: "var(--inspector-status-connecting)",
|
||||||
|
disconnected: "var(--inspector-status-disconnected)",
|
||||||
|
failed: "var(--inspector-status-failed)",
|
||||||
|
};
|
||||||
|
|
||||||
|
function getLabel(
|
||||||
|
status: ServerStatusIndicatorProps["status"],
|
||||||
|
latencyMs?: number,
|
||||||
|
retryCount?: number,
|
||||||
|
): string {
|
||||||
|
switch (status) {
|
||||||
|
case "connected":
|
||||||
|
return latencyMs != null ? `Connected (${latencyMs}ms)` : "Connected";
|
||||||
|
case "connecting":
|
||||||
|
return "Connecting...";
|
||||||
|
case "disconnected":
|
||||||
|
return "Disconnected";
|
||||||
|
case "failed":
|
||||||
|
return retryCount != null ? `Failed (${retryCount})` : "Failed";
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const Dot = Paper.withProps({
|
||||||
|
w: 10,
|
||||||
|
h: 10,
|
||||||
|
radius: "xl",
|
||||||
|
});
|
||||||
|
|
||||||
|
export function ServerStatusIndicator({
|
||||||
|
status,
|
||||||
|
latencyMs,
|
||||||
|
retryCount,
|
||||||
|
}: ServerStatusIndicatorProps) {
|
||||||
|
return (
|
||||||
|
<Group gap="xs">
|
||||||
|
<Dot
|
||||||
|
bg={statusColorVar[status]}
|
||||||
|
className={status === "connecting" ? "inspector-pulse" : undefined}
|
||||||
|
/>
|
||||||
|
<Text size="sm">{getLabel(status, latencyMs, retryCount)}</Text>
|
||||||
|
</Group>
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
@ -7,6 +7,7 @@ const meta: Meta<typeof ServerCard> = {
|
||||||
component: ServerCard,
|
component: ServerCard,
|
||||||
args: {
|
args: {
|
||||||
onToggleConnection: fn(),
|
onToggleConnection: fn(),
|
||||||
|
onSetConnectingServer: fn(),
|
||||||
onServerInfo: fn(),
|
onServerInfo: fn(),
|
||||||
onSettings: fn(),
|
onSettings: fn(),
|
||||||
onEdit: fn(),
|
onEdit: fn(),
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
|
import { useEffect } from "react";
|
||||||
import { Badge, Button, Card, Group, Menu, Stack, Text } from "@mantine/core";
|
import { Badge, Button, Card, Group, Menu, Stack, Text } from "@mantine/core";
|
||||||
import { StatusIndicator } from "../../elements/StatusIndicator/StatusIndicator";
|
import { ServerStatusIndicator } from "../../elements/ServerStatusIndicator/ServerStatusIndicator";
|
||||||
import { TransportBadge } from "../../elements/TransportBadge/TransportBadge";
|
import { TransportBadge } from "../../elements/TransportBadge/TransportBadge";
|
||||||
import { ConnectionToggle } from "../../elements/ConnectionToggle/ConnectionToggle";
|
import { ConnectionToggle } from "../../elements/ConnectionToggle/ConnectionToggle";
|
||||||
import { ContentViewer } from "../../elements/ContentViewer/ContentViewer";
|
import { ContentViewer } from "../../elements/ContentViewer/ContentViewer";
|
||||||
|
|
@ -15,6 +16,8 @@ export interface ServerCardProps {
|
||||||
retryCount?: number;
|
retryCount?: number;
|
||||||
error?: { message: string; details?: string };
|
error?: { message: string; details?: string };
|
||||||
canTestClientFeatures: boolean;
|
canTestClientFeatures: boolean;
|
||||||
|
activeServer?: string;
|
||||||
|
onSetActiveServer?: (name: string | undefined) => void;
|
||||||
onToggleConnection: (connect: boolean) => void;
|
onToggleConnection: (connect: boolean) => void;
|
||||||
onServerInfo: () => void;
|
onServerInfo: () => void;
|
||||||
onSettings: () => void;
|
onSettings: () => void;
|
||||||
|
|
@ -71,6 +74,8 @@ export function ServerCard({
|
||||||
retryCount,
|
retryCount,
|
||||||
error,
|
error,
|
||||||
canTestClientFeatures,
|
canTestClientFeatures,
|
||||||
|
activeServer,
|
||||||
|
onSetActiveServer,
|
||||||
onToggleConnection,
|
onToggleConnection,
|
||||||
onServerInfo,
|
onServerInfo,
|
||||||
onSettings,
|
onSettings,
|
||||||
|
|
@ -83,11 +88,35 @@ export function ServerCard({
|
||||||
onConfigureRoots,
|
onConfigureRoots,
|
||||||
compact = false,
|
compact = false,
|
||||||
}: ServerCardProps) {
|
}: ServerCardProps) {
|
||||||
|
const isThisConnecting = activeServer === name;
|
||||||
|
const isDimmed = activeServer !== undefined && activeServer !== name;
|
||||||
const isConnected = status === "connected";
|
const isConnected = status === "connected";
|
||||||
const isConnecting = status === "connecting";
|
const isConnecting = status === "connecting" || isThisConnecting;
|
||||||
|
const displayStatus = isThisConnecting ? "connecting" : status;
|
||||||
|
|
||||||
|
useEffect(() => {
|
||||||
|
if (isThisConnecting) {
|
||||||
|
onToggleConnection(true);
|
||||||
|
}
|
||||||
|
}, [isThisConnecting, onToggleConnection]);
|
||||||
|
|
||||||
|
function handleToggle(connect: boolean) {
|
||||||
|
if (connect && !activeServer) {
|
||||||
|
onSetActiveServer?.(name);
|
||||||
|
} else if (!connect && activeServer && activeServer === name) {
|
||||||
|
onSetActiveServer?.(undefined);
|
||||||
|
onToggleConnection(false);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Card withBorder padding="lg">
|
<Card
|
||||||
|
withBorder
|
||||||
|
padding="lg"
|
||||||
|
opacity={isDimmed ? 0.4 : 1}
|
||||||
|
style={isDimmed ? { pointerEvents: "none" } : undefined}
|
||||||
|
{...(isDimmed ? { "aria-disabled": true, inert: "" } : {})}
|
||||||
|
>
|
||||||
<Stack gap="sm">
|
<Stack gap="sm">
|
||||||
<Group justify="space-between" wrap="wrap">
|
<Group justify="space-between" wrap="wrap">
|
||||||
<HeaderLeft>
|
<HeaderLeft>
|
||||||
|
|
@ -95,21 +124,47 @@ export function ServerCard({
|
||||||
{version && <Badge variant="outline">{version}</Badge>}
|
{version && <Badge variant="outline">{version}</Badge>}
|
||||||
</HeaderLeft>
|
</HeaderLeft>
|
||||||
<HeaderRight>
|
<HeaderRight>
|
||||||
<StatusIndicator status={status} retryCount={retryCount} />
|
<ServerStatusIndicator
|
||||||
|
status={displayStatus}
|
||||||
|
retryCount={retryCount}
|
||||||
|
/>
|
||||||
<ConnectionToggle
|
<ConnectionToggle
|
||||||
checked={isConnected}
|
checked={isConnected}
|
||||||
loading={isConnecting}
|
loading={isConnecting}
|
||||||
disabled={false}
|
disabled={isDimmed}
|
||||||
onChange={onToggleConnection}
|
onChange={handleToggle}
|
||||||
/>
|
/>
|
||||||
</HeaderRight>
|
</HeaderRight>
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
{!compact && (
|
{!compact && (
|
||||||
<>
|
<>
|
||||||
<Group gap="sm">
|
<Group justify="space-between" mih={30}>
|
||||||
<TransportBadge transport={transport} />
|
<Group gap="sm">
|
||||||
<ModeText>{connectionMode}</ModeText>
|
<TransportBadge transport={transport} />
|
||||||
|
<ModeText>{connectionMode}</ModeText>
|
||||||
|
</Group>
|
||||||
|
{canTestClientFeatures && (
|
||||||
|
<Menu>
|
||||||
|
<Menu.Target>
|
||||||
|
<SubtleButton>Test Client Features ▾</SubtleButton>
|
||||||
|
</Menu.Target>
|
||||||
|
<Menu.Dropdown>
|
||||||
|
<Menu.Item onClick={onTestSampling}>
|
||||||
|
Simulate Sampling Request
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item onClick={onTestElicitationForm}>
|
||||||
|
Simulate Elicitation (Form)
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item onClick={onTestElicitationUrl}>
|
||||||
|
Simulate Elicitation (URL)
|
||||||
|
</Menu.Item>
|
||||||
|
<Menu.Item onClick={onConfigureRoots}>
|
||||||
|
Configure Roots
|
||||||
|
</Menu.Item>
|
||||||
|
</Menu.Dropdown>
|
||||||
|
</Menu>
|
||||||
|
)}
|
||||||
</Group>
|
</Group>
|
||||||
|
|
||||||
<ContentViewer type="text" content={command} copyable />
|
<ContentViewer type="text" content={command} copyable />
|
||||||
|
|
@ -131,27 +186,6 @@ export function ServerCard({
|
||||||
<ActionsRow>
|
<ActionsRow>
|
||||||
<SubtleButton onClick={onServerInfo}>Server Info</SubtleButton>
|
<SubtleButton onClick={onServerInfo}>Server Info</SubtleButton>
|
||||||
<SubtleButton onClick={onSettings}>Settings</SubtleButton>
|
<SubtleButton onClick={onSettings}>Settings</SubtleButton>
|
||||||
{canTestClientFeatures && (
|
|
||||||
<Menu>
|
|
||||||
<Menu.Target>
|
|
||||||
<SubtleButton>Test Client Features ▾</SubtleButton>
|
|
||||||
</Menu.Target>
|
|
||||||
<Menu.Dropdown>
|
|
||||||
<Menu.Item onClick={onTestSampling}>
|
|
||||||
Simulate Sampling Request
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item onClick={onTestElicitationForm}>
|
|
||||||
Simulate Elicitation (Form)
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item onClick={onTestElicitationUrl}>
|
|
||||||
Simulate Elicitation (URL)
|
|
||||||
</Menu.Item>
|
|
||||||
<Menu.Item onClick={onConfigureRoots}>
|
|
||||||
Configure Roots
|
|
||||||
</Menu.Item>
|
|
||||||
</Menu.Dropdown>
|
|
||||||
</Menu>
|
|
||||||
)}
|
|
||||||
</ActionsRow>
|
</ActionsRow>
|
||||||
</Group>
|
</Group>
|
||||||
</>
|
</>
|
||||||
|
|
|
||||||
|
|
@ -115,16 +115,16 @@ export function ToolDetailPanel({
|
||||||
/>
|
/>
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<Group>
|
<Group justify="flex-end">
|
||||||
|
{isExecuting && <CancelButton onClick={onCancel}>Cancel</CancelButton>}
|
||||||
<Button
|
<Button
|
||||||
fullWidth
|
size="md"
|
||||||
onClick={onExecute}
|
onClick={onExecute}
|
||||||
disabled={isExecuting}
|
disabled={isExecuting}
|
||||||
loading={isExecuting}
|
loading={isExecuting}
|
||||||
>
|
>
|
||||||
Execute Tool
|
Execute Tool
|
||||||
</Button>
|
</Button>
|
||||||
{isExecuting && <CancelButton onClick={onCancel}>Cancel</CancelButton>}
|
|
||||||
</Group>
|
</Group>
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
|
|
|
||||||
|
|
@ -29,7 +29,10 @@ function resolveMediaType(mimeType?: string): "image" | "audio" {
|
||||||
export function ToolResultPanel({ content, onClear }: ToolResultPanelProps) {
|
export function ToolResultPanel({ content, onClear }: ToolResultPanelProps) {
|
||||||
return (
|
return (
|
||||||
<Stack>
|
<Stack>
|
||||||
<Title order={4}>Results</Title>
|
<Group justify="space-between">
|
||||||
|
<Title order={4}>Results</Title>
|
||||||
|
<ClearButton onClick={onClear}>Clear</ClearButton>
|
||||||
|
</Group>
|
||||||
{content.length === 0 ? (
|
{content.length === 0 ? (
|
||||||
<Text c="dimmed">No results yet</Text>
|
<Text c="dimmed">No results yet</Text>
|
||||||
) : (
|
) : (
|
||||||
|
|
@ -59,9 +62,6 @@ export function ToolResultPanel({ content, onClear }: ToolResultPanelProps) {
|
||||||
})}
|
})}
|
||||||
</>
|
</>
|
||||||
)}
|
)}
|
||||||
<Group>
|
|
||||||
<ClearButton onClick={onClear}>Clear</ClearButton>
|
|
||||||
</Group>
|
|
||||||
</Stack>
|
</Stack>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ import {
|
||||||
} from "@mantine/core";
|
} from "@mantine/core";
|
||||||
import { useComputedColorScheme } from "@mantine/core";
|
import { useComputedColorScheme } from "@mantine/core";
|
||||||
import { MdLightMode, MdDarkMode } from "react-icons/md";
|
import { MdLightMode, MdDarkMode } from "react-icons/md";
|
||||||
import { StatusIndicator } from "../../elements/StatusIndicator/StatusIndicator";
|
import { ServerStatusIndicator } from "../../elements/ServerStatusIndicator/ServerStatusIndicator";
|
||||||
import mcpLogo from "../../../theme/assets/MCP.svg";
|
import mcpLogo from "../../../theme/assets/MCP.svg";
|
||||||
import mcpLogoDark from "../../../theme/assets/MCP-dark.svg";
|
import mcpLogoDark from "../../../theme/assets/MCP-dark.svg";
|
||||||
|
|
||||||
|
|
@ -133,7 +133,7 @@ export function ViewHeader(props: ViewHeaderProps) {
|
||||||
</CenterSection>
|
</CenterSection>
|
||||||
|
|
||||||
<RightSection>
|
<RightSection>
|
||||||
<StatusIndicator status={props.status} latencyMs={props.latencyMs} />
|
<ServerStatusIndicator status={props.status} latencyMs={props.latencyMs} />
|
||||||
<DisconnectButton onClick={props.onDisconnect}>
|
<DisconnectButton onClick={props.onDisconnect}>
|
||||||
Disconnect
|
Disconnect
|
||||||
</DisconnectButton>
|
</DisconnectButton>
|
||||||
|
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
import { useState } from "react";
|
import { useState } from "react";
|
||||||
import { Container, SimpleGrid, Stack, Text } from "@mantine/core";
|
import { ScrollArea, SimpleGrid, Stack, Text } from "@mantine/core";
|
||||||
import { ServerCard } from "../../groups/ServerCard/ServerCard";
|
import { ServerCard } from "../../groups/ServerCard/ServerCard";
|
||||||
import { ServerListControls } from "../../groups/ServerListControls/ServerListControls";
|
import { ServerListControls } from "../../groups/ServerListControls/ServerListControls";
|
||||||
import type { ServerCardProps } from "../../groups/ServerCard/ServerCard";
|
import type { ServerCardProps } from "../../groups/ServerCard/ServerCard";
|
||||||
|
|
@ -11,9 +11,8 @@ export interface ServerListScreenProps {
|
||||||
onImportServerJson: () => void;
|
onImportServerJson: () => void;
|
||||||
}
|
}
|
||||||
|
|
||||||
const PageContainer = Container.withProps({
|
const PageContainer = Stack.withProps({
|
||||||
size: "xl",
|
p: "xl",
|
||||||
py: "xl",
|
|
||||||
});
|
});
|
||||||
|
|
||||||
const EmptyState = Text.withProps({
|
const EmptyState = Text.withProps({
|
||||||
|
|
@ -29,6 +28,9 @@ export function ServerListScreen({
|
||||||
onImportServerJson,
|
onImportServerJson,
|
||||||
}: ServerListScreenProps) {
|
}: ServerListScreenProps) {
|
||||||
const [compact, setCompact] = useState<boolean>(false);
|
const [compact, setCompact] = useState<boolean>(false);
|
||||||
|
const [activeServer, setActiveServer] = useState<
|
||||||
|
string | undefined
|
||||||
|
>();
|
||||||
|
|
||||||
function handleToggleList() {
|
function handleToggleList() {
|
||||||
setCompact((prev) => (prev === true ? false : true));
|
setCompact((prev) => (prev === true ? false : true));
|
||||||
|
|
@ -36,32 +38,38 @@ export function ServerListScreen({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PageContainer>
|
<PageContainer>
|
||||||
<Stack gap="lg">
|
<ServerListControls
|
||||||
<ServerListControls
|
serverCount={servers.length}
|
||||||
serverCount={servers.length}
|
compact={compact}
|
||||||
compact={compact}
|
onToggleList={handleToggleList}
|
||||||
onToggleList={handleToggleList}
|
onAddManually={onAddManually}
|
||||||
onAddManually={onAddManually}
|
onImportConfig={onImportConfig}
|
||||||
onImportConfig={onImportConfig}
|
onImportServerJson={onImportServerJson}
|
||||||
onImportServerJson={onImportServerJson}
|
/>
|
||||||
/>
|
|
||||||
|
|
||||||
|
<ScrollArea.Autosize mah="calc(100vh - var(--app-shell-header-height, 60px) - var(--mantine-spacing-xl) * 2 - 60px)">
|
||||||
{servers.length === 0 ? (
|
{servers.length === 0 ? (
|
||||||
<EmptyState>
|
<EmptyState>
|
||||||
No servers configured. Add a server to get started.
|
No servers configured. Add a server to get started.
|
||||||
</EmptyState>
|
</EmptyState>
|
||||||
) : (
|
) : (
|
||||||
<SimpleGrid
|
<SimpleGrid
|
||||||
cols={{ base: 1, md: 2 }}
|
cols={{ base: 1, sm: 2, lg: 3 }}
|
||||||
spacing="lg"
|
spacing="lg"
|
||||||
className="grid-align-start"
|
className="grid-align-start"
|
||||||
>
|
>
|
||||||
{servers.map((server) => (
|
{servers.map((server) => (
|
||||||
<ServerCard key={server.name} compact={compact} {...server} />
|
<ServerCard
|
||||||
|
key={server.name}
|
||||||
|
compact={compact}
|
||||||
|
activeServer={activeServer}
|
||||||
|
onSetActiveServer={setActiveServer}
|
||||||
|
{...server}
|
||||||
|
/>
|
||||||
))}
|
))}
|
||||||
</SimpleGrid>
|
</SimpleGrid>
|
||||||
)}
|
)}
|
||||||
</Stack>
|
</ScrollArea.Autosize>
|
||||||
</PageContainer>
|
</PageContainer>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -184,9 +184,174 @@ export const ToolsActive: Story = {
|
||||||
selected: true,
|
selected: true,
|
||||||
onClick: fn(),
|
onClick: fn(),
|
||||||
},
|
},
|
||||||
{ name: "delete_records", selected: false, onClick: fn() },
|
{
|
||||||
{ name: "list_users", selected: false, onClick: fn() },
|
name: "delete_records",
|
||||||
{ name: "batch_process", selected: false, onClick: fn() },
|
title: "Delete Records",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list_users",
|
||||||
|
title: "List Users",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "batch_process",
|
||||||
|
title: "Batch Process",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "search_files",
|
||||||
|
title: "Search Files",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "read_file",
|
||||||
|
title: "Read File",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "write_file",
|
||||||
|
title: "Write File",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "get_schema",
|
||||||
|
title: "Get Schema",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "run_query",
|
||||||
|
title: "Run Query",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "create_migration",
|
||||||
|
title: "Create Migration",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "validate_config",
|
||||||
|
title: "Validate Config",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "deploy_service",
|
||||||
|
title: "Deploy Service",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rollback_deploy",
|
||||||
|
title: "Rollback Deploy",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "check_health",
|
||||||
|
title: "Check Health",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "generate_report",
|
||||||
|
title: "Generate Report",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "export_csv",
|
||||||
|
title: "Export CSV",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "import_data",
|
||||||
|
title: "Import Data",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "send_notification",
|
||||||
|
title: "Send Notification",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "schedule_task",
|
||||||
|
title: "Schedule Task",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "cancel_task",
|
||||||
|
title: "Cancel Task",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "list_connections",
|
||||||
|
title: "List Connections",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "refresh_cache",
|
||||||
|
title: "Refresh Cache",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "purge_logs",
|
||||||
|
title: "Purge Logs",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "encrypt_secret",
|
||||||
|
title: "Encrypt Secret",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "rotate_keys",
|
||||||
|
title: "Rotate Keys",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "sync_resources",
|
||||||
|
title: "Sync Resources",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "compile_template",
|
||||||
|
title: "Compile Template",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "analyze_performance",
|
||||||
|
title: "Analyze Performance",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "resize_image",
|
||||||
|
title: "Resize Image",
|
||||||
|
selected: false,
|
||||||
|
onClick: fn(),
|
||||||
|
},
|
||||||
]}
|
]}
|
||||||
selectedTool={{
|
selectedTool={{
|
||||||
name: "create_record",
|
name: "create_record",
|
||||||
|
|
|
||||||
|
|
@ -59,8 +59,8 @@ export const WithServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-everything",
|
command: "npx -y @modelcontextprotocol/server-everything",
|
||||||
status: "connected",
|
status: "disconnected",
|
||||||
canTestClientFeatures: true,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -80,9 +80,7 @@ export const WithServers: Story = {
|
||||||
transport: "http",
|
transport: "http",
|
||||||
connectionMode: "Direct",
|
connectionMode: "Direct",
|
||||||
command: "https://api.example.com/mcp",
|
command: "https://api.example.com/mcp",
|
||||||
status: "failed",
|
status: "disconnected",
|
||||||
retryCount: 3,
|
|
||||||
error: { message: "Connection timeout after 20s" },
|
|
||||||
canTestClientFeatures: false,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
|
|
@ -106,8 +104,8 @@ export const ManyServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-everything",
|
command: "npx -y @modelcontextprotocol/server-everything",
|
||||||
status: "connected",
|
status: "disconnected",
|
||||||
canTestClientFeatures: true,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -127,8 +125,8 @@ export const ManyServers: Story = {
|
||||||
transport: "http",
|
transport: "http",
|
||||||
connectionMode: "Direct",
|
connectionMode: "Direct",
|
||||||
command: "https://api.example.com/mcp",
|
command: "https://api.example.com/mcp",
|
||||||
status: "connected",
|
status: "disconnected",
|
||||||
canTestClientFeatures: true,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -137,7 +135,7 @@ export const ManyServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-postgres",
|
command: "npx -y @modelcontextprotocol/server-postgres",
|
||||||
status: "connecting",
|
status: "disconnected",
|
||||||
canTestClientFeatures: false,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
|
|
@ -147,8 +145,8 @@ export const ManyServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-github",
|
command: "npx -y @modelcontextprotocol/server-github",
|
||||||
status: "connected",
|
status: "disconnected",
|
||||||
canTestClientFeatures: true,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -157,9 +155,7 @@ export const ManyServers: Story = {
|
||||||
transport: "http",
|
transport: "http",
|
||||||
connectionMode: "Direct",
|
connectionMode: "Direct",
|
||||||
command: "https://slack-mcp.example.com/mcp",
|
command: "https://slack-mcp.example.com/mcp",
|
||||||
status: "failed",
|
status: "disconnected",
|
||||||
retryCount: 5,
|
|
||||||
error: { message: "Authentication failed" },
|
|
||||||
canTestClientFeatures: false,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
|
|
@ -169,7 +165,7 @@ export const ManyServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-memory",
|
command: "npx -y @modelcontextprotocol/server-memory",
|
||||||
status: "connected",
|
status: "disconnected",
|
||||||
canTestClientFeatures: false,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
|
|
@ -189,8 +185,8 @@ export const ManyServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-sqlite ./data.db",
|
command: "npx -y @modelcontextprotocol/server-sqlite ./data.db",
|
||||||
status: "connected",
|
status: "disconnected",
|
||||||
canTestClientFeatures: true,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
|
|
@ -199,9 +195,7 @@ export const ManyServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-brave-search",
|
command: "npx -y @modelcontextprotocol/server-brave-search",
|
||||||
status: "failed",
|
status: "disconnected",
|
||||||
retryCount: 2,
|
|
||||||
error: { message: "API key expired" },
|
|
||||||
canTestClientFeatures: false,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
|
|
@ -221,31 +215,7 @@ export const ManyServers: Story = {
|
||||||
transport: "stdio",
|
transport: "stdio",
|
||||||
connectionMode: "Via Proxy",
|
connectionMode: "Via Proxy",
|
||||||
command: "npx -y @modelcontextprotocol/server-sequential-thinking",
|
command: "npx -y @modelcontextprotocol/server-sequential-thinking",
|
||||||
status: "connecting",
|
status: "disconnected",
|
||||||
canTestClientFeatures: false,
|
|
||||||
...makeServerCallbacks(),
|
|
||||||
},
|
|
||||||
]}
|
|
||||||
onAddManually={fn()}
|
|
||||||
onImportConfig={fn()}
|
|
||||||
onImportServerJson={fn()}
|
|
||||||
/>
|
|
||||||
),
|
|
||||||
},
|
|
||||||
};
|
|
||||||
|
|
||||||
export const Connecting: Story = {
|
|
||||||
args: {
|
|
||||||
children: (
|
|
||||||
<ServerListScreen
|
|
||||||
servers={[
|
|
||||||
{
|
|
||||||
name: "everything-server",
|
|
||||||
version: "1.0.0",
|
|
||||||
transport: "stdio",
|
|
||||||
connectionMode: "Via Proxy",
|
|
||||||
command: "npx -y @modelcontextprotocol/server-everything",
|
|
||||||
status: "connecting",
|
|
||||||
canTestClientFeatures: false,
|
canTestClientFeatures: false,
|
||||||
...makeServerCallbacks(),
|
...makeServerCallbacks(),
|
||||||
},
|
},
|
||||||
|
|
|
||||||
Loading…
Reference in a new issue