# SmartMacros MCP reference for AI agents

Server: smartmacros-mcp 0.1.1 (npm). Transport: stdio. It controls the SmartMacros Chrome extension on the user's own machine.
Machine-readable tool schemas: https://smartmacros.pages.dev/mcp-tools.json
Human page: https://smartmacros.pages.dev/mcp

## How to work with it

1. Call connection_status. If connected is false, ask the user to open Chrome, switch on AI access in SmartMacros Settings and check the token.
2. Before writing steps for a page, call open_tab (or use the active tab) and page_snapshot. Use the locators it returns; do not guess selectors. find_elements helps when the snapshot is large.
3. Save reusable work with create_macro, then run_macro. Use run_steps for one-off actions that should not be saved.
4. run_macro and run_steps return when the run is finished or when it waits for the user (status waiting_user). Read needsUser.message, then call answer_run (retry, skip, skipRow, continue, stop) or tell the user what SmartMacros is asking.
5. Steps that delete, pay, confirm or transfer always ask the user before a repaired locator is used. Do not try to work around that.
6. After a run, results holds one entry per data row with status and the variables saved by extract steps; repairs counts steps SmartMacros fixed on its own (see list_repairs).

## Tools

### Macros
Read and write the macros saved in SmartMacros.

- list_macros(no arguments): All macros saved in SmartMacros with id, name, start URL, step count, data rows and last run status.
- get_macro(id): Full JSON of one macro: steps with their locators, data source, variables, triggers.
- create_macro(name, startUrl?, steps, dataSource?, vars?, tags?): Saves a new macro. Read smartmacros://guide first. Steps need only type, target (locators), value and options; ids, labels and defaults are filled in by SmartMacros.
- update_macro(id, name?, startUrl?, steps?, dataSource?, vars?, tags?): Replaces the given fields of an existing macro. Pass steps to replace the whole step list; pass dataSource to replace the data rows.
- delete_macro(id): Deletes a macro permanently.

### Runs
Start a macro, follow it, answer when it waits, stop it.

- run_macro(id, mode?, data?, wait?, timeoutSeconds?): Runs a saved macro in Chrome. mode 'once' runs it one time, 'rows' once per data row. With wait=true (default) the call returns when the run finishes or needs the user, with per-row results, variables and repairs.
- run_status(no arguments): State of the current or last run: status, row, step, repairs, variables, whether it waits for the user, last log lines.
- answer_run(choice): When run_status shows status waiting_user: retry the step, skip it, skip the row, continue after a pause, or stop. Picking an element on the page needs the user.
- stop_run(no arguments): Stops the current run. Rows already finished stay finished.

### Browser
Look at the open page and act on it without saving a macro.

- open_tab(url): Opens a URL in a new tab and makes it active. Returns the tab id for page_snapshot and find_elements.
- page_snapshot(tabId?, maxElements?, textChars?): Title, URL, visible text and the interactive elements of the active tab (or tabId), each with a ready-to-use locator. Use it before writing steps for a page.
- find_elements(description, tabId?, max?): Finds elements on the active tab (or tabId) that match a description such as 'email field' or 'Save button', scored, each with locators to put into a step target.
- run_steps(steps, url?, vars?, timeoutSeconds?): Runs a list of steps in the active tab (or at url) without saving a macro. Use it to control the browser directly: open a page, fill a form, read values. Same executor and repairs as saved macros.

### History and status
What ran, what was repaired, and whether the link is up.

- list_runs(limit?): Recent runs with macro name, rows done, repairs and results.
- list_repairs(limit?): Steps SmartMacros repaired: macro, step, old and new locator, who solved it, confidence.
- connection_status(no arguments): Whether the SmartMacros extension is connected to this bridge, and its build.

## Guide: Writing SmartMacros macros

A macro is JSON: { name, startUrl, steps: [...], dataSource?, vars? }.
SmartMacros fills in ids, labels and defaults, so a step only needs { type, target?, value?, options? }.

### Targets (how a step finds its element)

target = { locators: [ { kind, value }, ... ] }. Give two or three locators, best first. Kinds and value formats:
- id: "#email"                       - css: "form .btn-primary"            - xpath: "//button[1]"
- text: "Save lead" (exact visible text of a button, link or other element; add "tag": "button" to narrow)
- label: "Email" (the <label> text of an input, textarea or select)
- name: '[name="email"]'             - placeholder: '[placeholder="you@company.com"]'
- aria: '[aria-label="Close dialog"]' - testid: '[data-testid="lead-submit"]'   - role: "button:Save" (role:visible text)
A locator counts only when it matches exactly one element on the page.
Use page_snapshot or find_elements to get locators from the live page instead of guessing. When a locator stops matching later, SmartMacros repairs the step on its own and logs it.

### Values and variables

- {{name}} inserts a variable or a data column. Built-ins: {{$row}}, {{$date}}, {{$random}}, functions {{upper(name)}}, {{random(8)}}, {{pad($row,3)}}.
- Data rows: dataSource = { type: "inline", columns: ["name","email"], rows: [["Jane","jane@x.io"], ...] }. run_macro with mode "rows" runs once per row.
- extract / extractList / prompt / runJs store into variables; run results return them per row.

### Step types

