Welcome to the final chapter of our tutorial!
In Chapter 4: MCP Client Integration, we connected our tool to the outside world. Our tool can now successfully fetch data from external servers.
However, we have a small problem. Currently, our tool behaves like a genius mathematician who only speaks in complex equations. When it finds files, it returns raw computer code (JSON).
If we don't build a User Interface (UI), the user might see this in their console:
[{"uri":"file:///Users/demo/notes.txt","name":"notes.txt","server":"local"}]
While this is perfect for the AI, it is hard for a human to read quickly. We want to present the data elegantly, like a chef plating a meal after cooking it.
In this project, we use React to build our interface. Even though this tool runs in a text terminal (like Command Prompt or Terminal), we use a library that lets us build "visual" components just like a website.
We need to handle two specific visual moments in UI.tsx:
renderToolUseMessage)When the AI decides to run the tool, we want to show a friendly status update.
If the user asked for a specific server, we should say so. If not, we say we are checking everything.
// --- File: UI.tsx ---
export function renderToolUseMessage(input: Partial<{
server?: string;
}>): React.ReactNode {
// If a server name exists, mention it
return input.server
? `List MCP resources from server "${input.server}"`
: `List all MCP resources`;
}
Explanation:
input object (which we defined in Chapter 2: Data Schemas).? :). It checks: Is there a server name?renderToolResultMessage)
Once the tool finishes (after Chapter 3: Tool Definition runs), we get the output. We need to decide how to display it.
First, we handle the case where we found nothing.
export function renderToolResultMessage(
output: Output,
_progressMessages,
{ verbose }
): React.ReactNode {
// 1. Handle empty results
if (!output || output.length === 0) {
return (
<MessageResponse height={1}>
<Text dimColor>(No resources found)</Text>
</MessageResponse>
);
}
// ... (code continues)
Explanation:
output: This is the list of files we fetched.if (!output ...): If the list is empty, we don't want to show a blank screen.<Text dimColor>: We use a component to make the text gray ("dim"), indicating a minor status update.If we did find resources, we want to format them nicely.
// 2. Format the valid output
// We turn the object into a pretty text string
const formattedOutput = jsonStringify(output, null, 2);
// 3. Render it
return <OutputLine content={formattedOutput} verbose={verbose} />;
}
Explanation:
jsonStringify(..., 2): This takes the raw data and adds indentation (spacing), making it much easier to read than a single long line.<OutputLine />: This is a custom component that handles things like word-wrapping and coloring for us.How does the application know when to run these functions? The system acts like a coordinator between the AI, the Tool Logic, and the Screen.
The UI.tsx file imports several helper components that make our job easier:
MessageResponse: A container that creates a standard box for tool outputs. It ensures all tools look consistent.Text: A component from the ink library. It allows us to style text (colors, bold, dim) in the terminal.OutputLine: A helper that manages large blocks of text, ensuring they don't break the layout if the list of resources is huge.
Congratulations! You have completed the ListMcpResourcesTool. Let's review what we built across the five chapters:
You now have a fully functional AI tool that allows a user to ask, "What files are on my server?" and receive a verified, accurate, and beautifully formatted response.
End of Tutorial.
Generated by Code IQ