diff --git a/clients/web/.storybook/main.ts b/clients/web/.storybook/main.ts index 78cf7b39..c152c560 100644 --- a/clients/web/.storybook/main.ts +++ b/clients/web/.storybook/main.ts @@ -7,6 +7,7 @@ const config: StorybookConfig = { ], "addons": [ "@chromatic-com/storybook", + "@storybook/addon-actions", "@storybook/addon-vitest", "@storybook/addon-a11y", "@storybook/addon-docs", diff --git a/clients/web/package-lock.json b/clients/web/package-lock.json index 087803b8..e3ef6d3a 100644 --- a/clients/web/package-lock.json +++ b/clients/web/package-lock.json @@ -20,6 +20,7 @@ "@chromatic-com/storybook": "^5.0.1", "@eslint/js": "^9.39.4", "@storybook/addon-a11y": "^10.2.19", + "@storybook/addon-actions": "^9.0.8", "@storybook/addon-docs": "^10.2.19", "@storybook/addon-onboarding": "^10.2.19", "@storybook/addon-vitest": "^10.2.19", @@ -1695,6 +1696,17 @@ "storybook": "^10.2.19" } }, + "node_modules/@storybook/addon-actions": { + "version": "9.0.8", + "resolved": "https://registry.npmjs.org/@storybook/addon-actions/-/addon-actions-9.0.8.tgz", + "integrity": "sha512-LFePu7PPnWN0Il/uoUpmA5T0J0C7d6haJIbg0pXrjxW2MQVSYXE4S4LSUz8fOImltBDV3xAl6tLPYHFj6VcrOA==", + "dev": true, + "license": "MIT", + "funding": { + "type": "opencollective", + "url": "https://opencollective.com/storybook" + } + }, "node_modules/@storybook/addon-docs": { "version": "10.2.19", "resolved": "https://registry.npmjs.org/@storybook/addon-docs/-/addon-docs-10.2.19.tgz", diff --git a/clients/web/package.json b/clients/web/package.json index 0726c116..2927c5ba 100644 --- a/clients/web/package.json +++ b/clients/web/package.json @@ -23,30 +23,31 @@ "react-dom": "^19.2.4" }, "devDependencies": { + "@chromatic-com/storybook": "^5.0.1", "@eslint/js": "^9.39.4", + "@storybook/addon-a11y": "^10.2.19", + "@storybook/addon-actions": "^9.0.8", + "@storybook/addon-docs": "^10.2.19", + "@storybook/addon-onboarding": "^10.2.19", + "@storybook/addon-vitest": "^10.2.19", + "@storybook/react-vite": "^10.2.19", "@types/node": "^24.12.0", "@types/react": "^19.2.14", "@types/react-dom": "^19.2.3", "@vitejs/plugin-react": "^6.0.0", + "@vitest/browser-playwright": "^4.1.0", + "@vitest/coverage-v8": "^4.1.0", "eslint": "^9.39.4", "eslint-plugin-react-hooks": "^7.0.1", "eslint-plugin-react-refresh": "^0.5.2", + "eslint-plugin-storybook": "^10.2.19", "globals": "^17.4.0", + "playwright": "^1.58.2", + "prettier": "^3.8.1", + "storybook": "^10.2.19", "typescript": "~5.9.3", "typescript-eslint": "^8.56.1", - "prettier": "^3.8.1", "vite": "^8.0.0", - "storybook": "^10.2.19", - "@storybook/react-vite": "^10.2.19", - "@chromatic-com/storybook": "^5.0.1", - "@storybook/addon-vitest": "^10.2.19", - "@storybook/addon-a11y": "^10.2.19", - "@storybook/addon-docs": "^10.2.19", - "@storybook/addon-onboarding": "^10.2.19", - "eslint-plugin-storybook": "^10.2.19", - "vitest": "^4.1.0", - "playwright": "^1.58.2", - "@vitest/browser-playwright": "^4.1.0", - "@vitest/coverage-v8": "^4.1.0" + "vitest": "^4.1.0" } } diff --git a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx index 3fa2f3a3..da287ae3 100644 --- a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx +++ b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx @@ -13,6 +13,7 @@ const meta: Meta = { onRemoveHeader: fn(), onHeaderChange: fn(), onCopyResponse: fn(), + onTestCapability: fn(), clientCapabilities: [ { name: "experimental/customSampling", enabled: false }, { name: "experimental/batchRequests", enabled: true }, diff --git a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx index fc5c6120..bafe8628 100644 --- a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx +++ b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx @@ -51,6 +51,7 @@ export interface ExperimentalFeaturesPanelProps { onRemoveHeader: (index: number) => void; onHeaderChange: (index: number, key: string, value: string) => void; onCopyResponse: () => void; + onTestCapability: (name: string) => void; } export function ExperimentalFeaturesPanel({ @@ -67,6 +68,7 @@ export function ExperimentalFeaturesPanel({ onRemoveHeader, onHeaderChange, onCopyResponse, + onTestCapability, }: ExperimentalFeaturesPanelProps) { return ( @@ -94,7 +96,11 @@ export function ExperimentalFeaturesPanel({ )} - diff --git a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx index a4da9582..3a29c4b7 100644 --- a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx +++ b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx @@ -6,7 +6,6 @@ const meta: Meta = { title: "Molecules/ResourcePreviewPanel", component: ResourcePreviewPanel, args: { - onCopy: fn(), onSubscribe: fn(), onUnsubscribe: fn(), }, diff --git a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx index 92eab353..9e9c479b 100644 --- a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx +++ b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx @@ -8,7 +8,6 @@ export interface ResourcePreviewPanelProps { content: string; lastUpdated?: string; isSubscribed: boolean; - onCopy: () => void; onSubscribe: () => void; onUnsubscribe: () => void; } @@ -26,7 +25,6 @@ export function ResourcePreviewPanel({ content, lastUpdated, isSubscribed, - onCopy, onSubscribe, onUnsubscribe, }: ResourcePreviewPanelProps) { @@ -61,11 +59,9 @@ export function ResourcePreviewPanel({ type={resolveContentType(mimeType)} content={content} mimeType={mimeType} + copyable /> - diff --git a/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx b/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx index d1895a6b..86ff02a0 100644 --- a/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx +++ b/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx @@ -1,7 +1,6 @@ import { Badge, Button, - Checkbox, Divider, Group, Paper, @@ -134,11 +133,10 @@ export function SamplingRequestPanel({ {includeContext && ( - + + Include Context: + {includeContext} + )} {tools && tools.length > 0 && ( diff --git a/clients/web/src/components/molecules/ServerCard/ServerCard.stories.tsx b/clients/web/src/components/molecules/ServerCard/ServerCard.stories.tsx index e12c1c72..d42f42c7 100644 --- a/clients/web/src/components/molecules/ServerCard/ServerCard.stories.tsx +++ b/clients/web/src/components/molecules/ServerCard/ServerCard.stories.tsx @@ -89,6 +89,19 @@ export const WithClientFeatures: Story = { }, }; +export const LongServerName: Story = { + args: { + name: "my-organization-super-long-experimental-mcp-server-with-many-features-v2", + version: "1.0.0-beta.42", + transport: "stdio", + connectionMode: "Subprocess", + command: + "npx -y @my-organization/super-long-experimental-mcp-server-with-many-features-v2", + status: "connected", + canTestClientFeatures: true, + }, +}; + export const HttpDirect: Story = { args: { name: "Remote API Server", diff --git a/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx b/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx index 7637225c..d507188e 100644 --- a/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx +++ b/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx @@ -1,4 +1,4 @@ -import { Button, Divider, Group, Stack, Text, Title } from "@mantine/core"; +import { Button, Divider, Group, Stack, Text } from "@mantine/core"; import { AnnotationBadge } from "../../atoms/AnnotationBadge/AnnotationBadge"; import { ProgressDisplay } from "../../atoms/ProgressDisplay/ProgressDisplay"; import { SchemaForm } from "../SchemaForm/SchemaForm"; @@ -14,6 +14,7 @@ export interface ToolAnnotations { export interface ToolDetailPanelProps { name: string; + title?: string; description?: string; annotations?: ToolAnnotations; schema: JsonSchema; @@ -27,6 +28,7 @@ export interface ToolDetailPanelProps { export function ToolDetailPanel({ name, + title, description, annotations, schema, @@ -46,8 +48,39 @@ export function ToolDetailPanel({ annotations.hints); return ( - - Tool: {name} + + + {title ?? name} + + {title && ( + + {name} + + )} + {hasAnnotations && ( + + {annotations.audience && ( + + )} + {annotations.readOnly && ( + + )} + {annotations.destructive && ( + + )} + {annotations.longRunning && ( + + )} + {annotations.hints && ( + + {annotations.hints} + + )} + + )} {description && ( @@ -55,34 +88,6 @@ export function ToolDetailPanel({ )} - {hasAnnotations && ( - <> - Annotations: - - {annotations.audience && ( - - )} - {annotations.readOnly && ( - - )} - {annotations.destructive && ( - - )} - {annotations.longRunning && ( - - )} - {annotations.hints && ( - - {annotations.hints} - - )} - - - )} - void; } export function ToolListItem({ name, - annotations, + title, selected, onClick, }: ToolListItemProps) { @@ -28,18 +21,14 @@ export function ToolListItem({ style={{ borderRadius: "var(--mantine-radius-md)" }} onClick={onClick} > - - {name} - {annotations && annotations.length > 0 && ( - - {annotations.map((annotation) => ( - - ))} - + + + {title ?? name} + + {title && ( + + {name} + )} diff --git a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx index c1c76a0e..9a4ed5c8 100644 --- a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx +++ b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx @@ -21,23 +21,26 @@ export default meta; type Story = StoryObj; const sampleTools: ToolListItemProps[] = [ - { name: "send_message", selected: false, onClick: fn() }, + { + name: "send_message", + title: "Send Message", + selected: false, + onClick: fn(), + }, { name: "create_record", - annotations: [{ label: "read-only" }], + title: "Create Record", selected: false, onClick: fn(), }, { name: "delete_records", - annotations: [{ label: "destructive", variant: "destructive" }], selected: false, onClick: fn(), }, { name: "list_users", selected: false, onClick: fn() }, { name: "batch_process", - annotations: [{ label: "long-run", variant: "longRun" }], selected: false, onClick: fn(), }, @@ -45,6 +48,7 @@ const sampleTools: ToolListItemProps[] = [ const selectedToolData: ToolDetailPanelProps = { name: "create_record", + title: "Create Record", description: "Creates a new record with the given parameters", schema: { type: "object", @@ -79,7 +83,6 @@ const resultData: ResultPanelProps = { ), }, ], - onCopy: fn(), onClear: fn(), }; @@ -111,6 +114,51 @@ export const WithResult: Story = { }, }; +export const LongToolName: Story = { + args: { + tools: [ + ...sampleTools, + { + name: "organization_internal_database_complex_multi_table_join_query_with_aggregation_and_filtering", + selected: true, + onClick: fn(), + }, + ], + selectedTool: { + name: "organization_internal_database_complex_multi_table_join_query_with_aggregation_and_filtering", + annotations: { + readOnly: true, + longRunning: true, + }, + description: + "Executes a complex multi-table join query across the organization's internal database with support for aggregation functions, nested filtering, and pagination of large result sets.", + schema: { + type: "object", + properties: { + primary_table_name: { + type: "string", + description: "The main table to query from", + }, + join_configuration: { + type: "string", + description: "JSON configuration for table joins", + }, + aggregation_functions: { + type: "string", + description: "Comma-separated list of aggregation functions to apply", + }, + }, + required: ["primary_table_name"], + }, + formValues: {}, + isExecuting: false, + onFormChange: fn(), + onExecute: fn(), + onCancel: fn(), + }, + }, +}; + export const WithListChanged: Story = { args: { tools: sampleTools, diff --git a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx index 4bc50a2e..d69b5349 100644 --- a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx +++ b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx @@ -60,7 +60,7 @@ export function ToolsScreen({ - + {selectedTool ? ( ) : ( diff --git a/clients/web/src/theme/Paper.ts b/clients/web/src/theme/Paper.ts index df5026ff..041bb892 100644 --- a/clients/web/src/theme/Paper.ts +++ b/clients/web/src/theme/Paper.ts @@ -17,6 +17,14 @@ export const ThemePaper = Paper.extend({ }, }; } + if (props.variant === "contained") { + return { + root: { + overflow: "hidden", + minWidth: 0, + }, + }; + } return { root: {} }; }, });