From c04f96ffb8dd40b46cb229783fa36af537b3a77b Mon Sep 17 00:00:00 2001 From: Cliff Hall Date: Mon, 27 Apr 2026 18:29:58 -0400 Subject: [PATCH] feat(sampling): replace InlineSamplingRequest responseText with draftResult (#1239) (#1250) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Closes the Phase 5 audit partial: the inline card's prop interface now matches SamplingRequestPanel's, accepting an optional CreateMessageResult draft instead of a flat scalar. Lets future hook-layer wiring share a single draft store between the inline card and the full panel. Rendering: - No draft → existing layout (request preview, model hints, action buttons), with the readonly placeholder textarea removed. - Text draft → 2-line preview of draftResult.content.text (lineClamp 2). - Image / audio draft → "[Image content]" / "[Audio content]" label, matching the ContentViewer fallback convention. Updates the three consumer stories that threaded responseText="" through (PendingClientRequests, ToolsScreen) — no callback signatures changed. Co-authored-by: Claude Opus 4.7 (1M context) --- .../InlineSamplingRequest.stories.tsx | 36 ++++++++++++--- .../InlineSamplingRequest.tsx | 45 ++++++++++--------- .../PendingClientRequests.stories.tsx | 2 - .../ToolsScreen/ToolsScreen.stories.tsx | 1 - 4 files changed, 56 insertions(+), 28 deletions(-) diff --git a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx index a245cf5d..aef6897b 100644 --- a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx +++ b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.stories.tsx @@ -1,5 +1,8 @@ import type { Meta, StoryObj } from "@storybook/react-vite"; -import type { CreateMessageRequestParams } from "@modelcontextprotocol/sdk/types.js"; +import type { + CreateMessageRequestParams, + CreateMessageResult, +} from "@modelcontextprotocol/sdk/types.js"; import { fn } from "storybook/test"; import { InlineSamplingRequest } from "./InlineSamplingRequest"; @@ -16,13 +19,31 @@ const defaultRequest: CreateMessageRequestParams = { maxTokens: 1024, }; +const textDraft: CreateMessageResult = { + role: "assistant", + model: "claude-sonnet-4-20250514", + content: { + type: "text", + text: "The code looks good overall. Consider extracting the repeated logic into a helper function and adding type annotations to the public API.", + }, +}; + +const imageDraft: CreateMessageResult = { + role: "assistant", + model: "claude-sonnet-4-20250514", + content: { + type: "image", + data: "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAQAAAC1HAwCAAAAC0lEQVR42mNkYAAAAAYAAjCB0C8AAAAASUVORK5CYII=", + mimeType: "image/png", + }, +}; + const meta: Meta = { title: "Groups/InlineSamplingRequest", component: InlineSamplingRequest, args: { request: defaultRequest, queuePosition: "1 of 1", - responseText: "", onAutoRespond: fn(), onEditAndSend: fn(), onReject: fn(), @@ -49,10 +70,15 @@ export const WithModelHints: Story = { }, }; -export const PrefilledResponse: Story = { +export const WithTextDraft: Story = { args: { - responseText: - "The code looks good overall. Consider extracting the repeated logic into a helper function.", + draftResult: textDraft, + }, +}; + +export const WithNonTextDraft: Story = { + args: { + draftResult: imageDraft, }, }; diff --git a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx index afa7582a..0c453c4a 100644 --- a/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx +++ b/clients/web/src/components/groups/InlineSamplingRequest/InlineSamplingRequest.tsx @@ -1,18 +1,13 @@ -import { - Badge, - Button, - Group, - Paper, - Stack, - Text, - Textarea, -} from "@mantine/core"; -import type { CreateMessageRequestParams } from "@modelcontextprotocol/sdk/types.js"; +import { Badge, Button, Group, Paper, Stack, Text } from "@mantine/core"; +import type { + CreateMessageRequestParams, + CreateMessageResult, +} from "@modelcontextprotocol/sdk/types.js"; export interface InlineSamplingRequestProps { request: CreateMessageRequestParams; queuePosition: string; - responseText: string; + draftResult?: CreateMessageResult; onAutoRespond: () => void; onEditAndSend: () => void; onReject: () => void; @@ -35,6 +30,11 @@ const PreviewText = Text.withProps({ lineClamp: 2, }); +const DraftPreview = Text.withProps({ + size: "sm", + lineClamp: 2, +}); + const DetailButton = Button.withProps({ variant: "subtle", size: "xs", @@ -67,6 +67,17 @@ function extractPreview(request: CreateMessageRequestParams): string { return content.type === "text" ? content.text : `[${content.type}]`; } +function extractDraftPreview(draft: CreateMessageResult): string { + switch (draft.content.type) { + case "text": + return draft.content.text; + case "image": + return "[Image content]"; + case "audio": + return "[Audio content]"; + } +} + function extractModelHints( request: CreateMessageRequestParams, ): string[] | undefined { @@ -82,7 +93,7 @@ function formatModelHints(hints: string[]): string { export function InlineSamplingRequest({ request, queuePosition, - responseText, + draftResult, onAutoRespond, onEditAndSend, onReject, @@ -90,6 +101,7 @@ export function InlineSamplingRequest({ }: InlineSamplingRequestProps) { const modelHints = extractModelHints(request); const messagePreview = extractPreview(request); + const draftPreview = draftResult ? extractDraftPreview(draftResult) : null; return ( @@ -105,14 +117,7 @@ export function InlineSamplingRequest({ View Details -