Skip to main content
Tools give agents the ability to interact with external systems, perform calculations, and access data beyond their training. This guide covers everything you need to know about defining and using tools.

Defining Tools

Use defineTool() to create a tool definition:
import { defineTool } from '@agentlib/core'

const searchTool = defineTool({
  schema: {
    name: 'search',
    description: 'Search the web for information',
    parameters: {
      type: 'object',
      properties: {
        query: { type: 'string', description: 'Search query' },
      },
      required: ['query'],
    },
  },
  async execute({ query }) {
    // Call search API
    return { results: [`Result for: ${query}`] }
  },
})

Tool Schema

The schema follows JSON Schema format and is sent to the LLM:
const calculatorTool = defineTool({
  schema: {
    name: 'calculator',
    description: 'Evaluate a mathematical expression',
    parameters: {
      type: 'object',
      properties: {
        expression: {
          type: 'string',
          description: 'Math expression to evaluate (e.g., "2 + 2")',
        },
        precision: {
          type: 'number',
          description: 'Decimal places for the result',
        },
      },
      required: ['expression'],
    },
  },
  async execute({ expression, precision = 2 }) {
    const result = eval(String(expression))
    return { 
      result: Number(result.toFixed(precision)),
      expression 
    }
  },
})
Never use eval() in production. Use a safe math parser like mathjs instead.

Adding Tools to Agents

1

Register Individual Tools

Add tools one at a time using .tool():
import { createAgent } from '@agentlib/core'
import { openai } from '@agentlib/openai'

const agent = createAgent({ name: 'assistant' })
  .provider(openai({ apiKey: process.env.OPENAI_API_KEY }))
  .tool(searchTool)
  .tool(calculatorTool)
2

Or Define Tools Inline

You can define tools directly when configuring the agent:
const agent = createAgent({
  name: 'assistant',
  tools: [searchTool, calculatorTool],
})
  .provider(model)

Accessing Execution Context

Tools have access to the execution context including agent data:
interface AppData {
  userId: string
  apiKey: string
}

const databaseTool = defineTool({
  schema: {
    name: 'query_database',
    description: 'Query the user database',
    parameters: {
      type: 'object',
      properties: {
        sql: { type: 'string', description: 'SQL query' },
      },
      required: ['sql'],
    },
  },
  async execute({ sql }, ctx) {
    // Access agent data from context
    const userId = ctx.data.userId
    const apiKey = ctx.data.apiKey
    
    console.log(`User ${userId} querying database`)
    
    // Execute query with user credentials
    const results = await db.query(sql, { userId, apiKey })
    return results
  },
})

const agent = createAgent<AppData>({
  name: 'db-agent',
  data: { userId: 'user-123', apiKey: 'sk-...' },
})
  .provider(model)
  .tool(databaseTool)

Complex Tool Examples

File Operations

import fs from 'fs/promises'
import path from 'path'

const writeFileTool = defineTool({
  schema: {
    name: 'write_file',
    description: 'Write content to a file',
    parameters: {
      type: 'object',
      properties: {
        path: { type: 'string', description: 'File path' },
        content: { type: 'string', description: 'File content' },
      },
      required: ['path', 'content'],
    },
  },
  async execute({ path: filePath, content }) {
    await fs.writeFile(filePath, content, 'utf-8')
    return { success: true, path: filePath }
  },
})

const readFileTool = defineTool({
  schema: {
    name: 'read_file',
    description: 'Read the contents of a file',
    parameters: {
      type: 'object',
      properties: {
        path: { type: 'string', description: 'File path' },
      },
      required: ['path'],
    },
  },
  async execute({ path: filePath }) {
    const content = await fs.readFile(filePath, 'utf-8')
    return { content, path: filePath }
  },
})

API Calls

