Type-safe tool creation with Zod schema validation (re-exported from @langchain/core/tools).
function tool<T extends InteropZodType = InteropZodType>(
func: (input: InferInteropZodInput<T>, config?: RunnableConfig) => any,
config: {
name: string;
description: string;
schema: T;
}
): DynamicStructuredTool<T>;Examples:
import { tool } from "langchain";
import { z } from "zod";
// Simple tool
const searchTool = tool(
({ query }) => `Results for: ${query}`,
{
name: "search",
description: "Search for information",
schema: z.object({
query: z.string().describe("The search query"),
}),
}
);
// Multiple parameters
const calculatorTool = tool(
({ operation, x, y }) => {
switch (operation) {
case "add": return x + y;
case "subtract": return x - y;
case "multiply": return x * y;
case "divide": return x / y;
default: throw new Error(`Unknown: ${operation}`);
}
},
{
name: "calculator",
description: "Perform arithmetic",
schema: z.object({
operation: z.enum(["add", "subtract", "multiply", "divide"]),
x: z.number(),
y: z.number(),
}),
}
);
// Async tool
const weatherTool = tool(
async ({ location }) => {
const response = await fetch(`https://api.weather.com/${location}`);
return await response.json();
},
{
name: "get_weather",
description: "Get weather for a location",
schema: z.object({
location: z.string().describe("City or ZIP"),
}),
}
);
// With RunnableConfig
const databaseTool = tool(
async ({ query }, config) => {
const signal = config?.signal;
const result = await executeQuery(query, { signal });
return result;
},
{
name: "query_database",
description: "Execute database query",
schema: z.object({
query: z.string().describe("SQL query"),
}),
}
);class DynamicStructuredTool<T extends InteropZodType = InteropZodType> extends StructuredTool<T> {
name: string;
description: string;
schema: T;
invoke(input: InferInteropZodInput<T>, config?: RunnableConfig): Promise<any>;
stream(input: InferInteropZodInput<T>, config?: RunnableConfig): Promise<IterableReadableStream<any>>;
}abstract class StructuredTool<T extends InteropZodType = InteropZodType> extends Tool {
schema: T;
abstract _call(
arg: InferInteropZodInput<T>,
runManager?: CallbackManagerForToolRun
): Promise<string>;
invoke(input: InferInteropZodInput<T>, config?: RunnableConfig): Promise<string>;
}Custom Tool Example:
import { StructuredTool } from "langchain";
import { z } from "zod";
class CustomSearchTool extends StructuredTool {
name = "custom_search";
description = "Custom search implementation";
schema = z.object({
query: z.string(),
maxResults: z.number().default(10),
});
async _call({ query, maxResults }) {
const results = await this.performSearch(query, maxResults);
return JSON.stringify(results);
}
private async performSearch(query: string, max: number) {
// Implementation
return [];
}
}
const customTool = new CustomSearchTool();abstract class Tool {
name: string;
description: string;
returnDirect?: boolean;
abstract _call(arg: string, runManager?: CallbackManagerForToolRun): Promise<string>;
invoke(input: string, config?: RunnableConfig): Promise<string>;
call(arg: string, callbacks?: Callbacks): Promise<string>;
}class DynamicTool extends Tool {
name: string;
description: string;
constructor(config: {
name: string;
description: string;
func: (input: string, runManager?: CallbackManagerForToolRun) => Promise<string>;
returnDirect?: boolean;
});
_call(input: string, runManager?: CallbackManagerForToolRun): Promise<string>;
}Example:
import { DynamicTool } from "langchain";
const simpleTool = new DynamicTool({
name: "echo",
description: "Echo input",
func: async (input) => `You said: ${input}`,
});interface RunnableConfig {
signal?: AbortSignal;
callbacks?: Callbacks;
tags?: string[];
metadata?: Record<string, any>;
runName?: string;
}
interface CallbackManagerForToolRun {
handleToolStart?(tool: Tool, input: string): void;
handleToolEnd?(output: string): void;
handleToolError?(error: Error): void;
}
type ServerTool = StructuredTool | DynamicStructuredTool | Tool | DynamicTool;
interface ClientTool {
name: string;
description: string;
schema: InteropZodType;
}Error Handling:
const safeTool = tool(
async ({ url }) => {
try {
const response = await fetch(url, { timeout: 5000 });
if (!response.ok) return `Error: HTTP ${response.status}`;
return await response.text();
} catch (error) {
return `Error: ${error.message}`;
}
},
{
name: "fetch_url",
description: "Fetch URL content",
schema: z.object({ url: z.string().url() }),
}
);Validation:
const validatedTool = tool(
async ({ email, age }) => {
if (age < 18) return "Error: Must be 18 or older";
return `Registered: ${email}`;
},
{
name: "register_user",
description: "Register user",
schema: z.object({
email: z.string().email(),
age: z.number().min(0).max(150),
name: z.string().min(1),
}),
}
);Stateful Tool:
class CounterTool {
private count = 0;
getTool() {
return tool(
({ increment }) => {
this.count += increment;
return `Count: ${this.count}`;
},
{
name: "counter",
description: "Increment counter",
schema: z.object({ increment: z.number() }),
}
);
}
}
const counter = new CounterTool();
const counterTool = counter.getTool();Return Direct:
class DirectAnswerTool extends StructuredTool {
name = "direct_answer";
description = "Get answer without further processing";
returnDirect = true;
schema = z.object({ query: z.string() });
async _call({ query }) {
return await getDefinitiveAnswer(query);
}
}Tool Composition:
const fetchAndParseTool = tool(
async ({ url }) => {
const content = await fetchTool.invoke({ url });
const parsed = await parseTool.invoke({ content });
return parsed;
},
{
name: "fetch_and_parse",
description: "Fetch and parse URL",
schema: z.object({ url: z.string().url() }),
}
);