- navigate: Go to URL. value = the URL.
- tab: Tabs. value = 'new' (open a blank tab and continue there) | 'waitForNew' (default: continue in the tab the previous click opened) | 'close' (close the current tab).
- frame: Enter an iframe. target = the frame element.
- click: Click. target required.
- dblclick: Double click. target required.
- hover: Hover. target required.
- rightclick: Right click. target required.
- type: Type text into a field. target required, value = the text. options.pressEnter = true presses Enter afterwards, options.clear = false appends instead of replacing.
- select: Choose an option in a <select>. target required, value = visible option text or value.
- check: Tick or untick a checkbox or radio. target required, value = 'yes' (default) | 'no'.
- keypress: Press a key on the focused element. value = 'Enter' | 'Tab' | 'Escape' | 'Ctrl+A' (modifiers Ctrl, Alt, Shift, Meta joined with +).
- scroll: Scroll. With a target: scroll it into view. Without: value = 'bottom' or a pixel offset.
- upload: Attach a file to an <input type=file>. target required, value = file path (needs the helper, not available through the bridge).
- wait: Wait. value = milliseconds.
- waitFor: Wait until an element is visible or gone. target required, value = 'visible' (default) | 'hidden'.
- assert: Check something, fail the step if false. value = 'exists' (default) | 'notExists' | 'textContains:<substring>'. With a target the check is on that element; without a target textContains checks the whole page text.
- pause: Pause the run until the user presses Continue. value = message shown.
- prompt: Ask the user for a value in the page. value = variable name to store the answer, options.question = the question shown.
- extract: Save an element's text into a variable. target required, value = variable name. options.attr = 'text' (default) | 'value' | 'href' | 'html'.
- extractList: Save the text of every element matching the target into a list variable. value = variable name.
- setVar: Set a variable. options.name = variable name, value = expression, may use {{...}}.
- screenshot: Take a screenshot of the tab. value = file name.
- clipboard: Copy text to the clipboard. value = text.
- download: Download the file behind a link. target required, value = file name.
- webhook: POST the run variables as JSON to a URL. value = URL.
- runJs: Run JavaScript in the page. value = the code (it receives the run variables as `vars`). The return value appears in the run results.
- if: Start a conditional block. value = condition: '{{a}} == b', '!=', 'contains', 'startsWith', 'endsWith', '>', '<', '>=', '<=', '{{a}} empty', '{{a}} notEmpty'.
- else: Else branch of the nearest if.
- endIf: End of the conditional block.
- loop: Repeat the block. value = number of times (may be a {{variable}}).
- endLoop: End of the repeat block.
- comment: Note. value = text. Does nothing.
- aiDecide: Ask the AI configured in SmartMacros (needs the user's OpenRouter key) a short question about the page. value = question; the one-line answer is stored as a variable named after the question.
- mailWait: Mail step, not available through the bridge.
- mailRead: Mail step, not available through the bridge.
- mailOtp: Mail step, not available through the bridge.
- mailLink: Mail step, not available through the bridge.
- mailText: Mail step, not available through the bridge.

### Rules SmartMacros enforces

- Steps whose label contains delete, remove, pay, purchase, buy, confirm, submit order, checkout, send money or transfer always ask the user before a repaired locator is used.
- A step that cannot find its element after repairs pauses the run with status waiting_user. Use answer_run to retry, skip, skipRow, continue or stop, or wait for the user.
- Runs need Chrome open with the extension's AI access switched on.

### Examples

1. Log in and save a value
{ "name": "Read balance", "startUrl": "https://bank.example/login", "steps": [
  { "type": "navigate", "value": "https://bank.example/login" },
  { "type": "type", "target": { "locators": [{ "kind": "label", "value": "Username" }] }, "value": "{{user}}" },
  { "type": "type", "target": { "locators": [{ "kind": "label", "value": "Password" }] }, "value": "{{password}}", "options": { "pressEnter": true } },
  { "type": "waitFor", "target": { "locators": [{ "kind": "text", "value": "Accounts" }] }, "value": "visible" },
  { "type": "extract", "target": { "locators": [{ "kind": "css", "value": ".balance" }] }, "value": "balance" } ] }

2. Add every row of a table to a CRM
{ "name": "Add leads", "startUrl": "https://crm.example/leads/new", "dataSource": { "type": "inline", "columns": ["name","email"], "rows": [["Jane Cooper","jane@x.io"],["Tom Alvarez","tom@y.mx"]] }, "steps": [
  { "type": "navigate", "value": "https://crm.example/leads/new" },
  { "type": "type", "target": { "locators": [{ "kind": "id", "value": "#name" }, { "kind": "label", "value": "Full name" }] }, "value": "{{name}}" },
  { "type": "type", "target": { "locators": [{ "kind": "id", "value": "#email" }, { "kind": "label", "value": "Email" }] }, "value": "{{email}}" },
  { "type": "click", "target": { "locators": [{ "kind": "text", "value": "Save lead", "tag": "button" }, { "kind": "css", "value": "button[type=submit]" }] } },
  { "type": "assert", "value": "textContains:Lead saved" } ] }

3. One-off browser control (run_steps, nothing saved)
run_steps { "url": "https://example.com/search", "steps": [
  { "type": "type", "target": { "locators": [{ "kind": "css", "value": "input[type=search]" }, { "kind": "aria", "value": '[aria-label="Search"]' }] }, "value": "smartmacros", "options": { "pressEnter": true } },
  { "type": "waitFor", "target": { "locators": [{ "kind": "css", "value": ".results" }] }, "value": "visible" },
  { "type": "extractList", "target": { "locators": [{ "kind": "css", "value": ".results h3" }] }, "value": "titles" } ] }
