Anvia
Tools

Creating Tools

Define typed tools that agents can call.

Tools expose application-owned behavior to an agent. The model chooses when to call the tool, but your code owns the implementation.

Tool APIs

Use createTool() to define one tool. Use ToolSet to group reusable static tools for sharing, direct calls, and tests. Use ToolRegistry only when an agent needs a mutable or shared runtime tool store.

Minimal Tool

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

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

input validates arguments from the model before your handler runs. output validates the value returned by your handler before Anvia sends it back to the model.

Add the Tool to an Agent

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

The turn limit gives the agent room to call the tool and then ask the model for the final answer.

Naming

Use names that describe the action.

GoodAvoid
lookup_orderorder
create_refundrefund_tool
search_docssearch

Descriptions should explain when to use the tool, not how the tool is implemented.

Read-Only vs Side-Effect Tools

Read-only tools can usually run automatically.

const searchDocs = createTool({
  name: "search_docs",
  description: "Search public documentation.",
  input: z.object({ query: z.string() }),
  async execute({ query }) {
    return docs.search(query);
  },
});

Side-effect tools should usually be paired with Human in the Loop approval.

const refundOrder = createTool({
  name: "refund_order",
  description: "Refund an eligible order.",
  input: z.object({ orderId: z.string() }),
  output: z.object({ refunded: z.boolean() }),
  async execute({ orderId }) {
    return refunds.create(orderId);
  },
});

Keep the tool implementation explicit. Do not rely on the model prompt as the permission boundary.