diff --git a/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx b/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx
index 3515ab25..1d940599 100644
--- a/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx
+++ b/clients/web/src/components/atoms/MessageBubble/MessageBubble.tsx
@@ -1,4 +1,5 @@
-import { Image, Paper, Stack, Text } from "@mantine/core";
+import { Group, Image, Paper, Stack, Text } from "@mantine/core";
+import { CopyButton } from "../CopyButton/CopyButton";
export interface MessageBubbleProps {
index: number;
@@ -18,9 +19,12 @@ export function MessageBubble({
return (
-
- [{index}] role: {role}
-
+
+
+ [{index}] role: {role}
+
+
+
"{content}"
{imageContent && (
+
);
diff --git a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx
index c86161b9..625fd118 100644
--- a/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx
+++ b/clients/web/src/components/layouts/ConnectedLayout/ConnectedLayout.stories.tsx
@@ -5,9 +5,57 @@ import { ConnectedLayout } from "./ConnectedLayout";
import { ToolsScreen } from "../../organisms/ToolsScreen/ToolsScreen";
import { ResourcesScreen } from "../../organisms/ResourcesScreen/ResourcesScreen";
import { PromptsScreen } from "../../organisms/PromptsScreen/PromptsScreen";
+import { LoggingScreen } from "../../organisms/LoggingScreen/LoggingScreen";
+import { TasksScreen } from "../../organisms/TasksScreen/TasksScreen";
+import { HistoryScreen } from "../../organisms/HistoryScreen/HistoryScreen";
+
+import type { LogEntryProps } from "../../atoms/LogEntry/LogEntry";
+import type { LogLevel } from "../../atoms/LogEntry/LogEntry";
const allTabs = ["Tools", "Resources", "Prompts", "Logs", "Tasks", "History"];
+const logMessages: { level: LogLevel; message: string; logger?: string }[] = [
+ { level: "info", message: "Server started on port 3000" },
+ { level: "debug", message: "Loading configuration from /etc/mcp/config.json", logger: "config" },
+ { level: "warning", message: "Deprecated API endpoint called: /v1/tools", logger: "http" },
+ { level: "info", message: "Client connected: inspector-web-ui" },
+ { level: "error", message: "Failed to read resource: file not found at /data/missing.txt", logger: "resources" },
+ { level: "info", message: "Tool execution completed: search_files (245ms)", logger: "tools" },
+ { level: "debug", message: "Parsing JSON-RPC request body", logger: "transport" },
+ { level: "info", message: "Listing available tools for client session", logger: "tools" },
+ { level: "warning", message: "Slow query detected: 1200ms for resource lookup", logger: "db" },
+ { level: "info", message: "Resource subscription added: file:///config.json", logger: "resources" },
+ { level: "debug", message: "Heartbeat ping received from client", logger: "session" },
+ { level: "error", message: "Permission denied reading /etc/secrets.env", logger: "resources" },
+ { level: "info", message: "Prompt 'summarize' resolved with 2 messages", logger: "prompts" },
+ { level: "debug", message: "Cache hit for resource: db://users/42", logger: "cache" },
+ { level: "info", message: "Tool 'create_record' executed successfully in 89ms", logger: "tools" },
+ { level: "warning", message: "Client reconnection attempt #3", logger: "session" },
+ { level: "info", message: "New resource detected: file:///data/output.csv", logger: "resources" },
+ { level: "debug", message: "Serializing response payload (2.4KB)", logger: "transport" },
+ { level: "error", message: "Timeout waiting for tool response after 30s", logger: "tools" },
+ { level: "info", message: "Session initialized with capabilities: tools, resources, prompts" },
+ { level: "debug", message: "Validating input schema for tool 'batch_process'", logger: "tools" },
+ { level: "info", message: "Resource template resolved: db://tables/users/rows/15", logger: "resources" },
+ { level: "warning", message: "Memory usage at 78% — consider increasing limits", logger: "system" },
+ { level: "info", message: "Client disconnected gracefully", logger: "session" },
+ { level: "debug", message: "Flushing log buffer to disk (128 entries)", logger: "logging" },
+];
+
+function pad(n: number): string {
+ return n.toString().padStart(2, "0");
+}
+
+const sampleLogEntries: LogEntryProps[] = Array.from({ length: 50 }, (_, i) => {
+ const src = logMessages[i % logMessages.length];
+ return {
+ timestamp: `2026-03-17T10:00:${pad(i + 1)}Z`,
+ level: src.level,
+ message: src.message,
+ logger: src.logger,
+ };
+});
+
const meta: Meta = {
title: "Layouts/ConnectedLayout",
component: ConnectedLayout,
@@ -168,6 +216,7 @@ export const PromptsActive: Story = {
onGetPrompt: fn(),
}}
messages={{
+ onCopyAll: fn(),
messages: [
{ role: "user", content: 'Translate the following text to Spanish: "Hello, how are you?"' },
{ role: "assistant", content: "Hola, como estas?" },
@@ -180,6 +229,199 @@ export const PromptsActive: Story = {
},
};
+export const LoggingActive: Story = {
+ args: {
+ activeTab: "Logs",
+ children: (
+
+ ),
+ },
+};
+
+export const TasksActive: Story = {
+ args: {
+ activeTab: "Tasks",
+ children: (
+
+ ),
+ },
+};
+
+export const HistoryActive: Story = {
+ args: {
+ activeTab: "History",
+ children: (
+
+ ),
+ },
+};
+
export const LimitedTabs: Story = {
args: {
availableTabs: ["Tools", "Resources", "Prompts"],
diff --git a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx
index 0194f920..48205cce 100644
--- a/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx
+++ b/clients/web/src/components/molecules/PromptArgumentsForm/PromptArgumentsForm.tsx
@@ -52,7 +52,6 @@ export function PromptArgumentsForm({
)}
{promptArguments.length > 0 && (
<>
- Arguments
{promptArguments.map((arg) => (
void;
}
export function PromptMessagesDisplay({
messages,
+ onCopyAll,
}: PromptMessagesDisplayProps) {
return (
- Messages
+
+ Messages
+ {onCopyAll && messages.length > 0 && (
+
+ )}
+
{messages.length === 0 ? (
No messages to display
) : (
diff --git a/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx b/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx
index bab21dd5..e7c46eb8 100644
--- a/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx
+++ b/clients/web/src/components/molecules/ResourceListItem/ResourceListItem.stories.tsx
@@ -28,38 +28,3 @@ export const Selected: Story = {
selected: true,
},
};
-
-export const WithAnnotations: Story = {
- args: {
- name: "app-settings.json",
- uri: "file:///app-settings.json",
- selected: false,
- annotations: {
- audience: "application",
- priority: 0.9,
- },
- },
-};
-
-export const WithHighPriority: Story = {
- args: {
- name: "critical-data.json",
- uri: "file:///critical-data.json",
- selected: false,
- annotations: {
- audience: "user",
- priority: 0.8,
- },
- },
-};
-
-export const NoPriority: Story = {
- args: {
- name: "readme.md",
- uri: "file:///readme.md",
- selected: false,
- annotations: {
- audience: "user",
- },
- },
-};
diff --git a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx
index ebaa0dd0..14ce5b83 100644
--- a/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx
+++ b/clients/web/src/components/organisms/HistoryScreen/HistoryScreen.tsx
@@ -1,5 +1,6 @@
import {
Button,
+ Container,
Divider,
Group,
Paper,
@@ -51,6 +52,7 @@ export function HistoryScreen({
onExport,
}: HistoryScreenProps) {
return (
+
@@ -119,5 +121,6 @@ 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 8a98cc84..6b8f6a74 100644
--- a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx
+++ b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.stories.tsx
@@ -46,43 +46,49 @@ 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",
- },
+const logMessages: { level: string; message: string; logger?: string }[] = [
+ { level: "info", message: "Server started on port 3000" },
+ { level: "debug", message: "Loading configuration from /etc/mcp/config.json", logger: "config" },
+ { level: "warning", message: "Deprecated API endpoint called: /v1/tools", logger: "http" },
+ { level: "info", message: "Client connected: inspector-web-ui" },
+ { level: "error", message: "Failed to read resource: file not found at /data/missing.txt", logger: "resources" },
+ { level: "info", message: "Tool execution completed: search_files (245ms)", logger: "tools" },
+ { level: "debug", message: "Parsing JSON-RPC request body", logger: "transport" },
+ { level: "info", message: "Listing available tools for client session", logger: "tools" },
+ { level: "warning", message: "Slow query detected: 1200ms for resource lookup", logger: "db" },
+ { level: "info", message: "Resource subscription added: file:///config.json", logger: "resources" },
+ { level: "debug", message: "Heartbeat ping received from client", logger: "session" },
+ { level: "error", message: "Permission denied reading /etc/secrets.env", logger: "resources" },
+ { level: "info", message: "Prompt 'summarize' resolved with 2 messages", logger: "prompts" },
+ { level: "debug", message: "Cache hit for resource: db://users/42", logger: "cache" },
+ { level: "info", message: "Tool 'create_record' executed successfully in 89ms", logger: "tools" },
+ { level: "warning", message: "Client reconnection attempt #3", logger: "session" },
+ { level: "info", message: "New resource detected: file:///data/output.csv", logger: "resources" },
+ { level: "debug", message: "Serializing response payload (2.4KB)", logger: "transport" },
+ { level: "error", message: "Timeout waiting for tool response after 30s", logger: "tools" },
+ { level: "info", message: "Session initialized with capabilities: tools, resources, prompts" },
+ { level: "debug", message: "Validating input schema for tool 'batch_process'", logger: "tools" },
+ { level: "info", message: "Resource template resolved: db://tables/users/rows/15", logger: "resources" },
+ { level: "warning", message: "Memory usage at 78% — consider increasing limits", logger: "system" },
+ { level: "info", message: "Client disconnected gracefully", logger: "session" },
+ { level: "debug", message: "Flushing log buffer to disk (128 entries)", logger: "logging" },
];
+function pad(n: number): string {
+ return n.toString().padStart(2, "0");
+}
+
+const mixedEntries: LogEntryProps[] = Array.from({ length: 50 }, (_, i) => {
+ const src = logMessages[i % logMessages.length];
+ const seconds = pad(i + 1);
+ return {
+ timestamp: `2026-03-17T10:00:${seconds}Z`,
+ level: src.level,
+ message: src.message,
+ logger: src.logger,
+ };
+});
+
export const WithEntries: Story = {
args: {
controls: makeControls(),
diff --git a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx
index 9d32d31b..9a146e4e 100644
--- a/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx
+++ b/clients/web/src/components/organisms/LoggingScreen/LoggingScreen.tsx
@@ -1,10 +1,11 @@
import {
Button,
+ Card,
Checkbox,
Container,
Grid,
Group,
- Paper,
+
ScrollArea,
Stack,
Text,
@@ -34,12 +35,12 @@ export function LoggingScreen({
-
+
-
+
-
+
Log Stream
@@ -68,7 +69,7 @@ export function LoggingScreen({
)}
-
+
diff --git a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx
index 6628082f..24db4d58 100644
--- a/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx
+++ b/clients/web/src/components/organisms/PromptsScreen/PromptsScreen.stories.tsx
@@ -65,6 +65,7 @@ export const WithResult: Story = {
onGetPrompt: fn(),
},
messages: {
+ onCopyAll: fn(),
messages: [
{
role: "user",
diff --git a/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx b/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx
index 27273599..2e90baf9 100644
--- a/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx
+++ b/clients/web/src/components/organisms/TasksScreen/TasksScreen.tsx
@@ -1,4 +1,4 @@
-import { Button, Container, Group, Stack, Text, Title } from "@mantine/core";
+import { Button, Container, Group, Paper, Stack, Text, Title } from "@mantine/core";
import { TaskCard } from "../../molecules/TaskCard/TaskCard";
import type { TaskCardProps } from "../../molecules/TaskCard/TaskCard";
@@ -17,14 +17,14 @@ export function TasksScreen({
}: TasksScreenProps) {
return (
+
-
-
)}
+
);
}