# Lend a tool

> Lend the assistant a tool with useAssistedTool, scoped to a page or global.

A tool is a capability beyond pressing an action: reading or changing what the page shows, or going elsewhere. It runs in the browser. Lend one with `useAssistedTool(name, { description, parameters, execute })`.

## Lend it on a page

Call `useAssistedTool` in a component rendered under the `AssistedPage`. The tool belongs to the page: the page's note lists it, and it leaves with the page. From `examples/app/app/orders/page.tsx`:

```tsx
useAssistedTool("filter_orders", {
  description: "Show only the orders with one status, or all of them.",
  parameters: {
    type: "object",
    properties: { status: { type: "string", enum: filters } },
    required: ["status"],
  },
  execute: ({ status }: { status: Filter }) => {
    setFilter(status);
    return `Showing ${status === "all" ? "all orders" : `the orders that are ${status}`}.`;
  },
});
```

## Split the page in two

The tool's hook must render under the `AssistedPage`, while `content` needs the same state. So a page with tools is two components: the state and the `AssistedPage` above, the tools below. From `examples/app/app/orders/page.tsx`:

```tsx
export default function Orders() {
  const shop = useShop();
  const [filter, setFilter] = useState<Filter>("all");
  const money = moneyOf(shop.settings.currency);
  const rows = shop.orders
    .filter((order) => filter === "all" || order.status === filter)
    .map((order) => rowOf(order, money));
  return (
    <AssistedPage
      name="Orders"
      instructions="Every order of the shop, as filtered by status with filter_orders, and charted by status with chart_orders. Ship or cancel an order that is waiting to ship by its id. New order opens a dialog that introduces itself. Each order has a page of its own with its note and refund."
      content={() => ({ filter, orders: rows })}
    >
      <OrdersTable filter={filter} setFilter={setFilter} rows={rows} />
    </AssistedPage>
  );
}
```

`OrdersTable` lends `filter_orders`, shown above.

## Lend it everywhere

Call `useAssistedTool` outside any `AssistedPage`, under `AssistedProvider`. The tool is global: it works from every page and no note lists it. From `examples/app/app/shell.tsx`:

```tsx
useAssistedTool("get_shop_summary", {
  description: "How many orders and customers the shop has. Works from any page.",
  parameters: { type: "object", properties: {} },
  execute: () => ({ orders: shop.orders.length, customers: shop.customers.length }),
});
```

## Describe the parameters

`parameters` takes a JSON Schema object or a Standard Schema such as a zod schema. With JSON Schema, type the arguments of `execute` yourself, as above. With zod, they are inferred:

```tsx
const refund = z.object({ id: z.string().describe("The order's id.") });

export function RefundTool({ refundOrder }: { refundOrder: (id: string) => Promise<void> }) {
  useAssistedTool("refund_order", {
    description: "Refund an order.",
    parameters: refund,
    execute: async ({ id }) => {
      await refundOrder(id);
      return `Refunded ${id}.`;
    },
  });
  return null;
}
```

Define the schema at module scope.

The tool is lent again when its description or its parameters' JSON Schema changes. `execute` always runs the latest closure.

## Let failures speak

Throw from `execute` when the tool cannot do its job. The model gets `{ error: "<name> failed: <message>" }` and the turn goes on. Arguments the schema rejects answer with an `{ error }` too.

## Show the model an image

`toModelOutput` replaces what the model sees with text and file parts, while the result stays what the thread shows. From `examples/app/app/orders/page.tsx`, a tool that draws the orders by status on a canvas and hands the model the PNG:

```tsx
useAssistedTool("chart_orders", {
  description: "A bar chart of the shop's orders by status, as an image.",
  parameters: { type: "object", properties: {} },
  execute: () => {
    const counts = statuses.map((status) => [status, countOf(status)] as const);
    return { counts: Object.fromEntries(counts), png: barChart(counts) };
  },
  toModelOutput: ({ output }) => [
    { type: "text", text: `Orders by status: ${JSON.stringify(output.counts)}` },
    { type: "file", data: output.png, mediaType: "image/png", filename: "orders.png" },
  ],
});
```

`data` is base64. Up to 4 files of 5 MB each; more answers with an `{ error }`. Images show under the call in the thread. A model that takes no files gets the text and a line counting the files it missed.

## Show a card for the call

`render` takes a tool UI component for the tool's calls in the thread. Define it at module scope, so the same component is passed on every render.

To ask the user before the tool runs, see [Make an action consequential](https://superassist.app/how-to/make-an-action-consequential.md#ask-before-a-tool-runs).
