Building Custom Tools

Master tool design, implementation, testing, and deployment for production AI agents

From Design to Code: Implementation Patterns

With a solid design in hand, implementation becomes straightforward. Follow these patterns to build robust, maintainable, and production-ready tools.

Interactive: Complete Tool Implementation

// Define tool interface
interface GetWeatherTool {
  name: 'get_weather'
  description: 'Get current weather for a city'
  parameters: {
    city: string
    units?: 'celsius' | 'fahrenheit'
  }
}

// Implement tool function
async function getWeather(params: GetWeatherTool['parameters']) {
  // 1. Validate input
  if (!params.city || params.city.trim() === '') {
    throw new Error('City parameter is required')
  }
  
  // 2. Call external API
  try {
    const response = await fetch(
      `https://api.weather.com/v1/current?city=${params.city}`
    )
    
    if (!response.ok) {
      throw new Error(`Weather API error: ${response.status}`)
    }
    
    const data = await response.json()
    
    // 3. Transform and return result
    return {
      temperature: data.temp,
      conditions: data.weather,
      humidity: data.humidity,
      units: params.units || 'celsius'
    }
  } catch (error) {
    // 4. Handle errors gracefully
    console.error('Weather fetch failed:', error)
    throw new Error('Unable to fetch weather data')
  }
}

// Register with agent
agent.registerTool({
  name: 'get_weather',
  description: 'Get current weather for any city worldwide',
  execute: getWeather
})

Implementation Checklist

1
Input validation (types, ranges, required fields)
Validation
2
Error handling with try-catch blocks
Error Handling
3
Descriptive error messages for agents
Error Handling
4
Logging for debugging and monitoring
Observability
5
Timeout handling for external calls
Reliability
6
Result transformation to consistent format
Interface
7
Documentation comments
Documentation
8
Type safety with interfaces/types
Type Safety

Essential Patterns

1. Validate Early, Fail Fast
Check all inputs before processing. Return clear error messages that agents can understand.
if (!params.email) throw new Error('email is required')
2. Handle All Error Cases
Network failures, API errors, timeoutsโ€”catch them all and provide meaningful context.
catch (error) { logger.error('Tool failed', error); throw new ToolError('...') }
3. Return Consistent Format
Always return the same structure. Agents rely on predictable output schemas.
return { success: true, data: result, metadata: { timestamp } }
4. Log for Observability
Track invocations, performance, and errors. Essential for debugging production issues.
logger.info('Tool executed', { tool: 'get_weather', duration: 234 })