Tools

Tools are async TypeScript functions in src/tools/. Export a function and the agent can call it. No registration, no schema definitions, no wiring.

// src/tools/github.ts

/** Open a pull request against the base branch. Returns the PR number and URL. */
export async function openPr(title: string, body: string, head: string) {
    const { data } = await octokit.pulls.create({ title, body, head, ...repo() })
    return { number: data.number, url: data.html_url }
}

/** Get the diff for a pull request. */
export async function getPrDiff(number: number): Promise<string> {
    const { data } = await octokit.pulls.get({ pull_number: number, ...repo() })
    return data.diff_url
}

Axon reads the TypeScript signatures and JSDoc at boot and hands them to the model as typed, documented capabilities. The filename is the namespace: tools/github.ts becomes github.openPr and github.getPrDiff. You write the function. The agent knows how to use it.

Write real JSDoc

The JSDoc is the tool's documentation to the model — it's the difference between the agent guessing at a function and using it correctly. Say what the function does, what it returns, and anything surprising:

/**
 * Search the issue tracker. Returns at most 20 results, newest first.
 * Query syntax: plain text, or `label:bug`, `assignee:name` filters.
 */
export async function searchIssues(query: string) { ... }

Types are read from the signature — don't repeat them in prose. After adding or changing tools, axon prepare regenerates the type declarations (opening Axon runs it automatically).

Where tools run

Tools execute in the capsule — an isolated subprocess, separate from the agent process. Two consequences you'll feel while building:

A tool that crashes doesn't crash the agent. Throws, leaks, even process.exit() — the capsule takes the hit, the runtime surfaces a structured error, the agent adapts.

Module scope is per-capsule-lifetime. Clients and caches at module level are cheap and fine, but they're recreated when the capsule reloads (tool edits, .env changes). Anything the agent must remember across reloads belongs in data/.

// Recreated on capsule reload — fine for a client, wrong for durable state.
const octokit = new Octokit({ auth: process.env.GITHUB_TOKEN })

Every tool call is also policy-checked before the function body runs — see Policy.

Calling tools yourself

Tools aren't only for the model. Scripts and routes call them directly, fully typed:

const diff = await axon.tools.github.getPrDiff(42)

Tools you don't write

Installed modules contribute tool namespaces the same way — axon install @axon/linear and the agent has linear.*. Same discovery, same typing, same capsule. See Modules.


Next: Prompts — the context the agent loads per task.