Tools API Reference
Reference documentation for Umwelten's simplified tool calling system.
Overview
The tool calling system is built on Vercel AI SDK's tool()
helper, providing a streamlined approach to creating and using tools with AI models.
Core Components
Simple Registry
The tool registry manages available tools using a simple Map-based approach.
import { toolRegistry, registerTool, getTool, listTools, getAllTools } from '../stimulus/tools/simple-registry.js';
toolRegistry
Global registry instance for managing tools.
class SimpleToolRegistry {
private tools = new Map<string, any>();
register(name: string, toolDefinition: any): void
get(name: string): any | undefined
list(): string[]
getAll(): Record<string, any>
clear(): void
}
registerTool(name: string, toolDefinition: any): void
Register a tool with the global registry.
import { registerTool } from '../stimulus/tools/simple-registry.js';
import { myTool } from './my-tool.js';
registerTool('myTool', myTool);
getTool(name: string): any | undefined
Retrieve a tool by name.
import { getTool } from '../stimulus/tools/simple-registry.js';
const calculator = getTool('calculator');
if (calculator) {
const result = await calculator.execute({ operation: 'add', a: 5, b: 3 });
}
listTools(): string[]
Get a list of all registered tool names.
import { listTools } from '../stimulus/tools/simple-registry.js';
const toolNames = listTools();
console.log('Available tools:', toolNames);
// Output: ['calculator', 'statistics', 'randomNumber']
getAllTools(): Record<string, any>
Get all registered tools as an object.
import { getAllTools } from '../stimulus/tools/simple-registry.js';
const tools = getAllTools();
console.log('Tool count:', Object.keys(tools).length);
Tool Definition
Creating Tools
Tools are defined using Vercel AI SDK's tool()
helper:
import { tool } from 'ai';
import { z } from 'zod';
const myTool = tool({
description: "Human-readable description of what the tool does",
inputSchema: z.object({
// Define your input parameters using Zod
param1: z.string().describe("Description of parameter 1"),
param2: z.number().describe("Description of parameter 2"),
}),
execute: async (params) => {
// Your tool logic here
console.log(`[TOOL_NAME] Called with:`, params);
// Process parameters and return result
return {
result: "Tool execution result",
metadata: {
success: true,
timestamp: new Date().toISOString()
}
};
},
});
Input Schema Design
Design flexible input schemas to handle various parameter formats:
// Support multiple parameter formats
const flexibleSchema = z.object({
numbers: z.array(z.number()).describe("Array of numbers")
}).or(z.object({
data: z.array(z.number()).describe("Array of numbers")
})).or(z.object({
values: z.array(z.number()).describe("Array of numbers")
}));
// Handle optional parameters
const optionalSchema = z.object({
required: z.string().describe("Required parameter"),
optional: z.number().optional().describe("Optional parameter"),
withDefault: z.boolean().default(false).describe("Parameter with default")
});
Tool Execution
The execute
function receives validated parameters and returns results:
execute: async (params) => {
// Parameters are already validated by Zod
const { operation, a, b } = params;
// Perform tool logic
let result;
switch (operation) {
case 'add': result = a + b; break;
case 'subtract': result = a - b; break;
case 'multiply': result = a * b; break;
case 'divide':
if (b === 0) throw new Error("Division by zero");
result = a / b;
break;
default: throw new Error(`Unknown operation: ${operation}`);
}
// Return structured result
return {
operation,
operands: [a, b],
result,
expression: `${a} ${getOperationSymbol(operation)} ${b} = ${result}`
};
}
Built-in Tools
Calculator Tool
Name: calculator
Description: Performs basic arithmetic operations (add, subtract, multiply, divide)
Input Schema:
const calculatorSchema = z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]),
numbers: z.array(z.number()).length(2)
}).or(z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]),
a: z.number(),
b: z.number()
})).or(z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]),
num1: z.number(),
num2: z.number()
})).or(z.object({
expression: z.string()
}));
Usage:
const calculator = getTool('calculator');
const result = await calculator.execute({
operation: 'add',
a: 5,
b: 3
});
// Returns: { operation: 'add', operands: [5, 3], result: 8, expression: '5 + 3 = 8' }
Statistics Tool
Name: statistics
Description: Calculates basic statistics (mean, median, mode, standard deviation) for a list of numbers
Input Schema:
const statisticsSchema = z.object({
numbers: z.array(z.number()).min(1)
}).or(z.object({
data: z.array(z.number()).min(1)
}));
Usage:
const statistics = getTool('statistics');
const result = await statistics.execute({
numbers: [1, 2, 3, 4, 5]
});
// Returns: { count: 5, mean: 3, median: 3, mode: null, standardDeviation: 1.414214, ... }
Random Number Tool
Name: randomNumber
Description: Generates a random number within a specified range
Input Schema:
const randomNumberSchema = z.object({
min: z.number(),
max: z.number(),
integer: z.boolean().default(false)
}).or(z.object({
min: z.number(),
max: z.number()
}));
Usage:
const randomNumber = getTool('randomNumber');
const result = await randomNumber.execute({
min: 1,
max: 100,
integer: true
});
// Returns: { value: 73, range: { min: 1, max: 100 }, type: "integer" }
CLI Integration
Tools Command
List available tools:
umwelten tools list
Run interactive demo:
umwelten tools demo
umwelten tools demo --prompt "Calculate 25 * 4, then generate a random number"
umwelten tools demo --max-steps 3
Chat Integration
Use tools in chat sessions:
umwelten chat --provider ollama --model qwen3:latest --tools calculator,statistics
Integration with Interaction System
Setting Tools
import { Interaction } from '../interaction/interaction.js';
import { getAllTools } from '../stimulus/tools/simple-registry.js';
const interaction = new Interaction(modelDetails, systemPrompt);
const tools = getAllTools();
interaction.setTools(tools);
Tool Execution Flow
- Tool Registration: Tools are registered when imported
- Tool Discovery: CLI discovers tools via
getTool()
function - Tool Execution: Models call tools through Vercel AI SDK
- Result Processing: Tool results are returned to the model
Error Handling
Validation Errors
Zod automatically validates input parameters:
// Invalid parameters throw ZodError
try {
await calculator.execute({ operation: 'add', a: 'invalid', b: 3 });
} catch (error) {
if (error instanceof z.ZodError) {
console.error('Validation error:', error.errors);
}
}
Execution Errors
Handle errors in tool execution:
execute: async (params) => {
try {
// Tool logic
return result;
} catch (error) {
// Return error information
return {
error: error instanceof Error ? error.message : String(error),
success: false
};
}
}
Best Practices
Tool Design
- Clear Descriptions: Write descriptive tool descriptions
- Flexible Schemas: Support multiple input formats
- Error Handling: Graceful error handling and feedback
- Logging: Include logging for debugging
- Validation: Use Zod for robust parameter validation
Performance
- Async Operations: Use async/await for I/O operations
- Caching: Cache expensive operations when appropriate
- Resource Management: Clean up resources properly
- Timeout Handling: Implement timeouts for external calls
Security
- Input Validation: Always validate and sanitize inputs
- Rate Limiting: Implement rate limiting for external APIs
- Error Messages: Don't expose sensitive information in errors
- Access Control: Implement appropriate access controls
Examples
Custom Weather Tool
import { tool } from 'ai';
import { z } from 'zod';
import { registerTool } from '../stimulus/tools/simple-registry.js';
const weatherSchema = z.object({
city: z.string().describe("City name to get weather for"),
units: z.enum(["celsius", "fahrenheit"]).default("celsius")
});
export const weatherTool = tool({
description: "Get current weather information for a city",
inputSchema: weatherSchema,
execute: async (params) => {
console.log(`[WEATHER] Called with:`, params);
// Simulate API call
const { city, units } = params;
const temperature = Math.random() * 30 + 10;
const condition = ["sunny", "cloudy", "rainy"][Math.floor(Math.random() * 3)];
return {
city,
temperature: Math.round(temperature),
condition,
units,
timestamp: new Date().toISOString()
};
},
});
registerTool('weather', weatherTool);
File Analysis Tool
import { tool } from 'ai';
import { z } from 'zod';
import { registerTool } from '../stimulus/tools/simple-registry.js';
import fs from 'fs/promises';
const fileSchema = z.object({
path: z.string().describe("Path to the file to analyze"),
analysis: z.enum(["size", "content", "metadata"]).default("content")
});
export const fileAnalysisTool = tool({
description: "Analyze file content, size, or metadata",
inputSchema: fileSchema,
execute: async (params) => {
console.log(`[FILE_ANALYSIS] Called with:`, params);
const { path, analysis } = params;
try {
const stats = await fs.stat(path);
if (analysis === 'size') {
return {
path,
size: stats.size,
sizeInKB: Math.round(stats.size / 1024),
lastModified: stats.mtime.toISOString()
};
}
if (analysis === 'content') {
const content = await fs.readFile(path, 'utf-8');
return {
path,
content: content.substring(0, 1000) + (content.length > 1000 ? '...' : ''),
length: content.length,
lines: content.split('\n').length
};
}
return {
path,
exists: true,
isFile: stats.isFile(),
isDirectory: stats.isDirectory(),
permissions: stats.mode.toString(8)
};
} catch (error) {
return {
path,
error: error instanceof Error ? error.message : String(error),
exists: false
};
}
},
});
registerTool('fileAnalysis', fileAnalysisTool);
Migration from Old System
The new simplified system replaces the complex ToolDefinition
interface:
Before (Complex)
interface ToolDefinition<TParameters extends z.ZodSchema> {
name: string;
description: string;
parameters: TParameters;
execute: (args: z.infer<TParameters>, context: ToolExecutionContext) => Promise<ToolExecutionResult>;
metadata?: { category?: string; tags?: string[]; version?: string; experimental?: boolean; };
}
After (Simple)
const myTool = tool({
description: "Tool description",
inputSchema: z.object({ /* parameters */ }),
execute: async (params) => { /* logic */ }
});
Key Changes
- No custom interface: Use Vercel AI SDK's
tool()
helper directly - Simplified registration: Just call
registerTool(name, tool)
- Direct execution: No conversion layers or complex contexts
- Built-in validation: Zod schemas handle validation automatically
- Streamlined API: Fewer abstractions, more direct usage