or run

npx @tessl/cli init
Log in

Version

Tile

Overview

Evals

Files

docs

advanced

error-handling.mdtype-inference.md
glossary.mdindex.mdquick-reference.mdtask-index.md
tile.json

tools.mddocs/api-reference/

Tools API Reference

Complete API reference for creating and using tools in LangChain agents.

tool Function

/**
 * Create a structured tool from a function and schema
 *
 * @template T - The type of the input parameter, inferred from the schema
 * @param func - Function to execute when tool is called. Receives validated input matching the schema and optional config.
 *               Should return a string or promise resolving to a string with the tool's result.
 *               Function signature: (input: T, config?: ToolConfig) => any | Promise<any>
 * @param fields - Tool configuration object
 * @param fields.name - Tool name used by LLM to identify and call the tool. Use descriptive, action-oriented names
 *                      in snake_case (e.g., "search_web", "create_task"). Required.
 * @param fields.description - Description explaining what the tool does and when to use it. This helps the LLM decide
 *                             when to call the tool. Be specific and mention key parameters. Required.
 * @param fields.schema - Zod schema defining and validating the tool's input structure. Use .describe() on schema fields
 *                        to help the LLM understand each parameter. Required.
 * @param fields.responseFormat - Optional format for tool responses. "content" returns just the content string (default),
 *                                 "content_and_artifact" returns both content and structured artifact data.
 * @returns Structured tool instance that can be passed to createAgent() in the tools array
 *
 * @example
 * ```typescript
 * const calculator = tool(
 *   async ({ expression }) => String(eval(expression)),
 *   {
 *     name: "calculator",
 *     description: "Evaluate mathematical expressions for arithmetic calculations",
 *     schema: z.object({
 *       expression: z.string().describe("Mathematical expression to evaluate (e.g., '2 + 2', '10 * 5')"),
 *     }),
 *   }
 * );
 * ```
 */
function tool<T = any>(
  func: (input: T, config?: ToolConfig) => any | Promise<any>,
  fields: {
    name: string;
    description: string;
    schema: ZodType<T>;
    responseFormat?: "content" | "content_and_artifact";
  }
): StructuredTool<T>;

/**
 * Configuration object passed to tool functions during execution
 */
interface ToolConfig {
  /**
   * Configurable options from agent invocation (e.g., thread_id, user_id)
   */
  configurable?: Record<string, any>;

  /**
   * Metadata for this tool call (for logging, tracing)
   */
  metadata?: Record<string, any>;

  /**
   * Tags for categorizing this tool call
   */
  tags?: string[];

  /**
   * Maximum recursion depth for tool calls
   */
  recursionLimit?: number;

  /**
   * Custom name for this tool run (for tracing)
   */
  runName?: string;
}

Tool Runtime Type

/**
 * Tool runtime information
 */
type ToolRuntime = {
  name: string;
  description: string;
  schema: ZodType<any>;
};

Base Tool Class

/**
 * Base tool class
 */
abstract class Tool {
  /**
   * Tool name - used by LLM to identify the tool
   */
  name: string;

  /**
   * Tool description - used by LLM to understand when to use the tool
   */
  description: string;

  /**
   * Execute the tool with string input
   * @param arg - String input argument
   * @param callbacks - Optional callbacks
   * @param tags - Optional tags for tracing
   * @param metadata - Optional metadata
   * @returns Promise resolving to string result
   */
  abstract _call(
    arg: string,
    callbacks?: Callbacks,
    tags?: string[],
    metadata?: Record<string, any>
  ): Promise<string>;

  /**
   * Call the tool
   * @param arg - Input argument
   * @param callbacks - Optional callbacks
   * @param tags - Optional tags
   * @param metadata - Optional metadata
   * @returns Promise resolving to result
   */
  call(
    arg: string | ToolCall,
    callbacks?: Callbacks,
    tags?: string[],
    metadata?: Record<string, any>
  ): Promise<string>;
}

DynamicTool Class

/**
 * Dynamically created tool with string input
 */
class DynamicTool extends Tool {
  /**
   * Create a dynamic tool
   * @param fields - Tool configuration
   */
  constructor(fields: {
    name: string;
    description: string;
    func: (input: string, config?: ToolConfig) => Promise<string>;
    returnDirect?: boolean;
    verbose?: boolean;
  });

