AG2B

Tool

A Tool is what the agent calls during a loop iteration. It can do anything a function can — mutate state, trigger side effects, call code you've already written. The return value, if any, feeds back into the LLM on the next turn.

Usage

import { Tool } from '@ag2b/core';
import { z } from 'zod/v4';

const createNote = new Tool({
  name: 'createNote',
  description: 'Create a new note.',
  parameters: z.object({
    title: z.string(),
    body: z.string(),
  }),
  handler: ({ title, body }) => useNotes.getState().create({ title, body }),
});

Expose the tool to the agent by registering it with a Scope.

Multiple tool calls in one turn execute sequentially — in the order the LLM requested, never in parallel.

Async Handlers

Handlers can be async. The agent awaits the result before continuing the loop.

handler: ({ a, b }) => Promise.resolve(a + b)

handler: async ({ a, b }) => await sum(a, b)

Conditional Availability

Add an enabled predicate to gate the tool. It's evaluated at two points:

  • When the agent builds a request — a false result filters the tool out, so the LLM doesn't see it on that turn.
  • When the tool is invoked — if the LLM calls a tool that's now disabled (e.g., from a stale earlier turn). See Ag2bDisabledToolError.
import { Tool } from '@ag2b/core';
import { z } from 'zod/v4';

const createNote = new Tool({
  name: 'createNote',
  description: 'Create a new note.',
  parameters: z.object({
    title: z.string(),
    body: z.string(),
  }),
  enabled: () => useAuth.getState().isSignedIn,
  handler: ({ title, body }) => useNotes.getState().create({ title, body }),
});

Throws inside enabled are coerced to false — a broken predicate hides the tool, never crashes the loop.

Configuration

type ToolConfig<Parameters extends z.ZodObject, Return> = {
  name: string;
  description: string;
  parameters: Parameters;
  handler: (params: z.infer<Parameters>) => ToolHandlerReturn<Return>;
  enabled?: () => boolean;
};

type ToolHandlerReturn<R> = R | Promise<R>;

name

name: string

Unique identifier for the tool. The LLM uses this name to invoke it.

The rules:

  • Must match /^[a-zA-Z0-9_-]+$/
  • Must be unique across every registered Scope

If either rule is broken, the agent throws Ag2bError at setup — not recoverable by LLM, propagates to the caller.

description

description: string

What the tool does. Sent to the LLM verbatim; the model uses it to decide when to call.

Treat this as the LLM's only documentation for the tool.

parameters

parameters: z.ZodObject

Zod object schema for the tool's arguments. Converted to JSON Schema and sent to the LLM.

Always import from zod/v4

The zod/v4 works on both zod@^3.25.0 (where v4 ships as a subpath) and zod@^4.0.0 (where it's equivalent to zod). Mirror this in your code so it stays portable across the range.

The top level must be an object — LLMs call tools with named arguments. Use Zod's full toolkit (.describe(), .min(), .max(), etc.) to constrain and document each field. The JSON Schema sent to the LLM reflects all of it.

parameters: z.object({
  email: z.string().email().describe('User email'),
  quantity: z.number().int().positive().max(99),
}),

When the LLM invokes the tool, the runtime parses the arguments through this schema before calling your handler. On a successful parse, your handler runs with typed, validated args. On failure, Ag2bToolValidationError fires.

handler

handler: (args) => ToolHandlerReturn<R>

The function the agent calls when the LLM invokes the tool. Sync or async, both are awaited.

The return value is JSON-serialized into a tool message and feeds back to the LLM on the next turn.

enabled

enabled: (() => boolean) | undefined

Predicate that gates the tool. When omitted, the Scope's own enabled still gates the group. See Conditional Availability.

History

Each tool call leaves two records in history:

  • Request — an AssistantToolCall on the assistant message's calls array. The LLM emits it when it decides to invoke the tool.
  • Result — a ToolMessage appended after the handler returns. Its id matches the originating call. Errors are serialized into this same message, so the LLM can react to them on the next turn.

Hooks

The agent fires these around tool execution — each one is an interceptor over the value passing through. See Hooks for full signatures and examples:

Runtime Errors

Tool runtime errors don't abort the chat. The agent writes them as ToolMessages and lets the LLM recover.

Handler Throws

Any error raised inside a handler is caught by the agent and serialized as a tool message.

Ag2bToolValidationError

Thrown at invocation when LLM-provided arguments fail the Zod schema. Carries .tool (the tool name) and .issues (the Zod issue list).

Ag2bDisabledToolError

Thrown at invocation when the LLM calls a tool whose enabled returned false — typically a stale call from an earlier turn.

Ag2bUnknownToolError

Thrown at invocation when the LLM calls a name that no registered scope provides.

On this page