CrewAI Tutorial (TypeScript): adding tool use for intermediate developers
This tutorial shows you how to add tools to a CrewAI workflow in TypeScript so your agents can fetch external data, call APIs, and act on live information instead of guessing. You need this when a plain LLM response is not enough and your agent must inspect files, query services, or pull current data before answering.
What You'll Need
- •Node.js 18+ installed
- •A TypeScript project with
ts-nodeortsx - •CrewAI for TypeScript installed
- •An OpenAI API key set in your environment
- •Basic familiarity with CrewAI agents, tasks, and crews
- •A real use case for a tool, such as:
- •fetching weather
- •reading a local file
- •querying an internal HTTP endpoint
Step-by-Step
- •Start by installing the dependencies and setting up your environment. For this example, we’ll use one built-in CrewAI tool and one custom tool so you can see both patterns.
npm init -y
npm install @crewai/crewai @crewai/tools zod dotenv
npm install -D typescript tsx @types/node
- •Create a simple custom tool. This is the part most intermediate developers need: wrapping deterministic logic in a tool the agent can call when it needs exact data.
import { z } from "zod";
import { Tool } from "@crewai/tools";
export const getInvoiceStatusTool = new Tool({
name: "get_invoice_status",
description: "Look up invoice status by invoice ID.",
schema: z.object({
invoiceId: z.string().min(3),
}),
execute: async ({ invoiceId }) => {
const fakeDb = {
INV-1001: "paid",
INV-1002: "pending",
INV-1003: "overdue",
} as const;
return fakeDb[invoiceId as keyof typeof fakeDb] ?? "not_found";
},
});
- •Wire the tool into an agent. The key detail is that the agent must be explicitly given the tools array; otherwise it will answer from memory only.
import "dotenv/config";
import { Agent } from "@crewai/crewAI";
import { getInvoiceStatusTool } from "./tools/getInvoiceStatusTool";
const billingAgent = new Agent({
role: "Billing Support Specialist",
goal: "Answer invoice questions using tools when needed.",
backstory: "You handle customer billing requests and verify data before responding.",
tools: [getInvoiceStatusTool],
});
console.log(billingAgent.role);
- •Create a task and a crew that actually uses the agent. In production you want the task to make it obvious when the tool should be called, so the model has a clear trigger.
import { Task, Crew, Process } from "@crewai/crewAI";
import { billingAgent } from "./agent";
const task = new Task({
description:
"Check invoice INV-1002 and tell the user whether it is paid, pending, overdue, or not found.",
expectedOutput: "A short status summary with the invoice ID and result.",
agent: billingAgent,
});
const crew = new Crew({
agents: [billingAgent],
tasks: [task],
process: Process.sequential,
});
- •Run the crew and print the result. Keep this as a single entry point while you test tool behavior; once it works, you can split it into service code or an API route.
import { crew } from "./crew";
async function main() {
const result = await crew.kickoff();
console.log("\n=== RESULT ===\n");
console.log(result);
}
main().catch((error) => {
console.error(error);
process.exit(1);
});
- •If you want to test with a built-in HTTP-style tool instead of only custom logic, add one more tool and attach it to the same agent. This is useful when your agent needs live external context like exchange rates or internal service responses.
import { Agent } from "@crewai/crewAI";
import { SerperDevTool } from "@crewai/tools";
const searchTool = new SerperDevTool();
export const researchAgent = new Agent({
role: "Research Assistant",
goal: "Find current information using search when needed.",
backstory: "You verify facts with external sources before answering.",
tools: [searchTool],
});
Testing It
Run your script with npx tsx src/main.ts or whatever entry file you created. If the wiring is correct, the model should call get_invoice_status for INV-1002 instead of inventing an answer.
If it returns "pending", your tool path is working end to end. If the agent ignores the tool, check three things first:
- •The tool is included in
tools: [...] - •The task description clearly requires external lookup
- •Your model/API key is set correctly in
.env
A good next test is to change invoiceId to one that does not exist, like INV-9999. That confirms your fallback behavior is also deterministic.
Next Steps
- •Add multiple tools to one agent and compare when each gets selected
- •Wrap real HTTP calls in tools with timeouts and retries
- •Move from sequential crews to multi-agent workflows where one agent gathers data and another drafts the response
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