# Define a page tool

> Every field of a page tool, how to name and describe it so the AI uses it well, and what it can return.

A page tool is a plain JavaScript object in the shape of WebMCP, the draft web standard for tools a website offers to AI agents.

```js
const addToCart = {
  name: 'add_to_cart',
  title: 'Add to cart',
  description: "Add this page's product to the visitor's cart. Use it when the visitor wants to buy or add it.",
  inputSchema: {
    type: 'object',
    properties: {
      quantity: { type: 'integer', minimum: 1, description: 'How many to add' },
    },
    required: ['quantity'],
  },
  async execute({ quantity }, { signal }) {
    const cart = await addProductToCart(PRODUCT_ID, quantity, { signal }); // your own code
    return { added: quantity, items: cart.items.length, total: cart.total };
  },
};
```

## Fields

| Field | Required | What it is |
| --- | --- | --- |
| `name` | Yes | Letters, digits, `_` and `-`, up to 58 characters. Other characters become `_`. The agent sees `site__<name>`. |
| `description` | Yes | When to use the tool and what it does, up to 1,024 characters. Without one, the `title` (or the `name`) is used. |
| `title` | No | A short name for people. |
| `inputSchema` | No | A JSON Schema of an object: the input the agent fills in. Without one, the tool takes no input. |
| `annotations` | No | `consequentialHint: true` makes the visitor approve each call first. |
| `execute(input, { signal })` | Yes | Your code. Return a value or a promise of one; throw to report a failure. `signal` aborts when the visitor stops the call or moves on. |

## Write names and descriptions the AI understands

The agent chooses tools by reading their names and descriptions, so write them for that reader.

- **Name the action:** a verb and an object, such as `open_booking`, `read_cart` or `start_return`. Avoid names like `action1` or `handleClick`.
- **Say when to use it, not only what it does:** "Open the booking form for a meeting type. Use it whenever the visitor wants to book a call."
- **Say what it can't do:** "Works only for the product on this page."
- **One tool, one job.** Two clear tools work better than one tool with a mode switch.
- **Describe every input** with `description`, and limit choices with `enum`.

## Describe the input with JSON Schema

```js
inputSchema: {
  type: 'object',
  properties: {
    orderId: { type: 'string', description: 'The order number, such as A-1001' },
    reason: { type: 'string', enum: ['damaged', 'wrong-size', 'changed-mind'] },
    refundToCard: { type: 'boolean', description: 'Refund to the original card instead of store credit' },
    skus: { type: 'array', items: { type: 'string' }, description: 'The products to return' },
  },
  required: ['orderId', 'reason'],
},
```

The agent fills in the input, but treat it as you would a form submission: check it in `execute` before you act on it.

## What `execute` returns

What you return becomes the tool's result, as JSON:

| You return | The agent gets |
| --- | --- |
| An object, array, string, number or boolean | It as it is |
| Nothing | `null` |
| A `Date` | The date as text |
| A page element | Its tag, such as `"[dialog]"` |
| The same object twice, or a loop | `"[circular]"` where it repeats |
| A `BigInt` | Its digits as text |
| A function | Left out |
| More than 30,000 characters | `{ truncated: true, preview }` with the first 30,000 |

Return what the agent needs to answer well, such as `{ opened: true }` or `{ items: 2, total: '$48' }`. Never return secrets: results are saved in the conversation, and your team reads them in **Inbox**.

## Errors

Throw an `Error`, or reject the promise, and the agent gets your message, such as "The booking form is already open", up to 1,000 characters. It tells the visitor or tries another way. For an expected miss, such as an item out of stock, return a normal result like `{ ok: false, reason: 'out of stock' }`: it gives the agent more to work with than an error.

## Reading tools and actions

- A tool that only reads, such as the cart or the product on the page, runs straight away. You can mark it `annotations: { readOnlyHint: true }` for other WebMCP agents; help.ai doesn't use the hint yet.
- A tool that changes something the visitor would want to confirm, such as an order, a refund or a message to someone, should have `consequentialHint: true`. See [Ask before an action runs](https://docs.help.ai/page-tools/ask-before-a-page-tool-runs).

## Duplicates and mistakes

- **Same name twice:** the latest registration wins. Remove it (see [Add page tools to your site](https://docs.help.ai/page-tools/add-page-tools-to-your-site)) and the earlier one is back.
- **A broken tool:** one without a `name` or an `execute` function is left out, with a warning in the browser console. Your other tools still work, and the chat never breaks because of a tool.
- **A tool that's gone:** if the agent calls a tool your page no longer offers, because the visitor moved to another page, the agent learns that the page doesn't offer it right now and tells the visitor.
