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 }

Tools that navigate the page

If executeJs sets window.location (directly, via .href, .assign(), .replace()) or submits a form, the page can start unloading before the calling agent gets a result back. The editor warns you inline if it detects this pattern, but the warning can't guarantee the call actually completes, unload timing is up to the browser.

If you know a tool is meant to navigate (e.g. goToCheckout), that's fine, just be aware the agent may see the call as failed or timed out even though the navigation itself succeeded. Aigentably does best-effort detection of this: if the page unloads while a call is still in flight, it's logged as Interrupted in Recent Calls instead of silently disappearing, so at least you can tell it happened.

For tools where the agent needs a reliable result (confirmation of an action, data to act on), avoid navigating in executeJs and read/write through fetch instead.

Restricting who can call a tool

By default, any AI agent that discovers your tools can call them. The Allowed origins field lets you scope a tool to specific callers instead, using WebMCP's exposedTo option.

Enter a comma-separated list of origins, e.g. https://claude.ai, https://chatgpt.com. Only agents operating from those origins can invoke the tool. Leave it blank to keep the tool open to everyone, which is the right choice for most tools.

This is a good fit for sensitive actions, things like submitSupportTicket or applyCoupon, where you'd rather not have every agent that visits your page able to call them.

Path patterns scope a tool to specific pages. Allowed origins scope a tool to specific callers. Use both together for tight control: a tool only registered on /checkout and only callable by a named agent.

Tool history and rollback

Every time a tool is created, edited, or reverted, Aigentably saves a full snapshot. Click the history icon on any tool to see:

  • What changed between versions, field by field
  • Whether a change has been marked as reviewed
  • A Revert to this button on any past version

Reverting doesn't erase history, it creates a new version whose content matches the one you reverted to, so the full trail stays intact. Use this if a change turns out to be wrong, or if you want to confirm nobody else with dashboard access modified a tool's executeJs without you knowing.

Security warnings

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