docs
Complete API reference for creating and using tools in LangChain agents.
/**
* 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 information
*/
type ToolRuntime = {
name: string;
description: string;
schema: ZodType<any>;
};/**
* 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>;
}/**
* 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;
}/**
* 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>;
}/**
* 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;
}/**
* 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>;
}Naming Conventions:
search_web, create_task)search, create, update, delete, getsearch_web not search, send_email not sendtool1, helper, utilsDescription Guidelines:
Schema Design:
.describe() on every field to guide the LLM.optional().default(value)z.enum(["option1", "option2"]).min(), .max(), .email(), .url(), etc.Return Value Guidelines:
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"),
}),
}
);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(),
}),
}
);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"),
}),
}
);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 });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(),
}),
}
);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(),
}),
}
);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),
}),
}
);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(),
}),
}
);