diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
new file mode 100644
index 00000000..dca906dc
--- /dev/null
+++ b/.github/workflows/main.yml
@@ -0,0 +1,35 @@
+name: Run install, format, lint, build, and test on every push or pull request
+
+on: [push, pull_request]
+
+jobs:
+ build:
+ runs-on: ubuntu-latest
+
+ steps:
+ - name: Checkout code
+ uses: actions/checkout@v6
+
+ - name: Setup Node.js
+ uses: actions/setup-node@v6
+ with:
+ node-version: '20.x'
+ cache: 'npm'
+
+ - name: Install dependencies
+ run: npm install
+
+ - name: Check formatting
+ run: npm run format:check
+
+ - name: Run linter
+ run: npm run lint
+
+ - name: Run type check
+ run: npm run typecheck
+
+ - name: Run Build
+ run: npm run build
+
+ - name: Run tests
+ run: npm run test
diff --git a/clients/web/package-lock.json b/clients/web/package-lock.json
index 90eae869..087803b8 100644
--- a/clients/web/package-lock.json
+++ b/clients/web/package-lock.json
@@ -1,11 +1,11 @@
{
- "name": "web",
+ "name": "@modelcontextprotocol/inspector-web",
"version": "0.0.0",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
- "name": "web",
+ "name": "@modelcontextprotocol/inspector-web",
"version": "0.0.0",
"dependencies": {
"@emotion/react": "^11.14.0",
@@ -36,6 +36,7 @@
"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",
@@ -5009,6 +5010,22 @@
"node": ">= 0.8.0"
}
},
+ "node_modules/prettier": {
+ "version": "3.8.1",
+ "resolved": "https://registry.npmjs.org/prettier/-/prettier-3.8.1.tgz",
+ "integrity": "sha512-UOnG6LftzbdaHZcKoPFtOcCKztrQ57WkHDeRD9t/PTQtmT0NHSeWWepj6pS0z/N7+08BHFDQVUrfmfMRcZwbMg==",
+ "dev": true,
+ "license": "MIT",
+ "bin": {
+ "prettier": "bin/prettier.cjs"
+ },
+ "engines": {
+ "node": ">=14"
+ },
+ "funding": {
+ "url": "https://github.com/prettier/prettier?sponsor=1"
+ }
+ },
"node_modules/pretty-format": {
"version": "27.5.1",
"resolved": "https://registry.npmjs.org/pretty-format/-/pretty-format-27.5.1.tgz",
diff --git a/clients/web/package.json b/clients/web/package.json
index 6520e6ca..0726c116 100644
--- a/clients/web/package.json
+++ b/clients/web/package.json
@@ -7,6 +7,8 @@
"dev": "vite",
"build": "tsc -b && vite build",
"lint": "eslint .",
+ "format": "prettier --write src",
+ "format:check": "prettier --check src",
"preview": "vite preview",
"storybook": "storybook dev -p 6006",
"build-storybook": "storybook build"
@@ -32,6 +34,7 @@
"globals": "^17.4.0",
"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",
diff --git a/clients/web/src/App.css b/clients/web/src/App.css
index 290cc47e..4d054964 100644
--- a/clients/web/src/App.css
+++ b/clients/web/src/App.css
@@ -68,7 +68,7 @@
}
/* Dark mode overrides — tokens that need adjustment in dark scheme */
-[data-mantine-color-scheme='dark'] {
+[data-mantine-color-scheme="dark"] {
--inspector-surface-code: var(--mantine-color-dark-6);
--inspector-border-subtle: var(--mantine-color-dark-4);
}
diff --git a/clients/web/src/App.tsx b/clients/web/src/App.tsx
index f636eb58..a1a313e7 100644
--- a/clients/web/src/App.tsx
+++ b/clients/web/src/App.tsx
@@ -7,15 +7,15 @@ import {
useComputedColorScheme,
ActionIcon,
Group,
-} from '@mantine/core'
+} from "@mantine/core";
function App() {
- const { setColorScheme } = useMantineColorScheme()
- const computedColorScheme = useComputedColorScheme('light')
+ const { setColorScheme } = useMantineColorScheme();
+ const computedColorScheme = useComputedColorScheme("light");
const toggleColorScheme = () => {
- setColorScheme(computedColorScheme === 'dark' ? 'light' : 'dark')
- }
+ setColorScheme(computedColorScheme === "dark" ? "light" : "dark");
+ };
return (
@@ -28,7 +28,7 @@ function App() {
size="lg"
aria-label="Toggle color scheme"
>
- {computedColorScheme === 'dark' ? '\u2600' : '\u263E'}
+ {computedColorScheme === "dark" ? "\u2600" : "\u263E"}
@@ -36,7 +36,7 @@ function App() {
- )
+ );
}
-export default App
+export default App;
diff --git a/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.stories.tsx b/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.stories.tsx
index 0121aa2d..7226226c 100644
--- a/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.stories.tsx
+++ b/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.stories.tsx
@@ -1,52 +1,52 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { AnnotationBadge } from './AnnotationBadge'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { AnnotationBadge } from "./AnnotationBadge";
const meta: Meta = {
- title: 'Atoms/AnnotationBadge',
+ title: "Atoms/AnnotationBadge",
component: AnnotationBadge,
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Audience: Story = {
args: {
- label: 'user',
- variant: 'audience',
+ label: "user",
+ variant: "audience",
},
-}
+};
export const ReadOnly: Story = {
args: {
- label: 'read-only',
- variant: 'readOnly',
+ label: "read-only",
+ variant: "readOnly",
},
-}
+};
export const Destructive: Story = {
args: {
- label: 'destructive',
- variant: 'destructive',
+ label: "destructive",
+ variant: "destructive",
},
-}
+};
export const LongRun: Story = {
args: {
- label: 'long-run',
- variant: 'longRun',
+ label: "long-run",
+ variant: "longRun",
},
-}
+};
export const PriorityHigh: Story = {
args: {
- label: 'priority: high',
- variant: 'priority',
+ label: "priority: high",
+ variant: "priority",
},
-}
+};
export const Default: Story = {
args: {
- label: 'custom',
- variant: 'default',
+ label: "custom",
+ variant: "default",
},
-}
+};
diff --git a/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.tsx b/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.tsx
index 1b8fc159..bf696577 100644
--- a/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.tsx
+++ b/clients/web/src/components/atoms/AnnotationBadge/AnnotationBadge.tsx
@@ -1,27 +1,39 @@
-import { Badge } from '@mantine/core'
+import { Badge } from "@mantine/core";
-export type AnnotationVariant = 'audience' | 'readOnly' | 'destructive' | 'longRun' | 'priority' | 'default'
+export type AnnotationVariant =
+ | "audience"
+ | "readOnly"
+ | "destructive"
+ | "longRun"
+ | "priority"
+ | "default";
export interface AnnotationBadgeProps {
- label: string
- variant?: AnnotationVariant
+ label: string;
+ variant?: AnnotationVariant;
}
-const variantMap: Record = {
- audience: { color: 'gray', variant: 'outline' },
- readOnly: { color: 'dark', variant: 'filled' },
- destructive: { color: 'red', variant: 'outline' },
- longRun: { color: 'yellow', variant: 'outline' },
- priority: { color: 'orange', variant: 'light' },
- default: { color: 'gray', variant: 'light' },
-}
+const variantMap: Record<
+ AnnotationVariant,
+ { color: string; variant: string }
+> = {
+ audience: { color: "gray", variant: "outline" },
+ readOnly: { color: "dark", variant: "filled" },
+ destructive: { color: "red", variant: "outline" },
+ longRun: { color: "yellow", variant: "outline" },
+ priority: { color: "orange", variant: "light" },
+ default: { color: "gray", variant: "light" },
+};
-export function AnnotationBadge({ label, variant = 'default' }: AnnotationBadgeProps) {
- const { color, variant: badgeVariant } = variantMap[variant]
+export function AnnotationBadge({
+ label,
+ variant = "default",
+}: AnnotationBadgeProps) {
+ const { color, variant: badgeVariant } = variantMap[variant];
return (
{label}
- )
+ );
}
diff --git a/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.stories.tsx b/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.stories.tsx
index c7519033..0eae839d 100644
--- a/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.stories.tsx
+++ b/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.stories.tsx
@@ -1,8 +1,8 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { CapabilityItem } from './CapabilityItem';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { CapabilityItem } from "./CapabilityItem";
const meta: Meta = {
- title: 'Atoms/CapabilityItem',
+ title: "Atoms/CapabilityItem",
component: CapabilityItem,
};
@@ -11,14 +11,14 @@ type Story = StoryObj;
export const Supported: Story = {
args: {
- name: 'Tools',
+ name: "Tools",
supported: true,
},
};
export const SupportedWithCount: Story = {
args: {
- name: 'Tools',
+ name: "Tools",
supported: true,
count: 4,
},
@@ -26,7 +26,7 @@ export const SupportedWithCount: Story = {
export const NotSupported: Story = {
args: {
- name: 'Completions',
+ name: "Completions",
supported: false,
},
};
diff --git a/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.tsx b/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.tsx
index cdd7c733..2a2130ef 100644
--- a/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.tsx
+++ b/clients/web/src/components/atoms/CapabilityItem/CapabilityItem.tsx
@@ -1,4 +1,4 @@
-import { Group, Text } from '@mantine/core';
+import { Group, Text } from "@mantine/core";
export interface CapabilityItemProps {
name: string;
@@ -6,12 +6,18 @@ export interface CapabilityItemProps {
count?: number;
}
-export function CapabilityItem({ name, supported, count }: CapabilityItemProps) {
+export function CapabilityItem({
+ name,
+ supported,
+ count,
+}: CapabilityItemProps) {
const label = count != null ? `${name} (${count})` : name;
return (
- {supported ? '\u2713' : '\u2717'}
+
+ {supported ? "\u2713" : "\u2717"}
+
{label}
);
diff --git a/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.stories.tsx b/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.stories.tsx
index faab9443..4d33ed53 100644
--- a/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.stories.tsx
+++ b/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { ConnectionToggle } from './ConnectionToggle';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ConnectionToggle } from "./ConnectionToggle";
const meta: Meta = {
- title: 'Atoms/ConnectionToggle',
+ title: "Atoms/ConnectionToggle",
component: ConnectionToggle,
args: {
onChange: fn(),
diff --git a/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.tsx b/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.tsx
index b01d4281..3e210c67 100644
--- a/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.tsx
+++ b/clients/web/src/components/atoms/ConnectionToggle/ConnectionToggle.tsx
@@ -1,4 +1,4 @@
-import { Switch } from '@mantine/core';
+import { Switch } from "@mantine/core";
export interface ConnectionToggleProps {
checked: boolean;
@@ -7,7 +7,12 @@ export interface ConnectionToggleProps {
onChange: (checked: boolean) => void;
}
-export function ConnectionToggle({ checked, loading, disabled, onChange }: ConnectionToggleProps) {
+export function ConnectionToggle({
+ checked,
+ loading,
+ disabled,
+ onChange,
+}: ConnectionToggleProps) {
return (
= {
- title: 'Atoms/ContentViewer',
+ title: "Atoms/ContentViewer",
component: ContentViewer,
};
@@ -12,56 +12,57 @@ type Story = StoryObj;
export const PlainText: Story = {
args: {
- type: 'text',
- content: 'Hello, world!\nThis is plain text.',
+ type: "text",
+ content: "Hello, world!\nThis is plain text.",
},
};
export const JsonContent: Story = {
args: {
- type: 'json',
- content: '{"name":"my-app","version":"1.0.0","description":"Sample configuration file"}',
+ type: "json",
+ content:
+ '{"name":"my-app","version":"1.0.0","description":"Sample configuration file"}',
},
};
export const ImagePreview: Story = {
args: {
- type: 'image',
+ type: "image",
content:
- 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
- mimeType: 'image/png',
+ "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
+ mimeType: "image/png",
},
};
export const LongContent: Story = {
args: {
- type: 'json',
+ type: "json",
content: JSON.stringify({
- name: 'my-app',
- version: '2.5.0',
- description: 'A comprehensive application configuration',
- author: 'Inspector Team',
- license: 'MIT',
- repository: 'https://github.com/example/my-app',
+ name: "my-app",
+ version: "2.5.0",
+ description: "A comprehensive application configuration",
+ author: "Inspector Team",
+ license: "MIT",
+ repository: "https://github.com/example/my-app",
dependencies: {
- react: '^18.0.0',
- typescript: '^5.0.0',
- mantine: '^7.0.0',
+ react: "^18.0.0",
+ typescript: "^5.0.0",
+ mantine: "^7.0.0",
},
devDependencies: {
- vitest: '^1.0.0',
- storybook: '^8.0.0',
- eslint: '^9.0.0',
+ vitest: "^1.0.0",
+ storybook: "^8.0.0",
+ eslint: "^9.0.0",
},
scripts: {
- dev: 'vite',
- build: 'tsc && vite build',
- test: 'vitest',
- lint: 'eslint .',
+ dev: "vite",
+ build: "tsc && vite build",
+ test: "vitest",
+ lint: "eslint .",
},
settings: {
- theme: 'dark',
- language: 'en',
+ theme: "dark",
+ language: "en",
notifications: true,
maxRetries: 3,
},
@@ -71,8 +72,8 @@ export const LongContent: Story = {
export const WithCopyButton: Story = {
args: {
- type: 'text',
- content: 'Hello, world!\nThis text can be copied.',
+ type: "text",
+ content: "Hello, world!\nThis text can be copied.",
onCopy: fn(),
},
};
diff --git a/clients/web/src/components/atoms/ContentViewer/ContentViewer.tsx b/clients/web/src/components/atoms/ContentViewer/ContentViewer.tsx
index 22facfbe..90897d32 100644
--- a/clients/web/src/components/atoms/ContentViewer/ContentViewer.tsx
+++ b/clients/web/src/components/atoms/ContentViewer/ContentViewer.tsx
@@ -1,7 +1,7 @@
-import { Button, Code, Image, Stack } from '@mantine/core';
+import { Button, Code, Image, Stack } from "@mantine/core";
export interface ContentViewerProps {
- type: 'text' | 'json' | 'image' | 'audio';
+ type: "text" | "json" | "image" | "audio";
content: string;
mimeType?: string;
onCopy?: () => void;
@@ -15,22 +15,27 @@ function formatJson(content: string): string {
}
}
-export function ContentViewer({ type, content, mimeType, onCopy }: ContentViewerProps) {
+export function ContentViewer({
+ type,
+ content,
+ mimeType,
+ onCopy,
+}: ContentViewerProps) {
return (
- {type === 'json' && {formatJson(content)}}
- {type === 'text' && {content}}
- {type === 'image' && (
+ {type === "json" && {formatJson(content)}}
+ {type === "text" && {content}}
+ {type === "image" && (
)}
- {type === 'audio' && (
+ {type === "audio" && (
)}
{onCopy && (
diff --git a/clients/web/src/components/atoms/CopyButton/CopyButton.stories.tsx b/clients/web/src/components/atoms/CopyButton/CopyButton.stories.tsx
index 3b9fe08c..79cf324f 100644
--- a/clients/web/src/components/atoms/CopyButton/CopyButton.stories.tsx
+++ b/clients/web/src/components/atoms/CopyButton/CopyButton.stories.tsx
@@ -1,8 +1,8 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { CopyButton } from './CopyButton';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { CopyButton } from "./CopyButton";
const meta: Meta = {
- title: 'Atoms/CopyButton',
+ title: "Atoms/CopyButton",
component: CopyButton,
};
@@ -11,13 +11,13 @@ type Story = StoryObj;
export const Default: Story = {
args: {
- value: 'Hello, world!',
+ value: "Hello, world!",
},
};
export const LongValue: Story = {
args: {
value:
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.',
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur.",
},
};
diff --git a/clients/web/src/components/atoms/CopyButton/CopyButton.tsx b/clients/web/src/components/atoms/CopyButton/CopyButton.tsx
index 8b0908e7..487cab9f 100644
--- a/clients/web/src/components/atoms/CopyButton/CopyButton.tsx
+++ b/clients/web/src/components/atoms/CopyButton/CopyButton.tsx
@@ -1,4 +1,8 @@
-import { ActionIcon, CopyButton as MantineCopyButton, Tooltip } from '@mantine/core';
+import {
+ ActionIcon,
+ CopyButton as MantineCopyButton,
+ Tooltip,
+} from "@mantine/core";
export interface CopyButtonProps {
value: string;
@@ -8,9 +12,13 @@ export function CopyButton({ value }: CopyButtonProps) {
return (
{({ copied, copy }) => (
-
-
- {copied ? '\u2713' : '\u2398'}
+
+
+ {copied ? "\u2713" : "\u2398"}
)}
diff --git a/clients/web/src/components/atoms/InlineError/InlineError.stories.tsx b/clients/web/src/components/atoms/InlineError/InlineError.stories.tsx
index 3c310d51..18a5decf 100644
--- a/clients/web/src/components/atoms/InlineError/InlineError.stories.tsx
+++ b/clients/web/src/components/atoms/InlineError/InlineError.stories.tsx
@@ -1,8 +1,8 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { InlineError } from './InlineError';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { InlineError } from "./InlineError";
const meta: Meta = {
- title: 'Atoms/InlineError',
+ title: "Atoms/InlineError",
component: InlineError,
};
@@ -11,13 +11,13 @@ type Story = StoryObj;
export const ShortError: Story = {
args: {
- message: 'Connection timeout after 20s',
+ message: "Connection timeout after 20s",
},
};
export const WithRetryCount: Story = {
args: {
- message: 'Connection failed',
+ message: "Connection failed",
retryCount: 3,
maxRetries: 5,
},
@@ -25,26 +25,26 @@ export const WithRetryCount: Story = {
export const WithDetails: Story = {
args: {
- message: 'Connection refused',
+ message: "Connection refused",
details:
- 'Error: ECONNREFUSED 127.0.0.1:3000\n at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1494:16)',
+ "Error: ECONNREFUSED 127.0.0.1:3000\n at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1494:16)",
},
};
export const WithDocLink: Story = {
args: {
- message: 'Connection failed',
- docLink: 'https://example.com/troubleshooting',
+ message: "Connection failed",
+ docLink: "https://example.com/troubleshooting",
},
};
export const FullError: Story = {
args: {
- message: 'Connection refused',
+ message: "Connection refused",
details:
- 'Error: ECONNREFUSED 127.0.0.1:3000\n at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1494:16)',
+ "Error: ECONNREFUSED 127.0.0.1:3000\n at TCPConnectWrap.afterConnect [as oncomplete] (node:net:1494:16)",
retryCount: 3,
maxRetries: 5,
- docLink: 'https://example.com/troubleshooting',
+ docLink: "https://example.com/troubleshooting",
},
};
diff --git a/clients/web/src/components/atoms/InlineError/InlineError.tsx b/clients/web/src/components/atoms/InlineError/InlineError.tsx
index 27b394b1..e08dabaa 100644
--- a/clients/web/src/components/atoms/InlineError/InlineError.tsx
+++ b/clients/web/src/components/atoms/InlineError/InlineError.tsx
@@ -1,4 +1,4 @@
-import { Alert, Anchor, Group, Spoiler, Stack, Text } from '@mantine/core';
+import { Alert, Anchor, Group, Spoiler, Stack, Text } from "@mantine/core";
export interface InlineErrorProps {
message: string;
diff --git a/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.stories.tsx b/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.stories.tsx
index 1c92cc5a..f8094885 100644
--- a/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.stories.tsx
+++ b/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { ListChangedIndicator } from './ListChangedIndicator';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ListChangedIndicator } from "./ListChangedIndicator";
const meta: Meta = {
- title: 'Atoms/ListChangedIndicator',
+ title: "Atoms/ListChangedIndicator",
component: ListChangedIndicator,
};
diff --git a/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.tsx b/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.tsx
index 0c9db765..1931ac9d 100644
--- a/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.tsx
+++ b/clients/web/src/components/atoms/ListChangedIndicator/ListChangedIndicator.tsx
@@ -1,4 +1,4 @@
-import { Button, Flex, Group, Text } from '@mantine/core';
+import { Button, Flex, Group, Text } from "@mantine/core";
export interface ListChangedIndicatorProps {
visible: boolean;
@@ -8,17 +8,22 @@ export interface ListChangedIndicatorProps {
const Dot = Flex.withProps({
w: 8,
h: 8,
- style: { borderRadius: '50%' },
- bg: 'var(--inspector-status-connecting)',
+ style: { borderRadius: "50%" },
+ bg: "var(--inspector-status-connecting)",
});
-export function ListChangedIndicator({ visible, onRefresh }: ListChangedIndicatorProps) {
+export function ListChangedIndicator({
+ visible,
+ onRefresh,
+}: ListChangedIndicatorProps) {
if (!visible) return null;
return (
- List updated
+
+ List updated
+
diff --git a/clients/web/src/components/atoms/LogEntry/LogEntry.stories.tsx b/clients/web/src/components/atoms/LogEntry/LogEntry.stories.tsx
index 50eb94e1..39004c5b 100644
--- a/clients/web/src/components/atoms/LogEntry/LogEntry.stories.tsx
+++ b/clients/web/src/components/atoms/LogEntry/LogEntry.stories.tsx
@@ -1,8 +1,8 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { LogEntry } from './LogEntry';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { LogEntry } from "./LogEntry";
const meta: Meta = {
- title: 'Atoms/LogEntry',
+ title: "Atoms/LogEntry",
component: LogEntry,
};
@@ -11,73 +11,73 @@ type Story = StoryObj;
export const Debug: Story = {
args: {
- timestamp: '2026-03-17T10:00:00Z',
- level: 'debug',
- message: 'Initializing connection pool',
+ timestamp: "2026-03-17T10:00:00Z",
+ level: "debug",
+ message: "Initializing connection pool",
},
};
export const Info: Story = {
args: {
- timestamp: '2026-03-17T10:00:01Z',
- level: 'info',
- message: 'Server started on port 3000',
+ timestamp: "2026-03-17T10:00:01Z",
+ level: "info",
+ message: "Server started on port 3000",
},
};
export const Notice: Story = {
args: {
- timestamp: '2026-03-17T10:00:02Z',
- level: 'notice',
- message: 'Configuration reloaded successfully',
+ timestamp: "2026-03-17T10:00:02Z",
+ level: "notice",
+ message: "Configuration reloaded successfully",
},
};
export const Warning: Story = {
args: {
- timestamp: '2026-03-17T10:00:03Z',
- level: 'warning',
- message: 'Memory usage above 80%',
+ timestamp: "2026-03-17T10:00:03Z",
+ level: "warning",
+ message: "Memory usage above 80%",
},
};
export const Error: Story = {
args: {
- timestamp: '2026-03-17T10:00:04Z',
- level: 'error',
- message: 'Failed to connect to database',
+ timestamp: "2026-03-17T10:00:04Z",
+ level: "error",
+ message: "Failed to connect to database",
},
};
export const Critical: Story = {
args: {
- timestamp: '2026-03-17T10:00:05Z',
- level: 'critical',
- message: 'Disk space critically low',
+ timestamp: "2026-03-17T10:00:05Z",
+ level: "critical",
+ message: "Disk space critically low",
},
};
export const Alert: Story = {
args: {
- timestamp: '2026-03-17T10:00:06Z',
- level: 'alert',
- message: 'Service degradation detected',
+ timestamp: "2026-03-17T10:00:06Z",
+ level: "alert",
+ message: "Service degradation detected",
},
};
export const Emergency: Story = {
args: {
- timestamp: '2026-03-17T10:00:07Z',
- level: 'emergency',
- message: 'System is unusable',
+ timestamp: "2026-03-17T10:00:07Z",
+ level: "emergency",
+ message: "System is unusable",
},
};
export const WithLogger: Story = {
args: {
- timestamp: '2026-03-17T10:00:08Z',
- level: 'info',
- message: 'Tool execution completed',
- logger: 'mcp-server',
+ timestamp: "2026-03-17T10:00:08Z",
+ level: "info",
+ message: "Tool execution completed",
+ logger: "mcp-server",
},
};
diff --git a/clients/web/src/components/atoms/LogEntry/LogEntry.tsx b/clients/web/src/components/atoms/LogEntry/LogEntry.tsx
index 289c8b66..e6b5fc27 100644
--- a/clients/web/src/components/atoms/LogEntry/LogEntry.tsx
+++ b/clients/web/src/components/atoms/LogEntry/LogEntry.tsx
@@ -1,6 +1,14 @@
-import { Badge, Group, Text } from '@mantine/core';
+import { Badge, Group, Text } from "@mantine/core";
-export type LogLevel = 'debug' | 'info' | 'notice' | 'warning' | 'error' | 'critical' | 'alert' | 'emergency';
+export type LogLevel =
+ | "debug"
+ | "info"
+ | "notice"
+ | "warning"
+ | "error"
+ | "critical"
+ | "alert"
+ | "emergency";
export interface LogEntryProps {
timestamp: string;
@@ -10,40 +18,40 @@ export interface LogEntryProps {
}
const levelBadgeColor: Record = {
- debug: 'gray',
- info: 'blue',
- notice: 'teal',
- warning: 'yellow',
- error: 'red',
- critical: 'red',
- alert: 'red',
- emergency: 'red',
+ debug: "gray",
+ info: "blue",
+ notice: "teal",
+ warning: "yellow",
+ error: "red",
+ critical: "red",
+ alert: "red",
+ emergency: "red",
};
const levelBadgeVariant: Record = {
- debug: 'light',
- info: 'light',
- notice: 'light',
- warning: 'light',
- error: 'light',
- critical: 'filled',
- alert: 'filled',
- emergency: 'filled',
+ debug: "light",
+ info: "light",
+ notice: "light",
+ warning: "light",
+ error: "light",
+ critical: "filled",
+ alert: "filled",
+ emergency: "filled",
};
const levelMessageColor: Record = {
- debug: 'dimmed',
- info: 'blue',
+ debug: "dimmed",
+ info: "blue",
notice: undefined,
- warning: 'yellow',
- error: 'red',
- critical: 'red',
- alert: 'red',
- emergency: 'red',
+ warning: "yellow",
+ error: "red",
+ critical: "red",
+ alert: "red",
+ emergency: "red",
};
function isBoldLevel(level: LogLevel): boolean {
- return level === 'alert' || level === 'emergency';
+ return level === "alert" || level === "emergency";
}
export function LogEntry({ timestamp, level, message, logger }: LogEntryProps) {
diff --git a/clients/web/src/components/atoms/MessageBubble/MessageBubble.stories.tsx b/clients/web/src/components/atoms/MessageBubble/MessageBubble.stories.tsx
index 096cd94f..ec6ddbe7 100644
--- a/clients/web/src/components/atoms/MessageBubble/MessageBubble.stories.tsx
+++ b/clients/web/src/components/atoms/MessageBubble/MessageBubble.stories.tsx
@@ -1,8 +1,8 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { MessageBubble } from './MessageBubble';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { MessageBubble } from "./MessageBubble";
const meta: Meta = {
- title: 'Atoms/MessageBubble',
+ title: "Atoms/MessageBubble",
component: MessageBubble,
};
@@ -12,15 +12,15 @@ type Story = StoryObj;
export const UserText: Story = {
args: {
index: 0,
- role: 'user',
- content: 'Hello, my name is John and I like cats',
+ role: "user",
+ content: "Hello, my name is John and I like cats",
},
};
export const AssistantText: Story = {
args: {
index: 1,
- role: 'assistant',
+ role: "assistant",
content:
"Nice to meet you, John! It's wonderful that you enjoy cats. They're such fascinating and independent creatures. Do you have any cats of your own?",
},
@@ -29,11 +29,11 @@ export const AssistantText: Story = {
export const UserWithImage: Story = {
args: {
index: 0,
- role: 'user',
- content: 'Check this out',
+ role: "user",
+ content: "Check this out",
imageContent: {
- data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
- mimeType: 'image/png',
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
+ mimeType: "image/png",
},
},
};
@@ -41,8 +41,8 @@ export const UserWithImage: Story = {
export const LongMessage: Story = {
args: {
index: 0,
- role: 'user',
+ role: "user",
content:
- 'Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.',
+ "Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum. Sed ut perspiciatis unde omnis iste natus error sit voluptatem accusantium doloremque laudantium, totam rem aperiam, eaque ipsa quae ab illo inventore veritatis et quasi architecto beatae vitae dicta sunt explicabo.",
},
};
diff --git a/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx b/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx
index 97ded850..3515ab25 100644
--- a/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx
+++ b/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx
@@ -1,8 +1,8 @@
-import { Image, Paper, Stack, Text } from '@mantine/core';
+import { Image, Paper, Stack, Text } from "@mantine/core";
export interface MessageBubbleProps {
index: number;
- role: 'user' | 'assistant';
+ role: "user" | "assistant";
content: string;
imageContent?: { data: string; mimeType: string };
audioContent?: { data: string; mimeType: string };
diff --git a/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.stories.tsx b/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.stories.tsx
index b1c4d965..b8864cdb 100644
--- a/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.stories.tsx
+++ b/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.stories.tsx
@@ -1,8 +1,8 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { ProgressDisplay } from './ProgressDisplay';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { ProgressDisplay } from "./ProgressDisplay";
const meta: Meta = {
- title: 'Atoms/ProgressDisplay',
+ title: "Atoms/ProgressDisplay",
component: ProgressDisplay,
};
@@ -18,22 +18,22 @@ export const ZeroPercent: Story = {
export const HalfComplete: Story = {
args: {
progress: 50,
- description: 'Processing...',
+ description: "Processing...",
},
};
export const NearComplete: Story = {
args: {
progress: 95,
- description: 'Almost done',
- elapsed: '1m 30s',
+ description: "Almost done",
+ elapsed: "1m 30s",
},
};
export const Complete: Story = {
args: {
progress: 100,
- description: 'Done',
- elapsed: '2m 15s',
+ description: "Done",
+ elapsed: "2m 15s",
},
};
diff --git a/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.tsx b/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.tsx
index 70c72247..5bcf279e 100644
--- a/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.tsx
+++ b/clients/web/src/components/atoms/ProgressDisplay/ProgressDisplay.tsx
@@ -1,4 +1,4 @@
-import { Group, Progress, Stack, Text } from '@mantine/core';
+import { Group, Progress, Stack, Text } from "@mantine/core";
export interface ProgressDisplayProps {
progress: number;
@@ -6,7 +6,11 @@ export interface ProgressDisplayProps {
elapsed?: string;
}
-export function ProgressDisplay({ progress, description, elapsed }: ProgressDisplayProps) {
+export function ProgressDisplay({
+ progress,
+ description,
+ elapsed,
+}: ProgressDisplayProps) {
return (
diff --git a/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.stories.tsx b/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.stories.tsx
index 3c3ac7c9..9980ba83 100644
--- a/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.stories.tsx
+++ b/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.stories.tsx
@@ -1,8 +1,8 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { StatusIndicator } from './StatusIndicator';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { StatusIndicator } from "./StatusIndicator";
const meta: Meta = {
- title: 'Atoms/StatusIndicator',
+ title: "Atoms/StatusIndicator",
component: StatusIndicator,
};
@@ -11,32 +11,32 @@ type Story = StoryObj;
export const Connected: Story = {
args: {
- status: 'connected',
+ status: "connected",
latencyMs: 23,
},
};
export const Connecting: Story = {
args: {
- status: 'connecting',
+ status: "connecting",
},
};
export const Disconnected: Story = {
args: {
- status: 'disconnected',
+ status: "disconnected",
},
};
export const Failed: Story = {
args: {
- status: 'failed',
+ status: "failed",
},
};
export const FailedWithRetries: Story = {
args: {
- status: 'failed',
+ status: "failed",
retryCount: 3,
},
};
diff --git a/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.tsx b/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.tsx
index fc572c1a..0ceacc28 100644
--- a/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.tsx
+++ b/clients/web/src/components/atoms/StatusIndicator/StatusIndicator.tsx
@@ -1,47 +1,51 @@
-import { Flex, Group, Text } from '@mantine/core';
+import { Flex, Group, Text } from "@mantine/core";
export interface StatusIndicatorProps {
- status: 'connected' | 'connecting' | 'disconnected' | 'failed';
+ status: "connected" | "connecting" | "disconnected" | "failed";
latencyMs?: number;
retryCount?: number;
}
-const statusColorVar: Record = {
- connected: 'var(--inspector-status-connected)',
- connecting: 'var(--inspector-status-connecting)',
- disconnected: 'var(--inspector-status-disconnected)',
- failed: 'var(--inspector-status-failed)',
+const statusColorVar: Record = {
+ connected: "var(--inspector-status-connected)",
+ connecting: "var(--inspector-status-connecting)",
+ disconnected: "var(--inspector-status-disconnected)",
+ failed: "var(--inspector-status-failed)",
};
function getLabel(
- status: StatusIndicatorProps['status'],
+ status: StatusIndicatorProps["status"],
latencyMs?: number,
retryCount?: number,
): string {
switch (status) {
- case 'connected':
- return latencyMs != null ? `Connected (${latencyMs}ms)` : 'Connected';
- case 'connecting':
- return 'Connecting...';
- case 'disconnected':
- return 'Disconnected';
- case 'failed':
- return retryCount != null ? `Failed (${retryCount})` : 'Failed';
+ case "connected":
+ return latencyMs != null ? `Connected (${latencyMs}ms)` : "Connected";
+ case "connecting":
+ return "Connecting...";
+ case "disconnected":
+ return "Disconnected";
+ case "failed":
+ return retryCount != null ? `Failed (${retryCount})` : "Failed";
}
}
const Dot = Flex.withProps({
w: 10,
h: 10,
- style: { borderRadius: '50%' },
+ style: { borderRadius: "50%" },
});
-export function StatusIndicator({ status, latencyMs, retryCount }: StatusIndicatorProps) {
+export function StatusIndicator({
+ status,
+ latencyMs,
+ retryCount,
+}: StatusIndicatorProps) {
return (
{getLabel(status, latencyMs, retryCount)}
diff --git a/clients/web/src/components/atoms/TransportBadge/TransportBadge.stories.tsx b/clients/web/src/components/atoms/TransportBadge/TransportBadge.stories.tsx
index 26682ec6..144f0f69 100644
--- a/clients/web/src/components/atoms/TransportBadge/TransportBadge.stories.tsx
+++ b/clients/web/src/components/atoms/TransportBadge/TransportBadge.stories.tsx
@@ -1,22 +1,22 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { TransportBadge } from './TransportBadge'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { TransportBadge } from "./TransportBadge";
const meta: Meta = {
- title: 'Atoms/TransportBadge',
+ title: "Atoms/TransportBadge",
component: TransportBadge,
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Stdio: Story = {
args: {
- transport: 'stdio',
+ transport: "stdio",
},
-}
+};
export const Http: Story = {
args: {
- transport: 'http',
+ transport: "http",
},
-}
+};
diff --git a/clients/web/src/components/atoms/TransportBadge/TransportBadge.tsx b/clients/web/src/components/atoms/TransportBadge/TransportBadge.tsx
index dd206729..d581f002 100644
--- a/clients/web/src/components/atoms/TransportBadge/TransportBadge.tsx
+++ b/clients/web/src/components/atoms/TransportBadge/TransportBadge.tsx
@@ -1,7 +1,7 @@
-import { Badge } from '@mantine/core'
+import { Badge } from "@mantine/core";
export interface TransportBadgeProps {
- transport: 'stdio' | 'http'
+ transport: "stdio" | "http";
}
export function TransportBadge({ transport }: TransportBadgeProps) {
@@ -9,5 +9,5 @@ export function TransportBadge({ transport }: TransportBadgeProps) {
{transport.toUpperCase()}
- )
+ );
}
diff --git a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx
index b20e0a08..2980c32b 100644
--- a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx
+++ b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx
@@ -1,20 +1,20 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { Text } from '@mantine/core';
-import { ConnectedLayout } from './ConnectedLayout';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { Text } from "@mantine/core";
+import { ConnectedLayout } from "./ConnectedLayout";
-const allTabs = ['Tools', 'Resources', 'Prompts', 'Logs', 'Tasks', 'History'];
+const allTabs = ["Tools", "Resources", "Prompts", "Logs", "Tasks", "History"];
const meta: Meta = {
- title: 'Layouts/ConnectedLayout',
+ title: "Layouts/ConnectedLayout",
component: ConnectedLayout,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
- serverName: 'my-mcp-server',
- status: 'connected',
+ serverName: "my-mcp-server",
+ status: "connected",
latencyMs: 23,
availableTabs: allTabs,
- activeTab: 'Tools',
+ activeTab: "Tools",
onTabChange: fn(),
onDisconnect: fn(),
onToggleTheme: fn(),
@@ -26,28 +26,28 @@ type Story = StoryObj;
export const ToolsActive: Story = {
args: {
- activeTab: 'Tools',
+ activeTab: "Tools",
children: Tools screen content,
},
};
export const ResourcesActive: Story = {
args: {
- activeTab: 'Resources',
+ activeTab: "Resources",
children: Resources screen content,
},
};
export const LimitedTabs: Story = {
args: {
- availableTabs: ['Tools', 'Resources', 'Prompts'],
+ availableTabs: ["Tools", "Resources", "Prompts"],
children: Tools screen content,
},
};
export const LongServerName: Story = {
args: {
- serverName: 'my-very-long-server-name-that-might-overflow',
+ serverName: "my-very-long-server-name-that-might-overflow",
children: Tools screen content,
},
};
diff --git a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.tsx b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.tsx
index 366682a3..2417be8c 100644
--- a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.tsx
+++ b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.tsx
@@ -1,11 +1,11 @@
-import type { ReactNode } from 'react';
-import { ActionIcon, AppShell, Button, Group, Tabs, Text } from '@mantine/core';
-import { useComputedColorScheme } from '@mantine/core';
-import { StatusIndicator } from '../../atoms/StatusIndicator/StatusIndicator';
+import type { ReactNode } from "react";
+import { ActionIcon, AppShell, Button, Group, Tabs, Text } from "@mantine/core";
+import { useComputedColorScheme } from "@mantine/core";
+import { StatusIndicator } from "../../atoms/StatusIndicator/StatusIndicator";
export interface ConnectedLayoutProps {
serverName: string;
- status: 'connected' | 'connecting' | 'failed';
+ status: "connected" | "connecting" | "failed";
latencyMs?: number;
activeTab: string;
availableTabs: string[];
@@ -17,7 +17,7 @@ export interface ConnectedLayoutProps {
const ServerName = Text.withProps({
fw: 600,
- size: 'lg',
+ size: "lg",
});
export function ConnectedLayout({
@@ -63,7 +63,7 @@ export function ConnectedLayout({
aria-label="Toggle color scheme"
onClick={onToggleTheme}
>
- {colorScheme === 'dark' ? '\u2600' : '\u263E'}
+ {colorScheme === "dark" ? "\u2600" : "\u263E"}
- )
+ );
}
diff --git a/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.stories.tsx b/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.stories.tsx
index 609b5132..8ccc49f8 100644
--- a/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.stories.tsx
+++ b/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.stories.tsx
@@ -1,39 +1,39 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ElicitationUrlPanel } from './ElicitationUrlPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ElicitationUrlPanel } from "./ElicitationUrlPanel";
const meta: Meta = {
- title: 'Molecules/ElicitationUrlPanel',
+ title: "Molecules/ElicitationUrlPanel",
component: ElicitationUrlPanel,
args: {
onCopyUrl: fn(),
onOpenInBrowser: fn(),
onCancel: fn(),
- message: 'Please authenticate with the external service.',
- elicitationId: 'elicit-abc-123',
+ message: "Please authenticate with the external service.",
+ elicitationId: "elicit-abc-123",
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Waiting: Story = {
args: {
- url: 'https://auth.example.com/oauth/authorize?client_id=mcp-inspector&redirect_uri=http://localhost:3000/callback',
+ url: "https://auth.example.com/oauth/authorize?client_id=mcp-inspector&redirect_uri=http://localhost:3000/callback",
isWaiting: true,
},
-}
+};
export const WithLongUrl: Story = {
args: {
- url: 'https://auth.example.com/oauth/authorize?client_id=mcp-inspector&redirect_uri=http://localhost:3000/callback&scope=read+write+admin&state=xyzzy-12345&nonce=abcdef-67890&response_type=code&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256',
+ url: "https://auth.example.com/oauth/authorize?client_id=mcp-inspector&redirect_uri=http://localhost:3000/callback&scope=read+write+admin&state=xyzzy-12345&nonce=abcdef-67890&response_type=code&code_challenge=E9Melhoa2OwvFrEMTJguCHaoeK1t8URWbuGJSstw-cM&code_challenge_method=S256",
isWaiting: true,
},
-}
+};
export const Completed: Story = {
args: {
- url: 'https://auth.example.com/oauth/authorize?client_id=mcp-inspector',
+ url: "https://auth.example.com/oauth/authorize?client_id=mcp-inspector",
isWaiting: false,
},
-}
+};
diff --git a/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.tsx b/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.tsx
index 47d794bd..c60486f7 100644
--- a/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.tsx
+++ b/clients/web/src/components/molecules/ElicitationUrlPanel/ElicitationUrlPanel.tsx
@@ -1,13 +1,22 @@
-import { Alert, Button, Code, Divider, Group, Loader, Stack, Text } from '@mantine/core'
+import {
+ Alert,
+ Button,
+ Code,
+ Divider,
+ Group,
+ Loader,
+ Stack,
+ Text,
+} from "@mantine/core";
export interface ElicitationUrlPanelProps {
- message: string
- url: string
- elicitationId: string
- isWaiting: boolean
- onCopyUrl: () => void
- onOpenInBrowser: () => void
- onCancel: () => void
+ message: string;
+ url: string;
+ elicitationId: string;
+ isWaiting: boolean;
+ onCopyUrl: () => void;
+ onOpenInBrowser: () => void;
+ onCancel: () => void;
}
export function ElicitationUrlPanel({
@@ -56,5 +65,5 @@ export function ElicitationUrlPanel({
- )
+ );
}
diff --git a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx
index fde3a98e..3fa2f3a3 100644
--- a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx
+++ b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ExperimentalFeaturesPanel } from './ExperimentalFeaturesPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ExperimentalFeaturesPanel } from "./ExperimentalFeaturesPanel";
const meta: Meta = {
- title: 'Molecules/ExperimentalFeaturesPanel',
+ title: "Molecules/ExperimentalFeaturesPanel",
component: ExperimentalFeaturesPanel,
args: {
onToggleClientCapability: fn(),
@@ -14,105 +14,109 @@ const meta: Meta = {
onHeaderChange: fn(),
onCopyResponse: fn(),
clientCapabilities: [
- { name: 'experimental/customSampling', enabled: false },
- { name: 'experimental/batchRequests', enabled: true },
+ { name: "experimental/customSampling", enabled: false },
+ { name: "experimental/batchRequests", enabled: true },
],
customHeaders: [],
requestHistory: [],
requestJson: JSON.stringify(
{
- jsonrpc: '2.0',
+ jsonrpc: "2.0",
id: 1,
- method: 'experimental/myMethod',
+ method: "experimental/myMethod",
params: {},
},
null,
- 2
+ 2,
),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const WithServerCaps: Story = {
args: {
serverCapabilities: [
{
- name: 'experimental/streaming',
- description: 'Supports streaming responses for long-running operations',
- methods: ['experimental/stream.start', 'experimental/stream.cancel'],
+ name: "experimental/streaming",
+ description: "Supports streaming responses for long-running operations",
+ methods: ["experimental/stream.start", "experimental/stream.cancel"],
},
{
- name: 'experimental/caching',
- description: 'Server-side response caching with TTL support',
- methods: ['experimental/cache.get', 'experimental/cache.set', 'experimental/cache.clear'],
+ name: "experimental/caching",
+ description: "Server-side response caching with TTL support",
+ methods: [
+ "experimental/cache.get",
+ "experimental/cache.set",
+ "experimental/cache.clear",
+ ],
},
],
},
-}
+};
export const NoServerCaps: Story = {
args: {
serverCapabilities: [],
},
-}
+};
export const WithResponse: Story = {
args: {
serverCapabilities: [
{
- name: 'experimental/echo',
- description: 'Echoes back the request for testing',
- methods: ['experimental/echo'],
+ name: "experimental/echo",
+ description: "Echoes back the request for testing",
+ methods: ["experimental/echo"],
},
],
responseJson: JSON.stringify(
{
- jsonrpc: '2.0',
+ jsonrpc: "2.0",
id: 1,
result: {
- echo: 'Hello from experimental endpoint',
- timestamp: '2026-03-17T10:30:00Z',
+ echo: "Hello from experimental endpoint",
+ timestamp: "2026-03-17T10:30:00Z",
},
},
null,
- 2
+ 2,
),
customHeaders: [
- { key: 'X-Custom-Auth', value: 'Bearer token123' },
- { key: 'X-Request-Id', value: 'req-abc-456' },
+ { key: "X-Custom-Auth", value: "Bearer token123" },
+ { key: "X-Request-Id", value: "req-abc-456" },
],
},
-}
+};
export const WithHistory: Story = {
args: {
serverCapabilities: [
{
- name: 'experimental/metrics',
- description: 'Exposes server metrics',
+ name: "experimental/metrics",
+ description: "Exposes server metrics",
},
],
requestHistory: [
{
- timestamp: '2026-03-17 10:30:15',
- method: 'experimental/metrics.get',
- status: 'success',
+ timestamp: "2026-03-17 10:30:15",
+ method: "experimental/metrics.get",
+ status: "success",
durationMs: 42,
},
{
- timestamp: '2026-03-17 10:29:50',
- method: 'experimental/echo',
- status: 'success',
+ timestamp: "2026-03-17 10:29:50",
+ method: "experimental/echo",
+ status: "success",
durationMs: 15,
},
{
- timestamp: '2026-03-17 10:28:30',
- method: 'experimental/unknown',
- status: 'error',
+ timestamp: "2026-03-17 10:28:30",
+ method: "experimental/unknown",
+ status: "error",
durationMs: 120,
},
],
},
-}
+};
diff --git a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx
index e27e0596..fc5c6120 100644
--- a/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx
+++ b/clients/web/src/components/molecules/ExperimentalFeaturesPanel/ExperimentalFeaturesPanel.tsx
@@ -12,45 +12,45 @@ import {
TextInput,
Textarea,
Title,
-} from '@mantine/core'
+} from "@mantine/core";
export interface ExperimentalCapability {
- name: string
- description?: string
- methods?: string[]
+ name: string;
+ description?: string;
+ methods?: string[];
}
export interface ClientExperimentalCapability {
- name: string
- enabled: boolean
+ name: string;
+ enabled: boolean;
}
export interface KeyValuePair {
- key: string
- value: string
+ key: string;
+ value: string;
}
export interface RequestHistoryItem {
- timestamp: string
- method: string
- status: string
- durationMs: number
+ timestamp: string;
+ method: string;
+ status: string;
+ durationMs: number;
}
export interface ExperimentalFeaturesPanelProps {
- serverCapabilities: ExperimentalCapability[]
- clientCapabilities: ClientExperimentalCapability[]
- requestJson: string
- responseJson?: string
- customHeaders: KeyValuePair[]
- requestHistory: RequestHistoryItem[]
- onToggleClientCapability: (name: string, enabled: boolean) => void
- onRequestChange: (json: string) => void
- onSendRequest: () => void
- onAddHeader: () => void
- onRemoveHeader: (index: number) => void
- onHeaderChange: (index: number, key: string, value: string) => void
- onCopyResponse: () => void
+ serverCapabilities: ExperimentalCapability[];
+ clientCapabilities: ClientExperimentalCapability[];
+ requestJson: string;
+ responseJson?: string;
+ customHeaders: KeyValuePair[];
+ requestHistory: RequestHistoryItem[];
+ onToggleClientCapability: (name: string, enabled: boolean) => void;
+ onRequestChange: (json: string) => void;
+ onSendRequest: () => void;
+ onAddHeader: () => void;
+ onRemoveHeader: (index: number) => void;
+ onHeaderChange: (index: number, key: string, value: string) => void;
+ onCopyResponse: () => void;
}
export function ExperimentalFeaturesPanel({
@@ -90,7 +90,7 @@ export function ExperimentalFeaturesPanel({
)}
{cap.methods && cap.methods.length > 0 && (
- Methods: {cap.methods.join(', ')}
+ Methods: {cap.methods.join(", ")}
)}
@@ -112,7 +112,9 @@ export function ExperimentalFeaturesPanel({
key={clientCap.name}
label={clientCap.name}
checked={clientCap.enabled}
- onChange={(e) => onToggleClientCapability(clientCap.name, e.currentTarget.checked)}
+ onChange={(e) =>
+ onToggleClientCapability(clientCap.name, e.currentTarget.checked)
+ }
/>
))}
@@ -129,14 +131,22 @@ export function ExperimentalFeaturesPanel({
onHeaderChange(index, e.currentTarget.value, header.value)}
+ onChange={(e) =>
+ onHeaderChange(index, e.currentTarget.value, header.value)
+ }
/>
onHeaderChange(index, header.key, e.currentTarget.value)}
+ onChange={(e) =>
+ onHeaderChange(index, header.key, e.currentTarget.value)
+ }
/>
- onRemoveHeader(index)}>
+ onRemoveHeader(index)}
+ >
✕
@@ -203,5 +213,5 @@ export function ExperimentalFeaturesPanel({
>
)}
- )
+ );
}
diff --git a/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.stories.tsx b/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.stories.tsx
index 3e19fc34..ad2b4257 100644
--- a/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.stories.tsx
+++ b/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { HistoryEntry } from './HistoryEntry';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { HistoryEntry } from "./HistoryEntry";
const meta: Meta = {
- title: 'Molecules/HistoryEntry',
+ title: "Molecules/HistoryEntry",
component: HistoryEntry,
args: {
onToggleExpand: fn(),
@@ -17,10 +17,10 @@ type Story = StoryObj;
export const SuccessCollapsed: Story = {
args: {
- timestamp: '2026-03-17T10:30:00Z',
- method: 'tools/call',
- target: 'get_weather',
- status: 'success',
+ timestamp: "2026-03-17T10:30:00Z",
+ method: "tools/call",
+ target: "get_weather",
+ status: "success",
durationMs: 142,
isPinned: false,
isExpanded: false,
@@ -29,20 +29,20 @@ export const SuccessCollapsed: Story = {
export const SuccessExpanded: Story = {
args: {
- timestamp: '2026-03-17T10:30:00Z',
- method: 'tools/call',
- target: 'get_weather',
- status: 'success',
+ timestamp: "2026-03-17T10:30:00Z",
+ method: "tools/call",
+ target: "get_weather",
+ status: "success",
durationMs: 142,
isPinned: false,
isExpanded: true,
parameters: {
- city: 'San Francisco',
- units: 'celsius',
+ city: "San Francisco",
+ units: "celsius",
},
response: {
temperature: 18,
- conditions: 'Partly cloudy',
+ conditions: "Partly cloudy",
humidity: 65,
},
},
@@ -50,10 +50,10 @@ export const SuccessExpanded: Story = {
export const Error: Story = {
args: {
- timestamp: '2026-03-17T10:31:15Z',
- method: 'tools/call',
- target: 'query_database',
- status: 'error',
+ timestamp: "2026-03-17T10:31:15Z",
+ method: "tools/call",
+ target: "query_database",
+ status: "error",
durationMs: 3200,
isPinned: false,
isExpanded: false,
@@ -62,32 +62,32 @@ export const Error: Story = {
export const WithChildren: Story = {
args: {
- timestamp: '2026-03-17T10:32:00Z',
- method: 'tools/call',
- target: 'complex_operation',
- status: 'success',
+ timestamp: "2026-03-17T10:32:00Z",
+ method: "tools/call",
+ target: "complex_operation",
+ status: "success",
durationMs: 1250,
isPinned: false,
isExpanded: true,
parameters: {
- action: 'process',
+ action: "process",
},
response: {
- result: 'completed',
+ result: "completed",
},
children: [
{
- timestamp: '+120ms',
- method: 'sampling/createMessage',
- target: 'gpt-4',
- status: 'success',
+ timestamp: "+120ms",
+ method: "sampling/createMessage",
+ target: "gpt-4",
+ status: "success",
durationMs: 800,
},
{
- timestamp: '+950ms',
- method: 'elicitation/create',
- target: 'confirm_action',
- status: 'success',
+ timestamp: "+950ms",
+ method: "elicitation/create",
+ target: "confirm_action",
+ status: "success",
durationMs: 200,
},
],
@@ -96,13 +96,13 @@ export const WithChildren: Story = {
export const Pinned: Story = {
args: {
- timestamp: '2026-03-17T10:33:00Z',
- method: 'resources/read',
- target: 'config.json',
- status: 'success',
+ timestamp: "2026-03-17T10:33:00Z",
+ method: "resources/read",
+ target: "config.json",
+ status: "success",
durationMs: 45,
isPinned: true,
- pinLabel: 'Baseline',
+ pinLabel: "Baseline",
isExpanded: false,
},
};
diff --git a/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.tsx b/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.tsx
index 25c24307..600056e1 100644
--- a/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.tsx
+++ b/clients/web/src/components/molecules/HistoryEntry/HistoryEntry.tsx
@@ -1,10 +1,19 @@
-import { Badge, Button, Card, Code, Collapse, Group, Stack, Text } from '@mantine/core';
+import {
+ Badge,
+ Button,
+ Card,
+ Code,
+ Collapse,
+ Group,
+ Stack,
+ Text,
+} from "@mantine/core";
export interface HistoryChildEntry {
timestamp: string;
method: string;
target?: string;
- status: 'success' | 'error';
+ status: "success" | "error";
durationMs: number;
}
@@ -12,7 +21,7 @@ export interface HistoryEntryProps {
timestamp: string;
method: string;
target?: string;
- status: 'success' | 'error';
+ status: "success" | "error";
durationMs: number;
parameters?: Record;
response?: Record;
@@ -46,15 +55,23 @@ export function HistoryEntry({
- {timestamp}
+
+ {timestamp}
+
{method}
- {target && {target}}
+ {target && (
+
+ {target}
+
+ )}
-
- {status === 'success' ? 'OK' : 'Error'}
+
+ {status === "success" ? "OK" : "Error"}
- {durationMs}ms
+
+ {durationMs}ms
+
@@ -63,11 +80,11 @@ export function HistoryEntry({
Replay
- {isPinned ? 'Unpin' : 'Pin'}
- {isPinned && pinLabel ? ` (${pinLabel})` : ''}
+ {isPinned ? "Unpin" : "Pin"}
+ {isPinned && pinLabel ? ` (${pinLabel})` : ""}
- {isExpanded ? 'Collapse' : 'Expand'}
+ {isExpanded ? "Collapse" : "Expand"}
@@ -89,14 +106,25 @@ export function HistoryEntry({
{children.map((child, index) => (
- +--
- {child.timestamp}
- {child.method}
- {child.target && {child.target}}
-
- {child.status === 'success' ? 'OK' : 'Error'}
+
+ +--{" "}
+
+
+ {child.timestamp}
+
+
+ {child.method}
- {child.durationMs}ms
+ {child.target && {child.target}}
+
+ {child.status === "success" ? "OK" : "Error"}
+
+
+ {child.durationMs}ms
+
))}
diff --git a/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.stories.tsx b/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.stories.tsx
index c7757283..d880d18b 100644
--- a/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.stories.tsx
+++ b/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ImportServerJsonPanel } from './ImportServerJsonPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ImportServerJsonPanel } from "./ImportServerJsonPanel";
const meta: Meta = {
- title: 'Molecules/ImportServerJsonPanel',
+ title: "Molecules/ImportServerJsonPanel",
component: ImportServerJsonPanel,
args: {
onJsonChange: fn(),
@@ -15,126 +15,187 @@ const meta: Meta = {
onCancel: fn(),
selectedPackageIndex: 0,
envVars: [],
- serverName: '',
+ serverName: "",
validationResults: [],
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Empty: Story = {
args: {
- jsonContent: '',
+ jsonContent: "",
},
-}
+};
export const ValidJson: Story = {
args: {
jsonContent: JSON.stringify(
{
- name: 'my-mcp-server',
- version: '1.0.0',
- packages: [{ registryType: 'npm', identifier: 'my-mcp-server', runtimeHint: 'node' }],
+ name: "my-mcp-server",
+ version: "1.0.0",
+ packages: [
+ {
+ registryType: "npm",
+ identifier: "my-mcp-server",
+ runtimeHint: "node",
+ },
+ ],
},
null,
- 2
+ 2,
),
validationResults: [
- { type: 'success', message: 'Valid server.json format detected' },
- { type: 'success', message: 'Package configuration is valid' },
- { type: 'info', message: '1 package found' },
+ { type: "success", message: "Valid server.json format detected" },
+ { type: "success", message: "Package configuration is valid" },
+ { type: "info", message: "1 package found" },
],
},
-}
+};
export const MultiplePackages: Story = {
args: {
jsonContent: JSON.stringify(
{
- name: 'multi-server',
+ name: "multi-server",
packages: [
- { registryType: 'npm', identifier: '@scope/server', runtimeHint: 'node' },
- { registryType: 'pip', identifier: 'server-python', runtimeHint: 'python3' },
+ {
+ registryType: "npm",
+ identifier: "@scope/server",
+ runtimeHint: "node",
+ },
+ {
+ registryType: "pip",
+ identifier: "server-python",
+ runtimeHint: "python3",
+ },
],
},
null,
- 2
+ 2,
),
validationResults: [
- { type: 'success', message: 'Valid server.json format detected' },
- { type: 'info', message: '2 packages found - select one to install' },
+ { type: "success", message: "Valid server.json format detected" },
+ { type: "info", message: "2 packages found - select one to install" },
],
packages: [
- { registryType: 'npm', identifier: '@scope/server', runtimeHint: 'node' },
- { registryType: 'pip', identifier: 'server-python', runtimeHint: 'python3' },
+ { registryType: "npm", identifier: "@scope/server", runtimeHint: "node" },
+ {
+ registryType: "pip",
+ identifier: "server-python",
+ runtimeHint: "python3",
+ },
],
},
-}
+};
export const WithEnvVars: Story = {
args: {
jsonContent: JSON.stringify(
{
- name: 'api-server',
- packages: [{ registryType: 'npm', identifier: 'api-server', runtimeHint: 'node' }],
+ name: "api-server",
+ packages: [
+ {
+ registryType: "npm",
+ identifier: "api-server",
+ runtimeHint: "node",
+ },
+ ],
envVars: [
- { name: 'API_KEY', required: true },
- { name: 'API_URL', required: false },
+ { name: "API_KEY", required: true },
+ { name: "API_URL", required: false },
],
},
null,
- 2
+ 2,
),
validationResults: [
- { type: 'success', message: 'Valid server.json format detected' },
- { type: 'warning', message: '2 environment variables need to be configured' },
+ { type: "success", message: "Valid server.json format detected" },
+ {
+ type: "warning",
+ message: "2 environment variables need to be configured",
+ },
],
envVars: [
- { name: 'API_KEY', description: 'Your API authentication key', required: true, value: '' },
{
- name: 'API_URL',
- description: 'Base URL for the API endpoint',
+ name: "API_KEY",
+ description: "Your API authentication key",
+ required: true,
+ value: "",
+ },
+ {
+ name: "API_URL",
+ description: "Base URL for the API endpoint",
required: false,
- value: 'https://api.example.com',
+ value: "https://api.example.com",
},
],
},
-}
+};
export const FullyConfigured: Story = {
args: {
jsonContent: JSON.stringify(
{
- name: 'full-server',
- version: '2.1.0',
+ name: "full-server",
+ version: "2.1.0",
packages: [
- { registryType: 'npm', identifier: '@org/full-server', runtimeHint: 'node' },
- { registryType: 'docker', identifier: 'org/full-server:latest', runtimeHint: 'docker' },
+ {
+ registryType: "npm",
+ identifier: "@org/full-server",
+ runtimeHint: "node",
+ },
+ {
+ registryType: "docker",
+ identifier: "org/full-server:latest",
+ runtimeHint: "docker",
+ },
],
envVars: [
- { name: 'TOKEN', required: true },
- { name: 'DEBUG', required: false },
+ { name: "TOKEN", required: true },
+ { name: "DEBUG", required: false },
],
},
null,
- 2
+ 2,
),
validationResults: [
- { type: 'success', message: 'Valid server.json format detected' },
- { type: 'success', message: 'All required fields present' },
- { type: 'info', message: '2 packages found' },
- { type: 'warning', message: 'Environment variables require configuration' },
+ { type: "success", message: "Valid server.json format detected" },
+ { type: "success", message: "All required fields present" },
+ { type: "info", message: "2 packages found" },
+ {
+ type: "warning",
+ message: "Environment variables require configuration",
+ },
],
packages: [
- { registryType: 'npm', identifier: '@org/full-server', runtimeHint: 'node' },
- { registryType: 'docker', identifier: 'org/full-server:latest', runtimeHint: 'docker' },
+ {
+ registryType: "npm",
+ identifier: "@org/full-server",
+ runtimeHint: "node",
+ },
+ {
+ registryType: "docker",
+ identifier: "org/full-server:latest",
+ runtimeHint: "docker",
+ },
],
selectedPackageIndex: 1,
envVars: [
- { name: 'TOKEN', description: 'Authentication token', required: true, value: 'sk-abc123' },
- { name: 'DEBUG', description: 'Enable debug logging', required: false, value: 'true' },
+ {
+ name: "TOKEN",
+ description: "Authentication token",
+ required: true,
+ value: "sk-abc123",
+ },
+ {
+ name: "DEBUG",
+ description: "Enable debug logging",
+ required: false,
+ value: "true",
+ },
],
- serverName: 'My Custom Server',
+ serverName: "My Custom Server",
},
-}
+};
diff --git a/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.tsx b/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.tsx
index bbf32679..1857219b 100644
--- a/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.tsx
+++ b/clients/web/src/components/molecules/ImportServerJsonPanel/ImportServerJsonPanel.tsx
@@ -1,45 +1,58 @@
-import { Button, Divider, Group, Radio, Stack, Text, TextInput, Textarea, Title } from '@mantine/core'
+import {
+ Button,
+ Divider,
+ Group,
+ Radio,
+ Stack,
+ Text,
+ TextInput,
+ Textarea,
+ Title,
+} from "@mantine/core";
export interface ValidationResult {
- type: 'success' | 'warning' | 'info' | 'error'
- message: string
+ type: "success" | "warning" | "info" | "error";
+ message: string;
}
export interface PackageInfo {
- registryType: string
- identifier: string
- runtimeHint: string
+ registryType: string;
+ identifier: string;
+ runtimeHint: string;
}
export interface EnvVarInfo {
- name: string
- description?: string
- required: boolean
- value: string
+ name: string;
+ description?: string;
+ required: boolean;
+ value: string;
}
export interface ImportServerJsonPanelProps {
- jsonContent: string
- validationResults: ValidationResult[]
- packages?: PackageInfo[]
- selectedPackageIndex: number
- envVars: EnvVarInfo[]
- serverName: string
- onJsonChange: (content: string) => void
- onValidate: () => void
- onSelectPackage: (index: number) => void
- onEnvVarChange: (name: string, value: string) => void
- onServerNameChange: (name: string) => void
- onAddServer: () => void
- onCancel: () => void
+ jsonContent: string;
+ validationResults: ValidationResult[];
+ packages?: PackageInfo[];
+ selectedPackageIndex: number;
+ envVars: EnvVarInfo[];
+ serverName: string;
+ onJsonChange: (content: string) => void;
+ onValidate: () => void;
+ onSelectPackage: (index: number) => void;
+ onEnvVarChange: (name: string, value: string) => void;
+ onServerNameChange: (name: string) => void;
+ onAddServer: () => void;
+ onCancel: () => void;
}
-const validationIcons: Record = {
- success: { icon: '\u2713', color: 'green' },
- warning: { icon: '\u26A0', color: 'yellow' },
- info: { icon: '\u2139', color: 'blue' },
- error: { icon: '\u2717', color: 'red' },
-}
+const validationIcons: Record<
+ ValidationResult["type"],
+ { icon: string; color: string }
+> = {
+ success: { icon: "\u2713", color: "green" },
+ warning: { icon: "\u26A0", color: "yellow" },
+ info: { icon: "\u2139", color: "blue" },
+ error: { icon: "\u2717", color: "red" },
+};
export function ImportServerJsonPanel({
jsonContent,
@@ -78,13 +91,13 @@ export function ImportServerJsonPanel({
Validation Results:
{validationResults.map((result, index) => {
- const { icon, color } = validationIcons[result.type]
+ const { icon, color } = validationIcons[result.type];
return (
{icon}
{result.message}
- )
+ );
})}
{packages && packages.length > 1 && (
@@ -119,7 +132,9 @@ export function ImportServerJsonPanel({
description={envVar.description}
withAsterisk={envVar.required}
value={envVar.value}
- onChange={(e) => onEnvVarChange(envVar.name, e.currentTarget.value)}
+ onChange={(e) =>
+ onEnvVarChange(envVar.name, e.currentTarget.value)
+ }
/>
))}
>
@@ -143,5 +158,5 @@ export function ImportServerJsonPanel({
Add Server
- )
+ );
}
diff --git a/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.stories.tsx b/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.stories.tsx
index 764e7368..b2a94be7 100644
--- a/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.stories.tsx
+++ b/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.stories.tsx
@@ -1,50 +1,50 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { InlineElicitationRequest } from './InlineElicitationRequest'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { InlineElicitationRequest } from "./InlineElicitationRequest";
const meta: Meta = {
- title: 'Molecules/InlineElicitationRequest',
+ title: "Molecules/InlineElicitationRequest",
component: InlineElicitationRequest,
args: {
- message: 'Please provide your database connection details.',
- queuePosition: '1 of 1',
+ message: "Please provide your database connection details.",
+ queuePosition: "1 of 1",
onChange: fn(),
onSubmit: fn(),
onCancel: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const FormMode: Story = {
args: {
- mode: 'form',
+ mode: "form",
schema: {
- type: 'object',
+ type: "object",
properties: {
- host: { type: 'string', title: 'Host' },
- port: { type: 'integer', title: 'Port' },
- database: { type: 'string', title: 'Database' },
+ host: { type: "string", title: "Host" },
+ port: { type: "integer", title: "Port" },
+ database: { type: "string", title: "Database" },
},
- required: ['host', 'port'],
+ required: ["host", "port"],
},
values: {},
},
-}
+};
export const UrlMode: Story = {
args: {
- mode: 'url',
- url: 'https://example.com/auth/callback?session=abc123',
+ mode: "url",
+ url: "https://example.com/auth/callback?session=abc123",
isWaiting: false,
},
-}
+};
export const UrlWaiting: Story = {
args: {
- mode: 'url',
- url: 'https://example.com/auth/callback?session=abc123',
+ mode: "url",
+ url: "https://example.com/auth/callback?session=abc123",
isWaiting: true,
},
-}
+};
diff --git a/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.tsx b/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.tsx
index 1bda42bd..d51dcd32 100644
--- a/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.tsx
+++ b/clients/web/src/components/molecules/InlineElicitationRequest/InlineElicitationRequest.tsx
@@ -1,18 +1,27 @@
-import { Badge, Button, Code, Group, Loader, Paper, Stack, Text } from '@mantine/core'
-import type { JsonSchema } from '../SchemaForm/SchemaForm'
-import { SchemaForm } from '../SchemaForm/SchemaForm'
+import {
+ Badge,
+ Button,
+ Code,
+ Group,
+ Loader,
+ Paper,
+ Stack,
+ Text,
+} from "@mantine/core";
+import type { JsonSchema } from "../SchemaForm/SchemaForm";
+import { SchemaForm } from "../SchemaForm/SchemaForm";
export interface InlineElicitationRequestProps {
- mode: 'form' | 'url'
- message: string
- queuePosition: string
- schema?: JsonSchema
- values?: Record
- url?: string
- isWaiting?: boolean
- onChange: (values: Record) => void
- onSubmit: () => void
- onCancel: () => void
+ mode: "form" | "url";
+ message: string;
+ queuePosition: string;
+ schema?: JsonSchema;
+ values?: Record;
+ url?: string;
+ isWaiting?: boolean;
+ onChange: (values: Record) => void;
+ onSubmit: () => void;
+ onCancel: () => void;
}
export function InlineElicitationRequest({
@@ -28,7 +37,7 @@ export function InlineElicitationRequest({
onCancel,
}: InlineElicitationRequestProps) {
const badgeLabel =
- mode === 'form' ? 'elicitation/create (form)' : 'elicitation/create (url)'
+ mode === "form" ? "elicitation/create (form)" : "elicitation/create (url)";
return (
@@ -44,7 +53,7 @@ export function InlineElicitationRequest({
{message}
- {mode === 'form' && schema && (
+ {mode === "form" && schema && (
)}
- {mode === 'url' && url && (
+ {mode === "url" && url && (
<>
{url}
{isWaiting && (
@@ -68,7 +77,7 @@ export function InlineElicitationRequest({
Cancel
- {mode === 'form' && (
+ {mode === "form" && (
Submit
@@ -76,5 +85,5 @@ export function InlineElicitationRequest({
- )
+ );
}
diff --git a/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.stories.tsx b/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.stories.tsx
index 2877085b..41c842d1 100644
--- a/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.stories.tsx
+++ b/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.stories.tsx
@@ -1,40 +1,42 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { InlineSamplingRequest } from './InlineSamplingRequest'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { InlineSamplingRequest } from "./InlineSamplingRequest";
const meta: Meta = {
- title: 'Molecules/InlineSamplingRequest',
+ title: "Molecules/InlineSamplingRequest",
component: InlineSamplingRequest,
args: {
- queuePosition: '1 of 1',
- messagePreview: 'Please analyze the following code and suggest improvements for performance and readability.',
- responseText: '',
+ queuePosition: "1 of 1",
+ messagePreview:
+ "Please analyze the following code and suggest improvements for performance and readability.",
+ responseText: "",
onAutoRespond: fn(),
onEditAndSend: fn(),
onReject: fn(),
onViewDetails: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
-export const Default: Story = {}
+export const Default: Story = {};
export const WithModelHints: Story = {
args: {
- modelHints: ['claude-3-opus', 'claude-3-sonnet'],
+ modelHints: ["claude-3-opus", "claude-3-sonnet"],
},
-}
+};
export const PrefilledResponse: Story = {
args: {
- responseText: 'The code looks good overall. Consider extracting the repeated logic into a helper function.',
+ responseText:
+ "The code looks good overall. Consider extracting the repeated logic into a helper function.",
},
-}
+};
export const InQueue: Story = {
args: {
- queuePosition: '2 of 3',
+ queuePosition: "2 of 3",
},
-}
+};
diff --git a/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.tsx b/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.tsx
index ec5a6143..72b2622e 100644
--- a/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.tsx
+++ b/clients/web/src/components/molecules/InlineSamplingRequest/InlineSamplingRequest.tsx
@@ -1,14 +1,22 @@
-import { Badge, Button, Group, Paper, Stack, Text, Textarea } from '@mantine/core'
+import {
+ Badge,
+ Button,
+ Group,
+ Paper,
+ Stack,
+ Text,
+ Textarea,
+} from "@mantine/core";
export interface InlineSamplingRequestProps {
- queuePosition: string
- modelHints?: string[]
- messagePreview: string
- responseText: string
- onAutoRespond: () => void
- onEditAndSend: () => void
- onReject: () => void
- onViewDetails: () => void
+ queuePosition: string;
+ modelHints?: string[];
+ messagePreview: string;
+ responseText: string;
+ onAutoRespond: () => void;
+ onEditAndSend: () => void;
+ onReject: () => void;
+ onViewDetails: () => void;
}
export function InlineSamplingRequest({
@@ -32,7 +40,7 @@ export function InlineSamplingRequest({
{modelHints && (
- Model hints: {modelHints.join(', ')}
+ Model hints: {modelHints.join(", ")}
)}
@@ -65,5 +73,5 @@ export function InlineSamplingRequest({
- )
+ );
}
diff --git a/clients/web/src/components/molecules/LogControls/LogControls.stories.tsx b/clients/web/src/components/molecules/LogControls/LogControls.stories.tsx
index 38e18491..d619da8a 100644
--- a/clients/web/src/components/molecules/LogControls/LogControls.stories.tsx
+++ b/clients/web/src/components/molecules/LogControls/LogControls.stories.tsx
@@ -1,6 +1,6 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { LogControls } from './LogControls';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { LogControls } from "./LogControls";
const allLevelsVisible: Record = {
debug: true,
@@ -14,7 +14,7 @@ const allLevelsVisible: Record = {
};
const meta: Meta = {
- title: 'Molecules/LogControls',
+ title: "Molecules/LogControls",
component: LogControls,
args: {
onSetLevel: fn(),
@@ -30,16 +30,16 @@ type Story = StoryObj;
export const AllLevelsVisible: Story = {
args: {
- currentLevel: 'info',
- filterText: '',
+ currentLevel: "info",
+ filterText: "",
visibleLevels: allLevelsVisible,
},
};
export const FilteredLevels: Story = {
args: {
- currentLevel: 'warning',
- filterText: '',
+ currentLevel: "warning",
+ filterText: "",
visibleLevels: {
debug: false,
info: false,
@@ -55,16 +55,16 @@ export const FilteredLevels: Story = {
export const WithFilterText: Story = {
args: {
- currentLevel: 'info',
- filterText: 'connection timeout',
+ currentLevel: "info",
+ filterText: "connection timeout",
visibleLevels: allLevelsVisible,
},
};
export const DebugLevel: Story = {
args: {
- currentLevel: 'debug',
- filterText: '',
+ currentLevel: "debug",
+ filterText: "",
visibleLevels: allLevelsVisible,
},
};
diff --git a/clients/web/src/components/molecules/LogControls/LogControls.tsx b/clients/web/src/components/molecules/LogControls/LogControls.tsx
index bd07b7df..c9c840e8 100644
--- a/clients/web/src/components/molecules/LogControls/LogControls.tsx
+++ b/clients/web/src/components/molecules/LogControls/LogControls.tsx
@@ -1,25 +1,34 @@
-import { Button, Checkbox, Group, Select, Stack, Text, TextInput, Title } from '@mantine/core';
+import {
+ Button,
+ Checkbox,
+ Group,
+ Select,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+} from "@mantine/core";
const LOG_LEVELS = [
- 'debug',
- 'info',
- 'notice',
- 'warning',
- 'error',
- 'critical',
- 'alert',
- 'emergency',
+ "debug",
+ "info",
+ "notice",
+ "warning",
+ "error",
+ "critical",
+ "alert",
+ "emergency",
] as const;
const LEVEL_COLORS: Record = {
- debug: { c: 'dimmed' },
- info: { c: 'blue' },
- notice: { c: 'teal' },
- warning: { c: 'yellow' },
- error: { c: 'red' },
- critical: { c: 'red', fw: 700 },
- alert: { c: 'red', fw: 700 },
- emergency: { c: 'red', fw: 900 },
+ debug: { c: "dimmed" },
+ info: { c: "blue" },
+ notice: { c: "teal" },
+ warning: { c: "yellow" },
+ error: { c: "red" },
+ critical: { c: "red", fw: 700 },
+ alert: { c: "red", fw: 700 },
+ emergency: { c: "red", fw: 900 },
};
export interface LogControlsProps {
diff --git a/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.stories.tsx b/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.stories.tsx
index c5e69e96..030fac34 100644
--- a/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.stories.tsx
+++ b/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.stories.tsx
@@ -1,16 +1,16 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { PendingClientRequests } from './PendingClientRequests'
-import { InlineSamplingRequest } from '../InlineSamplingRequest/InlineSamplingRequest'
-import { InlineElicitationRequest } from '../InlineElicitationRequest/InlineElicitationRequest'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { PendingClientRequests } from "./PendingClientRequests";
+import { InlineSamplingRequest } from "../InlineSamplingRequest/InlineSamplingRequest";
+import { InlineElicitationRequest } from "../InlineElicitationRequest/InlineElicitationRequest";
const meta: Meta = {
- title: 'Molecules/PendingClientRequests',
+ title: "Molecules/PendingClientRequests",
component: PendingClientRequests,
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const SingleSampling: Story = {
args: {
@@ -27,7 +27,7 @@ export const SingleSampling: Story = {
/>
),
},
-}
+};
export const SingleElicitation: Story = {
args: {
@@ -38,12 +38,12 @@ export const SingleElicitation: Story = {
message="Please provide your database connection details."
queuePosition="1 of 1"
schema={{
- type: 'object',
+ type: "object",
properties: {
- host: { type: 'string', title: 'Host' },
- port: { type: 'integer', title: 'Port' },
+ host: { type: "string", title: "Host" },
+ port: { type: "integer", title: "Port" },
},
- required: ['host'],
+ required: ["host"],
}}
values={{}}
onChange={fn()}
@@ -52,7 +52,7 @@ export const SingleElicitation: Story = {
/>
),
},
-}
+};
export const MultipleMixed: Story = {
args: {
@@ -73,16 +73,16 @@ export const MultipleMixed: Story = {
message="Please confirm the deployment target."
queuePosition="2 of 2"
schema={{
- type: 'object',
+ type: "object",
properties: {
environment: {
- type: 'string',
- title: 'Environment',
- enum: ['staging', 'production'],
+ type: "string",
+ title: "Environment",
+ enum: ["staging", "production"],
},
- confirm: { type: 'boolean', title: 'Confirm deployment' },
+ confirm: { type: "boolean", title: "Confirm deployment" },
},
- required: ['environment', 'confirm'],
+ required: ["environment", "confirm"],
}}
values={{}}
onChange={fn()}
@@ -92,4 +92,4 @@ export const MultipleMixed: Story = {
>
),
},
-}
+};
diff --git a/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.tsx b/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.tsx
index 885c9e29..4cf4b6d4 100644
--- a/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.tsx
+++ b/clients/web/src/components/molecules/PendingClientRequests/PendingClientRequests.tsx
@@ -1,9 +1,9 @@
-import type { ReactNode } from 'react'
-import { Alert, Stack } from '@mantine/core'
+import type { ReactNode } from "react";
+import { Alert, Stack } from "@mantine/core";
export interface PendingClientRequestsProps {
- count: number
- children: ReactNode
+ count: number;
+ children: ReactNode;
}
export function PendingClientRequests({
@@ -18,5 +18,5 @@ export function PendingClientRequests({
>
{children}
- )
+ );
}
diff --git a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.stories.tsx b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.stories.tsx
index 3c2c80b7..af4c27c2 100644
--- a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.stories.tsx
+++ b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { PromptArgumentsForm } from './PromptArgumentsForm';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { PromptArgumentsForm } from "./PromptArgumentsForm";
const meta: Meta = {
- title: 'Molecules/PromptArgumentsForm',
+ title: "Molecules/PromptArgumentsForm",
component: PromptArgumentsForm,
args: {
onSelectPrompt: fn(),
@@ -16,9 +16,15 @@ export default meta;
type Story = StoryObj;
const samplePrompts = [
- { name: 'summarize', description: 'Summarize the given text into key points' },
- { name: 'translate', description: 'Translate text from one language to another' },
- { name: 'analyze', description: 'Analyze sentiment and tone of the text' },
+ {
+ name: "summarize",
+ description: "Summarize the given text into key points",
+ },
+ {
+ name: "translate",
+ description: "Translate text from one language to another",
+ },
+ { name: "analyze", description: "Analyze sentiment and tone of the text" },
];
export const NoSelection: Story = {
@@ -33,10 +39,14 @@ export const NoSelection: Story = {
export const Selected: Story = {
args: {
prompts: samplePrompts,
- selectedPrompt: 'translate',
+ selectedPrompt: "translate",
arguments: [
- { name: 'text', required: true, description: 'The text to translate' },
- { name: 'targetLanguage', required: true, description: 'The language to translate into' },
+ { name: "text", required: true, description: "The text to translate" },
+ {
+ name: "targetLanguage",
+ required: true,
+ description: "The language to translate into",
+ },
],
argumentValues: {},
},
@@ -45,11 +55,19 @@ export const Selected: Story = {
export const WithRequiredArgs: Story = {
args: {
prompts: samplePrompts,
- selectedPrompt: 'summarize',
+ selectedPrompt: "summarize",
arguments: [
- { name: 'text', required: true, description: 'The text to summarize' },
- { name: 'maxLength', required: true, description: 'Maximum length of summary in words' },
- { name: 'format', required: false, description: 'Output format (bullets or paragraph)' },
+ { name: "text", required: true, description: "The text to summarize" },
+ {
+ name: "maxLength",
+ required: true,
+ description: "Maximum length of summary in words",
+ },
+ {
+ name: "format",
+ required: false,
+ description: "Output format (bullets or paragraph)",
+ },
],
argumentValues: {},
},
@@ -58,14 +76,18 @@ export const WithRequiredArgs: Story = {
export const AllFilled: Story = {
args: {
prompts: samplePrompts,
- selectedPrompt: 'translate',
+ selectedPrompt: "translate",
arguments: [
- { name: 'text', required: true, description: 'The text to translate' },
- { name: 'targetLanguage', required: true, description: 'The language to translate into' },
+ { name: "text", required: true, description: "The text to translate" },
+ {
+ name: "targetLanguage",
+ required: true,
+ description: "The language to translate into",
+ },
],
argumentValues: {
- text: 'Hello, how are you?',
- targetLanguage: 'Spanish',
+ text: "Hello, how are you?",
+ targetLanguage: "Spanish",
},
},
};
diff --git a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx
index cc7319ab..3281729f 100644
--- a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx
+++ b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx
@@ -1,4 +1,4 @@
-import { Button, Select, Stack, Text, TextInput, Title } from '@mantine/core';
+import { Button, Select, Stack, Text, TextInput, Title } from "@mantine/core";
export interface PromptArgument {
name: string;
@@ -60,8 +60,10 @@ export function PromptArgumentsForm({
withAsterisk={arg.required}
description={arg.description}
placeholder={`Enter ${arg.name}...`}
- value={argumentValues[arg.name] || ''}
- onChange={(event) => onArgumentChange(arg.name, event.currentTarget.value)}
+ value={argumentValues[arg.name] || ""}
+ onChange={(event) =>
+ onArgumentChange(arg.name, event.currentTarget.value)
+ }
/>
))}
>
diff --git a/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.stories.tsx b/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.stories.tsx
index dde6c53f..f9dca729 100644
--- a/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.stories.tsx
+++ b/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { PromptMessagesDisplay } from './PromptMessagesDisplay';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { PromptMessagesDisplay } from "./PromptMessagesDisplay";
const meta: Meta = {
- title: 'Molecules/PromptMessagesDisplay',
+ title: "Molecules/PromptMessagesDisplay",
component: PromptMessagesDisplay,
args: {
onCopy: fn(),
@@ -17,8 +17,8 @@ export const SingleMessage: Story = {
args: {
messages: [
{
- role: 'user',
- content: 'What is the capital of France?',
+ role: "user",
+ content: "What is the capital of France?",
},
],
},
@@ -28,12 +28,13 @@ export const Conversation: Story = {
args: {
messages: [
{
- role: 'user',
- content: 'What is the capital of France?',
+ role: "user",
+ content: "What is the capital of France?",
},
{
- role: 'assistant',
- content: 'The capital of France is Paris. It is the largest city in France and serves as the country\'s political, economic, and cultural center.',
+ role: "assistant",
+ content:
+ "The capital of France is Paris. It is the largest city in France and serves as the country's political, economic, and cultural center.",
},
],
},
@@ -43,11 +44,11 @@ export const WithImage: Story = {
args: {
messages: [
{
- role: 'user',
- content: 'Here is a photo for you to analyze.',
+ role: "user",
+ content: "Here is a photo for you to analyze.",
imageContent: {
- data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==',
- mimeType: 'image/png',
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg==",
+ mimeType: "image/png",
},
},
],
@@ -58,24 +59,26 @@ export const LongConversation: Story = {
args: {
messages: [
{
- role: 'user',
- content: 'Can you help me write a haiku about programming?',
+ role: "user",
+ content: "Can you help me write a haiku about programming?",
},
{
- role: 'assistant',
- content: 'Sure! Here is a haiku about programming:\n\nSilent keys tap fast\nLogic flows through glowing screens\nBugs hide in the code',
+ role: "assistant",
+ content:
+ "Sure! Here is a haiku about programming:\n\nSilent keys tap fast\nLogic flows through glowing screens\nBugs hide in the code",
},
{
- role: 'user',
- content: 'That is great! Can you write another one about debugging?',
+ role: "user",
+ content: "That is great! Can you write another one about debugging?",
},
{
- role: 'assistant',
- content: 'Here is a debugging haiku:\n\nStack trace lines scroll down\nOne missing semicolon\nHours lost to a typo',
+ role: "assistant",
+ content:
+ "Here is a debugging haiku:\n\nStack trace lines scroll down\nOne missing semicolon\nHours lost to a typo",
},
{
- role: 'user',
- content: 'I love it! One more about open source?',
+ role: "user",
+ content: "I love it! One more about open source?",
},
],
},
diff --git a/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.tsx b/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.tsx
index 98bbeb6c..fad05017 100644
--- a/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.tsx
+++ b/clients/web/src/components/molecules/PromptMessagesDisplay/PromptMessagesDisplay.tsx
@@ -1,5 +1,5 @@
-import { Button, Stack, Text, Title } from '@mantine/core';
-import { MessageBubble } from '../../atoms/MessageBubble/MessageBubble';
+import { Button, Stack, Text, Title } from "@mantine/core";
+import { MessageBubble } from "../../atoms/MessageBubble/MessageBubble";
export interface PromptMessage {
role: string;
@@ -13,7 +13,10 @@ export interface PromptMessagesDisplayProps {
onCopy: () => void;
}
-export function PromptMessagesDisplay({ messages, onCopy }: PromptMessagesDisplayProps) {
+export function PromptMessagesDisplay({
+ messages,
+ onCopy,
+}: PromptMessagesDisplayProps) {
return (
Messages
@@ -24,7 +27,7 @@ export function PromptMessagesDisplay({ messages, onCopy }: PromptMessagesDispla
= {
- title: 'Molecules/ResourceListItem',
+ title: "Molecules/ResourceListItem",
component: ResourceListItem,
args: {
onClick: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Default: Story = {
args: {
- name: 'config.json',
- uri: 'file:///config.json',
+ name: "config.json",
+ uri: "file:///config.json",
selected: false,
},
-}
+};
export const Selected: Story = {
args: {
- name: 'config.json',
- uri: 'file:///config.json',
+ name: "config.json",
+ uri: "file:///config.json",
selected: true,
},
-}
+};
export const WithAnnotations: Story = {
args: {
- name: 'app-settings.json',
- uri: 'file:///app-settings.json',
+ name: "app-settings.json",
+ uri: "file:///app-settings.json",
selected: false,
annotations: {
- audience: 'application',
+ audience: "application",
priority: 0.9,
},
},
-}
+};
export const WithHighPriority: Story = {
args: {
- name: 'critical-data.json',
- uri: 'file:///critical-data.json',
+ name: "critical-data.json",
+ uri: "file:///critical-data.json",
selected: false,
annotations: {
- audience: 'user',
+ audience: "user",
priority: 0.8,
},
},
-}
+};
export const NoPriority: Story = {
args: {
- name: 'readme.md',
- uri: 'file:///readme.md',
+ name: "readme.md",
+ uri: "file:///readme.md",
selected: false,
annotations: {
- audience: 'user',
+ audience: "user",
},
},
-}
+};
diff --git a/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.tsx b/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.tsx
index 16636e3c..f2106a7e 100644
--- a/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.tsx
+++ b/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.tsx
@@ -1,32 +1,37 @@
-import { Group, Stack, Text, UnstyledButton } from '@mantine/core'
-import { AnnotationBadge } from '../../atoms/AnnotationBadge/AnnotationBadge'
+import { Group, Stack, Text, UnstyledButton } from "@mantine/core";
+import { AnnotationBadge } from "../../atoms/AnnotationBadge/AnnotationBadge";
export interface ResourceAnnotations {
- audience?: string
- priority?: number
+ audience?: string;
+ priority?: number;
}
export interface ResourceListItemProps {
- name: string
- uri: string
- annotations?: ResourceAnnotations
- selected: boolean
- onClick: () => void
+ name: string;
+ uri: string;
+ annotations?: ResourceAnnotations;
+ selected: boolean;
+ onClick: () => void;
}
function priorityLabel(priority: number): string {
- if (priority >= 0.7) return 'priority: high'
- if (priority >= 0.4) return 'priority: medium'
- return 'priority: low'
+ if (priority >= 0.7) return "priority: high";
+ if (priority >= 0.4) return "priority: medium";
+ return "priority: low";
}
-export function ResourceListItem({ name, annotations, selected, onClick }: ResourceListItemProps) {
+export function ResourceListItem({
+ name,
+ annotations,
+ selected,
+ onClick,
+}: ResourceListItemProps) {
return (
@@ -34,14 +39,20 @@ export function ResourceListItem({ name, annotations, selected, onClick }: Resou
{annotations && (
{annotations.audience && (
-
+
)}
{annotations.priority !== undefined && (
-
+
)}
)}
- )
+ );
}
diff --git a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx
index 2f8f45c8..a4da9582 100644
--- a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx
+++ b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.stories.tsx
@@ -1,68 +1,73 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ResourcePreviewPanel } from './ResourcePreviewPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ResourcePreviewPanel } from "./ResourcePreviewPanel";
const meta: Meta = {
- title: 'Molecules/ResourcePreviewPanel',
+ title: "Molecules/ResourcePreviewPanel",
component: ResourcePreviewPanel,
args: {
onCopy: fn(),
onSubscribe: fn(),
onUnsubscribe: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const JsonResource: Story = {
args: {
- uri: 'file:///config.json',
- mimeType: 'application/json',
- content: JSON.stringify({ name: 'mcp-inspector', version: '2.0.0', debug: true }, null, 2),
+ uri: "file:///config.json",
+ mimeType: "application/json",
+ content: JSON.stringify(
+ { name: "mcp-inspector", version: "2.0.0", debug: true },
+ null,
+ 2,
+ ),
isSubscribed: false,
- lastUpdated: '2026-03-17T10:30:00Z',
+ lastUpdated: "2026-03-17T10:30:00Z",
},
-}
+};
export const TextResource: Story = {
args: {
- uri: 'file:///readme.txt',
- mimeType: 'text/plain',
- content: 'This is a plain text resource with some example content.\nLine two of the content.',
+ uri: "file:///readme.txt",
+ mimeType: "text/plain",
+ content:
+ "This is a plain text resource with some example content.\nLine two of the content.",
isSubscribed: false,
},
-}
+};
export const Subscribed: Story = {
args: {
- uri: 'file:///data.json',
- mimeType: 'application/json',
- content: JSON.stringify({ status: 'active' }, null, 2),
+ uri: "file:///data.json",
+ mimeType: "application/json",
+ content: JSON.stringify({ status: "active" }, null, 2),
isSubscribed: true,
- lastUpdated: '2026-03-17T12:00:00Z',
+ lastUpdated: "2026-03-17T12:00:00Z",
},
-}
+};
export const NotSubscribed: Story = {
args: {
- uri: 'file:///data.json',
- mimeType: 'application/json',
- content: JSON.stringify({ status: 'active' }, null, 2),
+ uri: "file:///data.json",
+ mimeType: "application/json",
+ content: JSON.stringify({ status: "active" }, null, 2),
isSubscribed: false,
},
-}
+};
export const WithAnnotations: Story = {
args: {
- uri: 'file:///settings.json',
- mimeType: 'application/json',
- content: JSON.stringify({ theme: 'dark', lang: 'en' }, null, 2),
+ uri: "file:///settings.json",
+ mimeType: "application/json",
+ content: JSON.stringify({ theme: "dark", lang: "en" }, null, 2),
isSubscribed: false,
annotations: {
- audience: 'application',
+ audience: "application",
priority: 0.8,
},
- lastUpdated: '2026-03-17T09:00:00Z',
+ lastUpdated: "2026-03-17T09:00:00Z",
},
-}
+};
diff --git a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx
index 7264e775..92eab353 100644
--- a/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx
+++ b/clients/web/src/components/molecules/ResourcePreviewPanel/ResourcePreviewPanel.tsx
@@ -1,22 +1,22 @@
-import { Button, Group, Stack, Text, Title } from '@mantine/core'
-import { ContentViewer } from '../../atoms/ContentViewer/ContentViewer'
+import { Button, Group, Stack, Text, Title } from "@mantine/core";
+import { ContentViewer } from "../../atoms/ContentViewer/ContentViewer";
export interface ResourcePreviewPanelProps {
- uri: string
- mimeType: string
- annotations?: { audience?: string; priority?: number }
- content: string
- lastUpdated?: string
- isSubscribed: boolean
- onCopy: () => void
- onSubscribe: () => void
- onUnsubscribe: () => void
+ uri: string;
+ mimeType: string;
+ annotations?: { audience?: string; priority?: number };
+ content: string;
+ lastUpdated?: string;
+ isSubscribed: boolean;
+ onCopy: () => void;
+ onSubscribe: () => void;
+ onUnsubscribe: () => void;
}
-function resolveContentType(mimeType: string): 'json' | 'image' | 'text' {
- if (mimeType === 'application/json') return 'json'
- if (mimeType.startsWith('image/')) return 'image'
- return 'text'
+function resolveContentType(mimeType: string): "json" | "image" | "text" {
+ if (mimeType === "application/json") return "json";
+ if (mimeType.startsWith("image/")) return "image";
+ return "text";
}
export function ResourcePreviewPanel({
@@ -34,7 +34,10 @@ export function ResourcePreviewPanel({
Content Preview
- URI: {uri}
+ URI:{" "}
+
+ {uri}
+
MIME: {mimeType}
@@ -43,10 +46,14 @@ export function ResourcePreviewPanel({
Annotations:
{annotations.audience && (
- Audience: {annotations.audience}
+
+ Audience: {annotations.audience}
+
)}
{annotations.priority !== undefined && (
- Priority: {annotations.priority}
+
+ Priority: {annotations.priority}
+
)}
)}
@@ -64,7 +71,7 @@ export function ResourcePreviewPanel({
size="sm"
onClick={isSubscribed ? onUnsubscribe : onSubscribe}
>
- {isSubscribed ? 'Unsubscribe' : 'Subscribe'}
+ {isSubscribed ? "Unsubscribe" : "Subscribe"}
{lastUpdated && (
@@ -73,5 +80,5 @@ export function ResourcePreviewPanel({
)}
- )
+ );
}
diff --git a/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.stories.tsx b/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.stories.tsx
index 01e4302b..e8e2bffa 100644
--- a/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.stories.tsx
+++ b/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.stories.tsx
@@ -1,39 +1,39 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ResourceTemplateInput } from './ResourceTemplateInput'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ResourceTemplateInput } from "./ResourceTemplateInput";
const meta: Meta = {
- title: 'Molecules/ResourceTemplateInput',
+ title: "Molecules/ResourceTemplateInput",
component: ResourceTemplateInput,
args: {
onVariableChange: fn(),
onSubmit: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const SingleVariable: Story = {
args: {
- template: 'user/{id}',
+ template: "user/{id}",
variables: {},
},
-}
+};
export const MultipleVariables: Story = {
args: {
- template: 'org/{orgId}/user/{userId}',
+ template: "org/{orgId}/user/{userId}",
variables: {},
},
-}
+};
export const FilledIn: Story = {
args: {
- template: 'org/{orgId}/user/{userId}',
+ template: "org/{orgId}/user/{userId}",
variables: {
- orgId: 'acme-corp',
- userId: 'user-42',
+ orgId: "acme-corp",
+ userId: "user-42",
},
},
-}
+};
diff --git a/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.tsx b/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.tsx
index eea517ef..a5808cd5 100644
--- a/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.tsx
+++ b/clients/web/src/components/molecules/ResourceTemplateInput/ResourceTemplateInput.tsx
@@ -1,36 +1,39 @@
-import { Button, Group, Text, TextInput } from '@mantine/core'
+import { Button, Group, Text, TextInput } from "@mantine/core";
export interface ResourceTemplateInputProps {
- template: string
- variables: Record
- onVariableChange: (name: string, value: string) => void
- onSubmit: () => void
+ template: string;
+ variables: Record;
+ onVariableChange: (name: string, value: string) => void;
+ onSubmit: () => void;
}
interface TemplatePart {
- type: 'static' | 'variable'
- value: string
+ type: "static" | "variable";
+ value: string;
}
function parseTemplate(template: string): TemplatePart[] {
- const parts: TemplatePart[] = []
- const regex = /\{(\w+)\}/g
- let lastIndex = 0
- let match: RegExpExecArray | null
+ const parts: TemplatePart[] = [];
+ const regex = /\{(\w+)\}/g;
+ let lastIndex = 0;
+ let match: RegExpExecArray | null;
while ((match = regex.exec(template)) !== null) {
if (match.index > lastIndex) {
- parts.push({ type: 'static', value: template.slice(lastIndex, match.index) })
+ parts.push({
+ type: "static",
+ value: template.slice(lastIndex, match.index),
+ });
}
- parts.push({ type: 'variable', value: match[1] })
- lastIndex = regex.lastIndex
+ parts.push({ type: "variable", value: match[1] });
+ lastIndex = regex.lastIndex;
}
if (lastIndex < template.length) {
- parts.push({ type: 'static', value: template.slice(lastIndex) })
+ parts.push({ type: "static", value: template.slice(lastIndex) });
}
- return parts
+ return parts;
}
export function ResourceTemplateInput({
@@ -39,12 +42,12 @@ export function ResourceTemplateInput({
onVariableChange,
onSubmit,
}: ResourceTemplateInputProps) {
- const parts = parseTemplate(template)
+ const parts = parseTemplate(template);
return (
{parts.map((part, index) =>
- part.type === 'static' ? (
+ part.type === "static" ? (
{part.value}
@@ -54,14 +57,16 @@ export function ResourceTemplateInput({
size="sm"
label={part.value}
placeholder={part.value}
- value={variables[part.value] ?? ''}
- onChange={(e) => onVariableChange(part.value, e.currentTarget.value)}
+ value={variables[part.value] ?? ""}
+ onChange={(e) =>
+ onVariableChange(part.value, e.currentTarget.value)
+ }
/>
- )
+ ),
)}
Go
- )
+ );
}
diff --git a/clients/web/src/components/molecules/ResultPanel/ResultPanel.stories.tsx b/clients/web/src/components/molecules/ResultPanel/ResultPanel.stories.tsx
index fafa7813..c1af1c65 100644
--- a/clients/web/src/components/molecules/ResultPanel/ResultPanel.stories.tsx
+++ b/clients/web/src/components/molecules/ResultPanel/ResultPanel.stories.tsx
@@ -1,71 +1,71 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ResultPanel } from './ResultPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ResultPanel } from "./ResultPanel";
const meta: Meta = {
- title: 'Molecules/ResultPanel',
+ title: "Molecules/ResultPanel",
component: ResultPanel,
args: {
onCopy: fn(),
onClear: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Empty: Story = {
args: {
content: [],
},
-}
+};
export const TextResult: Story = {
args: {
content: [
{
- type: 'text',
- text: 'The current weather in San Francisco is 65°F and sunny.',
+ type: "text",
+ text: "The current weather in San Francisco is 65°F and sunny.",
},
],
},
-}
+};
export const JsonResult: Story = {
args: {
content: [
{
- type: 'json',
+ type: "json",
text: '{"temperature":65,"unit":"fahrenheit","condition":"sunny","city":"San Francisco"}',
},
],
},
-}
+};
export const ImageResult: Story = {
args: {
content: [
{
- type: 'image',
- data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
- mimeType: 'image/png',
+ type: "image",
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
+ mimeType: "image/png",
},
],
},
-}
+};
export const MixedContent: Story = {
args: {
content: [
{
- type: 'text',
- text: 'Here is the generated image based on your description:',
+ type: "text",
+ text: "Here is the generated image based on your description:",
},
{
- type: 'image',
- data: 'iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==',
- mimeType: 'image/png',
+ type: "image",
+ data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mP8/5+hHgAHggJ/PchI7wAAAABJRU5ErkJggg==",
+ mimeType: "image/png",
},
],
},
-}
+};
diff --git a/clients/web/src/components/molecules/ResultPanel/ResultPanel.tsx b/clients/web/src/components/molecules/ResultPanel/ResultPanel.tsx
index e231754f..e026b587 100644
--- a/clients/web/src/components/molecules/ResultPanel/ResultPanel.tsx
+++ b/clients/web/src/components/molecules/ResultPanel/ResultPanel.tsx
@@ -1,17 +1,17 @@
-import { Button, Group, Stack, Text, Title } from '@mantine/core'
-import { ContentViewer } from '../../atoms/ContentViewer/ContentViewer'
+import { Button, Group, Stack, Text, Title } from "@mantine/core";
+import { ContentViewer } from "../../atoms/ContentViewer/ContentViewer";
export interface ResultContentItem {
- type: string
- text?: string
- data?: string
- mimeType?: string
+ type: string;
+ text?: string;
+ data?: string;
+ mimeType?: string;
}
export interface ResultPanelProps {
- content: ResultContentItem[]
- onCopy: () => void
- onClear: () => void
+ content: ResultContentItem[];
+ onCopy: () => void;
+ onClear: () => void;
}
export function ResultPanel({ content, onCopy, onClear }: ResultPanelProps) {
@@ -27,22 +27,22 @@ export function ResultPanel({ content, onCopy, onClear }: ResultPanelProps) {
return (
- )
+ );
}
if (item.data !== undefined) {
return (
- )
+ );
}
- return null
+ return null;
})}
>
)}
@@ -55,5 +55,5 @@ export function ResultPanel({ content, onCopy, onClear }: ResultPanelProps) {
- )
+ );
}
diff --git a/clients/web/src/components/molecules/RootsTable/RootsTable.stories.tsx b/clients/web/src/components/molecules/RootsTable/RootsTable.stories.tsx
index 6db35068..1f071732 100644
--- a/clients/web/src/components/molecules/RootsTable/RootsTable.stories.tsx
+++ b/clients/web/src/components/molecules/RootsTable/RootsTable.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { RootsTable } from './RootsTable';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { RootsTable } from "./RootsTable";
const meta: Meta = {
- title: 'Molecules/RootsTable',
+ title: "Molecules/RootsTable",
component: RootsTable,
args: {
onRemoveRoot: fn(),
@@ -20,44 +20,42 @@ type Story = StoryObj;
export const WithRoots: Story = {
args: {
roots: [
- { name: 'Project Source', uri: 'file:///home/user/project/src' },
- { name: 'Configuration', uri: 'file:///home/user/project/config' },
- { name: 'Documentation', uri: 'file:///home/user/project/docs' },
+ { name: "Project Source", uri: "file:///home/user/project/src" },
+ { name: "Configuration", uri: "file:///home/user/project/config" },
+ { name: "Documentation", uri: "file:///home/user/project/docs" },
],
- newRootName: '',
- newRootPath: '',
+ newRootName: "",
+ newRootPath: "",
},
};
export const Empty: Story = {
args: {
roots: [],
- newRootName: '',
- newRootPath: '',
+ newRootName: "",
+ newRootPath: "",
},
};
export const AddingNew: Story = {
args: {
- roots: [
- { name: 'Project Source', uri: 'file:///home/user/project/src' },
- ],
- newRootName: 'Test Data',
- newRootPath: '/home/user/project/test-data',
+ roots: [{ name: "Project Source", uri: "file:///home/user/project/src" }],
+ newRootName: "Test Data",
+ newRootPath: "/home/user/project/test-data",
},
};
export const ManyRoots: Story = {
args: {
roots: [
- { name: 'Source Code', uri: 'file:///home/user/project/src' },
- { name: 'Configuration', uri: 'file:///home/user/project/config' },
- { name: 'Documentation', uri: 'file:///home/user/project/docs' },
- { name: 'Test Fixtures', uri: 'file:///home/user/project/test/fixtures' },
- { name: 'Build Output', uri: 'file:///home/user/project/dist' },
- { name: 'Scripts', uri: 'file:///home/user/project/scripts' },
+ { name: "Source Code", uri: "file:///home/user/project/src" },
+ { name: "Configuration", uri: "file:///home/user/project/config" },
+ { name: "Documentation", uri: "file:///home/user/project/docs" },
+ { name: "Test Fixtures", uri: "file:///home/user/project/test/fixtures" },
+ { name: "Build Output", uri: "file:///home/user/project/dist" },
+ { name: "Scripts", uri: "file:///home/user/project/scripts" },
],
- newRootName: '',
- newRootPath: '',
+ newRootName: "",
+ newRootPath: "",
},
};
diff --git a/clients/web/src/components/molecules/RootsTable/RootsTable.tsx b/clients/web/src/components/molecules/RootsTable/RootsTable.tsx
index a634dc5b..7d8f138c 100644
--- a/clients/web/src/components/molecules/RootsTable/RootsTable.tsx
+++ b/clients/web/src/components/molecules/RootsTable/RootsTable.tsx
@@ -9,7 +9,7 @@ import {
Text,
TextInput,
Title,
-} from '@mantine/core';
+} from "@mantine/core";
export interface RootEntry {
name: string;
@@ -94,14 +94,12 @@ export function RootsTable({
Browse
-
- Add
-
+ Add
- Roots give the server access to these directories. Only add directories you trust the server
- to access.
+ Roots give the server access to these directories. Only add directories
+ you trust the server to access.
);
diff --git a/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.stories.tsx b/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.stories.tsx
index 1fb34841..0bc6918a 100644
--- a/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.stories.tsx
+++ b/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.stories.tsx
@@ -1,14 +1,14 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { SamplingRequestPanel } from './SamplingRequestPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { SamplingRequestPanel } from "./SamplingRequestPanel";
const meta: Meta = {
- title: 'Molecules/SamplingRequestPanel',
+ title: "Molecules/SamplingRequestPanel",
component: SamplingRequestPanel,
args: {
- responseText: '',
- modelUsed: 'claude-3-sonnet',
- stopReason: 'end_turn',
+ responseText: "",
+ modelUsed: "claude-3-sonnet",
+ stopReason: "end_turn",
onResponseChange: fn(),
onModelChange: fn(),
onStopReasonChange: fn(),
@@ -16,93 +16,85 @@ const meta: Meta = {
onSend: fn(),
onReject: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const SimpleRequest: Story = {
args: {
- messages: [
- { role: 'user', content: 'What is the capital of France?' },
- ],
+ messages: [{ role: "user", content: "What is the capital of France?" }],
},
-}
+};
export const WithModelHints: Story = {
args: {
- messages: [
- { role: 'user', content: 'Summarize this document for me.' },
- ],
- modelHints: ['claude-3-sonnet', 'gpt-4'],
+ messages: [{ role: "user", content: "Summarize this document for me." }],
+ modelHints: ["claude-3-sonnet", "gpt-4"],
},
-}
+};
export const WithPriorities: Story = {
args: {
- messages: [
- { role: 'user', content: 'Translate this text to Spanish.' },
- ],
- modelHints: ['claude-3-sonnet'],
+ messages: [{ role: "user", content: "Translate this text to Spanish." }],
+ modelHints: ["claude-3-sonnet"],
costPriority: 0.3,
speedPriority: 0.5,
intelligencePriority: 0.9,
},
-}
+};
export const WithAllParams: Story = {
args: {
messages: [
- { role: 'user', content: 'Write a haiku about programming.' },
- { role: 'assistant', content: 'Here is a haiku:' },
+ { role: "user", content: "Write a haiku about programming." },
+ { role: "assistant", content: "Here is a haiku:" },
],
maxTokens: 1024,
- stopSequences: ['\n\n', 'END'],
+ stopSequences: ["\n\n", "END"],
temperature: 0.7,
- includeContext: 'thisServer',
+ includeContext: "thisServer",
},
-}
+};
export const WithTools: Story = {
args: {
messages: [
- { role: 'user', content: 'Look up the weather in San Francisco.' },
+ { role: "user", content: "Look up the weather in San Francisco." },
],
tools: [
{
- name: 'get_weather',
- description: 'Get the current weather for a given location.',
+ name: "get_weather",
+ description: "Get the current weather for a given location.",
inputSchema: {
- type: 'object',
+ type: "object",
properties: {
- location: { type: 'string', description: 'City name' },
+ location: { type: "string", description: "City name" },
},
- required: ['location'],
+ required: ["location"],
},
},
{
- name: 'search_web',
- description: 'Search the web for information.',
+ name: "search_web",
+ description: "Search the web for information.",
inputSchema: {
- type: 'object',
+ type: "object",
properties: {
- query: { type: 'string', description: 'Search query' },
+ query: { type: "string", description: "Search query" },
},
- required: ['query'],
+ required: ["query"],
},
},
],
- toolChoice: 'auto',
+ toolChoice: "auto",
},
-}
+};
export const PrefilledResponse: Story = {
args: {
- messages: [
- { role: 'user', content: 'What is 2 + 2?' },
- ],
- responseText: 'The answer is 4.',
- modelUsed: 'claude-3-haiku',
- stopReason: 'end_turn',
+ messages: [{ role: "user", content: "What is 2 + 2?" }],
+ responseText: "The answer is 4.",
+ modelUsed: "claude-3-haiku",
+ stopReason: "end_turn",
},
-}
+};
diff --git a/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx b/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx
index 86aadc0d..d1895a6b 100644
--- a/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx
+++ b/clients/web/src/components/molecules/SamplingRequestPanel/SamplingRequestPanel.tsx
@@ -11,48 +11,48 @@ import {
TextInput,
Textarea,
Title,
-} from '@mantine/core'
-import { MessageBubble } from '../../atoms/MessageBubble/MessageBubble'
+} from "@mantine/core";
+import { MessageBubble } from "../../atoms/MessageBubble/MessageBubble";
export interface SamplingMessage {
- role: string
- content: string
- imageContent?: { data: string; mimeType: string }
+ role: string;
+ content: string;
+ imageContent?: { data: string; mimeType: string };
}
export interface SamplingTool {
- name: string
- description?: string
- inputSchema: Record
+ name: string;
+ description?: string;
+ inputSchema: Record;
}
export interface SamplingRequestPanelProps {
- messages: SamplingMessage[]
- modelHints?: string[]
- costPriority?: number
- speedPriority?: number
- intelligencePriority?: number
- maxTokens?: number
- stopSequences?: string[]
- temperature?: number
- includeContext?: string
- tools?: SamplingTool[]
- toolChoice?: string
- responseText: string
- modelUsed: string
- stopReason: string
- onResponseChange: (text: string) => void
- onModelChange: (model: string) => void
- onStopReasonChange: (reason: string) => void
- onAutoRespond: () => void
- onSend: () => void
- onReject: () => void
+ messages: SamplingMessage[];
+ modelHints?: string[];
+ costPriority?: number;
+ speedPriority?: number;
+ intelligencePriority?: number;
+ maxTokens?: number;
+ stopSequences?: string[];
+ temperature?: number;
+ includeContext?: string;
+ tools?: SamplingTool[];
+ toolChoice?: string;
+ responseText: string;
+ modelUsed: string;
+ stopReason: string;
+ onResponseChange: (text: string) => void;
+ onModelChange: (model: string) => void;
+ onStopReasonChange: (reason: string) => void;
+ onAutoRespond: () => void;
+ onSend: () => void;
+ onReject: () => void;
}
function formatPriority(value: number): string {
- if (value < 0.4) return `low (${value})`
- if (value <= 0.7) return `medium (${value})`
- return `high (${value})`
+ if (value < 0.4) return `low (${value})`;
+ if (value <= 0.7) return `medium (${value})`;
+ return `high (${value})`;
}
export function SamplingRequestPanel({
@@ -88,7 +88,7 @@ export function SamplingRequestPanel({
@@ -105,25 +105,40 @@ export function SamplingRequestPanel({
))}
{costPriority !== undefined && (
- Cost Priority: {formatPriority(costPriority)}
+
+ Cost Priority: {formatPriority(costPriority)}
+
)}
{speedPriority !== undefined && (
- Speed Priority: {formatPriority(speedPriority)}
+
+ Speed Priority: {formatPriority(speedPriority)}
+
)}
{intelligencePriority !== undefined && (
- Intelligence Priority: {formatPriority(intelligencePriority)}
+
+ Intelligence Priority: {formatPriority(intelligencePriority)}
+
)}
)}
Parameters:
- Max Tokens: {maxTokens ?? 'not specified'}
- Stop Sequences: {stopSequences ? JSON.stringify(stopSequences) : 'not specified'}
- Temperature: {temperature !== undefined ? temperature : 'not specified'}
+ Max Tokens: {maxTokens ?? "not specified"}
+
+ Stop Sequences:{" "}
+ {stopSequences ? JSON.stringify(stopSequences) : "not specified"}
+
+
+ Temperature: {temperature !== undefined ? temperature : "not specified"}
+
{includeContext && (
-
+
)}
{tools && tools.length > 0 && (
@@ -159,9 +174,9 @@ export function SamplingRequestPanel({
/>
) : (
-
+
)}
@@ -152,7 +161,11 @@ export function ServerSettingsForm({
No request metadata configured
) : (
-
+
)}
@@ -163,13 +176,13 @@ export function ServerSettingsForm({
label="Connection Timeout"
suffix=" ms"
value={connectionTimeout}
- onChange={handleTimeoutChange('connectionTimeout')}
+ onChange={handleTimeoutChange("connectionTimeout")}
/>
@@ -181,19 +194,19 @@ export function ServerSettingsForm({
onOAuthChange('clientId', e.currentTarget.value)}
+ value={oauthClientId ?? ""}
+ onChange={(e) => onOAuthChange("clientId", e.currentTarget.value)}
/>
onOAuthChange('clientSecret', e.currentTarget.value)}
+ onChange={(e) => onOAuthChange("clientSecret", e.currentTarget.value)}
/>
onOAuthChange('scopes', e.currentTarget.value)}
+ value={oauthScopes ?? ""}
+ onChange={(e) => onOAuthChange("scopes", e.currentTarget.value)}
/>
);
diff --git a/clients/web/src/components/molecules/TaskCard/TaskCard.stories.tsx b/clients/web/src/components/molecules/TaskCard/TaskCard.stories.tsx
index ea6eac40..a622717a 100644
--- a/clients/web/src/components/molecules/TaskCard/TaskCard.stories.tsx
+++ b/clients/web/src/components/molecules/TaskCard/TaskCard.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { TaskCard } from './TaskCard';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { TaskCard } from "./TaskCard";
const meta: Meta = {
- title: 'Molecules/TaskCard',
+ title: "Molecules/TaskCard",
component: TaskCard,
args: {
onViewDetails: fn(),
@@ -18,77 +18,77 @@ type Story = StoryObj;
export const Running: Story = {
args: {
- taskId: 'task-abc-123',
- status: 'running',
- method: 'tools/call',
- target: 'generate_report',
+ taskId: "task-abc-123",
+ status: "running",
+ method: "tools/call",
+ target: "generate_report",
progress: 80,
- progressDescription: 'Generating report section 4 of 5...',
- startedAt: '2026-03-17T10:30:00Z',
- elapsed: '2m 15s',
+ progressDescription: "Generating report section 4 of 5...",
+ startedAt: "2026-03-17T10:30:00Z",
+ elapsed: "2m 15s",
},
};
export const RunningWithProgress: Story = {
args: {
- taskId: 'task-def-456',
- status: 'running',
- method: 'tools/call',
- target: 'analyze_data',
+ taskId: "task-def-456",
+ status: "running",
+ method: "tools/call",
+ target: "analyze_data",
progress: 45,
- progressDescription: 'Analyzing dataset...',
- startedAt: '2026-03-17T10:32:00Z',
- elapsed: '30s',
+ progressDescription: "Analyzing dataset...",
+ startedAt: "2026-03-17T10:32:00Z",
+ elapsed: "30s",
},
};
export const Waiting: Story = {
args: {
- taskId: 'task-ghi-789',
- status: 'waiting',
- method: 'resources/read',
- target: 'file:///data/report.csv',
+ taskId: "task-ghi-789",
+ status: "waiting",
+ method: "resources/read",
+ target: "file:///data/report.csv",
progress: 0,
- startedAt: '2026-03-17T10:35:00Z',
+ startedAt: "2026-03-17T10:35:00Z",
},
};
export const Completed: Story = {
args: {
- taskId: 'task-jkl-012',
- status: 'completed',
- method: 'tools/call',
- target: 'generate_summary',
+ taskId: "task-jkl-012",
+ status: "completed",
+ method: "tools/call",
+ target: "generate_summary",
progress: 100,
- startedAt: '2026-03-17T10:20:00Z',
- completedAt: '2026-03-17T10:25:00Z',
- elapsed: '5m 00s',
+ startedAt: "2026-03-17T10:20:00Z",
+ completedAt: "2026-03-17T10:25:00Z",
+ elapsed: "5m 00s",
},
};
export const Failed: Story = {
args: {
- taskId: 'task-mno-345',
- status: 'failed',
- method: 'tools/call',
- target: 'fetch_remote_data',
+ taskId: "task-mno-345",
+ status: "failed",
+ method: "tools/call",
+ target: "fetch_remote_data",
progress: 45,
- startedAt: '2026-03-17T10:28:00Z',
- completedAt: '2026-03-17T10:29:30Z',
- elapsed: '1m 30s',
- error: 'Connection refused: upstream server not responding after 3 retries',
+ startedAt: "2026-03-17T10:28:00Z",
+ completedAt: "2026-03-17T10:29:30Z",
+ elapsed: "1m 30s",
+ error: "Connection refused: upstream server not responding after 3 retries",
},
};
export const Cancelled: Story = {
args: {
- taskId: 'task-pqr-678',
- status: 'cancelled',
- method: 'tools/call',
- target: 'long_running_analysis',
+ taskId: "task-pqr-678",
+ status: "cancelled",
+ method: "tools/call",
+ target: "long_running_analysis",
progress: 30,
- startedAt: '2026-03-17T10:15:00Z',
- completedAt: '2026-03-17T10:18:00Z',
- elapsed: '3m 00s',
+ startedAt: "2026-03-17T10:15:00Z",
+ completedAt: "2026-03-17T10:18:00Z",
+ elapsed: "3m 00s",
},
};
diff --git a/clients/web/src/components/molecules/TaskCard/TaskCard.tsx b/clients/web/src/components/molecules/TaskCard/TaskCard.tsx
index 288996de..9748dc11 100644
--- a/clients/web/src/components/molecules/TaskCard/TaskCard.tsx
+++ b/clients/web/src/components/molecules/TaskCard/TaskCard.tsx
@@ -1,7 +1,12 @@
-import { Badge, Button, Card, Group, Stack, Text } from '@mantine/core';
-import { ProgressDisplay } from '../../atoms/ProgressDisplay/ProgressDisplay';
+import { Badge, Button, Card, Group, Stack, Text } from "@mantine/core";
+import { ProgressDisplay } from "../../atoms/ProgressDisplay/ProgressDisplay";
-export type TaskStatus = 'waiting' | 'running' | 'completed' | 'failed' | 'cancelled';
+export type TaskStatus =
+ | "waiting"
+ | "running"
+ | "completed"
+ | "failed"
+ | "cancelled";
export interface TaskCardProps {
taskId: string;
@@ -21,11 +26,11 @@ export interface TaskCardProps {
}
const STATUS_COLORS: Record = {
- waiting: 'gray',
- running: 'blue',
- completed: 'green',
- failed: 'red',
- cancelled: 'yellow',
+ waiting: "gray",
+ running: "blue",
+ completed: "green",
+ failed: "red",
+ cancelled: "yellow",
};
export function TaskCard({
@@ -44,7 +49,7 @@ export function TaskCard({
onCancel,
onDismiss,
}: TaskCardProps) {
- const isActive = status === 'waiting' || status === 'running';
+ const isActive = status === "waiting" || status === "running";
return (
@@ -56,18 +61,22 @@ export function TaskCard({
{status}
- {progress !== undefined && (
-
- )}
+ {progress !== undefined && }
- Method: {method}
+ Method:{" "}
+
+ {method}
+
{target && (
- Tool/Resource: {target}
+ Tool/Resource:{" "}
+
+ {target}
+
)}
diff --git a/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.stories.tsx b/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.stories.tsx
index 03798aa9..bb286017 100644
--- a/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.stories.tsx
+++ b/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.stories.tsx
@@ -1,9 +1,9 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ToolDetailPanel } from './ToolDetailPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ToolDetailPanel } from "./ToolDetailPanel";
const meta: Meta = {
- title: 'Molecules/ToolDetailPanel',
+ title: "Molecules/ToolDetailPanel",
component: ToolDetailPanel,
args: {
onFormChange: fn(),
@@ -12,85 +12,88 @@ const meta: Meta = {
formValues: {},
isExecuting: false,
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const SimpleStringParam: Story = {
args: {
- name: 'send_message',
+ name: "send_message",
schema: {
- type: 'object',
+ type: "object",
properties: {
- message: { type: 'string', description: 'The message to send' },
+ message: { type: "string", description: "The message to send" },
},
- required: ['message'],
+ required: ["message"],
},
},
-}
+};
export const MultipleParams: Story = {
args: {
- name: 'create_record',
- description: 'Creates a new record with the given parameters',
+ name: "create_record",
+ description: "Creates a new record with the given parameters",
schema: {
- type: 'object',
+ type: "object",
properties: {
- title: { type: 'string', description: 'Record title' },
- count: { type: 'number', description: 'Number of items' },
- enabled: { type: 'boolean', description: 'Whether the record is active' },
+ title: { type: "string", description: "Record title" },
+ count: { type: "number", description: "Number of items" },
+ enabled: {
+ type: "boolean",
+ description: "Whether the record is active",
+ },
},
- required: ['title'],
+ required: ["title"],
},
},
-}
+};
export const WithAnnotations: Story = {
args: {
- name: 'delete_records',
- description: 'Deletes records matching the given criteria',
+ name: "delete_records",
+ description: "Deletes records matching the given criteria",
annotations: {
- audience: 'admin',
+ audience: "admin",
readOnly: true,
destructive: true,
},
schema: {
- type: 'object',
+ type: "object",
properties: {
- pattern: { type: 'string', description: 'Pattern to match records' },
+ pattern: { type: "string", description: "Pattern to match records" },
},
},
},
-}
+};
export const Executing: Story = {
args: {
- name: 'long_query',
- description: 'Runs a long database query',
+ name: "long_query",
+ description: "Runs a long database query",
isExecuting: true,
schema: {
- type: 'object',
+ type: "object",
properties: {
- query: { type: 'string', description: 'SQL query to execute' },
+ query: { type: "string", description: "SQL query to execute" },
},
},
- formValues: { query: 'SELECT * FROM users' },
+ formValues: { query: "SELECT * FROM users" },
},
-}
+};
export const WithProgress: Story = {
args: {
- name: 'batch_process',
- description: 'Processes items in batch',
+ name: "batch_process",
+ description: "Processes items in batch",
isExecuting: true,
- progress: { percent: 60, description: 'Processing step 3 of 5' },
+ progress: { percent: 60, description: "Processing step 3 of 5" },
schema: {
- type: 'object',
+ type: "object",
properties: {
- batchSize: { type: 'number', description: 'Number of items per batch' },
+ batchSize: { type: "number", description: "Number of items per batch" },
},
},
formValues: { batchSize: 100 },
},
-}
+};
diff --git a/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx b/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx
index 57b645b4..7637225c 100644
--- a/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx
+++ b/clients/web/src/components/molecules/ToolDetailPanel/ToolDetailPanel.tsx
@@ -1,28 +1,28 @@
-import { Button, Divider, Group, Stack, Text, Title } from '@mantine/core'
-import { AnnotationBadge } from '../../atoms/AnnotationBadge/AnnotationBadge'
-import { ProgressDisplay } from '../../atoms/ProgressDisplay/ProgressDisplay'
-import { SchemaForm } from '../SchemaForm/SchemaForm'
-import type { JsonSchema } from '../SchemaForm/SchemaForm'
+import { Button, Divider, Group, Stack, Text, Title } from "@mantine/core";
+import { AnnotationBadge } from "../../atoms/AnnotationBadge/AnnotationBadge";
+import { ProgressDisplay } from "../../atoms/ProgressDisplay/ProgressDisplay";
+import { SchemaForm } from "../SchemaForm/SchemaForm";
+import type { JsonSchema } from "../SchemaForm/SchemaForm";
export interface ToolAnnotations {
- audience?: string
- readOnly?: boolean
- destructive?: boolean
- longRunning?: boolean
- hints?: string
+ audience?: string;
+ readOnly?: boolean;
+ destructive?: boolean;
+ longRunning?: boolean;
+ hints?: string;
}
export interface ToolDetailPanelProps {
- name: string
- description?: string
- annotations?: ToolAnnotations
- schema: JsonSchema
- formValues: Record
- isExecuting: boolean
- progress?: { percent: number; description?: string }
- onFormChange: (values: Record) => void
- onExecute: () => void
- onCancel: () => void
+ name: string;
+ description?: string;
+ annotations?: ToolAnnotations;
+ schema: JsonSchema;
+ formValues: Record;
+ isExecuting: boolean;
+ progress?: { percent: number; description?: string };
+ onFormChange: (values: Record) => void;
+ onExecute: () => void;
+ onCancel: () => void;
}
export function ToolDetailPanel({
@@ -43,7 +43,7 @@ export function ToolDetailPanel({
annotations.readOnly ||
annotations.destructive ||
annotations.longRunning ||
- annotations.hints)
+ annotations.hints);
return (
@@ -60,13 +60,20 @@ export function ToolDetailPanel({
Annotations:
{annotations.audience && (
-
+
+ )}
+ {annotations.readOnly && (
+
)}
- {annotations.readOnly && }
{annotations.destructive && (
)}
- {annotations.longRunning && }
+ {annotations.longRunning && (
+
+ )}
{annotations.hints && (
{annotations.hints}
@@ -85,10 +92,20 @@ export function ToolDetailPanel({
disabled={isExecuting}
/>
- {progress && }
+ {progress && (
+
+ )}
-
+
Execute Tool
{isExecuting && (
@@ -98,5 +115,5 @@ export function ToolDetailPanel({
)}
- )
+ );
}
diff --git a/clients/web/src/components/molecules/ToolListItem/ToolListItem.stories.tsx b/clients/web/src/components/molecules/ToolListItem/ToolListItem.stories.tsx
index 7f668efb..7eed916d 100644
--- a/clients/web/src/components/molecules/ToolListItem/ToolListItem.stories.tsx
+++ b/clients/web/src/components/molecules/ToolListItem/ToolListItem.stories.tsx
@@ -1,59 +1,59 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ToolListItem } from './ToolListItem'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ToolListItem } from "./ToolListItem";
const meta: Meta = {
- title: 'Molecules/ToolListItem',
+ title: "Molecules/ToolListItem",
component: ToolListItem,
args: {
onClick: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
export const Default: Story = {
args: {
- name: 'get_weather',
+ name: "get_weather",
selected: false,
},
-}
+};
export const Selected: Story = {
args: {
- name: 'get_weather',
+ name: "get_weather",
selected: true,
},
-}
+};
export const WithAnnotations: Story = {
args: {
- name: 'get_weather',
+ name: "get_weather",
selected: false,
annotations: [
- { label: 'user', variant: 'audience' },
- { label: 'read-only', variant: 'readOnly' },
+ { label: "user", variant: "audience" },
+ { label: "read-only", variant: "readOnly" },
],
},
-}
+};
export const MultipleAnnotations: Story = {
args: {
- name: 'update_database',
+ name: "update_database",
selected: false,
annotations: [
- { label: 'user', variant: 'audience' },
- { label: 'read-only', variant: 'readOnly' },
- { label: 'destructive', variant: 'destructive' },
- { label: 'long-run', variant: 'longRun' },
+ { label: "user", variant: "audience" },
+ { label: "read-only", variant: "readOnly" },
+ { label: "destructive", variant: "destructive" },
+ { label: "long-run", variant: "longRun" },
],
},
-}
+};
export const LongName: Story = {
args: {
- name: 'this_is_a_very_long_tool_name_that_might_need_to_wrap_across_multiple_lines_in_the_ui',
+ name: "this_is_a_very_long_tool_name_that_might_need_to_wrap_across_multiple_lines_in_the_ui",
selected: false,
},
-}
+};
diff --git a/clients/web/src/components/molecules/ToolListItem/ToolListItem.tsx b/clients/web/src/components/molecules/ToolListItem/ToolListItem.tsx
index af151c4d..c5eed2b8 100644
--- a/clients/web/src/components/molecules/ToolListItem/ToolListItem.tsx
+++ b/clients/web/src/components/molecules/ToolListItem/ToolListItem.tsx
@@ -1,26 +1,31 @@
-import { Group, Stack, Text, UnstyledButton } from '@mantine/core'
-import { AnnotationBadge } from '../../atoms/AnnotationBadge/AnnotationBadge'
-import type { AnnotationVariant } from '../../atoms/AnnotationBadge/AnnotationBadge'
+import { Group, Stack, Text, UnstyledButton } from "@mantine/core";
+import { AnnotationBadge } from "../../atoms/AnnotationBadge/AnnotationBadge";
+import type { AnnotationVariant } from "../../atoms/AnnotationBadge/AnnotationBadge";
export interface ToolAnnotation {
- label: string
- variant?: AnnotationVariant
+ label: string;
+ variant?: AnnotationVariant;
}
export interface ToolListItemProps {
- name: string
- annotations?: ToolAnnotation[]
- selected: boolean
- onClick: () => void
+ name: string;
+ annotations?: ToolAnnotation[];
+ selected: boolean;
+ onClick: () => void;
}
-export function ToolListItem({ name, annotations, selected, onClick }: ToolListItemProps) {
+export function ToolListItem({
+ name,
+ annotations,
+ selected,
+ onClick,
+}: ToolListItemProps) {
return (
@@ -38,5 +43,5 @@ export function ToolListItem({ name, annotations, selected, onClick }: ToolListI
)}
- )
+ );
}
diff --git a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.stories.tsx b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.stories.tsx
index eba8e926..7229bbfe 100644
--- a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.stories.tsx
+++ b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.stories.tsx
@@ -1,98 +1,100 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { HistoryScreen } from './HistoryScreen'
-import type { HistoryEntryProps } from '../../molecules/HistoryEntry/HistoryEntry'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { HistoryScreen } from "./HistoryScreen";
+import type { HistoryEntryProps } from "../../molecules/HistoryEntry/HistoryEntry";
const meta: Meta = {
component: HistoryScreen,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
- searchText: '',
+ searchText: "",
onSearchChange: fn(),
onMethodFilterChange: fn(),
onLoadMore: fn(),
onClearAll: fn(),
onExport: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
const entry1: HistoryEntryProps = {
- timestamp: '2026-03-17T10:00:00Z',
- method: 'tools/call',
- target: 'send_message',
- status: 'success',
+ timestamp: "2026-03-17T10:00:00Z",
+ method: "tools/call",
+ target: "send_message",
+ status: "success",
durationMs: 120,
- parameters: { message: 'Hello, world!' },
- response: { result: 'Message sent successfully' },
+ parameters: { message: "Hello, world!" },
+ response: { result: "Message sent successfully" },
isPinned: false,
isExpanded: false,
onToggleExpand: fn(),
onReplay: fn(),
onTogglePin: fn(),
-}
+};
const entry2: HistoryEntryProps = {
- timestamp: '2026-03-17T10:01:00Z',
- method: 'resources/read',
- target: 'config.json',
- status: 'success',
+ timestamp: "2026-03-17T10:01:00Z",
+ method: "resources/read",
+ target: "config.json",
+ status: "success",
durationMs: 45,
- parameters: { uri: 'file:///config.json' },
- response: { contents: [{ uri: 'file:///config.json', text: '{}' }] },
+ parameters: { uri: "file:///config.json" },
+ response: { contents: [{ uri: "file:///config.json", text: "{}" }] },
isPinned: false,
isExpanded: false,
onToggleExpand: fn(),
onReplay: fn(),
onTogglePin: fn(),
-}
+};
const entry3: HistoryEntryProps = {
- timestamp: '2026-03-17T10:02:00Z',
- method: 'tools/call',
- target: 'delete_records',
- status: 'error',
+ timestamp: "2026-03-17T10:02:00Z",
+ method: "tools/call",
+ target: "delete_records",
+ status: "error",
durationMs: 350,
parameters: { ids: [1, 2, 3] },
- response: { error: 'Permission denied' },
+ response: { error: "Permission denied" },
isPinned: false,
isExpanded: true,
onToggleExpand: fn(),
onReplay: fn(),
onTogglePin: fn(),
-}
+};
const pinnedEntry1: HistoryEntryProps = {
- timestamp: '2026-03-17T09:30:00Z',
- method: 'tools/list',
- status: 'success',
+ timestamp: "2026-03-17T09:30:00Z",
+ method: "tools/list",
+ status: "success",
durationMs: 80,
- response: { tools: ['send_message', 'list_users'] },
+ response: { tools: ["send_message", "list_users"] },
isPinned: true,
- pinLabel: 'Tool discovery',
+ pinLabel: "Tool discovery",
isExpanded: false,
onToggleExpand: fn(),
onReplay: fn(),
onTogglePin: fn(),
-}
+};
const pinnedEntry2: HistoryEntryProps = {
- timestamp: '2026-03-17T09:35:00Z',
- method: 'prompts/get',
- target: 'greeting',
- status: 'success',
+ timestamp: "2026-03-17T09:35:00Z",
+ method: "prompts/get",
+ target: "greeting",
+ status: "success",
durationMs: 60,
- parameters: { name: 'greeting' },
- response: { messages: [{ role: 'user', content: { type: 'text', text: 'Hello!' } }] },
+ parameters: { name: "greeting" },
+ response: {
+ messages: [{ role: "user", content: { type: "text", text: "Hello!" } }],
+ },
isPinned: true,
- pinLabel: 'Greeting prompt',
+ pinLabel: "Greeting prompt",
isExpanded: false,
onToggleExpand: fn(),
onReplay: fn(),
onTogglePin: fn(),
-}
+};
export const WithEntries: Story = {
args: {
@@ -101,7 +103,7 @@ export const WithEntries: Story = {
totalCount: 25,
displayedCount: 3,
},
-}
+};
export const Empty: Story = {
args: {
@@ -110,14 +112,14 @@ export const Empty: Story = {
totalCount: 0,
displayedCount: 0,
},
-}
+};
export const WithFilter: Story = {
args: {
entries: [entry1, entry3],
pinnedEntries: [],
- methodFilter: 'tools/call',
+ methodFilter: "tools/call",
totalCount: 10,
displayedCount: 2,
},
-}
+};
diff --git a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx
index 8d31d795..ebaa0dd0 100644
--- a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx
+++ b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx
@@ -1,31 +1,41 @@
-import { Button, Divider, Group, Paper, Select, Stack, Text, TextInput, Title } from '@mantine/core'
-import { HistoryEntry } from '../../molecules/HistoryEntry/HistoryEntry'
-import type { HistoryEntryProps } from '../../molecules/HistoryEntry/HistoryEntry'
+import {
+ Button,
+ Divider,
+ Group,
+ Paper,
+ Select,
+ Stack,
+ Text,
+ TextInput,
+ Title,
+} from "@mantine/core";
+import { HistoryEntry } from "../../molecules/HistoryEntry/HistoryEntry";
+import type { HistoryEntryProps } from "../../molecules/HistoryEntry/HistoryEntry";
export interface HistoryScreenProps {
- entries: HistoryEntryProps[]
- pinnedEntries: HistoryEntryProps[]
- searchText: string
- methodFilter?: string
- totalCount: number
- displayedCount: number
- onSearchChange: (text: string) => void
- onMethodFilterChange: (method: string) => void
- onLoadMore: () => void
- onClearAll: () => void
- onExport: () => void
+ entries: HistoryEntryProps[];
+ pinnedEntries: HistoryEntryProps[];
+ searchText: string;
+ methodFilter?: string;
+ totalCount: number;
+ displayedCount: number;
+ onSearchChange: (text: string) => void;
+ onMethodFilterChange: (method: string) => void;
+ onLoadMore: () => void;
+ onClearAll: () => void;
+ onExport: () => void;
}
const METHOD_OPTIONS = [
- 'tools/call',
- 'tools/list',
- 'resources/read',
- 'resources/list',
- 'prompts/get',
- 'prompts/list',
- 'sampling/createMessage',
- 'elicitation/create',
-]
+ "tools/call",
+ "tools/list",
+ "resources/read",
+ "resources/list",
+ "prompts/get",
+ "prompts/list",
+ "sampling/createMessage",
+ "elicitation/create",
+];
export function HistoryScreen({
entries,
@@ -55,7 +65,7 @@ export function HistoryScreen({
placeholder="Filter by method"
data={METHOD_OPTIONS}
value={methodFilter}
- onChange={(value) => onMethodFilterChange(value ?? '')}
+ onChange={(value) => onMethodFilterChange(value ?? "")}
clearable
/>
@@ -74,7 +84,10 @@ export function HistoryScreen({
) : (
{entries.map((entry) => (
-
+
))}
)}
@@ -85,7 +98,10 @@ export function HistoryScreen({
Pinned Requests ({pinnedEntries.length})
{pinnedEntries.map((entry) => (
-
+
))}
>
@@ -103,5 +119,5 @@ export function HistoryScreen({
- )
+ );
}
diff --git a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx
index 171b2972..8a98cc84 100644
--- a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx
+++ b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx
@@ -1,13 +1,13 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { LoggingScreen } from './LoggingScreen';
-import type { LogControlsProps } from '../../molecules/LogControls/LogControls';
-import type { LogEntryProps } from '../../atoms/LogEntry/LogEntry';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { LoggingScreen } from "./LoggingScreen";
+import type { LogControlsProps } from "../../molecules/LogControls/LogControls";
+import type { LogEntryProps } from "../../atoms/LogEntry/LogEntry";
function makeControls(): LogControlsProps {
return {
- currentLevel: 'info',
- filterText: '',
+ currentLevel: "info",
+ filterText: "",
visibleLevels: {
debug: true,
info: true,
@@ -28,7 +28,7 @@ function makeControls(): LogControlsProps {
const meta: Meta = {
component: LoggingScreen,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
onToggleAutoScroll: fn(),
onCopyAll: fn(),
@@ -47,12 +47,40 @@ export const Empty: Story = {
};
const mixedEntries: LogEntryProps[] = [
- { timestamp: '2026-03-17T10:00:01Z', level: 'info', message: 'Server started on port 3000' },
- { timestamp: '2026-03-17T10:00:02Z', level: 'debug', message: 'Loading configuration from /etc/mcp/config.json', logger: 'config' },
- { timestamp: '2026-03-17T10:00:03Z', level: 'warning', message: 'Deprecated API endpoint called: /v1/tools', logger: 'http' },
- { timestamp: '2026-03-17T10:00:04Z', level: 'info', message: 'Client connected: inspector-web-ui' },
- { timestamp: '2026-03-17T10:00:05Z', level: 'error', message: 'Failed to read resource: file not found at /data/missing.txt', logger: 'resources' },
- { timestamp: '2026-03-17T10:00:06Z', level: 'info', message: 'Tool execution completed: search_files (245ms)', logger: 'tools' },
+ {
+ timestamp: "2026-03-17T10:00:01Z",
+ level: "info",
+ message: "Server started on port 3000",
+ },
+ {
+ timestamp: "2026-03-17T10:00:02Z",
+ level: "debug",
+ message: "Loading configuration from /etc/mcp/config.json",
+ logger: "config",
+ },
+ {
+ timestamp: "2026-03-17T10:00:03Z",
+ level: "warning",
+ message: "Deprecated API endpoint called: /v1/tools",
+ logger: "http",
+ },
+ {
+ timestamp: "2026-03-17T10:00:04Z",
+ level: "info",
+ message: "Client connected: inspector-web-ui",
+ },
+ {
+ timestamp: "2026-03-17T10:00:05Z",
+ level: "error",
+ message: "Failed to read resource: file not found at /data/missing.txt",
+ logger: "resources",
+ },
+ {
+ timestamp: "2026-03-17T10:00:06Z",
+ level: "info",
+ message: "Tool execution completed: search_files (245ms)",
+ logger: "tools",
+ },
];
export const WithEntries: Story = {
@@ -63,14 +91,53 @@ export const WithEntries: Story = {
};
const allLevelEntries: LogEntryProps[] = [
- { timestamp: '2026-03-17T10:00:01Z', level: 'debug', message: 'Resolving transport handler for stdio connection', logger: 'transport' },
- { timestamp: '2026-03-17T10:00:02Z', level: 'info', message: 'MCP session initialized successfully' },
- { timestamp: '2026-03-17T10:00:03Z', level: 'notice', message: 'Server capabilities negotiated: tools, resources, prompts', logger: 'session' },
- { timestamp: '2026-03-17T10:00:04Z', level: 'warning', message: 'Rate limit approaching: 85% of quota used', logger: 'ratelimit' },
- { timestamp: '2026-03-17T10:00:05Z', level: 'error', message: 'Tool execution failed: timeout after 30s', logger: 'tools' },
- { timestamp: '2026-03-17T10:00:06Z', level: 'critical', message: 'Database connection pool exhausted', logger: 'db' },
- { timestamp: '2026-03-17T10:00:07Z', level: 'alert', message: 'Memory usage exceeds 95% threshold', logger: 'system' },
- { timestamp: '2026-03-17T10:00:08Z', level: 'emergency', message: 'System unresponsive - initiating graceful shutdown', logger: 'system' },
+ {
+ timestamp: "2026-03-17T10:00:01Z",
+ level: "debug",
+ message: "Resolving transport handler for stdio connection",
+ logger: "transport",
+ },
+ {
+ timestamp: "2026-03-17T10:00:02Z",
+ level: "info",
+ message: "MCP session initialized successfully",
+ },
+ {
+ timestamp: "2026-03-17T10:00:03Z",
+ level: "notice",
+ message: "Server capabilities negotiated: tools, resources, prompts",
+ logger: "session",
+ },
+ {
+ timestamp: "2026-03-17T10:00:04Z",
+ level: "warning",
+ message: "Rate limit approaching: 85% of quota used",
+ logger: "ratelimit",
+ },
+ {
+ timestamp: "2026-03-17T10:00:05Z",
+ level: "error",
+ message: "Tool execution failed: timeout after 30s",
+ logger: "tools",
+ },
+ {
+ timestamp: "2026-03-17T10:00:06Z",
+ level: "critical",
+ message: "Database connection pool exhausted",
+ logger: "db",
+ },
+ {
+ timestamp: "2026-03-17T10:00:07Z",
+ level: "alert",
+ message: "Memory usage exceeds 95% threshold",
+ logger: "system",
+ },
+ {
+ timestamp: "2026-03-17T10:00:08Z",
+ level: "emergency",
+ message: "System unresponsive - initiating graceful shutdown",
+ logger: "system",
+ },
];
export const MixedLevels: Story = {
diff --git a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx
index 7576a84a..b77ebb6d 100644
--- a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx
+++ b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx
@@ -1,8 +1,18 @@
-import { Button, Checkbox, Grid, Group, Paper, ScrollArea, Stack, Text, Title } from '@mantine/core';
-import { LogControls } from '../../molecules/LogControls/LogControls';
-import { LogEntry } from '../../atoms/LogEntry/LogEntry';
-import type { LogControlsProps } from '../../molecules/LogControls/LogControls';
-import type { LogEntryProps } from '../../atoms/LogEntry/LogEntry';
+import {
+ Button,
+ Checkbox,
+ Grid,
+ Group,
+ Paper,
+ ScrollArea,
+ Stack,
+ Text,
+ Title,
+} from "@mantine/core";
+import { LogControls } from "../../molecules/LogControls/LogControls";
+import { LogEntry } from "../../atoms/LogEntry/LogEntry";
+import type { LogControlsProps } from "../../molecules/LogControls/LogControls";
+import type { LogEntryProps } from "../../atoms/LogEntry/LogEntry";
export interface LoggingScreenProps {
controls: LogControlsProps;
diff --git a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx
index 94c87bc2..85ad79a8 100644
--- a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx
+++ b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx
@@ -1,10 +1,10 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { PromptsScreen } from './PromptsScreen';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { PromptsScreen } from "./PromptsScreen";
const meta: Meta = {
component: PromptsScreen,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
onRefreshList: fn(),
},
@@ -17,9 +17,12 @@ export const NoSelection: Story = {
args: {
promptForm: {
prompts: [
- { name: 'summarize', description: 'Summarize a document' },
- { name: 'translate', description: 'Translate text to another language' },
- { name: 'code-review', description: 'Review code for issues' },
+ { name: "summarize", description: "Summarize a document" },
+ {
+ name: "translate",
+ description: "Translate text to another language",
+ },
+ { name: "code-review", description: "Review code for issues" },
],
selectedPrompt: undefined,
arguments: [],
@@ -37,18 +40,25 @@ export const WithResult: Story = {
args: {
promptForm: {
prompts: [
- { name: 'summarize', description: 'Summarize a document' },
- { name: 'translate', description: 'Translate text to another language' },
- { name: 'code-review', description: 'Review code for issues' },
+ { name: "summarize", description: "Summarize a document" },
+ {
+ name: "translate",
+ description: "Translate text to another language",
+ },
+ { name: "code-review", description: "Review code for issues" },
],
- selectedPrompt: 'translate',
+ selectedPrompt: "translate",
arguments: [
- { name: 'text', required: true, description: 'The text to translate' },
- { name: 'targetLanguage', required: true, description: 'Target language code' },
+ { name: "text", required: true, description: "The text to translate" },
+ {
+ name: "targetLanguage",
+ required: true,
+ description: "Target language code",
+ },
],
argumentValues: {
- text: 'Hello, how are you?',
- targetLanguage: 'es',
+ text: "Hello, how are you?",
+ targetLanguage: "es",
},
onSelectPrompt: fn(),
onArgumentChange: fn(),
@@ -57,12 +67,13 @@ export const WithResult: Story = {
messages: {
messages: [
{
- role: 'user',
- content: 'Translate the following text to Spanish: "Hello, how are you?"',
+ role: "user",
+ content:
+ 'Translate the following text to Spanish: "Hello, how are you?"',
},
{
- role: 'assistant',
- content: 'Hola, como estas?',
+ role: "assistant",
+ content: "Hola, como estas?",
},
],
onCopy: fn(),
diff --git a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.tsx b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.tsx
index 50641a73..3653d2bd 100644
--- a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.tsx
+++ b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.tsx
@@ -1,9 +1,9 @@
-import { Grid, Paper, Stack, Text } from '@mantine/core';
-import { PromptArgumentsForm } from '../../molecules/PromptArgumentsForm/PromptArgumentsForm';
-import { PromptMessagesDisplay } from '../../molecules/PromptMessagesDisplay/PromptMessagesDisplay';
-import { ListChangedIndicator } from '../../atoms/ListChangedIndicator/ListChangedIndicator';
-import type { PromptArgumentsFormProps } from '../../molecules/PromptArgumentsForm/PromptArgumentsForm';
-import type { PromptMessagesDisplayProps } from '../../molecules/PromptMessagesDisplay/PromptMessagesDisplay';
+import { Grid, Paper, Stack, Text } from "@mantine/core";
+import { PromptArgumentsForm } from "../../molecules/PromptArgumentsForm/PromptArgumentsForm";
+import { PromptMessagesDisplay } from "../../molecules/PromptMessagesDisplay/PromptMessagesDisplay";
+import { ListChangedIndicator } from "../../atoms/ListChangedIndicator/ListChangedIndicator";
+import type { PromptArgumentsFormProps } from "../../molecules/PromptArgumentsForm/PromptArgumentsForm";
+import type { PromptMessagesDisplayProps } from "../../molecules/PromptMessagesDisplay/PromptMessagesDisplay";
export interface PromptsScreenProps {
promptForm: PromptArgumentsFormProps;
@@ -23,7 +23,10 @@ export function PromptsScreen({
-
+
diff --git a/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.stories.tsx b/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.stories.tsx
index 2be924f7..c00c265d 100644
--- a/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.stories.tsx
+++ b/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.stories.tsx
@@ -1,15 +1,15 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ResourcesScreen } from './ResourcesScreen'
-import type { ResourceListItemProps } from '../../molecules/ResourceListItem/ResourceListItem'
-import type { ResourceTemplateInputProps } from '../../molecules/ResourceTemplateInput/ResourceTemplateInput'
-import type { ResourcePreviewPanelProps } from '../../molecules/ResourcePreviewPanel/ResourcePreviewPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ResourcesScreen } from "./ResourcesScreen";
+import type { ResourceListItemProps } from "../../molecules/ResourceListItem/ResourceListItem";
+import type { ResourceTemplateInputProps } from "../../molecules/ResourceTemplateInput/ResourceTemplateInput";
+import type { ResourcePreviewPanelProps } from "../../molecules/ResourcePreviewPanel/ResourcePreviewPanel";
const meta: Meta = {
component: ResourcesScreen,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
- searchText: '',
+ searchText: "",
onSearchChange: fn(),
onRefreshList: fn(),
onSelectResource: fn(),
@@ -17,96 +17,96 @@ const meta: Meta = {
subscriptions: [],
templates: [],
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
const sampleResources: ResourceListItemProps[] = [
{
- name: 'config.json',
- uri: 'file:///config.json',
- annotations: { audience: 'developer', priority: 0.8 },
+ name: "config.json",
+ uri: "file:///config.json",
+ annotations: { audience: "developer", priority: 0.8 },
selected: false,
onClick: fn(),
},
{
- name: 'README.md',
- uri: 'file:///README.md',
+ name: "README.md",
+ uri: "file:///README.md",
selected: false,
onClick: fn(),
},
{
- name: 'schema.sql',
- uri: 'file:///schema.sql',
+ name: "schema.sql",
+ uri: "file:///schema.sql",
annotations: { priority: 0.5 },
selected: false,
onClick: fn(),
},
-]
+];
const sampleTemplates: ResourceTemplateInputProps[] = [
{
- template: 'file:///users/{userId}/profile',
- variables: { userId: '' },
+ template: "file:///users/{userId}/profile",
+ variables: { userId: "" },
onVariableChange: fn(),
onSubmit: fn(),
},
{
- template: 'db://tables/{tableName}/rows/{rowId}',
- variables: { tableName: '', rowId: '' },
+ template: "db://tables/{tableName}/rows/{rowId}",
+ variables: { tableName: "", rowId: "" },
onVariableChange: fn(),
onSubmit: fn(),
},
-]
+];
const selectedResourceProps: ResourcePreviewPanelProps = {
- uri: 'file:///config.json',
- mimeType: 'application/json',
- annotations: { audience: 'developer', priority: 0.8 },
+ uri: "file:///config.json",
+ mimeType: "application/json",
+ annotations: { audience: "developer", priority: 0.8 },
content: JSON.stringify(
{
- name: 'my-project',
- version: '1.0.0',
- settings: { debug: true, logLevel: 'info' },
+ name: "my-project",
+ version: "1.0.0",
+ settings: { debug: true, logLevel: "info" },
},
null,
- 2
+ 2,
),
- lastUpdated: '2026-03-17T10:30:00Z',
+ lastUpdated: "2026-03-17T10:30:00Z",
isSubscribed: true,
onCopy: fn(),
onSubscribe: fn(),
onUnsubscribe: fn(),
-}
+};
export const WithResources: Story = {
args: {
resources: sampleResources,
},
-}
+};
export const ResourceSelected: Story = {
args: {
resources: sampleResources.map((r) =>
- r.uri === 'file:///config.json' ? { ...r, selected: true } : r
+ r.uri === "file:///config.json" ? { ...r, selected: true } : r,
),
selectedResource: selectedResourceProps,
subscriptions: [
- { name: 'config.json', lastUpdated: '2026-03-17T10:30:00Z' },
+ { name: "config.json", lastUpdated: "2026-03-17T10:30:00Z" },
],
},
-}
+};
export const WithTemplates: Story = {
args: {
resources: sampleResources,
templates: sampleTemplates,
},
-}
+};
export const Empty: Story = {
args: {
resources: [],
},
-}
+};
diff --git a/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.tsx b/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.tsx
index e2f53510..4899f896 100644
--- a/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.tsx
+++ b/clients/web/src/components/organisms/ResourcesScreen/ResourcesScreen.tsx
@@ -1,27 +1,27 @@
-import { Accordion, Grid, Paper, Stack, Text, TextInput } from '@mantine/core'
-import { ListChangedIndicator } from '../../atoms/ListChangedIndicator/ListChangedIndicator'
-import { ResourceListItem } from '../../molecules/ResourceListItem/ResourceListItem'
-import { ResourcePreviewPanel } from '../../molecules/ResourcePreviewPanel/ResourcePreviewPanel'
-import { ResourceTemplateInput } from '../../molecules/ResourceTemplateInput/ResourceTemplateInput'
-import type { ResourceListItemProps } from '../../molecules/ResourceListItem/ResourceListItem'
-import type { ResourcePreviewPanelProps } from '../../molecules/ResourcePreviewPanel/ResourcePreviewPanel'
-import type { ResourceTemplateInputProps } from '../../molecules/ResourceTemplateInput/ResourceTemplateInput'
+import { Accordion, Grid, Paper, Stack, Text, TextInput } from "@mantine/core";
+import { ListChangedIndicator } from "../../atoms/ListChangedIndicator/ListChangedIndicator";
+import { ResourceListItem } from "../../molecules/ResourceListItem/ResourceListItem";
+import { ResourcePreviewPanel } from "../../molecules/ResourcePreviewPanel/ResourcePreviewPanel";
+import { ResourceTemplateInput } from "../../molecules/ResourceTemplateInput/ResourceTemplateInput";
+import type { ResourceListItemProps } from "../../molecules/ResourceListItem/ResourceListItem";
+import type { ResourcePreviewPanelProps } from "../../molecules/ResourcePreviewPanel/ResourcePreviewPanel";
+import type { ResourceTemplateInputProps } from "../../molecules/ResourceTemplateInput/ResourceTemplateInput";
export interface SubscriptionInfo {
- name: string
- lastUpdated?: string
+ name: string;
+ lastUpdated?: string;
}
export interface ResourcesScreenProps {
- resources: ResourceListItemProps[]
- templates: ResourceTemplateInputProps[]
- subscriptions: SubscriptionInfo[]
- selectedResource?: ResourcePreviewPanelProps
- listChanged: boolean
- searchText: string
- onSearchChange: (text: string) => void
- onRefreshList: () => void
- onSelectResource: (uri: string) => void
+ resources: ResourceListItemProps[];
+ templates: ResourceTemplateInputProps[];
+ subscriptions: SubscriptionInfo[];
+ selectedResource?: ResourcePreviewPanelProps;
+ listChanged: boolean;
+ searchText: string;
+ onSearchChange: (text: string) => void;
+ onRefreshList: () => void;
+ onSelectResource: (uri: string) => void;
}
export function ResourcesScreen({
@@ -35,23 +35,31 @@ export function ResourcesScreen({
onRefreshList,
}: ResourcesScreenProps) {
const filteredResources = resources.filter((r) =>
- r.name.toLowerCase().includes(searchText.toLowerCase())
- )
+ r.name.toLowerCase().includes(searchText.toLowerCase()),
+ );
return (
-
+
onSearchChange(e.currentTarget.value)}
/>
-
+
- Resources ({filteredResources.length})
+
+ Resources ({filteredResources.length})
+
{filteredResources.map((resource) => (
@@ -62,18 +70,25 @@ export function ResourcesScreen({
- Templates ({templates.length})
+
+ Templates ({templates.length})
+
{templates.map((template) => (
-
+
))}
- Subscriptions ({subscriptions.length})
+
+ Subscriptions ({subscriptions.length})
+
{subscriptions.map((sub) => (
@@ -81,7 +96,7 @@ export function ResourcesScreen({
{sub.name}
{sub.lastUpdated && (
- {' '}
+ {" "}
— {sub.lastUpdated}
)}
@@ -107,5 +122,5 @@ export function ResourcesScreen({
- )
+ );
}
diff --git a/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.stories.tsx b/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.stories.tsx
index 86c1f00e..47705eb0 100644
--- a/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.stories.tsx
+++ b/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.stories.tsx
@@ -1,11 +1,11 @@
-import type { Meta, StoryObj } from '@storybook/react-vite';
-import { fn } from 'storybook/test';
-import { ServerListScreen } from './ServerListScreen';
-import type { ServerCardProps } from '../../molecules/ServerCard/ServerCard';
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ServerListScreen } from "./ServerListScreen";
+import type { ServerCardProps } from "../../molecules/ServerCard/ServerCard";
const meta: Meta = {
component: ServerListScreen,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
onAddManually: fn(),
onImportConfig: fn(),
@@ -18,17 +18,17 @@ type Story = StoryObj;
function makeServerCallbacks(): Pick<
ServerCardProps,
- | 'onToggleConnection'
- | 'onCopyCommand'
- | 'onServerInfo'
- | 'onSettings'
- | 'onEdit'
- | 'onClone'
- | 'onRemove'
- | 'onTestSampling'
- | 'onTestElicitationForm'
- | 'onTestElicitationUrl'
- | 'onConfigureRoots'
+ | "onToggleConnection"
+ | "onCopyCommand"
+ | "onServerInfo"
+ | "onSettings"
+ | "onEdit"
+ | "onClone"
+ | "onRemove"
+ | "onTestSampling"
+ | "onTestElicitationForm"
+ | "onTestElicitationUrl"
+ | "onConfigureRoots"
> {
return {
onToggleConnection: fn(),
@@ -46,49 +46,49 @@ function makeServerCallbacks(): Pick<
}
const connectedStdioServer: ServerCardProps = {
- name: 'Local Dev Server',
- version: '1.2.0',
- transport: 'stdio',
- connectionMode: 'Direct',
- command: 'npx @modelcontextprotocol/server-filesystem /home/user/projects',
- status: 'connected',
+ name: "Local Dev Server",
+ version: "1.2.0",
+ transport: "stdio",
+ connectionMode: "Direct",
+ command: "npx @modelcontextprotocol/server-filesystem /home/user/projects",
+ status: "connected",
canTestClientFeatures: true,
...makeServerCallbacks(),
};
const disconnectedStdioServer: ServerCardProps = {
- name: 'Database Tools',
- version: '0.9.1',
- transport: 'stdio',
- connectionMode: 'Direct',
- command: 'python -m mcp_server_sqlite --db-path ./data.db',
- status: 'disconnected',
+ name: "Database Tools",
+ version: "0.9.1",
+ transport: "stdio",
+ connectionMode: "Direct",
+ command: "python -m mcp_server_sqlite --db-path ./data.db",
+ status: "disconnected",
canTestClientFeatures: false,
...makeServerCallbacks(),
};
const failedHttpServer: ServerCardProps = {
- name: 'Remote API Server',
- version: '2.0.0',
- transport: 'http',
- connectionMode: 'Streamable HTTP',
- command: 'https://api.example.com/mcp',
- status: 'failed',
+ name: "Remote API Server",
+ version: "2.0.0",
+ transport: "http",
+ connectionMode: "Streamable HTTP",
+ command: "https://api.example.com/mcp",
+ status: "failed",
retryCount: 3,
error: {
- message: 'Connection refused',
- details: 'ECONNREFUSED 127.0.0.1:8080 - The server may not be running.',
+ message: "Connection refused",
+ details: "ECONNREFUSED 127.0.0.1:8080 - The server may not be running.",
},
canTestClientFeatures: false,
...makeServerCallbacks(),
};
const connectingHttpServer: ServerCardProps = {
- name: 'Staging Server',
- transport: 'http',
- connectionMode: 'Streamable HTTP',
- command: 'https://staging.example.com/mcp',
- status: 'connecting',
+ name: "Staging Server",
+ transport: "http",
+ connectionMode: "Streamable HTTP",
+ command: "https://staging.example.com/mcp",
+ status: "connecting",
canTestClientFeatures: false,
...makeServerCallbacks(),
};
diff --git a/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.tsx b/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.tsx
index 62430af9..2f106621 100644
--- a/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.tsx
+++ b/clients/web/src/components/organisms/ServerListScreen/ServerListScreen.tsx
@@ -1,7 +1,14 @@
-import { Container, Group, SimpleGrid, Stack, Text, Title } from '@mantine/core';
-import { ServerCard } from '../../molecules/ServerCard/ServerCard';
-import { AddServerMenu } from '../../molecules/AddServerMenu/AddServerMenu';
-import type { ServerCardProps } from '../../molecules/ServerCard/ServerCard';
+import {
+ Container,
+ Group,
+ SimpleGrid,
+ Stack,
+ Text,
+ Title,
+} from "@mantine/core";
+import { ServerCard } from "../../molecules/ServerCard/ServerCard";
+import { AddServerMenu } from "../../molecules/AddServerMenu/AddServerMenu";
+import type { ServerCardProps } from "../../molecules/ServerCard/ServerCard";
export interface ServerListScreenProps {
servers: ServerCardProps[];
diff --git a/clients/web/src/components/organisms/TasksScreen/TasksScreen.stories.tsx b/clients/web/src/components/organisms/TasksScreen/TasksScreen.stories.tsx
index 8042d441..da08c3d4 100644
--- a/clients/web/src/components/organisms/TasksScreen/TasksScreen.stories.tsx
+++ b/clients/web/src/components/organisms/TasksScreen/TasksScreen.stories.tsx
@@ -1,99 +1,99 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { TasksScreen } from './TasksScreen'
-import type { TaskCardProps } from '../../molecules/TaskCard/TaskCard'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { TasksScreen } from "./TasksScreen";
+import type { TaskCardProps } from "../../molecules/TaskCard/TaskCard";
const meta: Meta = {
component: TasksScreen,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
onRefresh: fn(),
onClearHistory: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
const activeTask1: TaskCardProps = {
- taskId: 'task-001',
- status: 'running',
- method: 'tools/call',
- target: 'send_message',
+ taskId: "task-001",
+ status: "running",
+ method: "tools/call",
+ target: "send_message",
progress: 45,
- progressDescription: 'Sending message to remote server...',
- startedAt: '2026-03-17T10:00:00Z',
+ progressDescription: "Sending message to remote server...",
+ startedAt: "2026-03-17T10:00:00Z",
onViewDetails: fn(),
onViewResult: fn(),
onCancel: fn(),
onDismiss: fn(),
-}
+};
const activeTask2: TaskCardProps = {
- taskId: 'task-002',
- status: 'waiting',
- method: 'resources/read',
- target: 'config.json',
- startedAt: '2026-03-17T10:01:00Z',
+ taskId: "task-002",
+ status: "waiting",
+ method: "resources/read",
+ target: "config.json",
+ startedAt: "2026-03-17T10:01:00Z",
onViewDetails: fn(),
onViewResult: fn(),
onCancel: fn(),
onDismiss: fn(),
-}
+};
const completedTask1: TaskCardProps = {
- taskId: 'task-003',
- status: 'completed',
- method: 'tools/call',
- target: 'list_users',
- startedAt: '2026-03-17T09:50:00Z',
- completedAt: '2026-03-17T09:50:02Z',
- elapsed: '2.1s',
+ taskId: "task-003",
+ status: "completed",
+ method: "tools/call",
+ target: "list_users",
+ startedAt: "2026-03-17T09:50:00Z",
+ completedAt: "2026-03-17T09:50:02Z",
+ elapsed: "2.1s",
onViewDetails: fn(),
onViewResult: fn(),
onCancel: fn(),
onDismiss: fn(),
-}
+};
const completedTask2: TaskCardProps = {
- taskId: 'task-004',
- status: 'failed',
- method: 'tools/call',
- target: 'delete_records',
- startedAt: '2026-03-17T09:45:00Z',
- completedAt: '2026-03-17T09:45:01Z',
- elapsed: '0.8s',
- error: 'Permission denied: insufficient privileges',
+ taskId: "task-004",
+ status: "failed",
+ method: "tools/call",
+ target: "delete_records",
+ startedAt: "2026-03-17T09:45:00Z",
+ completedAt: "2026-03-17T09:45:01Z",
+ elapsed: "0.8s",
+ error: "Permission denied: insufficient privileges",
onViewDetails: fn(),
onViewResult: fn(),
onCancel: fn(),
onDismiss: fn(),
-}
+};
export const Mixed: Story = {
args: {
activeTasks: [activeTask1, activeTask2],
completedTasks: [completedTask1, completedTask2],
},
-}
+};
export const ActiveOnly: Story = {
args: {
activeTasks: [activeTask1, activeTask2],
completedTasks: [],
},
-}
+};
export const CompletedOnly: Story = {
args: {
activeTasks: [],
completedTasks: [completedTask1, completedTask2],
},
-}
+};
export const Empty: Story = {
args: {
activeTasks: [],
completedTasks: [],
},
-}
+};
diff --git a/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx b/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx
index 8595fe52..5cb92aa7 100644
--- a/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx
+++ b/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx
@@ -1,12 +1,12 @@
-import { Button, Group, Stack, Text, Title } from '@mantine/core'
-import { TaskCard } from '../../molecules/TaskCard/TaskCard'
-import type { TaskCardProps } from '../../molecules/TaskCard/TaskCard'
+import { Button, Group, Stack, Text, Title } from "@mantine/core";
+import { TaskCard } from "../../molecules/TaskCard/TaskCard";
+import type { TaskCardProps } from "../../molecules/TaskCard/TaskCard";
export interface TasksScreenProps {
- activeTasks: TaskCardProps[]
- completedTasks: TaskCardProps[]
- onRefresh: () => void
- onClearHistory: () => void
+ activeTasks: TaskCardProps[];
+ completedTasks: TaskCardProps[];
+ onRefresh: () => void;
+ onClearHistory: () => void;
}
export function TasksScreen({
@@ -51,5 +51,5 @@ export function TasksScreen({
)}
- )
+ );
}
diff --git a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx
index 8b5cb6b7..c1c76a0e 100644
--- a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx
+++ b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.stories.tsx
@@ -1,113 +1,119 @@
-import type { Meta, StoryObj } from '@storybook/react-vite'
-import { fn } from 'storybook/test'
-import { ToolsScreen } from './ToolsScreen'
-import type { ToolListItemProps } from '../../molecules/ToolListItem/ToolListItem'
-import type { ToolDetailPanelProps } from '../../molecules/ToolDetailPanel/ToolDetailPanel'
-import type { ResultPanelProps } from '../../molecules/ResultPanel/ResultPanel'
+import type { Meta, StoryObj } from "@storybook/react-vite";
+import { fn } from "storybook/test";
+import { ToolsScreen } from "./ToolsScreen";
+import type { ToolListItemProps } from "../../molecules/ToolListItem/ToolListItem";
+import type { ToolDetailPanelProps } from "../../molecules/ToolDetailPanel/ToolDetailPanel";
+import type { ResultPanelProps } from "../../molecules/ResultPanel/ResultPanel";
const meta: Meta = {
component: ToolsScreen,
- parameters: { layout: 'fullscreen' },
+ parameters: { layout: "fullscreen" },
args: {
- searchText: '',
+ searchText: "",
listChanged: false,
onSearchChange: fn(),
onRefreshList: fn(),
onSelectTool: fn(),
},
-}
+};
-export default meta
-type Story = StoryObj
+export default meta;
+type Story = StoryObj;
const sampleTools: ToolListItemProps[] = [
- { name: 'send_message', selected: false, onClick: fn() },
+ { name: "send_message", selected: false, onClick: fn() },
{
- name: 'create_record',
- annotations: [{ label: 'read-only' }],
+ name: "create_record",
+ annotations: [{ label: "read-only" }],
selected: false,
onClick: fn(),
},
{
- name: 'delete_records',
- annotations: [{ label: 'destructive', variant: 'destructive' }],
+ name: "delete_records",
+ annotations: [{ label: "destructive", variant: "destructive" }],
selected: false,
onClick: fn(),
},
- { name: 'list_users', selected: false, onClick: fn() },
+ { name: "list_users", selected: false, onClick: fn() },
{
- name: 'batch_process',
- annotations: [{ label: 'long-run', variant: 'longRun' }],
+ name: "batch_process",
+ annotations: [{ label: "long-run", variant: "longRun" }],
selected: false,
onClick: fn(),
},
-]
+];
const selectedToolData: ToolDetailPanelProps = {
- name: 'create_record',
- description: 'Creates a new record with the given parameters',
+ name: "create_record",
+ description: "Creates a new record with the given parameters",
schema: {
- type: 'object',
+ type: "object",
properties: {
- title: { type: 'string', description: 'Record title' },
- count: { type: 'number', description: 'Number of items' },
- enabled: { type: 'boolean', description: 'Whether the record is active' },
+ title: { type: "string", description: "Record title" },
+ count: { type: "number", description: "Number of items" },
+ enabled: { type: "boolean", description: "Whether the record is active" },
},
- required: ['title'],
+ required: ["title"],
},
formValues: {},
isExecuting: false,
onFormChange: fn(),
onExecute: fn(),
onCancel: fn(),
-}
+};
const resultData: ResultPanelProps = {
content: [
{
- type: 'text',
+ type: "text",
text: JSON.stringify(
- { id: 42, title: 'New Record', count: 5, enabled: true, createdAt: '2026-03-17T12:00:00Z' },
+ {
+ id: 42,
+ title: "New Record",
+ count: 5,
+ enabled: true,
+ createdAt: "2026-03-17T12:00:00Z",
+ },
null,
- 2
+ 2,
),
},
],
onCopy: fn(),
onClear: fn(),
-}
+};
function toolsWithSelected(selectedName: string): ToolListItemProps[] {
return sampleTools.map((tool) => ({
...tool,
selected: tool.name === selectedName,
- }))
+ }));
}
export const NoSelection: Story = {
args: {
tools: sampleTools,
},
-}
+};
export const ToolSelected: Story = {
args: {
- tools: toolsWithSelected('create_record'),
+ tools: toolsWithSelected("create_record"),
selectedTool: selectedToolData,
},
-}
+};
export const WithResult: Story = {
args: {
- tools: toolsWithSelected('create_record'),
+ tools: toolsWithSelected("create_record"),
selectedTool: selectedToolData,
result: resultData,
},
-}
+};
export const WithListChanged: Story = {
args: {
tools: sampleTools,
listChanged: true,
},
-}
+};
diff --git a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx
index 33445bcd..4bc50a2e 100644
--- a/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx
+++ b/clients/web/src/components/organisms/ToolsScreen/ToolsScreen.tsx
@@ -1,21 +1,21 @@
-import { Grid, Paper, Stack, Text, TextInput } from '@mantine/core'
-import { ListChangedIndicator } from '../../atoms/ListChangedIndicator/ListChangedIndicator'
-import { ToolListItem } from '../../molecules/ToolListItem/ToolListItem'
-import { ToolDetailPanel } from '../../molecules/ToolDetailPanel/ToolDetailPanel'
-import { ResultPanel } from '../../molecules/ResultPanel/ResultPanel'
-import type { ToolListItemProps } from '../../molecules/ToolListItem/ToolListItem'
-import type { ToolDetailPanelProps } from '../../molecules/ToolDetailPanel/ToolDetailPanel'
-import type { ResultPanelProps } from '../../molecules/ResultPanel/ResultPanel'
+import { Grid, Paper, Stack, Text, TextInput } from "@mantine/core";
+import { ListChangedIndicator } from "../../atoms/ListChangedIndicator/ListChangedIndicator";
+import { ToolListItem } from "../../molecules/ToolListItem/ToolListItem";
+import { ToolDetailPanel } from "../../molecules/ToolDetailPanel/ToolDetailPanel";
+import { ResultPanel } from "../../molecules/ResultPanel/ResultPanel";
+import type { ToolListItemProps } from "../../molecules/ToolListItem/ToolListItem";
+import type { ToolDetailPanelProps } from "../../molecules/ToolDetailPanel/ToolDetailPanel";
+import type { ResultPanelProps } from "../../molecules/ResultPanel/ResultPanel";
export interface ToolsScreenProps {
- tools: ToolListItemProps[]
- selectedTool?: ToolDetailPanelProps
- result?: ResultPanelProps
- listChanged: boolean
- searchText: string
- onSearchChange: (text: string) => void
- onRefreshList: () => void
- onSelectTool: (name: string) => void
+ tools: ToolListItemProps[];
+ selectedTool?: ToolDetailPanelProps;
+ result?: ResultPanelProps;
+ listChanged: boolean;
+ searchText: string;
+ onSearchChange: (text: string) => void;
+ onRefreshList: () => void;
+ onSelectTool: (name: string) => void;
}
export function ToolsScreen({
@@ -29,15 +29,20 @@ export function ToolsScreen({
onSelectTool,
}: ToolsScreenProps) {
const filteredTools = searchText
- ? tools.filter((tool) => tool.name.toLowerCase().includes(searchText.toLowerCase()))
- : tools
+ ? tools.filter((tool) =>
+ tool.name.toLowerCase().includes(searchText.toLowerCase()),
+ )
+ : tools;
return (
-
+
- )
+ );
}
diff --git a/clients/web/src/main.tsx b/clients/web/src/main.tsx
index 4ba48cdc..29a1d867 100644
--- a/clients/web/src/main.tsx
+++ b/clients/web/src/main.tsx
@@ -1,18 +1,18 @@
-import { StrictMode } from 'react'
-import { createRoot } from 'react-dom/client'
-import { MantineProvider } from '@mantine/core'
-import { Notifications } from '@mantine/notifications'
-import '@mantine/core/styles.css'
-import '@mantine/notifications/styles.css'
-import './App.css'
-import { theme } from './theme/theme'
-import App from './App'
+import { StrictMode } from "react";
+import { createRoot } from "react-dom/client";
+import { MantineProvider } from "@mantine/core";
+import { Notifications } from "@mantine/notifications";
+import "@mantine/core/styles.css";
+import "@mantine/notifications/styles.css";
+import "./App.css";
+import { theme } from "./theme/theme";
+import App from "./App";
-createRoot(document.getElementById('root')!).render(
+createRoot(document.getElementById("root")!).render(
,
-)
+);
diff --git a/clients/web/src/theme/ActionIcon.ts b/clients/web/src/theme/ActionIcon.ts
index b80fad6a..f771c234 100644
--- a/clients/web/src/theme/ActionIcon.ts
+++ b/clients/web/src/theme/ActionIcon.ts
@@ -1,8 +1,8 @@
-import { ActionIcon } from '@mantine/core';
+import { ActionIcon } from "@mantine/core";
export const ThemeActionIcon = ActionIcon.extend({
defaultProps: {
- variant: 'subtle',
- radius: 'md',
+ variant: "subtle",
+ radius: "md",
},
});
diff --git a/clients/web/src/theme/Alert.ts b/clients/web/src/theme/Alert.ts
index b356e264..19148629 100644
--- a/clients/web/src/theme/Alert.ts
+++ b/clients/web/src/theme/Alert.ts
@@ -1,7 +1,7 @@
-import { Alert } from '@mantine/core';
+import { Alert } from "@mantine/core";
export const ThemeAlert = Alert.extend({
defaultProps: {
- radius: 'md',
+ radius: "md",
},
});
diff --git a/clients/web/src/theme/AppShell.ts b/clients/web/src/theme/AppShell.ts
index bf521d35..e7bc189d 100644
--- a/clients/web/src/theme/AppShell.ts
+++ b/clients/web/src/theme/AppShell.ts
@@ -1,7 +1,7 @@
-import { AppShell } from '@mantine/core';
+import { AppShell } from "@mantine/core";
export const ThemeAppShell = AppShell.extend({
defaultProps: {
- padding: 'md',
+ padding: "md",
},
});
diff --git a/clients/web/src/theme/Badge.ts b/clients/web/src/theme/Badge.ts
index 9c0f6791..6b35b5d1 100644
--- a/clients/web/src/theme/Badge.ts
+++ b/clients/web/src/theme/Badge.ts
@@ -1,8 +1,8 @@
-import { Badge } from '@mantine/core';
+import { Badge } from "@mantine/core";
export const ThemeBadge = Badge.extend({
defaultProps: {
- size: 'sm',
- radius: 'sm',
+ size: "sm",
+ radius: "sm",
},
});
diff --git a/clients/web/src/theme/Button.ts b/clients/web/src/theme/Button.ts
index 0b307c51..c24820c9 100644
--- a/clients/web/src/theme/Button.ts
+++ b/clients/web/src/theme/Button.ts
@@ -1,7 +1,7 @@
-import { Button } from '@mantine/core';
+import { Button } from "@mantine/core";
export const ThemeButton = Button.extend({
defaultProps: {
- radius: 'md',
+ radius: "md",
},
});
diff --git a/clients/web/src/theme/Card.ts b/clients/web/src/theme/Card.ts
index e5d4e98a..46383bb0 100644
--- a/clients/web/src/theme/Card.ts
+++ b/clients/web/src/theme/Card.ts
@@ -1,9 +1,9 @@
-import { Card } from '@mantine/core';
+import { Card } from "@mantine/core";
export const ThemeCard = Card.extend({
defaultProps: {
- padding: 'lg',
- radius: 'md',
+ padding: "lg",
+ radius: "md",
withBorder: true,
},
});
diff --git a/clients/web/src/theme/Code.ts b/clients/web/src/theme/Code.ts
index 41619d1d..b50d4a4b 100644
--- a/clients/web/src/theme/Code.ts
+++ b/clients/web/src/theme/Code.ts
@@ -1,9 +1,9 @@
-import { Code } from '@mantine/core';
+import { Code } from "@mantine/core";
export const ThemeCode = Code.extend({
styles: () => ({
root: {
- backgroundColor: 'var(--inspector-surface-code)',
+ backgroundColor: "var(--inspector-surface-code)",
},
}),
});
diff --git a/clients/web/src/theme/Paper.ts b/clients/web/src/theme/Paper.ts
index 7212f63a..df5026ff 100644
--- a/clients/web/src/theme/Paper.ts
+++ b/clients/web/src/theme/Paper.ts
@@ -1,19 +1,19 @@
-import { Paper } from '@mantine/core';
+import { Paper } from "@mantine/core";
export const ThemePaper = Paper.extend({
classNames: (_theme, props) => {
- if (props.variant === 'code') return { root: 'paper-code' };
+ if (props.variant === "code") return { root: "paper-code" };
return {};
},
styles: (_theme, props) => {
- if (props.variant === 'code') {
+ if (props.variant === "code") {
return {
root: {
- padding: 'var(--mantine-spacing-md)',
- backgroundColor: 'var(--inspector-surface-code)',
- fontFamily: 'var(--mantine-font-family-monospace)',
- fontSize: 'var(--mantine-font-size-sm)',
- overflow: 'auto',
+ padding: "var(--mantine-spacing-md)",
+ backgroundColor: "var(--inspector-surface-code)",
+ fontFamily: "var(--mantine-font-family-monospace)",
+ fontSize: "var(--mantine-font-size-sm)",
+ overflow: "auto",
},
};
}
diff --git a/clients/web/src/theme/Select.ts b/clients/web/src/theme/Select.ts
index 28bf7b06..2ec7e460 100644
--- a/clients/web/src/theme/Select.ts
+++ b/clients/web/src/theme/Select.ts
@@ -1,7 +1,7 @@
-import { Select } from '@mantine/core';
+import { Select } from "@mantine/core";
export const ThemeSelect = Select.extend({
defaultProps: {
- radius: 'md',
+ radius: "md",
},
});
diff --git a/clients/web/src/theme/Switch.ts b/clients/web/src/theme/Switch.ts
index 7e1ed842..1c5c0f87 100644
--- a/clients/web/src/theme/Switch.ts
+++ b/clients/web/src/theme/Switch.ts
@@ -1,7 +1,7 @@
-import { Switch } from '@mantine/core';
+import { Switch } from "@mantine/core";
export const ThemeSwitch = Switch.extend({
defaultProps: {
- radius: 'xl',
+ radius: "xl",
},
});
diff --git a/clients/web/src/theme/TextInput.ts b/clients/web/src/theme/TextInput.ts
index e0578e98..d7fe2e14 100644
--- a/clients/web/src/theme/TextInput.ts
+++ b/clients/web/src/theme/TextInput.ts
@@ -1,7 +1,7 @@
-import { TextInput } from '@mantine/core';
+import { TextInput } from "@mantine/core";
export const ThemeTextInput = TextInput.extend({
defaultProps: {
- radius: 'md',
+ radius: "md",
},
});
diff --git a/clients/web/src/theme/index.ts b/clients/web/src/theme/index.ts
index e99cc21a..4d1d198a 100644
--- a/clients/web/src/theme/index.ts
+++ b/clients/web/src/theme/index.ts
@@ -1,11 +1,11 @@
-export { ThemeActionIcon } from './ActionIcon';
-export { ThemeAlert } from './Alert';
-export { ThemeAppShell } from './AppShell';
-export { ThemeBadge } from './Badge';
-export { ThemeButton } from './Button';
-export { ThemeCard } from './Card';
-export { ThemeCode } from './Code';
-export { ThemePaper } from './Paper';
-export { ThemeSelect } from './Select';
-export { ThemeSwitch } from './Switch';
-export { ThemeTextInput } from './TextInput';
+export { ThemeActionIcon } from "./ActionIcon";
+export { ThemeAlert } from "./Alert";
+export { ThemeAppShell } from "./AppShell";
+export { ThemeBadge } from "./Badge";
+export { ThemeButton } from "./Button";
+export { ThemeCard } from "./Card";
+export { ThemeCode } from "./Code";
+export { ThemePaper } from "./Paper";
+export { ThemeSelect } from "./Select";
+export { ThemeSwitch } from "./Switch";
+export { ThemeTextInput } from "./TextInput";
diff --git a/clients/web/src/theme/theme.ts b/clients/web/src/theme/theme.ts
index d25bc64f..9363d36d 100644
--- a/clients/web/src/theme/theme.ts
+++ b/clients/web/src/theme/theme.ts
@@ -1,4 +1,4 @@
-import { createTheme } from '@mantine/core';
+import { createTheme } from "@mantine/core";
import {
ThemeActionIcon,
ThemeAlert,
@@ -11,17 +11,17 @@ import {
ThemeSelect,
ThemeSwitch,
ThemeTextInput,
-} from './index';
+} from "./index";
export const theme = createTheme({
/* ── Color ──────────────────────────────────────────────── */
- primaryColor: 'dark',
+ primaryColor: "dark",
primaryShade: { light: 7, dark: 4 },
autoContrast: true,
/* ── Shape ──────────────────────────────────────────────── */
- defaultRadius: 'md',
- cursorType: 'pointer',
+ defaultRadius: "md",
+ cursorType: "pointer",
/* ── Typography ─────────────────────────────────────────── */
fontFamily:
@@ -29,7 +29,7 @@ export const theme = createTheme({
fontFamilyMonospace:
'ui-monospace, SFMono-Regular, "SF Mono", Menlo, Consolas, "Liberation Mono", monospace',
headings: {
- fontWeight: '600',
+ fontWeight: "600",
},
/* ── Component overrides ────────────────────────────────── */