Phase 2: Refactor Server/Settings/ViewShell cluster to MCP SDK types

ServerCard: Replace flat scalar props (name, version, transport, status,
error) with grouped objects (config: MCPServerConfig, info?: Implementation,
connection: ConnectionState). Remove auto-connect useEffect — dumb
components must not self-dispatch side effects. Derive transport and
command/url from config.

ServerInfoContent: Replace flat props with initializeResult: InitializeResult
and clientCapabilities: ClientCapabilities from MCP SDK. Derive capability
lists by iterating the ServerCapabilities record.

ServerSettingsForm: Collapse flat setting props (connectionMode, headers,
metadata, timeouts, oauth*) into settings: InspectorServerSettings. Add
InspectorServerSettings type to core/mcp/types.ts.

ImportServerJsonPanel: Replace scattered scalar props (jsonContent,
selectedPackageIndex, serverName) with draft: InspectorServerJsonDraft.

ViewHeader: Replace serverName: string with serverInfo: Implementation.
Use ConnectionStatus from core types.

ConnectedView: Update to pass serverInfo: Implementation instead of
serverName. Use ConnectionStatus type.

Update all stories for ServerCard, ServerInfoContent, ServerSettingsForm,
ImportServerJsonPanel, ViewHeader, ServerListScreen, ConnectedView, and
UnconnectedView.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
cliffhall 2026-04-21 15:22:08 -04:00
commit 771a67ee20
2 changed files with 562 additions and 539 deletions

View file

