Documentation Index
Fetch the complete documentation index at: https://mintlify.com/CopilotKit/CopilotKit/llms.txt
Use this file to discover all available pages before exploring further.
Overview
defineTool() is a helper function for creating type-safe server-side tools that execute in your runtime environment. It provides a clean API for defining tool schemas and execution logic with full TypeScript type inference.
Function Signature
function defineTool<TParameters extends z.ZodTypeAny>(config: {
name: string;
description: string;
parameters: TParameters;
execute: (args: z.infer<TParameters>) => Promise<unknown>;
}): ToolDefinition<TParameters>
Parameters
Unique identifier for the tool. Used by the LLM to call the tool.Convention: Use camelCase or snake_case.name: 'searchDatabase'
// or
name: 'search_database'
Human-readable description of what the tool does. This is shown to the LLM to help it decide when to use the tool.Best practices:
- Be specific about what the tool does
- Mention the type of data it returns
- Include any important constraints or requirements
description: 'Search the product database by name or category. Returns up to 10 matching products with their IDs, names, and prices.'
Zod schema defining the tool’s input parameters. Provides type safety and validation.import { z } from 'zod';
parameters: z.object({
query: z.string().describe('Search query'),
category: z.string().optional().describe('Filter by category'),
limit: z.number().default(10).describe('Maximum results')
})
execute
(args: z.infer<TParameters>) => Promise<unknown>
required
Async function that executes the tool logic. Receives validated parameters and returns the result.
- Parameters are automatically validated against the Zod schema
- Return value should be JSON-serializable
- Errors are caught and returned as error responses
execute: async ({ query, category, limit }) => {
const results = await database.search(query, {
category,
limit
});
return results;
}
Return Value
Returns a ToolDefinition object that can be used with BuiltInAgent:
interface ToolDefinition<TParameters extends z.ZodTypeAny> {
name: string;
description: string;
parameters: TParameters;
execute: (args: z.infer<TParameters>) => Promise<unknown>;
}
Usage Examples
import { defineTool } from '@copilotkit/agent';
import { z } from 'zod';
const getCurrentTime = defineTool({
name: 'getCurrentTime',
description: 'Get the current time in a specific timezone',
parameters: z.object({
timezone: z.string().describe('IANA timezone name (e.g., "America/New_York")')
}),
execute: async ({ timezone }) => {
const date = new Date();
const formatter = new Intl.DateTimeFormat('en-US', {
timeZone: timezone,
hour: '2-digit',
minute: '2-digit',
second: '2-digit',
timeZoneName: 'short'
});
return {
time: formatter.format(date),
timezone
};
}
});
const searchProducts = defineTool({
name: 'searchProducts',
description: 'Search the product catalog by name, description, or category',
parameters: z.object({
query: z.string().describe('Search query'),
category: z.enum(['electronics', 'clothing', 'books', 'home']).optional(),
minPrice: z.number().optional().describe('Minimum price in USD'),
maxPrice: z.number().optional().describe('Maximum price in USD'),
limit: z.number().default(10).describe('Maximum number of results')
}),
execute: async ({ query, category, minPrice, maxPrice, limit }) => {
const results = await db.products.findMany({
where: {
OR: [
{ name: { contains: query, mode: 'insensitive' } },
{ description: { contains: query, mode: 'insensitive' } }
],
...(category && { category }),
...(minPrice && { price: { gte: minPrice } }),
...(maxPrice && { price: { lte: maxPrice } })
},
take: limit
});
return {
count: results.length,
products: results.map(p => ({
id: p.id,
name: p.name,
price: p.price,
category: p.category
}))
};
}
});
const getWeatherForecast = defineTool({
name: 'getWeatherForecast',
description: 'Get weather forecast for a location for the next 7 days',
parameters: z.object({
location: z.string().describe('City name or coordinates (lat,lon)'),
units: z.enum(['metric', 'imperial']).default('metric')
}),
execute: async ({ location, units }) => {
const apiKey = process.env.WEATHER_API_KEY;
const response = await fetch(
`https://api.weather.com/forecast?location=${encodeURIComponent(location)}&units=${units}&key=${apiKey}`
);
if (!response.ok) {
throw new Error(`Weather API error: ${response.statusText}`);
}
const data = await response.json();
return {
location: data.location,
forecast: data.daily.slice(0, 7).map(day => ({
date: day.date,
temperature: {
high: day.temp.max,
low: day.temp.min
},
conditions: day.weather[0].description,
precipitation: day.pop
}))
};
}
});
const readFile = defineTool({
name: 'readFile',
description: 'Read contents of a file from the project directory',
parameters: z.object({
path: z.string().describe('Relative path to the file'),
encoding: z.enum(['utf8', 'base64']).default('utf8')
}),
execute: async ({ path, encoding }) => {
const fs = require('fs').promises;
const pathModule = require('path');
// Security: Ensure path is within project directory
const safePath = pathModule.join(process.cwd(), path);
if (!safePath.startsWith(process.cwd())) {
throw new Error('Path must be within project directory');
}
const content = await fs.readFile(safePath, encoding);
return {
path,
content,
encoding,
size: content.length
};
}
});
const createUser = defineTool({
name: 'createUser',
description: 'Create a new user account with validation',
parameters: z.object({
email: z.string().email().describe('User email address'),
name: z.string().min(2).max(100).describe('Full name'),
age: z.number().int().min(13).max(120).describe('User age'),
preferences: z.object({
newsletter: z.boolean().default(false),
notifications: z.boolean().default(true),
theme: z.enum(['light', 'dark', 'auto']).default('auto')
}).optional()
}),
execute: async ({ email, name, age, preferences }) => {
// Check if email already exists
const existingUser = await db.users.findUnique({
where: { email }
});
if (existingUser) {
return {
success: false,
error: 'Email already registered'
};
}
// Create user
const user = await db.users.create({
data: {
email,
name,
age,
preferences: preferences || {}
}
});
return {
success: true,
user: {
id: user.id,
email: user.email,
name: user.name
}
};
}
});
class DatabaseService {
async search(query: string) { /* ... */ }
}
function createSearchTool(db: DatabaseService) {
return defineTool({
name: 'searchDatabase',
description: 'Search the database',
parameters: z.object({
query: z.string()
}),
execute: async ({ query }) => {
return await db.search(query);
}
});
}
// Usage
const db = new DatabaseService();
const searchTool = createSearchTool(db);
const agent = new BuiltInAgent({
model: 'openai/gpt-4o',
apiKey: process.env.OPENAI_API_KEY,
tools: [searchTool]
});
import { BuiltInAgent, defineTool } from '@copilotkit/agent';
import { z } from 'zod';
const tools = [
defineTool({
name: 'getInventory',
description: 'Get current inventory levels',
parameters: z.object({
productId: z.string()
}),
execute: async ({ productId }) => {
return await inventory.get(productId);
}
}),
defineTool({
name: 'updateInventory',
description: 'Update inventory quantity',
parameters: z.object({
productId: z.string(),
quantity: z.number().int().positive()
}),
execute: async ({ productId, quantity }) => {
return await inventory.update(productId, quantity);
}
})
];
const agent = new BuiltInAgent({
model: 'openai/gpt-4o',
apiKey: process.env.OPENAI_API_KEY,
prompt: 'You are an inventory management assistant.',
tools,
maxSteps: 5 // Allow multiple tool calls
});
Type Safety
defineTool provides full TypeScript type inference:
const tool = defineTool({
name: 'example',
description: 'Example tool',
parameters: z.object({
name: z.string(),
age: z.number()
}),
execute: async ({ name, age }) => {
// TypeScript knows:
// - name is string
// - age is number
return { name, age };
}
});
// Type error if parameters don't match:
execute: async ({ name, age, invalid }) => {
// ^^^^^^^
// Error: Object literal may only specify known properties
}
Error Handling
Errors thrown in execute are caught and returned as error responses:
const tool = defineTool({
name: 'riskyOperation',
description: 'Operation that might fail',
parameters: z.object({
id: z.string()
}),
execute: async ({ id }) => {
const item = await database.findById(id);
if (!item) {
throw new Error('Item not found');
}
if (!item.available) {
throw new Error('Item is not available');
}
return item;
}
});
// Errors are caught and returned as:
// { error: 'Item not found' }
Validation
Parameters are automatically validated against the Zod schema:
const tool = defineTool({
name: 'validateInput',
description: 'Tool with validation',
parameters: z.object({
email: z.string().email(),
age: z.number().int().min(0).max(120)
}),
execute: async ({ email, age }) => {
// If we reach here, validation passed
return { email, age };
}
});
// Invalid input like { email: "invalid", age: -5 }
// will be rejected before execute() is called
Best Practices
- Clear descriptions: Help the LLM understand when to use the tool
- Specific parameters: Use Zod’s rich validation features
- Add descriptions to parameters: Use
.describe() for better LLM understanding
- Return structured data: Return objects, not primitive values
- Handle errors gracefully: Throw descriptive errors
- Keep tools focused: One tool should do one thing well
- Use TypeScript: Leverage type inference for safety
Common Patterns
Optional Parameters with Defaults
parameters: z.object({
query: z.string(),
limit: z.number().default(10),
offset: z.number().default(0)
})
Enums for Fixed Values
parameters: z.object({
status: z.enum(['pending', 'approved', 'rejected']),
priority: z.enum(['low', 'medium', 'high']).default('medium')
})
Nested Objects
parameters: z.object({
user: z.object({
name: z.string(),
email: z.string().email()
}),
settings: z.object({
theme: z.enum(['light', 'dark']),
notifications: z.boolean()
})
})
Arrays
parameters: z.object({
tags: z.array(z.string()).min(1).max(10),
ids: z.array(z.number().int()).optional()
})
Union Types
parameters: z.object({
identifier: z.union([
z.string().uuid(),
z.number().int().positive()
])
})
See Also