Recommended resources
The freely available resources this lesson will direct you to.
Welcome back. In our last lesson, we established the "contract" for a tool by defining its schema with Zod, covering the description, inputSchema, and outputSchema. This blueprint ensures our tools are predictable and that the language model understands their capabilities.
Now, we move from the blueprint to the implementation. This lesson focuses on writing the core logic of a custom tool within its execute function. We will build a complete tool, paying close attention to the three pillars of a production-ready implementation: robust input validation, comprehensive error handling, and the formatting of structured results. By the end of this lesson, you will have a practical, reusable pattern for building any tool your agentic system requires.
1. The execute Function: Bringing a Tool to Life
The execute function is where your tool performs its action. It can contain any arbitrary Node.js/TypeScript code, from simple calculations to complex asynchronous operations like database queries or external API calls. Since you're building a production system, most of your tools will likely involve asynchronous logic.
The Vercel AI SDK automatically provides the execute function with a typed object containing the arguments parsed by the LLM, which have already been validated against your inputSchema.
Let's look at a practical example. Imagine we're building a tool to get the weather. The execute function would need to call a weather API.
Vercel AI SDK Masterclass: From Fundamentals to Deep Research
The "Vercel AI SDK Masterclass" video provides a great real-world example of implementing a getWeather tool.
Watch the section from the tool implementation. Notice how the execute function is an async function that makes a fetch request, unwraps the JSON response, and returns the relevant data. This is a standard pattern you will use frequently.
The video demonstrates a key concept: the execute function is the bridge between the LLM's intent and the real-world action. Your experience with backend development and API integration in Node.js will be directly applicable here.
2. Input Validation and Error Handling
A production system cannot assume that everything will work perfectly. We must anticipate and handle potential failures. In the context of a tool, errors can occur in two main places:
- Invalid Input from the LLM: The model might misunderstand the prompt and generate arguments that don't match your
inputSchema. - Execution Failure: The logic inside your
executefunction might fail (e.g., a network error, an invalid API key, or an unexpected response from an external service).
Handling Invalid Input
The Vercel AI SDK provides a strong first line of defense. Before your execute function is ever called, the SDK validates the arguments generated by the model against the inputSchema you defined. If the validation fails, the SDK throws a specific error.
The AI SDK Core documentation details the specific error types you can encounter.
Read the section on Handling Errors. Focus on the two primary errors mentioned: NoSuchToolError and InvalidToolInputError. Understanding InvalidToolInputError is crucial for debugging why a tool might not be running as expected.
When you encounter an InvalidToolInputError, it typically means you need to improve your tool's description or the .describe() strings on your schema fields to give the LLM better guidance.

Handling Execution Errors
Errors within the execute function are your responsibility to handle. As a best practice, you should wrap your core logic in a try...catch block.
When an error is thrown from execute, the AI SDK catches it and, instead of crashing, appends a tool-error part to the generation result. This is a powerful feature for agentic loops, as it allows the agent (or a supervising agent) to see that the tool failed and decide on a next step—such as retrying or informing the user.
Let's see how the documentation describes this mechanism.
This documentation page explains how execution errors are managed.
Now, review the part of the same section that discusses tool-error parts, starting from the example. This shows you how to programmatically access the errors after a generation completes.
For advanced observability, which is critical for your production system goal, the AI SDK offers lifecycle callbacks. These allow you to hook into the start and end of a tool's execution to log performance, inputs, and, most importantly, any errors that occur.
This section of the documentation introduces powerful callbacks for monitoring tool execution.
Read the section on lifecycle callbacks. The experimental_onToolCallFinish callback is particularly useful. You can implement centralized logging here to capture every tool failure across your entire system, complete with timing information, the tool name, and the input that caused the error.
3. Formatting a Structured Result
Upon successful execution, your tool must return a result. Following the schema you defined in the previous lesson, this result should be a structured object, not a simple string. This ensures the output is predictable and can be easily consumed by the LLM in subsequent steps or by other parts of your application.
The object you return from execute must conform to the shape defined in your tool's outputSchema. The AI SDK will validate this for you, providing another layer of type safety and preventing malformed data from propagating.

This structured input/output flow is the essence of reliable tool use in agentic systems. It transforms the unpredictable nature of natural language into the deterministic world of typed data structures.
4. A Complete, Production-Ready Tool
Let's synthesize everything we've discussed into a single, complete example. We will create a tool that fetches the current price of a cryptocurrency from the CoinGecko API. This tool will include:
- Clear descriptions for the LLM.
- Typed input and output schemas.
- An asynchronous API call.
- Robust
try...catcherror handling. - Formatting of the successful result into a structured object.
import { tool } from 'ai';
import { z } from 'zod';
// Define a tool for fetching cryptocurrency prices
export const cryptoPriceTool = tool({
description: 'Get the current price of a cryptocurrency in a given currency.',
// Zod schema for input validation
inputSchema: z.object({
cryptoId: z.string().describe("The ID of the cryptocurrency, e.g., 'bitcoin', 'ethereum'."),
vsCurrency: z.string().describe("The currency to compare against, e.g., 'usd', 'eur'.").default('usd'),
}),
// Zod schema for output validation
outputSchema: z.object({
cryptoId: z.string(),
vsCurrency: z.string(),
price: z.number(),
}),
// The core logic of the tool
execute: async ({ cryptoId, vsCurrency }) => {
try {
// Perform the API call within a try...catch block
const url = `https://api.coingecko.com/api/v3/simple/price?ids=${cryptoId}&vs_currencies=${vsCurrency}`;
const response = await fetch(url);
if (!response.ok) {
// Handle non-successful HTTP responses
throw new Error(`API request failed with status ${response.status}`);
}
const data = await response.json();
// Check if the API returned the expected data structure
if (!data[cryptoId] || !data[cryptoId][vsCurrency]) {
throw new Error(`Price data not found for ${cryptoId} in ${vsCurrency}`);
}
// Format the successful result into a structured object
return {
cryptoId,
vsCurrency,
price: data[cryptoId][vsCurrency],
};
} catch (error) {
// Log the error and re-throw to let the AI SDK handle it
console.error("Error in cryptoPriceTool:", error);
// Re-throwing the error allows the AI SDK to create a `tool-error` part
throw error;
}
},
});
This example serves as a robust template. You can adapt this pattern of schema definition, async execution with try...catch, and structured returns to build the various tools needed for your agentic infrastructure's tool store.
Conclusion
In this lesson, you have moved from defining a tool's interface to implementing its core logic. You now have a comprehensive, production-oriented pattern for creating custom tools that are robust, predictable, and observable.
Key Takeaways:
- The
executefunction contains the core logic of a tool and can perform any synchronous or asynchronous action. - Input validation is handled automatically by the AI SDK using the
inputSchema, which throws anInvalidToolInputErroron failure. - Execution errors inside
executeshould be handled withtry...catchblocks. Errors thrown from this function are caught by the SDK and reported astool-errorparts, enabling agents to react to tool failures. - The
experimental_onToolCallFinishcallback is a powerful mechanism for centralized error logging and observability. - A tool's
executefunction must return a structured object that conforms to itsoutputSchema, ensuring end-to-end type safety.
You are now equipped to build the individual capabilities—the tools—that your worker agents will use. In the next lesson, we will take the final step in this module by implementing a basic agent that can intelligently select and use the tool you've just built to answer a user's question.