Write Tools by Hand

Full control over every field — best for auth-protected pages, complex logic, or precise DOM targeting.

When to write by hand

  • Your page is behind a login (AI generation can't scrape it)
  • You need specific DOM selectors that AI wouldn't guess
  • You want deterministic, well-tested executeJs logic

The input schema

The input schema defines what parameters the tool accepts. It follows JSON Schema format.

Using the Form Builder

Add each parameter with a name, type, and optional description. The required toggle marks a field as mandatory.

Supported types: string, number, boolean, array, object.

Using Raw JSON

Switch to Raw JSON mode for full schema control:

{
  "type": "object",
  "properties": {
    "productId": {
      "type": "string",
      "description": "The product SKU, e.g. shoe-01"
    },
    "quantity": {
      "type": "number",
      "description": "Number of items to add. Defaults to 1 if omitted."
    }
  },
  "required": ["productId"]
}

Write clear description values — AI agents read these to understand what to pass. "Product ID" is vague. "The product SKU from the URL, e.g. shoe-01" is useful.

The executeJs field

JavaScript that runs in the visitor's browser when the tool is called.

Available globals:

  • args — the input parameters, typed per your schema
  • document, window, fetch — full browser environment
  • No Node.js APIs (no require, fs, process)

Return value: A plain object returned to the AI agent as the tool result. Always return something meaningful.

// Click a button
const btn = document.querySelector('#submit-btn')
if (!btn) return { success: false, error: 'Button not found' }
btn.click()
return { success: true }
// Call an internal API
const res = await fetch('/api/cart/add', {
  method: 'POST',
  headers: { 'Content-Type': 'application/json' },
  body: JSON.stringify({ productId: args.productId, quantity: args.quantity ?? 1 })
})
const data = await res.json()
return { success: res.ok, ...data }
// Read a value from the page
const price = document.querySelector('[data-price]')?.textContent
return { price: price?.trim() ?? null }

Security warnings

The editor shows live warnings for dangerous patterns. See executeJs Reference for the full list.