import type { AgentToolResult, ToolRenderResultOptions } from "@earendil-works/pi-coding-agent"; import { Text } from "@earendil-works/pi-tui"; type McpToolResultDetails = Record & { error?: unknown }; type McpToolContentBlock = AgentToolResult["content"][number]; interface RenderTheme { fg: (name: string, text: string) => string; } interface McpToolRenderContext { isError: boolean; } export interface McpToolResultDisplay { lines: string[]; truncated: boolean; } function blockToLines(block: McpToolContentBlock): string[] { if (block.type === "text") { return block.text.split("\n"); } return [`[image: ${block.mimeType}]`]; } export function formatMcpToolResultLines( result: Pick, "content">, expanded: boolean, maxCollapsedLines = 3, ): McpToolResultDisplay { const allLines = result.content.flatMap(blockToLines); const lines = allLines.length > 0 ? allLines : ["(empty result)"]; if (expanded || lines.length <= maxCollapsedLines) { return { lines, truncated: false }; } return { lines: [...lines.slice(0, maxCollapsedLines), "…"], truncated: true, }; } export function renderMcpToolResult( result: AgentToolResult, options: ToolRenderResultOptions, theme: RenderTheme, context?: McpToolRenderContext, ) { if (options.isPartial) { return new Text(theme.fg("warning", "Running MCP tool..."), 0, 0); } const hasErrorDetails = Boolean(result.details.error); const display = formatMcpToolResultLines(result, options.expanded || context?.isError === true || hasErrorDetails); const output = display.lines .map((line) => line === "…" ? theme.fg("muted", line) : theme.fg("toolOutput", line)) .join("\n"); const hint = display.truncated && !options.expanded ? `\n${theme.fg("muted", "(Ctrl+O to expand)")}` : ""; return new Text(`${output}${hint}`, 0, 0); }