In this exercise, we're going to learn how to work with tools in the AI SDK to create a front-end that displays tool interactions with full type safety and autocomplete. The key challenge is to properly type our UI messages so that they understand the shape of our tool definitions.
Our first task is to extract the tool definitions from inside the streamText call in api/chat.ts and move them to the module scope. This allows us to both use the tools at runtime and infer types from them.
The tools are currently defined within the streamText function call:
const result = streamText({model: google('gemini-2.5-flash'),messages: convertToModelMessages(messages),system: `...`,tools: {writeFile: tool({...}),readFile: tool({...}),// ...other tools},
We need to move these tool definitions into the module scope variable that's currently marked with TODO:
// Move the tool definitions hereconst tools = TODO
After moving the tool definitions, we need to create a custom UI message type that knows about our tools. The UIMessage type from the AI SDK accepts type parameters, and we need to pass in information about our tools:
// Replace this TODO with the correct typeexport type MyUIMessage = TODO
We'll use the InferUITools utility type from aito infer the correct tool types from our tool definitions.
Once we have our custom MyUIMessage type, we need to use it in our front-end components:
root.tsx, we need to pass MyUIMessage as a type argument to useChatcomponents.tsx, we need to update the parts prop type to use MyUIMessage['parts'] instead of UIMessage['parts']writeFile Tool DisplayFinally, we need to implement the missing JSX for the writeFile tool
The writeFile tool JSX should show:
It should be styled similarly to the other tool displays.
When everything is correctly implemented, we should be able to:
writeFile tool displayGood luck, and I'll see you in the solution!