Write a custom tool for your agent in TypeScript or Python.
Tools you write in code come in two languages, TypeScript and Python. This guide covers both. Pick the tab for your language in each step. Both kinds are bundled into the binary at compile time, so there is no runtime installation. For the full field reference, see the tool reference.
TypeScript tools run in an embedded JavaScript runtime. Python tools use RustPython by default,
which supports pure-Python packages and needs no Python installation at runtime. Select the
static interpreter to use CPython 3.14 for packages with C extensions. Both use the agentc tool
development kit (TDK), which gives you typed tool definitions.
Generate a ready-to-use package with agentc tool init, then install its dependencies.
agentc tool init math --language javascript
cd math
pnpm installThis creates a package.json, a tsconfig.json, and src/index.ts, with the TDK and esbuild already
configured.
A tool has a name, a description, a JSON Schema for its inputs, and an execute function. The name and
descriptions are what the model reads to decide when and how to call it, so write them carefully.
Export a tool object from the package entry point. Import the Tool type from the TDK for full type
coverage.
import type { Tool, ToolInput, ToolOutput } from "@agentc-sh/tdk";
type AddArgs = { a: number; b: number };
export const adder: Tool<AddArgs, number> = {
name: "adder",
description: "Adds two numbers together.",
parameters: {
type: "object",
properties: {
a: { type: "number", description: "The first number." },
b: { type: "number", description: "The second number." }
},
required: ["a", "b"]
},
async execute(input: ToolInput<AddArgs>): Promise<ToolOutput<number>> {
return { output: input.args.a + input.args.b };
}
};A single package can export as many tools as you need.
The package exists, but the agent does not see it until you declare a tool block pointing source at
the package directory.
tool "adder" {
kind = "javascript"
source = "./math"
}The block name matches the export name by default. If they differ, set export to the export name.
A tool can return a state_update alongside its output: an array of RFC 6902 JSON Patch operations.
For the ReAct graph, target paths under /context/. The patches are persisted and streamed to the
client as state delta events, which is how a tool passes data to a frontend.
async execute(input: ToolInput<AddArgs>): Promise<ToolOutput<number>> {
return {
output: input.args.a + input.args.b,
state_update: [
{ op: "add", path: "/context/last_sum", value: input.args.a + input.args.b }
]
};
}Rebuild so the tool is bundled into the binary:
agentc buildtool block for each kind.© 2026 pogue.dev. All rights reserved.
CC BY 4.0