Anvia
Agents

Tools on Agents

Attach tools to agents and control how the model may call them.

Tools let an agent call application-owned behavior. The model chooses the tool and arguments; your code owns validation, permissions, side effects, and returned data.

Add One Tool

import { AgentBuilder, createTool } from "@anvia/core";
import { z } from "zod";

const lookupOrder = createTool({
  name: "lookup_order",
  description: "Look up an order by order id.",
  input: z.object({
    orderId: z.string(),
  }),
  output: z.object({
    status: z.string(),
  }),
  async execute({ orderId }) {
    return orders.findById(orderId);
  },
});

const agent = new AgentBuilder("support", model)
  .instructions("Use tools when order status is needed.")
  .tool(lookupOrder)
  .defaultMaxTurns(3)
  .build();

Use clear tool names and descriptions. The model relies on them when deciding whether to call a tool.

Add Multiple Tools

const agent = new AgentBuilder("support", model)
  .tools([lookupOrder, createReturnLabel, updateAddress])
  .defaultMaxTurns(4)
  .build();

Keep tools narrow. A focused tool is easier to validate, approve, test, and observe.

Control Tool Choice

const agent = new AgentBuilder("support", model)
  .tool(lookupOrder)
  .toolChoice("auto")
  .build();
ChoiceBehavior
"auto"The model may call a tool
"required"The model should call a tool
"none"Tools are available on the agent but disabled for the model call
{ type: "function", name: "lookup_order" }Prefer one specific tool

Provider support for strict tool-choice behavior can vary.

Tool Loop

When the model calls a tool, Anvia:

  1. validates tool arguments
  2. calls your tool handler
  3. appends a tool-result message
  4. calls the model again
  5. returns the final assistant answer

Set a low turn limit while building the workflow.

const response = await agent.prompt("Where is order A-100?").maxTurns(3).send();

Require Approval

Use hook-based tool approval when a tool should not run until your app approves it.

import { createHook, requireApproval } from "@anvia/core";

const approvalHook = createHook({
  onToolCall({ toolName }) {
    if (toolName === "refund_order") {
      return requireApproval({
        reason: "Refunds require staff approval.",
        rejectMessage: "Refund was not approved.",
      });
    }
  },
});

const response = await agent
  .prompt("Refund order A-100.")
  .withHook(approvalHook)
  .withToolApprovalHandler(async (approval) => {
    const approved = await approvals.waitForDecision(approval);

    return {
      ...approval,
      status: approved ? "approved" : "rejected",
      resolvedAt: new Date().toISOString(),
    };
  })
  .send();

For the full approval flow, read Human in the Loop.