Create Custom Agent Tool
Add a tool that an AIAgent node can call during workflow execution.
This guide shows how to build a standalone custom tool that an AIAgent can call when the model needs external capability or fresh data.
If you already have a runnable node and just want to expose it to the agent, use Use a Node as an Agent Tool instead. That path usually avoids writing a second adapter class.
For small app-local helpers without a separate @tool() class, use callableTool(...) from @codemation/core (Zod inputSchema / outputSchema, execute, optional credentialRequirements). The legacy CallableToolFactory.callableTool(...) entry point remains equivalent. See AI Agent.
Use this guide when
Reach for a custom agent tool when:
- the model needs deterministic computation
- the model needs fresh or external data
- you want the model to call an internal API instead of guessing
- the capability does not naturally belong in a reusable workflow node
Example scenario
Imagine you want an agent that can search your internal documentation before it answers.
The pattern is:
- define a tool config
- implement the tool with input and output schemas
- attach the tool config to an
AIAgent
Step 1: define the tool config
import type { ToolConfig, TypeToken } from "@codemation/core";
export class SearchDocsToolConfig implements ToolConfig {
readonly type: TypeToken<unknown> = SearchDocsTool;
constructor(
public readonly name: string = "search_docs",
public readonly description: string = "Search product documentation for the current task.",
) {}
}Step 2: implement the tool
import type { Tool, ToolExecuteArgs } from "@codemation/core";
import { tool } from "@codemation/core";
import { z } from "zod";
const searchToolInputSchema = z.object({
query: z.string(),
});
const searchToolOutputSchema = z.object({
results: z.array(
z.object({
title: z.string(),
url: z.string(),
}),
),
});
@tool()
export class SearchDocsTool implements Tool<
SearchDocsToolConfig,
typeof searchToolInputSchema,
typeof searchToolOutputSchema
> {
readonly defaultDescription = "Search documentation and return the best matching pages.";
readonly inputSchema = searchToolInputSchema;
readonly outputSchema = searchToolOutputSchema;
async execute(
args: ToolExecuteArgs<SearchDocsToolConfig, z.input<typeof searchToolInputSchema>>,
): Promise<z.output<typeof searchToolOutputSchema>> {
const query = args.input.query.trim();
return {
results: [
{
title: `Result for ${query}`,
url: "https://example.test/docs",
},
],
};
}
}Step 3: attach the tool to an AIAgent
new AIAgent({
name: "Answer product questions",
messages: [
{
role: "system",
content: "Use the search_docs tool when you need product documentation context, then answer clearly.",
},
{ role: "user", content: ({ item }) => JSON.stringify(item.json) },
],
chatModel: new OpenAIChatModelConfig("OpenAI", "gpt-4o-mini"),
tools: [new SearchDocsToolConfig()],
});Why tools are useful
Tools let the model stay focused on reasoning while deterministic code handles:
- lookups
- calculations
- external API access
- capability boundaries that should not live inside a prompt