Refactor ServerCard stack to use id-based callbacks

Distinguishes server `id` (the unique map key in MCPConfig.mcpServers)
from `name` (display label) so containers can dispatch by id without
relying on display strings. ServerCard, ConnectionToggle, ServerListScreen,
and supporting stories all updated.

- ServerCard: add `id` prop; all action callbacks now `(id: string) => void`
- ConnectionToggle: collapse `onConnect`/`onDisconnect` into single `onToggle`
- ServerListScreen: lift `activeServer` to a prop; combine `onSetActive` +
  `onToggleConnection` into a single `onToggleConnection(id)` callback
- Stories: use UUIDs for server ids so callbacks visibly carry ids, not names

Removes the "Direct"/"Via Proxy" connection mode concept, which is no
longer planned. ServerSettingsForm drops the section, ServerCard drops
the badge, InspectorServerSettings drops the field.

Removes the "Test Client Features" dropdown from ServerCard along with
its four handlers; that surface will live elsewhere.

TransportBadge now collapses `sse` and `streamable-http` into a single
"HTTP" label; ServerCard adds a grey transport descriptor next to the
badge ("Standard I/O", "SSE (Server Sent Events) [deprecated]",
"Streamable HTTP"). The version badge moves from the header row to sit
beside the transport badge.

ServerCard header no longer wraps; long server names truncate with
ellipsis.

Specification docs (`v2_scope.md`, `v2_screenshots.md`,
`v2_ux_interfaces.md`, `v2_ux_storybook.md`) updated to match.

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-04-25 18:33:38 -04:00
commit 1149288ea6
2 changed files with 196 additions and 326 deletions

View file

@ -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<typeof ServerSettingsForm> = {
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" },

View file

@ -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"
>
<Accordion.Item value="connectionMode">
<Accordion.Control>Connection Mode</Accordion.Control>
<Accordion.Panel>
<Select
data={CONNECTION_MODE_OPTIONS}
value={settings.connectionMode}
onChange={(value) => {
if (value) onConnectionModeChange(value as "proxy" | "direct");
}}
description={
settings.connectionMode === "proxy"
? "Route through inspector proxy (required for STDIO)"
: undefined
}
/>
</Accordion.Panel>
</Accordion.Item>
<Accordion.Item value="headers">
<Accordion.Control>Custom Headers</Accordion.Control>
<Accordion.Panel>