Documentation Index
Fetch the complete documentation index at: https://mintlify.com/tambo-ai/tambo/llms.txt
Use this file to discover all available pages before exploring further.
Overview
Tools are functions that the AI can execute to perform actions. In Tambo, tools run client-side in the browser, giving them access to:
- DOM manipulation
- React state
- Browser APIs (localStorage, geolocation, etc.)
- Authenticated API calls
- User-specific data
Tools complement components—while components render UI, tools perform actions and fetch data.
The TamboTool interface defines how to register a tool:
import { TamboTool } from '@tambo-ai/react';
import { z } from 'zod';
const tools: TamboTool[] = [
{
name: "getWeather",
description: "Fetches current weather for a location",
tool: async (params: { location: string }) => {
const response = await fetch(
`/api/weather?q=${encodeURIComponent(params.location)}`
);
return await response.json();
},
inputSchema: z.object({
location: z.string().describe("City name or location"),
}),
outputSchema: z.object({
temperature: z.number(),
condition: z.string(),
humidity: z.number(),
}),
},
];
name
Unique identifier for the tool. Should be descriptive and follow camelCase:
name: "searchDocuments" // Good
name: "search_documents" // Also okay
name: "search" // Too generic
description
Explains to the AI what the tool does and when to use it:
description: "Searches the user's documents by keyword and returns matching results with titles, excerpts, and URLs. Use when the user wants to find specific documents."
The function to execute. Can be sync or async:
tool: async (params) => {
// Perform the action
const result = await performAction(params);
return result;
}
The function receives validated params matching the inputSchema.
Defines the tool’s parameters using Zod:
inputSchema: z.object({
query: z.string().describe("Search query"),
maxResults: z.number().min(1).max(100).default(10),
filters: z.object({
dateFrom: z.string().optional(),
dateTo: z.string().optional(),
author: z.string().optional(),
}).optional(),
})
outputSchema
Defines the expected return type:
outputSchema: z.object({
results: z.array(z.object({
title: z.string(),
excerpt: z.string(),
url: z.string(),
})),
totalCount: z.number(),
})
While optional, defining an output schema helps with:
- Type safety
- AI understanding of return values
- Validation of tool responses
transformToContent (Optional)
Transforms the tool result into content parts for the AI:
transformToContent: (result) => [
{ type: "text", text: result.text },
{ type: "image_url", image_url: { url: result.imageUrl } },
]
By default, results are stringified and wrapped in a text content part. Use transformToContent when your tool returns rich content like images or audio.
Pass tools to TamboProvider:
import { TamboProvider } from '@tambo-ai/react';
const tools: TamboTool[] = [
{
name: "getCurrentUser",
description: "Gets the current user's profile information",
tool: async () => {
const response = await fetch('/api/user/me');
return await response.json();
},
inputSchema: z.object({}),
outputSchema: z.object({
id: z.string(),
name: z.string(),
email: z.string(),
}),
},
{
name: "updateSettings",
description: "Updates user settings",
tool: async ({ theme, notifications }) => {
await fetch('/api/settings', {
method: 'POST',
body: JSON.stringify({ theme, notifications }),
});
return { success: true };
},
inputSchema: z.object({
theme: z.enum(["light", "dark"]).optional(),
notifications: z.boolean().optional(),
}),
outputSchema: z.object({
success: z.boolean(),
}),
},
];
function App() {
return (
<TamboProvider
apiKey={process.env.NEXT_PUBLIC_TAMBO_API_KEY!}
userKey="user-123"
tools={tools}
>
<YourApp />
</TamboProvider>
);
}
- User message - “What’s the weather in San Francisco?”
- AI decides - Calls
getWeather tool with { location: "San Francisco" }
- Validation - Input validated against
inputSchema
- Execution - Tool function runs client-side
- Result - Return value sent back to AI
- Response - AI incorporates result into its response
Common Use Cases
Fetching Data
const tools: TamboTool[] = [
{
name: "searchProducts",
description: "Searches the product catalog",
tool: async ({ query, category }) => {
const response = await fetch(
`/api/products/search?q=${query}&category=${category}`
);
return await response.json();
},
inputSchema: z.object({
query: z.string(),
category: z.string().optional(),
}),
outputSchema: z.object({
products: z.array(z.object({
id: z.string(),
name: z.string(),
price: z.number(),
})),
}),
},
];
Modifying State
const tools: TamboTool[] = [
{
name: "addToCart",
description: "Adds a product to the shopping cart",
tool: async ({ productId, quantity }) => {
// Access React state through closure
addItemToCart({ productId, quantity });
return { success: true };
},
inputSchema: z.object({
productId: z.string(),
quantity: z.number().min(1).default(1),
}),
outputSchema: z.object({
success: z.boolean(),
}),
},
];
Browser APIs
const tools: TamboTool[] = [
{
name: "getUserLocation",
description: "Gets the user's current geographic location",
tool: async () => {
return new Promise((resolve, reject) => {
navigator.geolocation.getCurrentPosition(
(position) => resolve({
latitude: position.coords.latitude,
longitude: position.coords.longitude,
}),
(error) => reject(error)
);
});
},
inputSchema: z.object({}),
outputSchema: z.object({
latitude: z.number(),
longitude: z.number(),
}),
},
];
DOM Manipulation
const tools: TamboTool[] = [
{
name: "scrollToSection",
description: "Scrolls the page to a specific section",
tool: async ({ sectionId }) => {
const element = document.getElementById(sectionId);
if (element) {
element.scrollIntoView({ behavior: 'smooth' });
return { success: true };
}
return { success: false, error: "Section not found" };
},
inputSchema: z.object({
sectionId: z.string(),
}),
outputSchema: z.object({
success: z.boolean(),
error: z.string().optional(),
}),
},
];
When should you use a tool vs a component?
- Performing an action (update cart, send email, save data)
- Fetching data that will be displayed in text or existing UI
- No new UI needed - the result is used in the conversation
- Browser APIs - accessing geolocation, clipboard, etc.
Use a Component When:
- Displaying data in a custom visualization
- Rendering UI - charts, cards, forms, etc.
- Interactive elements that persist across messages
- Rich presentation beyond plain text
Use Both Together:
Tools can fetch data that components then display:
const tools: TamboTool[] = [
{
name: "fetchSalesData",
description: "Fetches sales data for charting",
tool: async ({ startDate, endDate }) => {
const response = await fetch(
`/api/sales?start=${startDate}&end=${endDate}`
);
return await response.json();
},
inputSchema: z.object({
startDate: z.string(),
endDate: z.string(),
}),
outputSchema: z.object({
data: z.array(z.object({
date: z.string(),
revenue: z.number(),
})),
}),
},
];
const components: TamboComponent[] = [
{
name: "SalesChart",
description: "Displays sales data as a line chart",
component: SalesChart,
propsSchema: z.object({
data: z.array(z.object({
date: z.string(),
revenue: z.number(),
})),
}),
},
];
User: “Show me sales for last month”
AI:
- Calls
fetchSalesData tool
- Receives data
- Renders
SalesChart component with the data
Error Handling
Throw errors from tools to signal failure to the AI:
const tools: TamboTool[] = [
{
name: "deleteDocument",
description: "Permanently deletes a document",
tool: async ({ documentId }) => {
const response = await fetch(`/api/documents/${documentId}`, {
method: 'DELETE',
});
if (!response.ok) {
throw new Error(`Failed to delete document: ${response.statusText}`);
}
return { success: true };
},
inputSchema: z.object({
documentId: z.string(),
}),
outputSchema: z.object({
success: z.boolean(),
}),
},
];
The AI receives the error and can respond appropriately to the user.
Best Practices
Clear Descriptions
// ✅ Good
description: "Searches user's emails by subject, sender, or content. Returns up to 20 matching emails with preview text."
// ❌ Bad
description: "Search emails"
Use schema constraints:
inputSchema: z.object({
email: z.string().email(),
age: z.number().min(0).max(120),
role: z.enum(["admin", "user", "guest"]),
})
Type Safety
Infer types from schemas:
const inputSchema = z.object({
userId: z.string(),
message: z.string(),
});
type ToolInput = z.infer<typeof inputSchema>;
const tools: TamboTool[] = [
{
name: "sendMessage",
tool: async ({ userId, message }: ToolInput) => {
// TypeScript knows the types
},
inputSchema,
outputSchema: z.object({ success: z.boolean() }),
description: "Sends a message to a user",
},
];
Handle Async Properly
Always use async/await, never .then():
// ✅ Good
tool: async ({ query }) => {
const response = await fetch(`/api/search?q=${query}`);
return await response.json();
}
// ❌ Bad
tool: ({ query }) => {
return fetch(`/api/search?q=${query}`).then(r => r.json());
}
Next Steps