const fetchDataTool = defineTool({
  schema: {
    name: 'fetch_data',
    description: 'Fetch data from an API endpoint',
    parameters: {
      type: 'object',
      properties: {
        url: { type: 'string', description: 'API URL' },
        method: { 
          type: 'string', 
          enum: ['GET', 'POST', 'PUT', 'DELETE'],
          description: 'HTTP method' 
        },
        body: { type: 'object', description: 'Request body for POST/PUT' },
      },
      required: ['url'],
    },
  },
  async execute({ url, method = 'GET', body }) {
    const response = await fetch(url, {
      method,
      headers: { 'Content-Type': 'application/json' },
      body: body ? JSON.stringify(body) : undefined,
    })
    
    const data = await response.json()
    return { data, status: response.status }
  },
})

Database Operations

import { sql } from '@vercel/postgres'

const queryTool = defineTool({
  schema: {
    name: 'query_users',
    description: 'Query user information from the database',
    parameters: {
      type: 'object',
      properties: {
        userId: { type: 'string', description: 'User ID to query' },
      },
      required: ['userId'],
    },
  },
  async execute({ userId }) {
    const result = await sql`
      SELECT * FROM users WHERE id = ${userId}
    `
    return result.rows[0]
  },
})

Tool Error Handling

Handle errors gracefully in tools:
const riskyTool = defineTool({
  schema: {
    name: 'risky_operation',
    description: 'Perform a potentially failing operation',
    parameters: {
      type: 'object',
      properties: {
        input: { type: 'string' },
      },
      required: ['input'],
    },
  },
  async execute({ input }) {
    try {
      const result = await someRiskyOperation(input)
      return { success: true, result }
    } catch (error) {
      // Return structured error instead of throwing
      return {
        success: false,
        error: error.message,
        suggestion: 'Try a different input format',
      }
    }
  },
})
Returning error information instead of throwing allows the agent to see what went wrong and potentially retry with corrected input.

Restricting Tool Access

Control which tools an agent can use with policies:
const agent = createAgent({ name: 'limited-agent' })
  .provider(model)
  .tool(searchTool)
  .tool(calculatorTool)
  .tool(writeFileTool)
  .policy({
    allowedTools: ['search', 'calculator'], // Only these tools allowed
  })

const result = await agent.run('Search for AI news and write to file.txt')
// The agent will use search and calculator, but cannot use write_file

Observing Tool Calls

Monitor tool usage with event listeners:
agent.on('tool:before', ({ name, args }) => {
  console.log(`[TOOL] Calling ${name} with:`, args)
})

agent.on('tool:after', ({ name, result, error }) => {
  if (error) {
    console.error(`[TOOL] ${name} failed:`, error)
  } else {
    console.log(`[TOOL] ${name} returned:`, result)
  }
})

const result = await agent.run('What is 2+2 and search for AI news?')

Complete Tool Example

import 'dotenv/config'
import { createAgent, defineTool } from '@agentlib/core'
import { openai } from '@agentlib/openai'

const searchTool = defineTool({
  schema: {
    name: 'search',
    description: 'Search the web for information',
    parameters: {
      type: 'object',
      properties: {
        query: { type: 'string' },
      },
      required: ['query'],
    },
  },
  async execute({ query }) {
    return { results: [`Result for: ${query}`] }
  },
})

const calculatorTool = defineTool({
  schema: {
    name: 'calculator',
    description: 'Evaluate a math expression',
    parameters: {
      type: 'object',
      properties: {
        expression: { type: 'string' },
      },
      required: ['expression'],
    },
  },
  async execute({ expression }) {
    return { result: eval(String(expression)) }
  },
})

const agent = createAgent({ name: 'react-agent' })
  .provider(openai({
    apiKey: process.env.OPENAI_API_KEY,
    model: 'gpt-4o',
  }))
  .tool(searchTool)
  .tool(calculatorTool)

agent.on('tool:after', ({ name, result }) => {
  console.log(`✓ ${name}:`, result)
})

const result = await agent.run(
  'What is the population of Tokyo multiplied by 2?'
)

console.log('\nFinal answer:', result.output)

Next Steps

Build docs developers (and LLMs) love