LangChain Tutorial (TypeScript): building custom tools for beginners

By Cyprian AaronsUpdated 2026-04-21
langchainbuilding-custom-tools-for-beginnerstypescript

This tutorial shows you how to build a custom LangChain tool in TypeScript, register it with an agent, and run it against real user input. You need this when the built-in tools are not enough and your agent has to call your own business logic, internal APIs, or deterministic helpers.

What You'll Need

  • Node.js 18+
  • TypeScript 5+
  • An OpenAI API key
  • langchain
  • @langchain/openai
  • zod
  • A project set up with ESM support
  • Basic familiarity with async/await and LangChain agents

Install the packages:

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

  1. Start by creating a small TypeScript file that will hold your custom tool. A good beginner tool is one that does one thing predictably, like returning a policy summary from an ID.
import { DynamicStructuredTool } from "langchain/tools";
import { z } from "zod";

const policyLookupSchema = z.object({
  policyId: z.string().min(1).describe("The policy identifier"),
});

const lookupPolicyTool = new DynamicStructuredTool({
  name: "lookup_policy",
  description: "Look up a policy summary by policy ID.",
  schema: policyLookupSchema,
  func: async ({ policyId }) => {
    const fakeDatabase = {
      POL123: "Policy POL123: active, premium paid, renewal due in 30 days.",
      POL456: "Policy POL456: pending underwriting review.",
    };

    return fakeDatabase[policyId] ?? `No policy found for ${policyId}`;
  },
});
  1. Next, make sure the tool returns plain text that the model can reason over. For beginner-friendly tools, keep the input schema strict and the output deterministic.
async function testToolDirectly() {
  const result = await lookupPolicyTool.invoke({ policyId: "POL123" });
  console.log(result);
}

testToolDirectly();
  1. Now wire the tool into a LangChain agent. This is where the model gets access to your custom function and can decide when to use it.
import { ChatOpenAI } from "@langchain/openai";
import { createOpenAIToolsAgent } from "langchain/agents";
import { AgentExecutor } from "langchain/agents";
import { ChatPromptTemplate } from "@langchain/core/prompts";

const llm = new ChatOpenAI({
  model: "gpt-4o-mini",
  temperature: 0,
});

const prompt = ChatPromptTemplate.fromMessages([
  ["system", "You are a helpful insurance assistant. Use tools when needed."],
  ["human", "{input}"],
]);

const tools = [lookupPolicyTool];
const agent = await createOpenAIToolsAgent({
  llm,
  tools,
  prompt,
});

const executor = new AgentExecutor({
  agent,
  tools,
});
  1. Run the agent with a real question that should trigger your tool. Keep the prompt specific so you can see whether the agent calls the tool correctly.
const response = await executor.invoke({
  input: "Check policy POL123 and tell me its status.",
});

console.log(response.output);
  1. If you want to grow this into something production-shaped, add validation and failure handling inside the tool function. Tools should fail cleanly because agents will call them with bad inputs sooner or later.
const safeLookupPolicyTool = new DynamicStructuredTool({
  name: "lookup_policy_safe",
  description: "Look up a policy summary by policy ID.",
  schema: policyLookupSchema,
  func: async ({ policyId }) => {
    try {
      const fakeDatabase: Record<string, string> = {
        POL123: "Policy POL123: active, premium paid, renewal due in 30 days.",
        POL456: "Policy POL456: pending underwriting review.",
      };

      return fakeDatabase[policyId] ?? `No policy found for ${policyId}`;
    } catch (error) {
      return `Tool error while looking up ${policyId}`;
    }
  },
});

Testing It

Run the file with tsx so you do not need a separate build step during development.

npx tsx src/index.ts

You should see two things:

  • The direct tool invocation output for POL123
  • The agent’s final answer using that tool output

If the agent ignores the tool, tighten the user prompt and make sure your tool name and description are specific enough. If you get schema errors, check that the object passed to invoke() matches the Zod schema exactly.

Next Steps

  • Add multiple tools and compare how the agent chooses between them
  • Replace the fake database with a real internal API call using fetch
  • Learn structured outputs so your tool results can feed downstream workflows cleanly

Keep learning

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

Related Guides