Welcome back! In the previous chapter, the Interaction Classifier, we taught our AI how to sort tools into "Search," "Read," and "Action" to keep our chat history tidy.
Now that we have sorted the tools, we face a new challenge: How do we actually display the results?
Imagine you ask your AI: "Generate an image of a sunset" or "Download this large file."
Without a visualization engine, the tool might return raw data like this:
"data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAU..." (A million random characters).{"progress": 50, "total": 100} (Repeated 50 times in your chat).This is overwhelming and unusable for a human.
We need a Result Visualization Engine. Think of this like a Universal Media Player on your computer.
Our engine looks at the raw data coming from the tool and automatically decides the best way to show it to you.
Before looking at the code, let's trace what happens when a tool finishes its job.
Result object.Here is the decision process:
Let's look at UI.tsx. This file acts as the brain of our visualization engine.
When a tool takes a long time (like downloading a file), we don't want to spam the chat. We want a smooth bar.
// File: UI.tsx
export function renderToolUseProgressMessage(lastProgress: any) {
const { progress, total, progressMessage } = lastProgress.data;
// If we know the total (e.g., 50 out of 100), show a percentage bar
if (total !== undefined && total > 0) {
const ratio = Math.min(1, Math.max(0, progress / total)); // Calculate 0.0 to 1.0
return (
<Box flexDirection="row">
<ProgressBar ratio={ratio} width={20} />
<Text>{Math.round(ratio * 100)}%</Text>
</Box>
);
}
// If we don't know the total, just show "Processing..."
return <Text>Running... {progress}</Text>;
}
Explanation:
ProgressBar: A custom component that draws characters like [โโโโโโโโโโ].total exists. If yes, it calculates the percentage. If no, it just shows the current number.Tools can return a mix of text and images. We need to loop through the output and handle each part differently.
// File: UI.tsx
export function renderToolResultMessage(output: any) {
// If the output is a list of blocks (Standard MCP format)
if (Array.isArray(output)) {
return output.map((item) => {
// CASE A: It is an image
if (item.type === 'image') {
return <Text>[Image Displayed Here]</Text>;
}
// CASE B: It is text
return <OutputLine content={item.text} />;
});
}
}
Explanation:
item.type === 'image': We identify the data type. In a real app, we would render the base64 string as an <img> tag. Here, we show a placeholder.OutputLine: A helper that ensures text wraps nicely on the screen.Large Language Models have a limit on how much they can read (Context Window). If a tool returns a 100,000-word essay, it might push important instructions out of the AI's memory.
We add a safety check:
// File: UI.tsx
const MCP_OUTPUT_WARNING_THRESHOLD_TOKENS = 10_000;
// inside renderToolResultMessage...
const estimatedTokens = getContentSizeEstimate(mcpOutput);
const showWarning = estimatedTokens > MCP_OUTPUT_WARNING_THRESHOLD_TOKENS;
if (showWarning) {
return (
<Box flexDirection="column">
<Text color="warning">
โ ๏ธ Large response (~{estimatedTokens} tokens). This fills context quickly.
</Text>
{/* Still show the content below the warning */}
{contentElement}
</Box>
);
}
Explanation:
Sometimes tools return raw JSON that looks technical. Our engine tries to detect specific tools and make them pretty.
Take sending a Slack message.
{"ok": true, "channel": "C0245...", "message_link": "http..."}// File: UI.tsx
export function trySlackSendCompact(output: any, input: any) {
// Check if the output contains a specific Slack link pattern
if (output.includes('"message_link"')) {
// Extract the URL and Channel name using logic helper
const { channel, url } = parseSlackDetails(output, input);
// Return a pretty, clickable sentence
return (
<Text>
Sent a message to <Link url={url}>{channel}</Link>
</Text>
);
}
return null; // Not a slack message, do nothing
}
Explanation:
message_link) to identify the tool.In this chapter, we built the Result Visualization Engine.
We have handled the Tools (Chapter 1), the Organization (Chapter 2), and the Visuals (Chapter 3).
However, there is one final piece of the puzzle. Sometimes, an AI tries to send a JSON command, but it messes up the format (e.g., adding extra text or forgetting quotes). If our system is too strict, it will crash. We need a way to "repair" broken JSON.
Next Chapter: Intelligent JSON Unwrapper
Generated by Code IQ