Tool class

The Tool class provides a type-safe way to create tools that agents can use.

Constructor

new Tool<TInput>({
  name: string;
  description: string;
  version?: string;
  inputSchema?: z.ZodType<TInput> | JSONSchema;
  execute: (params: {
    input: TInput;
    context?: ToolContext;
  }) => Promise<ToolResponse>;
  validate?: (input: TInput) => Promise<ValidationResult>;
})

Parameters

  • name (required): Unique identifier for the tool
  • description (required): Clear description of what the tool does
  • version: Semantic version number
  • inputSchema: Schema for input validation (Zod schema or JSON Schema)
  • execute (required): Function that performs the tool’s operation
  • validate: Custom validation function

Types

ToolResponse

interface ToolResponse {
  isError: boolean;
  data: string | Array<ToolResponseBlock>;
  error?: {
    code: string;
    message: string;
    details?: unknown;
  };
  duration?: number;
  tokenUsage?: TokenUsage;
}

interface ToolResponseBlock {
  type: 'text' | 'image';
  text?: string;
  image?: {
    type: 'base64';
    media_type: string;
    data: string;
  };
}

ToolContext

interface ToolContext {
  agentId?: string;
  executionId?: string;
  parentToolExecutionId?: string;
  extraContext?: Record<string, unknown>;
  metadata?: Record<string, unknown>;
}

JSONSchema

interface JSONSchema {
  type: string;
  properties?: Record<string, unknown>;
  required?: string[];
  [k: string]: unknown;
}

ValidationResult

interface ValidationResult {
  isValid: boolean;
  errors?: ValidationError[];
}

interface ValidationError {
  field: string;
  message: string;
}

Examples

Basic tool with JSON Schema

import { Tool } from '@agenite/tool';

interface CalculatorInput {
  operation: 'add' | 'subtract' | 'multiply' | 'divide';
  a: number;
  b: number;
}

const calculatorTool = new Tool<CalculatorInput>({
  name: 'calculator',
  description: 'Perform basic math operations',
  version: '1.0.0',
  inputSchema: {
    type: 'object',
    properties: {
      operation: {
        type: 'string',
        enum: ['add', 'subtract', 'multiply', 'divide'],
      },
      a: { type: 'number' },
      b: { type: 'number' },
    },
    required: ['operation', 'a', 'b'],
  },
  execute: async ({ input }) => {
    try {
      let result: number;
      switch (input.operation) {
        case 'add': result = input.a + input.b; break;
        case 'subtract': result = input.a - input.b; break;
        case 'multiply': result = input.a * input.b; break;
        case 'divide':
          if (input.b === 0) throw new Error('Division by zero');
          result = input.a / input.b;
          break;
      }
      return {
        isError: false,
        data: result.toString(),
      };
    } catch (error) {
      return {
        isError: true,
        data: error.message,
        error: {
          code: 'CALC_ERROR',
          message: error.message,
        },
      };
    }
  },
});

Tool with Zod schema

import { Tool } from '@agenite/tool';
import { z } from 'zod';

const weatherSchema = z.object({
  location: z.object({
    city: z.string(),
    country: z.string().optional(),
    coordinates: z.object({
      lat: z.number(),
      lon: z.number(),
    }).optional(),
  }),
  units: z.enum(['metric', 'imperial']).default('metric'),
});

type WeatherInput = z.infer<typeof weatherSchema>;

const createWeatherTool = (apiKey: string) => {
  const client = new WeatherAPI(apiKey);

  return new Tool<WeatherInput>({
    name: 'weather',
    description: 'Get current weather for a location',
    version: '1.0.0',
    inputSchema: weatherSchema,
    execute: async ({ input }) => {
      try {
        const weather = await client.getWeather(
          input.location,
          input.units
        );
        return {
          isError: false,
          data: JSON.stringify(weather),
        };
      } catch (error) {
        return {
          isError: true,
          data: 'Failed to get weather data',
          error: {
            code: 'WEATHER_ERROR',
            message: error.message,
          },
        };
      }
    },
  });
};

Tool with rich response format

import { Tool } from '@agenite/tool';
import { readFile } from 'fs/promises';

interface FileReadInput {
  path: string;
  format?: 'text' | 'base64';
}

const fileReaderTool = new Tool<FileReadInput>({
  name: 'file-reader',
  description: 'Read the contents of a file',
  version: '1.0.0',
  inputSchema: {
    type: 'object',
    properties: {
      path: { type: 'string' },
      format: { 
        type: 'string',
        enum: ['text', 'base64'],
        default: 'text',
      },
    },
    required: ['path'],
  },
  execute: async ({ input }) => {
    try {
      const content = await readFile(input.path);
      
      if (input.format === 'base64' && content.toString().endsWith('.jpg')) {
        // Return image data for JPG files when base64 format is requested
        return {
          isError: false,
          data: [
            {
              type: 'image',
              image: {
                type: 'base64',
                media_type: 'image/jpeg',
                data: content.toString('base64'),
              },
            },
            {
              type: 'text',
              text: 'File read successfully',
            },
          ],
        };
      }
      
      // Default to text response
      return {
        isError: false,
        data: content.toString('utf-8'),
      };
    } catch (error) {
      return {
        isError: true,
        data: `Could not read file: ${error.message}`,
        error: {
          code: 'FILE_ERROR',
          message: error.message,
        },
      };
    }
  },
});

Tool with custom validation

import { Tool } from '@agenite/tool';

interface QueryInput {
  sql: string;
  parameters?: Record<string, unknown>;
}

const createDatabaseTool = (db: Database) => {
  return new Tool<QueryInput>({
    name: 'database-query',
    description: 'Execute SQL queries against the database',
    version: '1.0.0',
    inputSchema: {
      type: 'object',
      properties: {
        sql: { type: 'string' },
        parameters: {
          type: 'object',
          additionalProperties: true,
        },
      },
      required: ['sql'],
    },
    // Custom validation to prevent dangerous SQL operations
    validate: async (input) => {
      const dangerousKeywords = ['DROP', 'DELETE', 'TRUNCATE', 'ALTER'];
      const sql = input.sql.toUpperCase();
      
      for (const keyword of dangerousKeywords) {
        if (sql.includes(keyword)) {
          return {
            isValid: false,
            errors: [{
              field: 'sql',
              message: `Dangerous operation detected: ${keyword} is not allowed`,
            }],
          };
        }
      }
      
      return { isValid: true };
    },
    execute: async ({ input }) => {
      try {
        const result = await db.query(input.sql, input.parameters);
        return {
          isError: false,
          data: JSON.stringify(result),
        };
      } catch (error) {
        return {
          isError: true,
          data: `Database error: ${error.message}`,
          error: {
            code: 'DB_ERROR',
            message: error.message,
          },
        };
      }
    },
  });
};

Best practices

  1. Type safety: Leverage TypeScript generics and Zod schemas for input validation
  2. Error handling: Use structured error responses with codes and details
  3. Schema validation: Use either JSON Schema or Zod for input validation
  4. Rich responses: Utilize text and image responses when appropriate
  5. Performance tracking: Monitor execution times and token usage
  6. Context utilization: Pass relevant context to tools for better integration

Next steps