diff --git a/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.stories.tsx b/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.stories.tsx index faa765db..2ed0b159 100644 --- a/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.stories.tsx +++ b/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.stories.tsx @@ -6,8 +6,7 @@ const meta: Meta = { title: "Elements/ConnectionToggle", component: ConnectionToggle, args: { - onConnect: fn(), - onDisconnect: fn(), + onToggle: fn(), }, }; diff --git a/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.tsx b/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.tsx index 5dcb6265..9ab70e2b 100644 --- a/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.tsx +++ b/clients/web/src/components/elements/ConnectionToggle/ConnectionToggle.tsx @@ -4,15 +4,13 @@ import type { ConnectionStatus } from "@inspector/core/mcp/types.js"; export interface ConnectionToggleProps { status: ConnectionStatus; disabled?: boolean; - onConnect: () => void; - onDisconnect: () => void; + onToggle: () => void; } export function ConnectionToggle({ status, disabled = false, - onConnect, - onDisconnect, + onToggle, }: ConnectionToggleProps) { const isConnected = status === "connected"; const isConnecting = status === "connecting"; @@ -22,13 +20,7 @@ export function ConnectionToggle({ size="lg" checked={isConnected || isConnecting} disabled={disabled || isConnecting} - onChange={(event) => { - if (event.currentTarget.checked) { - onConnect(); - } else { - onDisconnect(); - } - }} + onChange={onToggle} /> ); } diff --git a/clients/web/src/components/elements/TransportBadge/TransportBadge.tsx b/clients/web/src/components/elements/TransportBadge/TransportBadge.tsx index 129d88e1..2a553967 100644 --- a/clients/web/src/components/elements/TransportBadge/TransportBadge.tsx +++ b/clients/web/src/components/elements/TransportBadge/TransportBadge.tsx @@ -7,7 +7,7 @@ export interface TransportBadgeProps { const transportLabel: Record = { stdio: "STDIO", - sse: "SSE", + sse: "HTTP", "streamable-http": "HTTP", }; diff --git a/clients/web/src/components/groups/ServerCard/ServerCard.stories.tsx b/clients/web/src/components/groups/ServerCard/ServerCard.stories.tsx index 65aed920..64ff1273 100644 --- a/clients/web/src/components/groups/ServerCard/ServerCard.stories.tsx +++ b/clients/web/src/components/groups/ServerCard/ServerCard.stories.tsx @@ -33,16 +33,11 @@ const meta: Meta = { component: ServerCard, args: { onToggleConnection: fn(), - onSetActiveServer: fn(), onServerInfo: fn(), onSettings: fn(), onEdit: fn(), onClone: fn(), onRemove: fn(), - onTestSampling: fn(), - onTestElicitationForm: fn(), - onTestElicitationUrl: fn(), - onConfigureRoots: fn(), }, }; @@ -51,61 +46,47 @@ type Story = StoryObj; export const Connected: Story = { args: { + id: "7f3b2a8e-1c4d-4e5f-9a0b-3c2d1e4f5a6b", name: "My MCP Server", config: stdioConfig, info: { name: "My MCP Server", version: "1.2.0" }, connection: connected, - connectionMode: "Subprocess", - canTestClientFeatures: false, }, }; export const Disconnected: Story = { args: { + id: "7f3b2a8e-1c4d-4e5f-9a0b-3c2d1e4f5a6b", name: "My MCP Server", config: stdioConfig, info: { name: "My MCP Server", version: "1.2.0" }, connection: disconnected, - connectionMode: "Subprocess", - canTestClientFeatures: false, }, }; export const Connecting: Story = { args: { + id: "7f3b2a8e-1c4d-4e5f-9a0b-3c2d1e4f5a6b", name: "My MCP Server", config: stdioConfig, info: { name: "My MCP Server", version: "1.2.0" }, connection: connecting, - connectionMode: "Subprocess", - canTestClientFeatures: false, }, }; export const Failed: Story = { args: { + id: "7f3b2a8e-1c4d-4e5f-9a0b-3c2d1e4f5a6b", name: "My MCP Server", config: stdioConfig, info: { name: "My MCP Server", version: "1.2.0" }, connection: failed, - connectionMode: "Subprocess", - canTestClientFeatures: false, - }, -}; - -export const WithClientFeatures: Story = { - args: { - name: "My MCP Server", - config: stdioConfig, - info: { name: "My MCP Server", version: "1.2.0" }, - connection: connected, - connectionMode: "Subprocess", - canTestClientFeatures: true, }, }; export const LongServerName: Story = { args: { + id: "a1b2c3d4-e5f6-4789-9abc-def012345678", name: "my-organization-super-long-experimental-mcp-server-with-many-features-v2", config: { command: @@ -116,42 +97,37 @@ export const LongServerName: Story = { version: "1.0.0-beta.42", }, connection: connected, - connectionMode: "Subprocess", - canTestClientFeatures: true, }, }; export const HttpDirect: Story = { args: { + id: "9e8d7c6b-5a4f-4321-bdc6-fedcba098765", name: "Remote API Server", config: httpConfig, info: { name: "Remote API Server", version: "2.0.0" }, connection: connected, - connectionMode: "Direct", - canTestClientFeatures: false, }, }; export const CompactConnected: Story = { args: { + id: "7f3b2a8e-1c4d-4e5f-9a0b-3c2d1e4f5a6b", name: "My MCP Server", config: stdioConfig, info: { name: "My MCP Server", version: "1.2.0" }, connection: connected, - connectionMode: "Subprocess", - canTestClientFeatures: false, compact: true, }, }; export const CompactDisconnected: Story = { args: { + id: "7f3b2a8e-1c4d-4e5f-9a0b-3c2d1e4f5a6b", name: "My MCP Server", config: stdioConfig, info: { name: "My MCP Server", version: "1.2.0" }, connection: disconnected, - connectionMode: "Subprocess", - canTestClientFeatures: false, compact: true, }, }; diff --git a/clients/web/src/components/groups/ServerCard/ServerCard.tsx b/clients/web/src/components/groups/ServerCard/ServerCard.tsx index 75f8292d..14804548 100644 --- a/clients/web/src/components/groups/ServerCard/ServerCard.tsx +++ b/clients/web/src/components/groups/ServerCard/ServerCard.tsx @@ -1,4 +1,4 @@ -import { Badge, Button, Card, Group, Menu, Stack, Text } from "@mantine/core"; +import { Badge, Button, Card, Group, Stack, Text } from "@mantine/core"; import type { Implementation } from "@modelcontextprotocol/sdk/types.js"; import type { ConnectionState, @@ -12,33 +12,33 @@ import { ContentViewer } from "../../elements/ContentViewer/ContentViewer"; import { InlineError } from "../../elements/InlineError/InlineError"; export interface ServerCardProps { + /** Stable unique identifier — the MCPConfig.mcpServers map key. */ + id: string; + /** Display label shown in the card header. May or may not equal id. */ name: string; config: MCPServerConfig; info?: Implementation; connection: ConnectionState; - connectionMode: string; - canTestClientFeatures: boolean; activeServer?: string; - onSetActiveServer?: (name: string | undefined) => void; - onToggleConnection: (connect: boolean) => void; - onServerInfo: () => void; - onSettings: () => void; - onEdit: () => void; - onClone: () => void; - onRemove: () => void; - onTestSampling?: () => void; - onTestElicitationForm?: () => void; - onTestElicitationUrl?: () => void; - onConfigureRoots?: () => void; + onToggleConnection: (id: string) => void; + onServerInfo: (id: string) => void; + onSettings: (id: string) => void; + onEdit: (id: string) => void; + onClone: (id: string) => void; + onRemove: (id: string) => void; compact?: boolean; } const HeaderLeft = Group.withProps({ gap: "sm", + wrap: "nowrap", + miw: 0, + flex: 1, }); const HeaderRight = Group.withProps({ gap: "sm", + wrap: "nowrap", }); const ActionsRow = Group.withProps({ @@ -48,6 +48,9 @@ const ActionsRow = Group.withProps({ const ServerName = Text.withProps({ fw: 600, size: "lg", + truncate: "end", + miw: 0, + flex: 1, }); const ModeText = Text.withProps({ @@ -70,6 +73,12 @@ function getTransport(config: MCPServerConfig): ServerType { return config.type ?? "stdio"; } +const TRANSPORT_DESCRIPTION: Record = { + stdio: "Standard I/O", + sse: "SSE (Server Sent Events) [deprecated]", + "streamable-http": "Streamable HTTP", +}; + function getCommandOrUrl(config: MCPServerConfig): string { if (config.type === "sse" || config.type === "streamable-http") { return config.url; @@ -78,27 +87,21 @@ function getCommandOrUrl(config: MCPServerConfig): string { } export function ServerCard({ + id, name, config, info, connection, - connectionMode, - canTestClientFeatures, activeServer, - onSetActiveServer, onToggleConnection, onServerInfo, onSettings, onEdit, onClone, onRemove, - onTestSampling, - onTestElicitationForm, - onTestElicitationUrl, - onConfigureRoots, compact = false, }: ServerCardProps) { - const isDimmed = activeServer !== undefined && activeServer !== name; + const isDimmed = activeServer !== undefined && activeServer !== id; const transport = getTransport(config); const commandOrUrl = getCommandOrUrl(config); const version = info?.version; @@ -111,10 +114,9 @@ export function ServerCard({ {...(isDimmed ? { "aria-disabled": true, inert: true } : {})} > - + {name} - {version && {version}} { - onSetActiveServer?.(name); - onToggleConnection(true); - }} - onDisconnect={() => { - onSetActiveServer?.(undefined); - onToggleConnection(false); - }} + onToggle={() => onToggleConnection(id)} /> {!compact && ( <> - - - - {connectionMode} - - {canTestClientFeatures && ( - - - Test Client Features ▾ - - - - Simulate Sampling Request - - - Simulate Elicitation (Form) - - - Simulate Elicitation (URL) - - - Configure Roots - - - - )} + + {version && {version}} + + {TRANSPORT_DESCRIPTION[transport]} - Clone - Edit - Remove + onClone(id)}>Clone + onEdit(id)}>Edit + onRemove(id)}>Remove - Server Info - Settings + onServerInfo(id)}> + Server Info + + onSettings(id)}> + Settings + diff --git a/clients/web/src/components/groups/ServerInfoContent/ServerInfoContent.tsx b/clients/web/src/components/groups/ServerInfoContent/ServerInfoContent.tsx index 147d246d..407e6a78 100644 --- a/clients/web/src/components/groups/ServerInfoContent/ServerInfoContent.tsx +++ b/clients/web/src/components/groups/ServerInfoContent/ServerInfoContent.tsx @@ -90,7 +90,7 @@ export function ServerInfoContent({ {serverInfo.name} Version - {serverInfo.version} + {serverInfo.version ?? "—"} Protocol {protocolVersion} diff --git a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx index de732b81..3d82268d 100644 --- a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx +++ b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.stories.tsx @@ -8,7 +8,6 @@ import { } from "./ServerSettingsForm"; const defaultSettings: InspectorServerSettings = { - connectionMode: "proxy", headers: [], metadata: [], connectionTimeout: 30000, @@ -25,12 +24,6 @@ function InteractiveRender(args: ServerSettingsFormProps) { args.onExpandedSectionsChange(sections); updateArgs({ expandedSections: sections }); }} - onConnectionModeChange={(mode) => { - args.onConnectionModeChange(mode); - updateArgs({ - settings: { ...args.settings, connectionMode: mode }, - }); - }} onAddHeader={() => { args.onAddHeader(); updateArgs({ @@ -115,9 +108,8 @@ const meta: Meta = { component: ServerSettingsForm, render: InteractiveRender, args: { - expandedSections: ["connectionMode"], + expandedSections: ["headers"], onExpandedSectionsChange: fn(), - onConnectionModeChange: fn(), onAddHeader: fn(), onRemoveHeader: fn(), onHeaderChange: fn(), @@ -154,7 +146,6 @@ export const WithOAuth: Story = { args: { settings: { ...defaultSettings, - connectionMode: "direct", oauthClientId: "my-client-id", oauthClientSecret: "super-secret-value", oauthScopes: "read write admin", @@ -165,7 +156,6 @@ export const WithOAuth: Story = { export const AllConfigured: Story = { args: { settings: { - connectionMode: "proxy", headers: [ { key: "Authorization", value: "Bearer token-abc-123" }, { key: "X-Request-Id", value: "req-456" }, diff --git a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx index 3db4cf7f..cf9e451d 100644 --- a/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx +++ b/clients/web/src/components/groups/ServerSettingsForm/ServerSettingsForm.tsx @@ -4,15 +4,16 @@ import { Button, Group, NumberInput, - Select, Stack, Text, TextInput, } from "@mantine/core"; -import type { InspectorServerSettings } from "@inspector/core/mcp/types.js"; +import type { + InspectorServerSettings, + OAuthSettings, +} from "@inspector/core/mcp/types.js"; export type ServerSettingsSection = - | "connectionMode" | "headers" | "metadata" | "timeouts" @@ -22,28 +23,19 @@ export interface ServerSettingsFormProps { settings: InspectorServerSettings; expandedSections: ServerSettingsSection[]; onExpandedSectionsChange: (sections: ServerSettingsSection[]) => void; - onConnectionModeChange: (mode: "proxy" | "direct") => void; onAddHeader: () => void; onRemoveHeader: (index: number) => void; onHeaderChange: (index: number, key: string, value: string) => void; onAddMetadata: () => void; onRemoveMetadata: (index: number) => void; onMetadataChange: (index: number, key: string, value: string) => void; - onTimeoutChange: (field: string, value: number) => void; + onTimeoutChange: ( + field: "connectionTimeout" | "requestTimeout", + value: number, + ) => void; onOAuthChange: (oauth: OAuthSettings) => void; } -export interface OAuthSettings { - clientId: string; - clientSecret: string; - scopes: string; -} - -const CONNECTION_MODE_OPTIONS = [ - { value: "proxy", label: "Via Proxy" }, - { value: "direct", label: "Direct" }, -]; - const RemoveIcon = ActionIcon.withProps({ color: "red", variant: "subtle", @@ -103,7 +95,6 @@ export function ServerSettingsForm({ settings, expandedSections, onExpandedSectionsChange, - onConnectionModeChange, onAddHeader, onRemoveHeader, onHeaderChange, @@ -113,11 +104,13 @@ export function ServerSettingsForm({ onTimeoutChange, onOAuthChange, }: ServerSettingsFormProps) { - const handleTimeoutChange = (field: string) => (value: number | string) => { - const numValue = - typeof value === "string" ? parseInt(value, 10) || 0 : value; - onTimeoutChange(field, numValue); - }; + const handleTimeoutChange = + (field: "connectionTimeout" | "requestTimeout") => + (value: number | string) => { + const numValue = + typeof value === "string" ? parseInt(value, 10) || 0 : value; + onTimeoutChange(field, numValue); + }; function currentOAuth(): OAuthSettings { return { @@ -136,24 +129,6 @@ export function ServerSettingsForm({ } variant="separated" > - - Connection Mode - -