Docs
Adding a Tool
Adding a Tool
How to create a new tool with a Zod schema and register it with agents.
Tool Anatomy
// lib/ai/tools/shared/my-tool.ts
import { tool } from "ai";
import { z } from "zod";
export const myTool = tool({
description: "What this tool does — the LLM reads this to decide when to call it",
inputSchema: z.object({
query: z.string().describe("The search query to look up"),
maxResults: z.number().optional().describe("Maximum number of results, default 5"),
}),
execute: async ({ query, maxResults = 5 }) => {
// Always return an object
// Never throw — return { error: "..." } on failure
try {
const results = await someApiCall(query, maxResults);
return { results };
} catch (err) {
return { error: "Failed to fetch results", details: String(err) };
}
},
});Steps
Create the tool file
Location: lib/ai/tools/shared/your-tool-name.ts
Use kebab-case for the filename. Export the tool with a descriptive camelCase name.
Define the Zod schema
Every field must have a .describe() string. The LLM uses these descriptions to fill in parameters when calling the tool. Without descriptions, the model guesses — and guesses wrong.
inputSchema: z.object({
topic: z.string().describe("The main topic or subject to research"),
platform: z.enum(["LinkedIn", "Twitter", "Instagram", "Blog"])
.describe("The target social media platform"),
tone: z.string().optional()
.describe("Writing tone: professional, casual, humorous. Default: professional"),
}),Implement execute
- Return a plain object — never
throw - Return
{ error: "..." }on failure so the agent can surface the error in its response - If the output should be saved as an asset, structure it consistently (the ASSET_REGISTRY builder will read it)
Add to the agent's tools map
In the agent file that should have access to this tool:
const agent = new ToolLoopAgent({
tools: {
web_search: tavilySearch(),
my_tool: myTool, // add here — key is the tool name the LLM uses
},
});The key in the tools object is the name the LLM calls when it uses the tool.
Register in ASSET_REGISTRY (if asset-producing)
In lib/ai/utils.ts, add a builder under the tool's key name:
my_tool: ({ input, output }) => {
const value = (output as any)?.value ?? output;
if (!value?.result) return null;
return {
type: "my_asset_type",
title: String(input.topic ?? "Output").slice(0, 100),
content: String(value.result),
metadata: { platform: input.platform },
};
},Factory Tools
If your tool needs team context (for namespaced storage, team-scoped DB queries, etc.), export a factory function:
export function buildMyTool(teamId: string) {
return tool({
description: "...",
inputSchema: z.object({ ... }),
execute: async (input) => {
// teamId is available via closure
await db.insert(someTable).values({ teamId, ...input });
return { success: true };
},
});
}
// In the agent:
tools: {
my_tool: buildMyTool(team.id),
}