@ -1,6 +1,31 @@
import type {
ClientCapabilities,
InitializeResult,
} from "@modelcontextprotocol/sdk/types.js";
import type { Meta, StoryObj } from "@storybook/react-vite";
import { ServerInfoContent } from "./ServerInfoContent";
const fullResult: InitializeResult = {
protocolVersion: "2025-03-26",
serverInfo: { name: "Everything Server", version: "2.1.0" },
capabilities: {
tools: { listChanged: true },
resources: { subscribe: true, listChanged: true },
prompts: { listChanged: true },
logging: {},
completions: {},
},
instructions:
"This server provides access to the project management system. Use the list_projects tool first to discover available projects before querying tasks. Rate limiting applies: max 60 requests per minute.",
};
const fullClientCaps: ClientCapabilities = {
roots: { listChanged: true },
sampling: {},
elicitation: {},
experimental: {},
};
const meta: Meta<typeof ServerInfoContent> = {
title: "Groups/ServerInfoContent",
component: ServerInfoContent,
@ -11,80 +36,54 @@ type Story = StoryObj<typeof ServerInfoContent>;
export const FullCapabilities: Story = {
args: {
name: "Everything Server",
version: "2.1.0",
protocolVersion: "2025-03-26",
initializeResult: fullResult,
clientCapabilities: fullClientCaps,
transport: "stdio",
serverCapabilities: [
{ capability: "tools", supported: true, count: 12 },
{ capability: "resources", supported: true, count: 8 },
{ capability: "prompts", supported: true, count: 5 },
{ capability: "logging", supported: true },
{ capability: "completions", supported: true },
],
clientCapabilities: [
{ capability: "roots", supported: true, count: 3 },
{ capability: "sampling", supported: true },
{ capability: "elicitation", supported: true },
{ capability: "experimental", supported: true },
],
},
};
export const MinimalCapabilities: Story = {
args: {
name: "Simple Server",
version: "1.0.0",
protocolVersion: "2025-03-26",
initializeResult: {
protocolVersion: "2025-03-26",
serverInfo: { name: "Simple Server", version: "1.0.0" },
capabilities: {
tools: { listChanged: false },
},
},
clientCapabilities: {
roots: { listChanged: true },
},
transport: "streamable-http",
serverCapabilities: [
{ capability: "tools", supported: true, count: 2 },
{ capability: "resources", supported: false },
{ capability: "prompts", supported: false },
],
clientCapabilities: [
{ capability: "roots", supported: true },
{ capability: "sampling", supported: false },
{ capability: "elicitation", supported: false },
],
},
};
export const WithInstructions: Story = {
args: {
name: "Guided Server",
version: "1.5.0",
protocolVersion: "2025-03-26",
initializeResult: fullResult,
clientCapabilities: {
roots: { listChanged: true },
sampling: {},
},
transport: "stdio",
serverCapabilities: [
{ capability: "tools", supported: true, count: 4 },
{ capability: "resources", supported: true, count: 2 },
{ capability: "prompts", supported: true, count: 1 },
],
clientCapabilities: [
{ capability: "roots", supported: true },
{ capability: "sampling", supported: true },
],
instructions:
"This server provides access to the project management system. Use the list_projects tool first to discover available projects before querying tasks. Rate limiting applies: max 60 requests per minute.",
},
};
export const WithOAuth: Story = {
args: {
name: "Authenticated Server",
version: "3.0.0",
protocolVersion: "2025-03-26",
initializeResult: {
protocolVersion: "2025-03-26",
serverInfo: { name: "Authenticated Server", version: "3.0.0" },
capabilities: {
tools: { listChanged: true },
resources: { subscribe: true },
},
},
clientCapabilities: {
roots: { listChanged: true },
},
transport: "streamable-http",
serverCapabilities: [
{ capability: "tools", supported: true, count: 6 },
{ capability: "resources", supported: true, count: 3 },
],
clientCapabilities: [
{ capability: "roots", supported: true },
{ capability: "sampling", supported: false },
],
oauthDetails: {
oauth: {
authUrl: "https://auth.example.com/oauth2/authorize",
scopes: ["read", "write", "admin"],
accessToken: "eyJhbGciOiJSUzI1NiIs...truncated",

View file

@ -7,20 +7,27 @@ import {
Text,
Title,
} from "@mantine/core";
import type {
ClientCapabilities,
InitializeResult,
} from "@modelcontextprotocol/sdk/types.js";
import type { ServerType } from "@inspector/core/mcp/types.js";
import {
CapabilityItem,
type CapabilityItemProps,
type CapabilityKey,
} from "../../elements/CapabilityItem/CapabilityItem";
export interface OAuthDetails {
authUrl?: string;
scopes?: string[];
accessToken?: string;
}
export interface ServerInfoContentProps {
name: string;
version: string;
protocolVersion: string;
transport: string;
serverCapabilities: CapabilityItemProps[];
clientCapabilities: CapabilityItemProps[];
instructions?: string;
oauthDetails?: { authUrl?: string; scopes?: string[]; accessToken?: string };
initializeResult: InitializeResult;
clientCapabilities: ClientCapabilities;
transport: ServerType;
oauth?: OAuthDetails;
}
const ValueText = Text.withProps({
@ -32,26 +39,37 @@ function formatScopes(scopes: string[]): string {
return scopes.join(", ");
}
function getCapabilityEntries(
capabilities: Record<string, unknown>,
): { capability: CapabilityKey; supported: boolean }[] {
return Object.entries(capabilities).map(([key, value]) => ({
capability: key as CapabilityKey,
supported: value != null,
}));
}
export function ServerInfoContent({
name,
version,
protocolVersion,
transport,
serverCapabilities,
initializeResult,
clientCapabilities,
instructions,
oauthDetails,
transport,
oauth,
}: ServerInfoContentProps) {
const { serverInfo, protocolVersion, capabilities, instructions } =
initializeResult;
const serverCaps = getCapabilityEntries(capabilities);
const clientCaps = getCapabilityEntries(clientCapabilities);
return (
<Stack gap="md">
<Title order={3}>Server Information</Title>
<SimpleGrid cols={2}>
<Text size="sm">Name</Text>
<ValueText>{name}</ValueText>
<ValueText>{serverInfo.name}</ValueText>
<Text size="sm">Version</Text>
<ValueText>{version}</ValueText>
<ValueText>{serverInfo.version}</ValueText>
<Text size="sm">Protocol</Text>
<ValueText>{protocolVersion}</ValueText>
@ -63,23 +81,21 @@ export function ServerInfoContent({
<SimpleGrid cols={2}>
<Stack gap="xs">
<Title order={5}>Server Capabilities</Title>
{serverCapabilities.map((cap) => (
{serverCaps.map((cap) => (
<CapabilityItem
key={cap.capability}
capability={cap.capability}
supported={cap.supported}
count={cap.count}
/>
))}
</Stack>
<Stack gap="xs">
<Title order={5}>Client Capabilities</Title>
{clientCapabilities.map((cap) => (
{clientCaps.map((cap) => (
<CapabilityItem
key={cap.capability}
capability={cap.capability}
supported={cap.supported}
count={cap.count}
/>
))}
</Stack>
@ -92,26 +108,26 @@ export function ServerInfoContent({
</Stack>
)}
{oauthDetails && (
{oauth && (
<Stack gap="xs">
<Title order={5}>OAuth Details</Title>
<Stack gap="xs">
{oauthDetails.authUrl && (
{oauth.authUrl && (
<SimpleGrid cols={2}>
<Text size="sm">Auth URL</Text>
<Code>{oauthDetails.authUrl}</Code>
<Code>{oauth.authUrl}</Code>
</SimpleGrid>
)}
{oauthDetails.scopes && oauthDetails.scopes.length > 0 && (
{oauth.scopes && oauth.scopes.length > 0 && (
<SimpleGrid cols={2}>
<Text size="sm">Scopes</Text>
<ValueText>{formatScopes(oauthDetails.scopes)}</ValueText>
<ValueText>{formatScopes(oauth.scopes)}</ValueText>
</SimpleGrid>
)}
{oauthDetails.accessToken && (
{oauth.accessToken && (
<SimpleGrid cols={2}>
<Text size="sm">Access Token</Text>
<Code>{oauthDetails.accessToken}</Code>
<Code>{oauth.accessToken}</Code>
</SimpleGrid>
)}
</Stack>