Simple Functions
Basic Function Tool
Create a tool from a simple function:Copy
import { tool } from '@ag-kit/tools';
import { z } from 'zod';
const addNumbersTool = tool(
async ({ a, b }) => {
return ToolResult({
success: true,
data: { result: a + b }
});
},
{
name: 'add_numbers',
description: 'Add two numbers together',
schema: z.object({
a: z.number().describe('First number'),
b: z.number().describe('Second number')
})
}
);
String Processing Tool
Create tools for text processing:Copy
const textProcessTool = tool(
async ({ text, operation, options = {} }) => {
let processedText = text;
if (options.trim) {
processedText = processedText.trim();
}
if (options.removeSpaces) {
processedText = processedText.replace(/\s+/g, '');
}
let result;
switch (operation) {
case 'uppercase':
result = processedText.toUpperCase();
break;
case 'lowercase':
result = processedText.toLowerCase();
break;
case 'reverse':
result = processedText.split('').reverse().join('');
break;
case 'word_count':
result = processedText.split(/\s+/).length;
break;
}
return ToolResult({
success: true,
data: {
original: text,
processed: result,
operation
}
});
},
{
name: 'process_text',
description: 'Process text with various operations',
schema: z.object({
text: z.string().describe('Input text'),
operation: z.enum(['uppercase', 'lowercase', 'reverse', 'word_count']),
options: z.object({
trim: z.boolean().default(true),
removeSpaces: z.boolean().default(false)
}).optional()
})
}
);
Async Operations
HTTP Request Tool
Create tools that make HTTP requests:Copy
const httpRequestTool = tool({
name: 'http_request',
description: 'Make HTTP requests to external APIs',
schema: z.object({
url: z.string().url(),
method: z.enum(['GET', 'POST', 'PUT', 'DELETE']).default('GET'),
headers: z.record(z.string()).optional(),
body: z.any().optional(),
timeout: z.number().default(30000)
}),
invoke: async ({ url, method, headers, body, timeout }) => {
try {
const controller = new AbortController();
const timeoutId = setTimeout(() => controller.abort(), timeout);
const response = await fetch(url, {
method,
headers: {
'Content-Type': 'application/json',
...headers
},
body: body ? JSON.stringify(body) : undefined,
signal: controller.signal
});
clearTimeout(timeoutId);
if (!response.ok) {
return ToolResult({
success: false,
error: `HTTP ${response.status}: ${response.statusText}`,
error_type: 'network'
});
}
const data = await response.json();
return ToolResult({
success: true,
data: {
status: response.status,
headers: Object.fromEntries(response.headers.entries()),
body: data
}
});
} catch (error) {
if (error.name === 'AbortError') {
return ToolResult({
success: false,
error: 'Request timeout',
error_type: 'network'
});
}
return ToolResult({
success: false,
error: error.message,
error_type: 'execution'
});
}
}
});
Complex Parameters
Validation and Transformation
Create tools with complex validation:Copy
const userManagementTool = tool({
name: 'manage_user',
description: 'Manage user accounts with validation',
schema: z.object({
action: z.enum(['create', 'update', 'delete', 'get']),
userId: z.string().uuid().optional(),
userData: z.object({
email: z.string().email(),
name: z.string().min(2).max(50),
age: z.number().min(13).max(120),
roles: z.array(z.enum(['admin', 'user', 'moderator'])).default(['user']),
preferences: z.object({
theme: z.enum(['light', 'dark']).default('light'),
notifications: z.boolean().default(true),
language: z.string().length(2).default('en')
}).optional()
}).optional()
}).refine(
(data) => {
if (data.action === 'create' && !data.userData) {
return false;
}
if (['update', 'delete', 'get'].includes(data.action) && !data.userId) {
return false;
}
return true;
},
{
message: 'userData required for create, userId required for update/delete/get'
}
),
invoke: async ({ action, userId, userData }) => {
try {
switch (action) {
case 'create':
const newUser = {
id: crypto.randomUUID(),
...userData,
createdAt: new Date().toISOString()
};
// Simulate database save
await saveUser(newUser);
return ToolResult({
success: true,
data: { user: newUser, message: 'User created successfully' }
});
case 'update':
const existingUser = await getUser(userId);
if (!existingUser) {
return ToolResult({
success: false,
error: 'User not found',
error_type: 'execution'
});
}
const updatedUser = {
...existingUser,
...userData,
updatedAt: new Date().toISOString()
};
await saveUser(updatedUser);
return ToolResult({
success: true,
data: { user: updatedUser, message: 'User updated successfully' }
});
case 'delete':
await deleteUser(userId);
return ToolResult({
success: true,
data: { message: 'User deleted successfully' }
});
case 'get':
const user = await getUser(userId);
if (!user) {
return ToolResult({
success: false,
error: 'User not found',
error_type: 'execution'
});
}
return ToolResult({
success: true,
data: { user }
});
default:
return ToolResult({
success: false,
error: 'Invalid action',
error_type: 'validation'
});
}
} catch (error) {
return ToolResult({
success: false,
error: error.message,
error_type: 'execution'
});
}
}
});
// Mock database functions
async function saveUser(user: any) {
// Implementation here
}
async function getUser(userId: string) {
// Implementation here
}
async function deleteUser(userId: string) {
// Implementation here
}
Error Handling
Comprehensive Error Handling
Implement robust error handling patterns:Copy
const robustApiTool = tool({
name: 'robust_api_call',
description: 'Make API calls with comprehensive error handling',
schema: z.object({
endpoint: z.string().url(),
retries: z.number().min(0).max(5).default(3),
backoffMs: z.number().min(100).max(10000).default(1000)
}),
invoke: async ({ endpoint, retries, backoffMs }) => {
let lastError: Error | null = null;
for (let attempt = 0; attempt <= retries; attempt++) {
try {
const response = await fetch(endpoint, {
timeout: 10000,
headers: {
'User-Agent': 'AG-Kit Tool/1.0'
}
});
if (!response.ok) {
throw new Error(`HTTP ${response.status}: ${response.statusText}`);
}
const data = await response.json();
return ToolResult({
success: true,
data: {
result: data,
attempts: attempt + 1,
endpoint
}
});
} catch (error) {
lastError = error;
// Don't retry on certain errors
if (error.message.includes('404') || error.message.includes('401')) {
break;
}
// Wait before retry (exponential backoff)
if (attempt < retries) {
const delay = backoffMs * Math.pow(2, attempt);
await new Promise(resolve => setTimeout(resolve, delay));
}
}
}
return new ToolResult({
success: false,
error: `Failed after ${retries + 1} attempts: ${lastError?.message}`,
error_type: 'network',
executionTime: Date.now()
});
}
});
Input Sanitization
Sanitize and validate inputs:Copy
const sanitizedInputTool = tool({
name: 'process_user_input',
description: 'Process user input with sanitization',
schema: z.object({
userInput: z.string(),
allowHtml: z.boolean().default(false),
maxLength: z.number().default(1000)
}),
invoke: async ({ userInput, allowHtml, maxLength }) => {
try {
// Length validation
if (userInput.length > maxLength) {
return ToolResult({
success: false,
error: `Input too long (max ${maxLength} characters)`,
error_type: 'validation'
});
}
// Sanitize input
let sanitized = userInput.trim();
if (!allowHtml) {
// Remove HTML tags
sanitized = sanitized.replace(/<[^>]*>/g, '');
// Escape special characters
sanitized = sanitized
.replace(/&/g, '&')
.replace(/</g, '<')
.replace(/>/g, '>')
.replace(/"/g, '"')
.replace(/'/g, ''');
}
// Check for suspicious patterns
const suspiciousPatterns = [
/javascript:/i,
/data:text\/html/i,
/vbscript:/i,
/<script/i
];
const hasSuspiciousContent = suspiciousPatterns.some(
pattern => pattern.test(sanitized)
);
if (hasSuspiciousContent) {
return ToolResult({
success: false,
error: 'Input contains suspicious content',
error_type: 'validation'
});
}
return ToolResult({
success: true,
data: {
original: userInput,
sanitized,
length: sanitized.length,
wasModified: userInput !== sanitized
}
});
} catch (error) {
return ToolResult({
success: false,
error: error.message,
error_type: 'execution'
});
}
}
});
Integration Patterns
Tool Composition
Combine multiple function tools:Copy
// Create individual tools
const validateEmailTool = tool({
name: 'validate_email',
description: 'Validate email address format',
schema: z.object({
email: z.string()
}),
invoke: async ({ email }) => {
const isValid = /^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(email);
return ToolResult({
success: true,
data: { email, isValid }
});
}
});
const sendEmailTool = tool({
name: 'send_email',
description: 'Send email message',
schema: z.object({
to: z.string().email(),
subject: z.string(),
body: z.string()
}),
invoke: async ({ to, subject, body }) => {
// Email sending logic
return ToolResult({
success: true,
data: { messageId: 'msg_123', sentAt: new Date().toISOString() }
});
}
});
// Composite tool that uses both
const emailWorkflowTool = tool({
name: 'email_workflow',
description: 'Validate and send email in one operation',
schema: z.object({
to: z.string(),
subject: z.string(),
body: z.string()
}),
invoke: async ({ to, subject, body }) => {
// Validate email first
const validationResult = await validateEmailTool.invoke({ email: to });
if (!validationResult.success || !validationResult.data.isValid) {
return ToolResult({
success: false,
error: 'Invalid email address',
error_type: 'validation'
});
}
// Send email
const sendResult = await sendEmailTool.invoke({ to, subject, body });
return ToolResult({
success: sendResult.success,
data: {
validation: validationResult.data,
email: sendResult.data
},
error: sendResult.error,
error_type: sendResult.error_type
});
}
});
With Agents
Use function tools with AG-Kit agents:Copy
import { Agent, OpenAIProvider } from '@ag-kit/agents';
const agent = new Agent({
name: 'multi-tool-agent',
model: new OpenAIProvider({
apiKey: process.env.OPENAI_API_KEY!,
defaultModel: 'gpt-4'
}),
tools: [
addNumbersTool,
textProcessTool,
httpRequestTool,
userManagementTool
],
instructions: `You are a helpful assistant with access to various tools:
- Mathematical operations
- Text processing
- HTTP requests
- User management
Use these tools to help users accomplish their tasks.`
});
const response = await agent.run({
input: 'Calculate 15 + 27, then convert the result to uppercase text'
});
Best Practices
Performance Optimization
Optimize function tools for performance:Copy
// Use connection pooling for database tools
class DatabasePool {
private connections: any[] = [];
async getConnection() {
if (this.connections.length > 0) {
return this.connections.pop();
}
return await createNewConnection();
}
releaseConnection(conn: any) {
if (this.connections.length < 10) {
this.connections.push(conn);
} else {
conn.close();
}
}
}
const dbPool = new DatabasePool();
const optimizedDbTool = tool({
name: 'optimized_db_query',
description: 'Database query with connection pooling',
schema: z.object({
query: z.string(),
params: z.array(z.any()).default([])
}),
invoke: async ({ query, params }) => {
const conn = await dbPool.getConnection();
try {
const result = await conn.query(query, params);
return ToolResult({ success: true, data: result });
} finally {
dbPool.releaseConnection(conn);
}
}
});
Caching
Implement caching for expensive operations:Copy
const cache = new Map();
const cachedApiTool = tool({
name: 'cached_api_call',
description: 'API call with caching',
schema: z.object({
url: z.string().url(),
cacheTtl: z.number().default(300000) // 5 minutes
}),
invoke: async ({ url, cacheTtl }) => {
const cached = cache.get(url);
if (cached && Date.now() - cached.timestamp < cacheTtl) {
return ToolResult({
success: true,
data: { ...cached.data, fromCache: true }
});
}
try {
const response = await fetch(url);
const data = await response.json();
cache.set(url, {
data,
timestamp: Date.now()
});
return ToolResult({
success: true,
data: { ...data, fromCache: false }
});
} catch (error) {
return ToolResult({
success: false,
error: error.message,
error_type: 'network'
});
}
}
});