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:
parent
29ce55290f
commit
1149288ea6
2 changed files with 196 additions and 326 deletions
|
|
@ -1,7 +1,6 @@
|
|||
import type { Meta, StoryObj } from "@storybook/react-vite";
|
||||
import { fn } from "storybook/test";
|
||||
import { ServerListScreen } from "./ServerListScreen";
|
||||
import type { ServerCardProps } from "../../groups/ServerCard/ServerCard";
|
||||
import { ServerListScreen, type ServerEntry } from "./ServerListScreen";
|
||||
|
||||
const meta: Meta<typeof ServerListScreen> = {
|
||||
title: "Screens/ServerListScreen",
|
||||
|
|
@ -11,64 +10,40 @@ const meta: Meta<typeof ServerListScreen> = {
|
|||
onAddManually: fn(),
|
||||
onImportConfig: fn(),
|
||||
onImportServerJson: fn(),
|
||||
},
|
||||
};
|
||||
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ServerListScreen>;
|
||||
|
||||
function makeServerCallbacks(): Pick<
|
||||
ServerCardProps,
|
||||
| "onToggleConnection"
|
||||
| "onServerInfo"
|
||||
| "onSettings"
|
||||
| "onEdit"
|
||||
| "onClone"
|
||||
| "onRemove"
|
||||
| "onTestSampling"
|
||||
| "onTestElicitationForm"
|
||||
| "onTestElicitationUrl"
|
||||
| "onConfigureRoots"
|
||||
> {
|
||||
return {
|
||||
onToggleConnection: fn(),
|
||||
onServerInfo: fn(),
|
||||
onSettings: fn(),
|
||||
onEdit: fn(),
|
||||
onClone: fn(),
|
||||
onRemove: fn(),
|
||||
onTestSampling: fn(),
|
||||
onTestElicitationForm: fn(),
|
||||
onTestElicitationUrl: fn(),
|
||||
onConfigureRoots: fn(),
|
||||
};
|
||||
}
|
||||
},
|
||||
};
|
||||
|
||||
const connectedStdioServer: ServerCardProps = {
|
||||
export default meta;
|
||||
type Story = StoryObj<typeof ServerListScreen>;
|
||||
|
||||
const connectedStdioServer: ServerEntry = {
|
||||
id: "5e8c3d1f-2a4b-4c6d-8e7f-1a2b3c4d5e6f",
|
||||
name: "Local Dev Server",
|
||||
config: {
|
||||
command: "npx @modelcontextprotocol/server-filesystem /home/user/projects",
|
||||
},
|
||||
info: { name: "Local Dev Server", version: "1.2.0" },
|
||||
connection: { status: "connected" },
|
||||
connectionMode: "Direct",
|
||||
canTestClientFeatures: true,
|
||||
...makeServerCallbacks(),
|
||||
};
|
||||
|
||||
const disconnectedStdioServer: ServerCardProps = {
|
||||
const disconnectedStdioServer: ServerEntry = {
|
||||
id: "b3a7c1d2-9f8e-4a5b-bc6d-7e8f9a0b1c2d",
|
||||
name: "Database Tools",
|
||||
config: {
|
||||
command: "python -m mcp_server_sqlite --db-path ./data.db",
|
||||
},
|
||||
info: { name: "Database Tools", version: "0.9.1" },
|
||||
connection: { status: "disconnected" },
|
||||
connectionMode: "Direct",
|
||||
canTestClientFeatures: false,
|
||||
...makeServerCallbacks(),
|
||||
};
|
||||
|
||||
const failedHttpServer: ServerCardProps = {
|
||||
const failedHttpServer: ServerEntry = {
|
||||
id: "c4d5e6f7-8a9b-4c0d-9e1f-2a3b4c5d6e7f",
|
||||
name: "Remote API Server",
|
||||
config: {
|
||||
type: "streamable-http",
|
||||
|
|
@ -83,21 +58,16 @@ const failedHttpServer: ServerCardProps = {
|
|||
details: "ECONNREFUSED 127.0.0.1:8080 - The server may not be running.",
|
||||
},
|
||||
},
|
||||
connectionMode: "Streamable HTTP",
|
||||
canTestClientFeatures: false,
|
||||
...makeServerCallbacks(),
|
||||
};
|
||||
|
||||
const connectingHttpServer: ServerCardProps = {
|
||||
const connectingHttpServer: ServerEntry = {
|
||||
id: "d6e7f8a9-0b1c-4d2e-bf3a-4b5c6d7e8f90",
|
||||
name: "Staging Server",
|
||||
config: {
|
||||
type: "streamable-http",
|
||||
url: "https://staging.example.com/mcp",
|
||||
},
|
||||
connection: { status: "connecting" },
|
||||
connectionMode: "Streamable HTTP",
|
||||
canTestClientFeatures: false,
|
||||
...makeServerCallbacks(),
|
||||
};
|
||||
|
||||
export const MultipleServers: Story = {
|
||||
|
|
|
|||
|
|
@ -4,11 +4,24 @@ import { ServerCard } from "../../groups/ServerCard/ServerCard";
|
|||
import { ServerListControls } from "../../groups/ServerListControls/ServerListControls";
|
||||
import type { ServerCardProps } from "../../groups/ServerCard/ServerCard";
|
||||
|
||||
export type ServerEntry = Pick<
|
||||
ServerCardProps,
|
||||
"id" | "name" | "config" | "info" | "connection"
|
||||
>;
|
||||
|
||||
export interface ServerListScreenProps {
|
||||
servers: ServerCardProps[];
|
||||
servers: ServerEntry[];
|
||||
/** Id of the server the wiring layer treats as active (drives card dimming). */
|
||||
activeServer?: string;
|
||||
onAddManually: () => void;
|
||||
onImportConfig: () => void;
|
||||
onImportServerJson: () => 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;
|
||||
}
|
||||
|
||||
const PageContainer = Stack.withProps({
|
||||
|
|
@ -23,12 +36,18 @@ const EmptyState = Text.withProps({
|
|||
|
||||
export function ServerListScreen({
|
||||
servers,
|
||||
activeServer,
|
||||
onAddManually,
|
||||
onImportConfig,
|
||||
onImportServerJson,
|
||||
onToggleConnection,
|
||||
onServerInfo,
|
||||
onSettings,
|
||||
onEdit,
|
||||
onClone,
|
||||
onRemove,
|
||||
}: ServerListScreenProps) {
|
||||
const [compact, setCompact] = useState<boolean>(false);
|
||||
const [activeServer, setActiveServer] = useState<string | undefined>();
|
||||
|
||||
function handleToggleList() {
|
||||
setCompact((prev) => !prev);
|
||||
|
|
@ -58,10 +77,15 @@ export function ServerListScreen({
|
|||
>
|
||||
{servers.map((server) => (
|
||||
<ServerCard
|
||||
key={server.name}
|
||||
key={server.id}
|
||||
compact={compact}
|
||||
activeServer={activeServer}
|
||||
onSetActiveServer={setActiveServer}
|
||||
onToggleConnection={onToggleConnection}
|
||||
onServerInfo={onServerInfo}
|
||||
onSettings={onSettings}
|
||||
onEdit={onEdit}
|
||||
onClone={onClone}
|
||||
onRemove={onRemove}
|
||||
{...server}
|
||||
/>
|
||||
))}
|
||||
|
|
|
|||
Loading…
Reference in a new issue