How to Add Custom Tools to Open Agents: A Step-by-Step Guide
To add custom tools to Open Agents, create a TypeScript file in packages/agent/tools/ that exports a ToolDefinition object with name, description, inputs (Zod schema), and an async run function, then register it by re-exporting from packages/agent/tools/index.ts.
Open Agents (vercel-labs/open-agents) provides a flexible framework for building LLM agents using the @vercel/agent package. When you need to extend an agent's capabilities beyond built-in utilities like fetch or bash, you can add custom tools by following the declarative interface defined in the codebase.
Understanding the Open Agents Tool Architecture
The tool ecosystem in Open Agents follows a strict, type-safe pattern defined in packages/agent/types.ts. Every tool must implement the ToolDefinition interface, which requires four mandatory properties:
name– The identifier the LLM uses to request the tooldescription– A concise explanation of what the tool doesinputs– A Zod schema defining the expected parametersrun– An async function that executes the tool logic
All built-in tools reside in packages/agent/tools/, with each tool occupying its own TypeScript file. The packages/agent/tools/index.ts file serves as the central registry, re-exporting every tool so the agent runtime can discover them dynamically.
Step-by-Step Guide to Adding Custom Tools to Open Agents
Step 1: Create the Tool File
Create a new TypeScript file inside packages/agent/tools/. Name it descriptively to match your tool's purpose, such as weather-check.ts or database-query.ts.
Step 2: Implement the ToolDefinition Interface
Structure your tool to match the pattern used in built-in tools like packages/agent/tools/write.ts. Import the ToolDefinition type from ../types and define your tool object with the required fields.
// packages/agent/tools/my-tool.ts
import { z } from "zod";
import type { ToolDefinition } from "../types";
export const myTool: ToolDefinition = {
name: "myTool",
description: "Returns a greeting for the supplied name.",
inputs: z.object({
name: z.string().describe("Name of the person to greet"),
}),
async run({ name }) {
// Custom logic goes here – could call external APIs, read files, etc.
return `Hello, ${name}!`;
},
};
Step 3: Register the Tool in the Index
Open packages/agent/tools/index.ts and add an export statement for your new tool. This step is critical because packages/agent/open-harness-agent.ts imports the entire tool registry from this index file to build the ToolRegistry.
// packages/agent/tools/index.ts
export * from "./write";
export * from "./bash";
export * from "./fetch";
export * from "./read";
export * from "./todo";
export * from "./task";
export * from "./skill";
export * from "./grep";
export * from "./glob";
// ---- add your export below ----
export * from "./my-tool";
How the Open Agents Runtime Loads Custom Tools
When you instantiate an agent using packages/agent/open-harness-agent.ts, the harness creates a ToolRegistry by importing every tool exported from packages/agent/tools/index.ts. During a reasoning step, the LLM may request a tool call by name; the harness looks up the tool in the registry, validates the supplied arguments against the tool's Zod schema, and then executes the run method.
Results are fed back to the LLM, allowing further reasoning or additional tool calls. This architecture means that once you add your custom tool to the tools directory and export it from index.ts, the agent automatically picks it up without requiring additional configuration in the harness.
Summary
- Location: Place custom tool files in
packages/agent/tools/following the naming convention used by built-in tools. - Structure: Implement the
ToolDefinitioninterface frompackages/agent/types.tswithname,description,inputs(Zod schema), andrunfunction. - Registration: Export the tool from
packages/agent/tools/index.tssopackages/agent/open-harness-agent.tscan load it into theToolRegistry. - Execution: The agent runtime automatically validates inputs against your Zod schema and invokes the
runmethod when the LLM requests the tool.
Frequently Asked Questions
What is the ToolDefinition interface in Open Agents?
The ToolDefinition interface is defined in packages/agent/types.ts and serves as the contract for all tools in the Open Agents framework. It requires four properties: a name string for tool identification, a description string explaining functionality, an inputs Zod schema for parameter validation, and a run async function that executes the tool logic.
How do I validate inputs for custom Open Agents tools?
Input validation uses Zod schemas assigned to the inputs property of your ToolDefinition. When the LLM invokes your tool, the Open Agents runtime automatically validates the provided arguments against this schema before executing the run function. If validation fails, the agent receives an error without the run method executing.
Can I add external API calls to custom Open Agents tools?
Yes, the run function in your tool definition can contain any asynchronous logic, including external API calls, database queries, or file system operations. The example in packages/agent/tools/my-tool.ts demonstrates this pattern: the run method is async and can use fetch, ORM clients, or other Node.js APIs to retrieve data before returning results to the LLM.
Where does Open Agents register custom tools?
Custom tools are registered in packages/agent/tools/index.ts. This file acts as the central registry by re-exporting all tool modules. The agent harness in packages/agent/open-harness-agent.ts imports from this index to build the ToolRegistry, so any tool not exported here will not be available to the agent runtime.
Have a question about this repo?
These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:
curl -s "https://instagit.com/install.md" Maintain an open-source project? Get it listed too →