feat(sampling): replace InlineSamplingRequest responseText with draftResult (#1239) (#1250)

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) <noreply@anthropic.com>
This commit is contained in:
Cliff Hall 2026-04-27 18:29:58 -04:00 • committed by GitHub
commit c04f96ffb8
No known key found for this signature in database
GPG key ID: B5690EEEBB952194

View file

@ -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 (
<RequestContainer>
@ -105,14 +117,7 @@ export function InlineSamplingRequest({
<DetailButton onClick={onViewDetails}>View Details</DetailButton>
<Textarea
size="sm"
value={responseText}
placeholder="Response..."
autosize
minRows={2}
readOnly
/>
{draftPreview !== null && <DraftPreview>{draftPreview}</DraftPreview>}
<ActionsRow>
<CompactButton onClick={onAutoRespond}>Auto-respond</CompactButton>