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

# How the Script Works

> What happens every time the OpenHermit script loads on a page.

The script (`script.js`) is a self-contained vanilla JavaScript file under 20KB. It runs as an IIFE (immediately invoked function expression) — it never pollutes the global scope and will never break your page.

## Execution order

Every time the script loads on a page, it does the following in sequence:

<Steps>
  <Step title="Ping (immediate)">
    Before anything else, the script fires a POST to `/api/ping` with the current page URL and script version. This confirms the script is correctly installed — your dashboard installation badge updates within seconds.
  </Step>

  <Step title="Inject discoverability tags">
    The script adds machine-readable metadata to the page `<head>` so AI agents can find the manifest:

    ```html theme={null}
    <meta name="webmcp" content="https://openhermit.com/api/manifest?key=YOUR_KEY" />
    <link rel="webmcp" href="https://openhermit.com/api/manifest?key=YOUR_KEY" />
    ```
  </Step>

  <Step title="Detect AI agents">
    The script checks the user-agent string for known AI crawlers (GPTBot, ClaudeBot, PerplexityBot, Google-Extended, Operator, and others) and headless browser signals. If an agent is detected, it's recorded with each event.
  </Step>

  <Step title="Scan for forms">
    All `<form>` elements on the page are scanned. For each form, the script:

    * Extracts all input fields (name, type, required, placeholder)
    * Classifies the form type: `contact_form`, `booking`, `newsletter`, `signup`, `purchase`, `quote`, `login`, `search`, `upload`, or `support`
    * Generates a human-readable name from aria-labels, legends, nearby headings, or the form ID
    * Generates a `tool_name` (snake\_case, used in the manifest)
    * Injects `data-mcp-action`, `data-mcp-description`, `data-mcp-params` attributes
  </Step>

  <Step title="Detect third-party widgets">
    The script detects embedded widgets that aren't standard HTML forms:

    | Widget   | Detection method                                      |
    | -------- | ----------------------------------------------------- |
    | Calendly | `window.Calendly` object or `.calendly-inline-widget` |
    | Typeform | `tf-widget` element or typeform embed script          |
    | HubSpot  | `window.hbspt` or `.hs-form`                          |
    | Intercom | `window.Intercom`                                     |
    | Drift    | `window.drift`                                        |
    | Crisp    | `window.$crisp`                                       |
  </Step>

  <Step title="Extract business info">
    The script reads Schema.org JSON-LD from the page (`@type: LocalBusiness`, `Organization`, etc.) to extract the business name, description, address, and phone number. This enriches the manifest.
  </Step>

  <Step title="Sync to OpenHermit">
    All detected actions (and business info) are sent to `/api/actions/sync`. OpenHermit stores new actions or updates existing ones — preserving any agent prompts you've already configured. The API returns your configured prompts back.
  </Step>

  <Step title="Inject agent prompts into DOM">
    Any configured prompts are injected as a hidden off-screen `div` with an `aria-label`. This means both DOM-reading and accessibility-tree-reading agents can find your instructions without any visual impact on the page.
  </Step>

  <Step title="Track form submissions">
    Event listeners are added to form submit buttons. When a form is submitted, a `completion` or `error` event is sent to `/api/events` via `navigator.sendBeacon` (fire-and-forget — never slows down the page).
  </Step>

  <Step title="Watch for dynamic content (SPAs)">
    A `MutationObserver` watches for new content added to the DOM. If a form appears after the initial load (common in React/Vue/Angular apps), the script re-runs detection automatically.
  </Step>
</Steps>

***

## Form type detection

The script classifies forms by scanning their HTML for keyword signals:

| Type           | Signals                                              |
| -------------- | ---------------------------------------------------- |
| `contact_form` | contact, message, inquiry, reach out, get in touch   |
| `booking`      | book, appointment, schedule, reserve, calendar, slot |
| `newsletter`   | newsletter, subscribe, email list, updates, notify   |
| `signup`       | sign up, register, create account, join, membership  |
| `purchase`     | buy, checkout, order, purchase, payment, cart        |
| `quote`        | quote, estimate, calculate, price, cost, proposal    |
| `login`        | login, sign in, password, username                   |
| `search`       | search, find, look for                               |
| `upload`       | upload, attach, file, CV, resume, document           |
| `support`      | support, help, ticket, issue, problem, complaint     |

If no signal matches, the form is classified as `contact_form` by default.

***

## Performance impact

* Script size: **\< 20KB** uncompressed
* Loads `async` — never blocks page rendering
* Uses `sendBeacon` for event tracking — fire-and-forget, zero latency impact
* Only runs after `DOMContentLoaded` — doesn't affect Time to First Byte or FCP

***

## Security

The script never reads or transmits form field values. It only reads:

* Field names, types, and labels (for schema building)
* Page URL and title
* User agent string (for agent detection)
* Schema.org structured data (if present)

Sensitive user input is never captured.
