Overview
ThedefineTool() function creates a tool definition with full type inference. Tools are functions that agents can invoke during execution to perform actions like API calls, database queries, calculations, or any other programmatic task.
Signature
function defineTool<TData = unknown>(
definition: ToolDefinition<TData>
): ToolDefinition<TData>
Parameters
Tool definition object containing schema and execute function:
Show ToolDefinition Properties
Show ToolDefinition Properties
Show ToolSchema Properties
Show ToolSchema Properties
Async function that executes the tool’s logic.
args: Parsed arguments matching the parameters schemactx: Execution context with access to state, data, memory, and events
Returns
The same tool definition with proper type inference
Type Parameters
Type of custom state data accessible via
ctx.data in the execute functionUsage Examples
Basic Tool
import { defineTool } from '@agentlib/core'
const getCurrentTime = defineTool({
schema: {
name: 'getCurrentTime',
description: 'Get the current time in a specific timezone',
parameters: {
type: 'object',
properties: {
timezone: {
type: 'string',
description: 'IANA timezone identifier (e.g., America/New_York)',
default: 'UTC'
}
},
required: []
}
},
execute: async (args) => {
const timezone = args.timezone as string || 'UTC'
return new Date().toLocaleString('en-US', { timeZone: timezone })
}
})
Tool with Multiple Parameters
const calculator = defineTool({
schema: {
name: 'calculate',
description: 'Perform arithmetic operations on two numbers',
parameters: {
type: 'object',
properties: {
operation: {
type: 'string',
enum: ['add', 'subtract', 'multiply', 'divide'],
description: 'The arithmetic operation to perform'
},
a: {
type: 'number',
description: 'First operand'
},
b: {
type: 'number',
description: 'Second operand'
}
},
required: ['operation', 'a', 'b']
}
},
execute: async (args) => {
const { operation, a, b } = args as { operation: string; a: number; b: number }
switch (operation) {
case 'add': return a + b
case 'subtract': return a - b
case 'multiply': return a * b
case 'divide':
if (b === 0) throw new Error('Division by zero')
return a / b
default:
throw new Error(`Unknown operation: ${operation}`)
}
}
})
Tool with External API Call
import fetch from 'node-fetch'
const searchWeb = defineTool({
schema: {
name: 'searchWeb',
description: 'Search the web for information',
parameters: {
type: 'object',
properties: {
query: {
type: 'string',
description: 'Search query'
},
limit: {
type: 'number',
description: 'Maximum number of results',
default: 5,
minimum: 1,
maximum: 10
}
},
required: ['query']
}
},
execute: async (args) => {
const { query, limit = 5 } = args as { query: string; limit?: number }
const response = await fetch(
`https://api.search.example.com/search?q=${encodeURIComponent(query)}&limit=${limit}`,
{ headers: { 'Authorization': `Bearer ${process.env.SEARCH_API_KEY}` } }
)
if (!response.ok) {
throw new Error(`Search API error: ${response.statusText}`)
}
const data = await response.json()
return data.results
}
})
Tool with Typed State Access
interface UserContext {
userId: string
preferences: {
language: string
timezone: string
}
}
const getUserPreferences = defineTool<UserContext>({
schema: {
name: 'getUserPreferences',
description: 'Retrieve user preferences from context',
parameters: {
type: 'object',
properties: {},
required: []
}
},
execute: async (args, ctx) => {
// ctx.data is typed as UserContext
return {
userId: ctx.data.userId,
language: ctx.data.preferences.language,
timezone: ctx.data.preferences.timezone
}
}
})
Tool that Emits Events
const processData = defineTool({
schema: {
name: 'processData',
description: 'Process a large dataset',
parameters: {
type: 'object',
properties: {
datasetId: { type: 'string' }
},
required: ['datasetId']
}
},
execute: async (args, ctx) => {
const { datasetId } = args as { datasetId: string }
ctx.emit('processing:started', { datasetId })
// Simulate processing
for (let i = 0; i < 100; i += 10) {
await new Promise(resolve => setTimeout(resolve, 100))
ctx.emit('processing:progress', { datasetId, progress: i })
}
ctx.emit('processing:completed', { datasetId })
return { success: true, recordsProcessed: 1000 }
}
})
Tool with Database Access
import { Pool } from 'pg'
const dbPool = new Pool({ connectionString: process.env.DATABASE_URL })
const queryDatabase = defineTool({
schema: {
name: 'queryDatabase',
description: 'Execute a safe SELECT query on the database',
parameters: {
type: 'object',
properties: {
table: {
type: 'string',
enum: ['users', 'orders', 'products'],
description: 'Table to query'
},
filters: {
type: 'object',
description: 'Key-value filters',
additionalProperties: true
},
limit: {
type: 'number',
default: 10,
maximum: 100
}
},
required: ['table']
}
},
execute: async (args) => {
const { table, filters = {}, limit = 10 } = args as {
table: string
filters?: Record<string, any>
limit?: number
}
const whereClauses = Object.entries(filters)
.map(([key, _], idx) => `${key} = $${idx + 1}`)
.join(' AND ')
const query = `
SELECT * FROM ${table}
${whereClauses ? `WHERE ${whereClauses}` : ''}
LIMIT $${Object.keys(filters).length + 1}
`
const values = [...Object.values(filters), limit]
const result = await dbPool.query(query, values)
return result.rows
}
})
Tool with Complex Return Type
interface WeatherData {
location: string
temperature: number
conditions: string
humidity: number
wind: { speed: number; direction: string }
forecast: Array<{ day: string; high: number; low: number }>
}
const getWeather = defineTool({
schema: {
name: 'getWeather',
description: 'Get detailed weather information for a location',
parameters: {
type: 'object',
properties: {
location: {
type: 'string',
description: 'City name or coordinates'
},
includeForecast: {
type: 'boolean',
description: 'Include 5-day forecast',
default: false
}
},
required: ['location']
}
},
execute: async (args): Promise<WeatherData> => {
const { location, includeForecast = false } = args as {
location: string
includeForecast?: boolean
}
// Fetch from weather API
const weatherData: WeatherData = {
location,
temperature: 22,
conditions: 'Partly cloudy',
humidity: 65,
wind: { speed: 15, direction: 'NW' },
forecast: includeForecast ? [
{ day: 'Monday', high: 24, low: 18 },
{ day: 'Tuesday', high: 26, low: 19 }
] : []
}
return weatherData
}
})
Tool with Error Handling
const sendEmail = defineTool({
schema: {
name: 'sendEmail',
description: 'Send an email to a recipient',
parameters: {
type: 'object',
properties: {
to: {
type: 'string',
format: 'email',
description: 'Recipient email address'
},
subject: {
type: 'string',
description: 'Email subject'
},
body: {
type: 'string',
description: 'Email body content'
}
},
required: ['to', 'subject', 'body']
}
},
execute: async (args, ctx) => {
const { to, subject, body } = args as {
to: string
subject: string
body: string
}
try {
// Validate email format
const emailRegex = /^[^\s@]+@[^\s@]+\.[^\s@]+$/
if (!emailRegex.test(to)) {
throw new Error(`Invalid email address: ${to}`)
}
// Send email via API
ctx.emit('email:sending', { to, subject })
// await emailService.send({ to, subject, body })
ctx.emit('email:sent', { to, subject })
return {
success: true,
messageId: 'msg-' + Date.now(),
timestamp: new Date().toISOString()
}
} catch (error) {
ctx.emit('email:error', { to, error })
throw new Error(`Failed to send email: ${(error as Error).message}`)
}
}
})
Using Tools with Agents
import { createAgent, defineTool } from '@agentlib/core'
import { openai } from '@agentlib/openai'
const tools = [
getCurrentTime,
calculator,
searchWeb,
getWeather
]
const agent = createAgent({
name: 'multi-tool-agent',
model: openai(),
tools,
reasoning: 'react'
})
const result = await agent.run('What is the weather in Paris and what time is it there?')
console.log(result.output)
Parameter Schema Guidelines
Theparameters field must be a valid JSON Schema object:
parameters: {
type: 'object', // Must be 'object'
properties: {
paramName: {
type: 'string' | 'number' | 'boolean' | 'array' | 'object',
description: 'Clear description for the LLM',
// Optional constraints:
enum: ['option1', 'option2'], // For string/number
minimum: 0, // For number
maximum: 100, // For number
minLength: 1, // For string
maxLength: 500, // For string
pattern: '^[a-z]+$', // For string (regex)
format: 'email' | 'uri' | 'date', // For string
items: { type: 'string' }, // For array
default: 'default value' // Default if not provided
}
},
required: ['paramName'], // Required parameters
additionalProperties: false // Reject extra properties
}
Best Practices
- Clear descriptions: LLMs use descriptions to decide when and how to use tools
- Type safety: Use TypeScript types for
argsin execute function - Error handling: Throw descriptive errors for invalid inputs or failures
- Return serializable data: Ensure return values can be JSON-stringified
- Use constraints: Add min/max, enums, patterns to validate inputs
- Emit events: Use
ctx.emit()for progress tracking and debugging - Access context: Use
ctx.datafor user-specific state - Keep tools focused: Each tool should do one thing well
See Also
- createAgent() - Create agents with tools
- AgentInstance - Agent instance methods
- ExecutionContext - Context available in tools