LangChain Tutorial (TypeScript): building custom tools for intermediate developers
This tutorial shows you how to build a custom LangChain tool in TypeScript, register it with an agent, and call it safely from model-driven workflows. You need this when the built-in tools are too generic and you want the agent to query your own APIs, databases, or internal services with predictable inputs and outputs.
What You'll Need
- •Node.js 18+
- •TypeScript 5+
- •
npmorpnpm - •An OpenAI API key
- •Packages:
- •
langchain - •
@langchain/openai - •
zod - •
typescript - •
tsxfor running TypeScript directly during development
- •
Install them:
npm install langchain @langchain/openai zod
npm install -D typescript tsx @types/node
Set your API key:
export OPENAI_API_KEY="your-key-here"
Step-by-Step
- •Start by defining a tool with a strict schema. The point is to make the tool input explicit so the model cannot send random shapes and break your code.
import { z } from "zod";
import { DynamicStructuredTool } from "@langchain/core/tools";
const customerLookupTool = new DynamicStructuredTool({
name: "customer_lookup",
description: "Look up a customer record by email address.",
schema: z.object({
email: z.string().email(),
}),
func: async ({ email }) => {
const fakeDb = {
"alice@bank.com": { id: "cus_001", tier: "gold", balance: 12400 },
"bob@bank.com": { id: "cus_002", tier: "silver", balance: 3200 },
};
const record = fakeDb[email as keyof typeof fakeDb];
return record ? JSON.stringify(record) : JSON.stringify({ error: "Customer not found" });
},
});
- •Next, wire the tool into an agent. Use a chat model that supports tool calling, then pass the tool list into the agent executor.
import { ChatOpenAI } from "@langchain/openai";
import { initializeAgentExecutorWithOptions } from "langchain/agents";
const model = new ChatOpenAI({
model: "gpt-4o-mini",
temperature: 0,
});
const tools = [customerLookupTool];
const executor = await initializeAgentExecutorWithOptions(tools, model, {
agentType: "openai-tools",
verbose: true,
});
- •Now run a query against the agent. Keep the prompt specific so you can see whether the model chooses the tool correctly and formats the result back into natural language.
const result = await executor.invoke({
input: "Find the customer record for alice@bank.com and summarize their tier and balance.",
});
console.log(result.output);
- •Add a second tool to show how intermediate developers should structure production workflows. In real systems, one tool reads data and another writes or transforms it; keep each tool small and single-purpose.
const riskScoreTool = new DynamicStructuredTool({
name: "risk_score",
description: "Calculate a simple risk score from account balance and tier.",
schema: z.object({
tier: z.enum(["gold", "silver", "bronze"]),
balance: z.number(),
}),
func: async ({ tier, balance }) => {
const base = tier === "gold" ? 10 : tier === "silver" ? 30 : 50;
const balancePenalty = balance < 5000 ? 20 : balance < 10000 ? 10 : 0;
return JSON.stringify({ riskScore: base + balancePenalty });
},
});
tools.push(riskScoreTool);
- •If you want more control, call tools directly instead of always going through an agent. This is useful when your application already knows which business action to take and you just want LangChain’s schema validation plus execution wrapper.
const directResult = await customerLookupTool.invoke({
email: "bob@bank.com",
});
console.log(directResult);
const scoreResult = await riskScoreTool.invoke({
tier: "silver",
balance: 3200,
});
console.log(scoreResult);
Testing It
Run the file with tsx so you can iterate without compiling first:
npx tsx src/index.ts
You should see verbose agent logs showing whether the model called customer_lookup, followed by a final answer in plain English. If it fails to call the tool, check that your model supports tool calling and that your schema is narrow enough for the prompt.
Also test invalid inputs by calling the tool directly with a bad email or missing fields. Zod should reject malformed payloads before your business logic runs, which is exactly what you want in production.
Next Steps
- •Replace the fake DB with a real service client or internal REST endpoint.
- •Add retries, timeouts, and structured error handling around each tool.
- •Learn LangChain message history and memory patterns so your tools work inside multi-turn workflows.
Keep learning
- •The complete AI Agents Roadmap — my full 8-step breakdown
- •Free: The AI Agent Starter Kit — PDF checklist + starter code
- •Work with me — I build AI for banks and insurance companies
By Cyprian Aarons, AI Consultant at Topiax.
Want the complete 8-step roadmap?
Grab the free AI Agent Starter Kit — architecture templates, compliance checklists, and a 7-email deep-dive course.
Get the Starter Kit