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:
parent
c25a092ed1
commit
771a67ee20
2 changed files with 562 additions and 539 deletions
|
|
@ -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",
|
||||
|
|
|
|||
|
|
@ -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>
|
||||
|
|
|
|||
Loading…
Reference in a new issue