  /**
   * The function to execute
   */
  func: (input: string, config?: ToolConfig) => Promise<string>;

  /**
   * Whether to return the tool output directly to user
   */
  returnDirect: boolean;
}

StructuredTool Class

/**
 * Tool with structured input schema
 */
abstract class StructuredTool<T = any> extends Tool {
  /**
   * Zod schema for input validation
   */
  schema: ZodType<T>;

  /**
   * Execute the tool with typed input
   * @param arg - Typed input argument matching schema
   * @param config - Optional tool configuration
   * @returns Promise resolving to string result
   */
  protected abstract _call(arg: T, config?: ToolConfig): Promise<string>;

  /**
   * Get input schema as JSON Schema
   * @returns JSON Schema representation
   */
  get inputSchema(): Record<string, any>;
}

DynamicStructuredTool Class

/**
 * Dynamically created structured tool
 */
class DynamicStructuredTool<T = any> extends StructuredTool<T> {
  /**
   * Create a dynamic structured tool
   * @param fields - Tool configuration with schema
   */
  constructor(fields: {
    name: string;
    description: string;
    schema: ZodType<T>;
    func: (input: T, config?: ToolConfig) => Promise<string>;
    returnDirect?: boolean;
    verbose?: boolean;
  });

  /**
   * The function to execute
   */
  func: (input: T, config?: ToolConfig) => Promise<string>;

  /**
   * Whether to return the tool output directly to user
   */
  returnDirect: boolean;
}

ToolCall Type

/**
 * Information about a tool call made by the model
 */
interface ToolCall {
  /**
   * Unique identifier for this tool call
   */
  id: string;

  /**
   * Name of the tool being called
   */
  name: string;

  /**
   * Arguments passed to the tool
   */
  args: Record<string, any>;
}

Tool Design Patterns

Best Practices

Naming Conventions:

  • Use snake_case for tool names (e.g., search_web, create_task)
  • Use action-oriented names (verbs): search, create, update, delete, get
  • Be specific: search_web not search, send_email not send
  • Avoid generic names: tool1, helper, utils

Description Guidelines:

  • Start with what the tool does: "Search the web for information"
  • Mention key parameters: "Search the web for information using a query string"
  • Include when to use it: "Use this when you need current information"
  • Keep it concise: 1-2 sentences maximum
  • Be specific about capabilities and limitations

Schema Design:

  • Use .describe() on every field to guide the LLM
  • Make optional parameters truly optional with .optional()
  • Provide sensible defaults with .default(value)
  • Use enums for constrained choices: z.enum(["option1", "option2"])
  • Validate inputs: .min(), .max(), .email(), .url(), etc.
  • Use descriptive property names

