agentwire

@kovenlabs/agentwire-tools

The defineTool factory and the tool registry.

pnpm add @kovenlabs/agentwire-tools

Tool inputs are typed via Standard Schema, so any compliant validator works — Zod, Valibot, ArkType. The examples use Zod.

Exports

ExportKindSummary
defineTool.serverfntool that runs execute on the server
defineTool.approvalfnserver tool gated behind approve/deny
defineTool.clientfntool resolved by your client handlers
defineTool.interactivefntool deferred to UI, settled via the bus
AgentToolDefinitiontypethe neutral definition defineTool.* returns
ToolKindtype"server" | "approval" | "client" | "interactive"
ToolDisplaytype{ label, description? }
getToolKindfnlook up a tool's kind by name
getToolDisplayfnlook up a tool's label/description
getInteractiveToolNamesfnnames of all interactive tools
registerToolfnlow-level registry write
_resetRegistryForTestingfnclear the registry (tests)

defineTool

Each method produces a neutral AgentToolDefinition and registers the tool's kind + display in the registry.

defineTool.server

Runs on the server; the result is returned to the model.

import { defineTool } from "@kovenlabs/agentwire-tools";
import { z } from "zod";

export const searchFlights = defineTool.server({
  name: "searchFlights",
  description: "Search available flights between two cities on a date.",
  label: "Searching flights",
  inputSchema: z.object({ from: z.string(), to: z.string(), date: z.string() }),
  execute: async ({ from, to, date }) => flightsApi.search({ from, to, date }),
});

defineTool.approval

A server tool gated behind approve/deny. approvalDescription (falling back to description) is the sentence shown on the approval card.

export const bookFlight = defineTool.approval({
  name: "bookFlight",
  description: "Book the selected flight and charge the traveler's card.",
  approvalDescription: "This charges your card and books the flight. Refunds may incur a fee.",
  label: "Booking flight",
  inputSchema: z.object({ flightId: z.string(), passengerId: z.string() }),
  execute: ({ flightId, passengerId }) => bookingApi.book(flightId, passengerId),
});

defineTool.client

No execute — you resolve it in useAgentChat's toolHandlers.

export const navigate = defineTool.client({
  name: "navigate",
  description: "Navigate the traveler to a screen, e.g. /trips.",
  label: "Navigating",
  inputSchema: z.object({ to: z.string() }),
});

defineTool.interactive

No execute — deferred to the UI and settled via the bus. See Interactive tools.

export const pickFlight = defineTool.interactive({
  name: "pickFlight",
  description: "Show the traveler the flights and let them choose one.",
  label: "Choosing a flight",
  inputSchema: z.object({
    options: z.array(z.object({ id: z.string(), airline: z.string(), price: z.number() })),
  }),
});

The definition shape

interface AgentToolDefinition<S> {
  name: string;
  kind: "server" | "approval" | "client" | "interactive";
  description: string;
  inputSchema: S;
  execute?: (input: InferredInput) => Promise<unknown> | unknown; // server/approval only
  needsApproval?: boolean;
  display: { label: string; description?: string };
}

Pass these to toAiToolSet to get AI SDK tools.

Registry

Populated as a side effect of defineTool.*. Lets the UI and chat runtime look a tool up by name without importing its module.

  • getToolKind(name) → the registered kind, or undefined.
  • getToolDisplay(name) → { label, description? }, falling back to { label: name }.
  • getInteractiveToolNames() → names of every tool registered as interactive. useAgentChat uses this to auto-detect interactive tools.
  • registerTool(name, meta) / _resetRegistryForTesting() — lower-level escape hatches.