Skip to main content

Define a tool

Create a file in tools/:
// tools/get-weather.ts
import { tool } from "veryfront/tool";
import { z } from "zod";

export default tool({
  description: "Get the current weather for a city",
  inputSchema: z.object({
    city: z.string().describe("City name"),
    units: z.enum(["celsius", "fahrenheit"]).default("celsius"),
  }),
  execute: async ({ city, units }) => {
    const response = await fetch(`https://api.weather.com/v1?city=${city}&units=${units}`);
    const data = await response.json();
    return { temperature: data.temp, conditions: data.conditions };
  },
});
The filename becomes the tool’s ID. This tool registers as "get-weather" (hyphens from the filename are preserved).

How agents use tools

When you add a tool to an agent, the framework sends the Zod schema to the model. The model decides when to call the tool and provides the parameters:
// agents/assistant.ts
import { agent } from "veryfront/agent";

export default agent({
  model: "openai/gpt-4o",
  system: "You are a weather assistant. Use the getWeather tool to answer weather questions.",
  tools: { getWeather: true },
  maxSteps: 3,
});
When a user asks “What’s the weather in Tokyo?”, the agent:
  1. Sends the question to the model
  2. The model calls getWeather({ city: "Tokyo" })
  3. The tool returns { temperature: 22, conditions: "sunny" }
  4. The model formats a natural language response

Tool configuration

PropertyTypeDescription
descriptionstringWhat the tool does (shown to the model)
inputSchemaz.ZodSchemaZod schema for input validation
execute(params) => Promise<unknown>Function that runs when the tool is called
idstringOverride the auto-generated ID

Returning errors

Throw from execute to signal an error. The agent sees the error message and can retry or respond accordingly:
export default tool({
  description: "Look up a user by email",
  inputSchema: z.object({ email: z.string().email() }),
  execute: async ({ email }) => {
    const user = await db.users.findByEmail(email);
    if (!user) throw new Error(`No user found with email ${email}`);
    return { id: user.id, name: user.name };
  },
});

Tools with context

Tools can access the request context when executed during an API route:
export default tool({
  description: "Get the current user's profile",
  inputSchema: z.object({}),
  execute: async (_, { context }) => {
    const userId = context?.userId;
    if (!userId) throw new Error("Not authenticated");
    return await db.users.findById(userId);
  },
});
Pass context from the API route:
// app/api/chat/route.ts
const result = await agent.stream({
  messages,
  context: { userId: "user-123" },
});

Inline tools

For one-off tools that don’t need auto-discovery, define them inline:
import { agent } from "veryfront/agent";
import { tool } from "veryfront/tool";
import { z } from "zod";

export default agent({
  model: "openai/gpt-4o",
  system: "You are a math tutor.",
  tools: {
    calculate: tool({
      description: "Evaluate a math expression",
      inputSchema: z.object({ expression: z.string() }),
      execute: async ({ expression }) => ({ result: eval(expression) }),
    }),
  },
});

Next