> ## Documentation Index
> Fetch the complete documentation index at: https://docs.salesive.com/llms.txt
> Use this file to discover all available pages before exploring further.

# AI tools (WebMCP)

> Register tools from your app that Ola — the merchant's AI assistant — can call, with merchant approval

Your embedded app can offer **tools** to Ola, the AI assistant built into every
merchant's Salesive dashboard. A tool is a JS function in your page with a name,
a description, and a JSON Schema for its input. Once registered, Ola can discover
it and call it while chatting with the merchant — a delivery app can expose
`track_delivery`, a bookings app `find_free_slot`, an accounting app
`get_unpaid_invoices`.

The API follows [WebMCP](https://github.com/webmachinelearning/webmcp) — the
W3C proposal that exposes `navigator.modelContext` so pages can register tools
for AI agents. You don't need a browser that supports WebMCP:
`salesive-dev-tools` ships a complete implementation that works everywhere, and
mirrors into the native API when one exists.

## How a call flows

1. Your page registers tools with `modelContext.registerTool(...)`.
2. The dashboard forwards the **descriptors** (name, description, schema —
   never your functions) to Ola's backend.
3. When Ola decides to use your tool, the merchant sees an **approval card in
   the chat** — your app's logo, the tool name, and the arguments — with
   **Approve**, **Reject**, and **Always allow**.
4. On approval, the call is posted into your iframe, your `execute()` runs
   **in your page** (your session, your data), and the result goes back to Ola.

<Note>
  Tools run entirely client-side in your iframe. They involve no OAuth scopes,
  no API tokens, and no server-side access — a tool can only do what your page
  can already do.
</Note>

## Registering a tool

```js theme={null}
import { modelContext } from "salesive-dev-tools/webmcp";

modelContext.registerTool({
  name: "track_delivery",                    // letters, digits, _ (≤ 30 chars)
  title: "Track delivery",                   // shown on the approval card
  description:
    "Track a delivery by order id and return its live status and ETA.",
  inputSchema: {
    type: "object",
    properties: {
      orderId: { type: "string", description: "The Salesive order id" },
    },
    required: ["orderId"],
  },
  annotations: { readOnlyHint: true },       // reads only — modifies nothing
  async execute({ orderId }) {
    const status = await fetchDeliveryStatus(orderId);
    return { content: [{ type: "text", text: `Delivery ${orderId}: ${status}` }] };
  },
});
```

`execute` may return an MCP-style `{ content: [{ type: "text", text }] }`
object, a plain string, or any JSON-serializable value — the SDK normalizes it.
Throw an error to report failure; Ola is told and continues gracefully.

Unregister with `modelContext.unregisterTool("track_delivery")`, or pass an
`AbortSignal` at registration (spec-style):

```js theme={null}
const controller = new AbortController();
modelContext.registerTool({ ...tool }, { signal: controller.signal });
controller.abort(); // unregistered
```

<Tip>
  Register tools at startup (e.g. inside `onAppWake` or on module load). Your
  app is pre-loaded in the background when the dashboard opens, so your tools
  are available to Ola even before the merchant ever opens your app.
</Tip>

## Browser support — handled in the package

`salesive-dev-tools/webmcp` is a **self-contained implementation**, not a thin
wrapper over a browser API:

* **No native WebMCP** (every stable browser today): the module itself is the
  implementation, and it also installs itself as `navigator.modelContext` and
  `document.modelContext` when they're absent — code written against the
  standard API runs unchanged.
* **Native WebMCP present** (e.g. Chrome's origin trial): the native object is
  left untouched, and your registrations are mirrored into it too — so
  browser-level agents see your tools alongside Ola.

Import the SDK's `modelContext` as your source of truth and you never need to
feature-detect anything.

## Approval, "Always allow", and revocation

Every call needs the merchant's explicit approval in the Ola chat. The approval
card shows **your app's logo and name from the installation record** (not from
your registration payload), the tool title, and the exact arguments.

* **Approve** — this one call runs.
* **Always allow** — this call runs, and future calls of **this specific tool**
  from **this installation** run without asking.
* **Reject** — Ola is told the merchant declined and moves on.

Merchants revoke "Always allow" grants any time from your app's detail page in
the dashboard (*AI tools you always allow*). Grants are per store — installing
your app on a second store starts with a clean slate.

<Warning>
  A pending approval waits up to **2 minutes**, and Ola's whole turn waits with
  it. If the merchant ignores the card, sends another message, or the app
  can't respond, the call resolves as an error and Ola continues without it.
  Design tools to be fast and non-blocking; return errors rather than hanging.
</Warning>

## Limits

| Limit         | Value                                            |
| ------------- | ------------------------------------------------ |
| Tools per app | 32                                               |
| Tool name     | `[a-zA-Z0-9_]`, ≤ 30 chars                       |
| Description   | ≤ 500 chars                                      |
| Title         | ≤ 80 chars                                       |
| Input schema  | JSON Schema, `type: "object"`, ≤ 8 KB serialized |
| Result        | ≤ 16 KB (longer results are truncated)           |
| Call timeout  | \~2 min including merchant approval              |

Registrations exceeding a limit are rejected server-side; the rest of the
payload still registers. Names are automatically namespaced per app
(`app__yourapp_xxxx__tool_name`), so two apps can expose tools with the same
local name without conflict.

## Full example — React

```jsx theme={null}
import { useEffect } from "react";
import { modelContext } from "salesive-dev-tools/webmcp";

export default function App() {
  useEffect(() => {
    const controller = new AbortController();

    modelContext.registerTool(
      {
        name: "get_open_orders",
        title: "Get open orders",
        description: "List this store's open delivery orders with their ETAs.",
        inputSchema: { type: "object", properties: {} },
        annotations: { readOnlyHint: true },
        async execute() {
          const orders = await api.listOpenOrders();
          return {
            content: [
              { type: "text", text: orders.map((o) => `#${o.id}: ${o.eta}`).join("\n") },
            ],
          };
        },
      },
      { signal: controller.signal },
    );

    return () => controller.abort(); // unregister on unmount
  }, []);

  return <Dashboard />;
}
```

## Security model

* Your `execute` functions **never leave your page** — only descriptors are
  shared, and calls come back to your iframe over the same origin-checked
  App Bridge as every other event.
* Every call is gated by merchant approval (or a prior explicit "Always allow"
  for that exact tool).
* Tool results are treated as third-party data by Ola — descriptions and
  results are length-capped and sandboxed with untrusted-content guidance.
* Only Ola (the default dashboard assistant) can use app tools; other agents
  on the platform can't see them.

## Next steps

<CardGroup cols={2}>
  <Card title="App Bridge" icon="bridge" href="/apps/app-bridge">
    Lifecycle events, runtime permissions, and the file picker.
  </Card>

  <Card title="Build & publish" icon="rocket" href="/apps/building-publishing">
    Submit your app for marketplace review.
  </Card>
</CardGroup>
