Tool Result

What a tool actually returned, rendered by content type. An MCP result is a list of blocks that can mix text, images and embedded resources — JSON.stringify turns a returned screenshot into four thousand characters of base64.

src/components/ui/tool-result.tsx

About

From the component's own source, where it was written.

What a tool actually returned, rendered by content type.

ToolCall shows the invocation. This shows the payload coming back, which is where MCP gets awkward: a single result is a *list* of content blocks, and they can mix — text, an image, an embedded resource — in one response. Rendering JSON.stringify(result) is the usual shortcut and it turns a returned screenshot into four thousand characters of base64.

So each block is rendered as what it is, and only text and JSON are copyable, because those are the ones anyone wants on the clipboard.

**isError is part of the payload, not the transport.** An MCP tool reports failure with a normal, successful response carrying isError: true — so a UI that only styles transport errors shows a failed tool call as a success. This takes the flag explicitly.

Install

npx astralyx-ui add tool-result

Usage

import { ToolResult } from '@/components/ui/tool-result'

<ToolResult tool="search_docs" content={result.content} isError={result.isError} durationMs={120} />

Composer

Change the props and the source below follows.

search_docs120ms

Found 3 matching documents in the help centre.

{
  "hits": [
    {
      "id": "doc_18",
      "score": 0.94,
      "title": "Refund policy"
    },
    {
      "id": "doc_41",
      "score": 0.71,
      "title": "Chargebacks"
    }
  ]
}

file:///docs/refunds.md

# Refund policy

Refunds are issued to the original payment method within 5 working days.
Props
tsx
<ToolResult
  tool="search_docs"
  content={result.content}
  isError={false}
  durationMs={120}
/>

Examples

Mixed content blocks

API

Generated from the source, so it cannot fall behind the code. One table per exported component, then hooks, functions and types.

ToolResult

Extends Omit<ComponentProps<'div'>, 'content'>.

PropTypeDefaultDescription
contentToolContent[]requiredBlocks of { type: 'text' | 'json' | 'image' | 'resource' }. Each is rendered as what it is — an image is a picture, not base64.
isErrorbooleanfalsePart of the payload, not the transport: MCP reports tool failure with a successful response carrying isError. A UI that only styles transport errors shows a failed call as a success.
toolstring—Tool name, shown in the header.
durationMsnumber——
maxBlocksnumber3Collapses the rest behind a disclosure.
errorLabelstring'Tool reported an error'—
emptyLabelstring'Returned nothing.'—
moreLabel(hidden: number) => string(hidden) => `Show ${hidden} more block${hidden === 1 ? '' : 's'}`—
copyLabelstring'Copy'—

Types

TypeDefinitionDescription
ToolContent
| { type: 'text'; text: string }
| { type: 'json'; value: unknown }
| { type: 'image'; data: string; mimeType?: string; alt?: string }
| { type: 'resource'; uri: string; mimeType?: string; text?: string }
What a tool actually returned, rendered by content type.

Behaviour

TopicDetailDescription
copyingtext and JSON onlyThose are the blocks anyone wants on the clipboard.