Return Value Guidelines:

  • Always return a string (or promise resolving to string)
  • Return concise, relevant information (avoid large dumps)
  • Format for readability (use JSON.stringify with indentation)
  • Include error context in error returns (don't throw exceptions)
  • Return structured data as formatted strings

Advanced Tool Patterns

Tool with Retry Logic

import { tool } from "langchain";
import { z } from "zod";

const resilientApiTool = tool(
  async ({ endpoint, maxRetries = 3 }) => {
    for (let i = 0; i < maxRetries; i++) {
      try {
        const response = await fetch(endpoint);
        const data = await response.json();
        return JSON.stringify(data);
      } catch (error) {
        if (i === maxRetries - 1) {
          return `Error after ${maxRetries} retries: ${error.message}`;
        }
        await new Promise(resolve => setTimeout(resolve, 1000 * (i + 1)));
      }
    }
  },
  {
    name: "resilient_api_call",
    description: "Call an API endpoint with automatic retry on failure",
    schema: z.object({
      endpoint: z.string().url().describe("The API endpoint URL to call"),
      maxRetries: z.number().default(3).describe("Maximum number of retry attempts"),
    }),
  }
);

Tool with Validation

import { tool } from "langchain";
import { z } from "zod";

const validatedSearchTool = tool(
  async ({ query, filters }) => {
    // Input is already validated by Zod schema
    // Additional business logic validation
    if (query.length < 3) {
      return "Error: Query must be at least 3 characters";
    }

    if (filters && filters.dateFrom && filters.dateTo) {
      const from = new Date(filters.dateFrom);
      const to = new Date(filters.dateTo);
      if (from > to) {
        return "Error: dateFrom must be before dateTo";
      }
    }

    // Perform search
    const results = await performSearch(query, filters);
    return JSON.stringify(results, null, 2);
  },
  {
    name: "validated_search",
    description: "Search with comprehensive validation",
    schema: z.object({
      query: z.string().min(1).describe("Search query"),
      filters: z.object({
        category: z.string().optional(),
        dateFrom: z.string().optional(),
        dateTo: z.string().optional(),
      }).optional(),
    }),
  }
);

Tool with Side Effects

import { tool } from "langchain";
import { z } from "zod";

const databaseUpdateTool = tool(
  async ({ userId, updates }, config) => {
    // Access configurable from agent invocation
    const { thread_id } = config?.configurable || {};

    try {
      // Perform database update
      await db.users.update(userId, updates);

      // Log the action
      await auditLog.create({
        action: "user_update",
        userId,
        updates,
        threadId: thread_id,
        timestamp: new Date().toISOString(),
      });

      return `Successfully updated user ${userId}`;
    } catch (error) {
      // Return error message (don't throw)
      return `Error updating user: ${error.message}`;
    }
  },
  {
    name: "update_user",
    description: "Update user profile information in the database",
    schema: z.object({
      userId: z.string().describe("User ID to update"),
      updates: z.object({
        name: z.string().optional(),
        email: z.string().email().optional(),
        preferences: z.record(z.any()).optional(),
      }).describe("Fields to update"),
    }),
  }
);

Tool Factory Pattern

import { tool, type Tool } from "langchain";
import { z } from "zod";

/**
 * Factory for creating CRUD tools for a resource
 */
function createCRUDTools<T>(
  resourceName: string,
  schema: z.ZodType<T>,
  api: {
    create: (data: T) => Promise<T>;
    read: (id: string) => Promise<T | null>;
    update: (id: string, data: Partial<T>) => Promise<T>;
    delete: (id: string) => Promise<void>;
  }
): Tool[] {
  return [
    tool(
      async (data) => {
        const created = await api.create(data);
        return JSON.stringify(created);
      },
      {
        name: `create_${resourceName}`,
        description: `Create a new ${resourceName}`,
        schema: schema,
      }
    ),
    tool(
      async ({ id }) => {
        const item = await api.read(id);
        return item ? JSON.stringify(item) : `${resourceName} not found`;
      },
      {
        name: `get_${resourceName}`,
        description: `Get a ${resourceName} by ID`,
        schema: z.object({ id: z.string() }),
      }
    ),
    tool(
      async ({ id, updates }) => {
        const updated = await api.update(id, updates);
        return JSON.stringify(updated);
      },
      {
        name: `update_${resourceName}`,
        description: `Update a ${resourceName}`,
        schema: z.object({
          id: z.string(),
          updates: schema.partial(),
        }),
      }
    ),
    tool(
      async ({ id }) => {
        await api.delete(id);
        return `${resourceName} deleted successfully`;
      },
      {
        name: `delete_${resourceName}`,
        description: `Delete a ${resourceName}`,
        schema: z.object({ id: z.string() }),
      }
    ),
  ];
}

// Usage
const taskTools = createCRUDTools("task", TaskSchema, taskApi);
const agent = createAgent({ model: "openai:gpt-4o", tools: taskTools });

Streaming Tool Results

import { tool } from "langchain";
import { z } from "zod";

const streamingTool = tool(
  async ({ query }) => {
    // For long-running operations, return progress updates
    const chunks = [];

    chunks.push(`Starting search for: ${query}`);

    // Perform operation in stages
    const stage1 = await performStage1(query);
    chunks.push(`Stage 1 complete: Found ${stage1.count} items`);

    const stage2 = await performStage2(stage1.results);
    chunks.push(`Stage 2 complete: Processed ${stage2.count} items`);

    const final = await performStage3(stage2.results);
    chunks.push(`Final results:\n${JSON.stringify(final, null, 2)}`);

    // Return all chunks joined
    return chunks.join("\n\n");
  },
  {
    name: "streaming_search",
    description: "Perform multi-stage search with progress updates",
    schema: z.object({
      query: z.string(),
    }),
  }
);

Tool Performance Optimization

Caching:

import { tool } from "langchain";
import { z } from "zod";

const cache = new Map();

const cachedTool = tool(
  async ({ query }) => {
    // Check cache first
    if (cache.has(query)) {
      return `[CACHED] ${cache.get(query)}`;
    }

    // Expensive operation
    const result = await expensiveOperation(query);

    // Cache result
    cache.set(query, result);

    return result;
  },
  {
    name: "cached_search",
    description: "Search with result caching",
    schema: z.object({
      query: z.string(),
    }),
  }
);

Batching:

import { tool } from "langchain";
import { z } from "zod";

// Batch multiple requests together
let batchQueue: Array<{ query: string; resolve: (value: string) => void }> = [];
let batchTimeout: NodeJS.Timeout | null = null;

const batchedTool = tool(
  async ({ query }) => {
    return new Promise<string>((resolve) => {
      batchQueue.push({ query, resolve });

      if (batchTimeout) clearTimeout(batchTimeout);

      batchTimeout = setTimeout(async () => {
        const batch = [...batchQueue];
        batchQueue = [];

        // Execute all queries in one batch
        const results = await executeBatch(batch.map(b => b.query));

        // Resolve all promises
        batch.forEach((item, i) => {
          item.resolve(results[i]);
        });
      }, 100); // Wait 100ms to collect more requests
    });
  },
  {
    name: "batched_search",
    description: "Search with automatic batching for efficiency",
    schema: z.object({
      query: z.string(),
    }),
  }
);

Error Handling Patterns

Graceful Degradation:

import { tool } from "langchain";
import { z } from "zod";

const resilientTool = tool(
  async ({ query, fallbackEnabled = true }) => {
    try {
      // Try primary method
      return await primarySearch(query);
    } catch (primaryError) {
      if (!fallbackEnabled) {
        return `Error: ${primaryError.message}`;
      }

      try {
        // Try fallback method
        return await fallbackSearch(query);
      } catch (fallbackError) {
        // Return informative error
        return `Error: Primary and fallback methods failed.\nPrimary: ${primaryError.message}\nFallback: ${fallbackError.message}`;
      }
    }
  },
  {
    name: "resilient_search",
    description: "Search with automatic fallback on failure",
    schema: z.object({
      query: z.string(),
      fallbackEnabled: z.boolean().default(true),
    }),
  }
);

Timeout Protection:

import { tool } from "langchain";
import { z } from "zod";

const timeoutTool = tool(
  async ({ query, timeoutMs = 5000 }) => {
    const timeoutPromise = new Promise<string>((_, reject) =>
      setTimeout(() => reject(new Error("Operation timed out")), timeoutMs)
    );

    const operationPromise = performOperation(query);

    try {
      return await Promise.race([operationPromise, timeoutPromise]);
    } catch (error) {
      return `Error: ${error.message}`;
    }
  },
  {
    name: "timeout_protected_operation",
    description: "Perform operation with timeout protection",
    schema: z.object({
      query: z.string(),
      timeoutMs: z.number().default(5000),
    }),
  }
);

Security Considerations

Input Sanitization:

import { tool } from "langchain";
import { z } from "zod";

const secureTool = tool(
  async ({ query }) => {
    // Sanitize input
    const sanitized = query
      .replace(/<script>/gi, "")
      .replace(/javascript:/gi, "")
      .trim();

    // Validate sanitized input
    if (sanitized !== query) {
      return "Error: Input contains potentially malicious content";
    }

    // Safe to proceed
    return await performSearch(sanitized);
  },
  {
    name: "secure_search",
    description: "Search with input sanitization",
    schema: z.object({
      query: z.string().max(1000),
    }),
  }
);

Permission Checking:

import { tool } from "langchain";
import { z } from "zod";

const permissionCheckTool = tool(
  async ({ userId, action }, config) => {
    // Get user context from config
    const currentUserId = config?.configurable?.user_id;

    // Check permission
    if (userId !== currentUserId) {
      const hasPermission = await checkPermission(currentUserId, "manage_users");
      if (!hasPermission) {
        return "Error: Permission denied";
      }
    }

    // Perform action
    return await performAction(userId, action);
  },
  {
    name: "user_action",
    description: "Perform action with permission checking",
    schema: z.object({
      userId: z.string(),
      action: z.string(),
    }),
  }
);

Related Documentation

  • Tool Guide - Complete usage guide
  • Agent API Reference - Agent creation with tools
  • Quick Reference - Quick tool patterns