This documentation is available as Markdown. For the complete index, see llms.txt. Skip to content

MCP tools

For the complete documentation index, see llms.txt

unotest is an MCP server. Your editor’s agent calls these tools to explore your app, run scenarios, debug failures and write tests. You never wire them by hand.

Before writing a step

Find the DSL function for a task in words, before reaching for a pause, page code or shell.

  • dsl_lookup { task } — How do I task in a scenario: the few DSL functions for it, best first, each with signature, option keys, the release that added it, one example line and where the shipped guide describes it. Plain words work: wait until the dialog stops animating finds waitForAnimations. Words no function holds return none and a hint. The same index answers npx @unotest/web api <word>.

Core lifecycle & snapshots

Open/close the browser and read what the agent “sees” — a semantic outline, not pixels.

  • new_context { viewport?, device?, clock? } — Open a fresh, isolated browser context, replacing the open one. viewport ({ width, height, deviceScaleFactor }, all optional) and device (a Playwright preset by exact name, e.g. "iPhone 14") set the window for the context’s life; omitted, the project config decides. clock: true installs the page clock for explore_step clock_freeze / clock_tick / clock_run. Unknown fields are refused.
  • close_context — — Tear down the active context.
  • get_url — — URL of the active page.
  • get_title — — Document title of the active page.
  • get_page_snapshot — — Compact, region-grouped semantic outline of the page (with [ref=eN] handles).
  • get_aria_snapshot — — YAML ARIA tree; same-origin iframes stitched in.
  • get_frame_snapshot { locator } — Outline scoped to one iframe.
  • find_element { role, name?, near?, nth? } — Targeted role+name search (≤20 hits + total count). Pierces open shadow roots.
  • ground_element { intent, ordinal?, frame? } — Resolve an intent — “the checkout button” — into a locator on the live page. Present only when a grounder is configured (a box’s Picker, or a model server of your own); a server started without one announces no such tool.
  • check_locator { locator, limit? } — Evaluate a locator written exactly as it appears in a scenario against the live page and report what it really matches — verify a selector without re-running the test.

Exploration & recording

The agent drives the live app and records a scenario.

  • explore_start { scenario_name, title?, description?, env?, viewport?, device?, clock? } — Begin a recording session, opening the browser if none is open yet — viewport, device and clock as on new_context. With a browser already open they are refused: call new_context with them first. Returns available variables, flows, the active environment and grounder: {available, reason?, hint?} — whether intent locators work for this session. env records this session against the .env.<env> / .secrets.<env> overlays.
  • explore_stop { explorationId } — End recording.
  • explore_state { explorationId } — Inspect the session and recorded entries.
  • explore_step { action, locator?, value?, ... } — Execute one action — record it (with a session) or run ad-hoc.
  • explore_steps { explorationId, steps[], autoRun? } — Run a whole sequence in one call — same per-step shape as explore_step. Use it when the block is already known (a login: goto + fill + fill + click).
  • explore_step_result { taskId } — Collect a step or batch that took longer than the client will wait. A step still running after 90 seconds answers { status: "accepted", taskId } rather than holding the call — an MCP client stops waiting at about two minutes and loses the answer. The step is not cut short: it keeps its own timeout and records into the session as usual. Poll this for running / done / failed; while it runs, the browser context refuses other actions and names the task to wait for.
  • explore_record { action, section, description, ... } — Record an action with mandatory section + intent.
  • explore_remove_step { explorationId, entryId } — Delete a recorded entry.
  • generate_dsl_from_exploration { explorationId } — Emit DSL (+ warnings) from the session.
  • save_exploration_as_test { explorationId, scenarioName? } — Write the scenario to disk; extract flow: steps into helpers.
  • explore_run_flow { explorationId, name, ... } — Replay a saved flow to seed state, recorded as one step.

Debugger & runtime

Run a scenario on the scenario engine and step through it.

  • run_test { name, test?, breakpoints?, wait?, waitMs?, env?, updateScreenshots? } — Run a scenario and block until it settles. test names one test_* of a file of several; without it, such a file starts nothing and answers status: "test_required" with its tests and the call to make, and an unknown name answers test_not_found with the same list; returns the outcome, runId, effective baseUrl and the active environment. The session auto-attaches to the run’s browser, applying the run’s dialogPolicy. env runs this one scenario against a different environment — the spawned runner child gets that name as UNOTEST_ENV. updateScreenshots: true writes each assertScreenshot snapshot as its new baseline instead of comparing, as e2e --update-screenshots does. A failed visual assert answers next.visual: the baseline and the expected / actual / diff / candidate images.
  • step { runtimeId } — Execute one step of a paused runtime.
  • resume { runtimeId } — Resume to completion or the next breakpoint.
  • inspect_runtime { runtimeId } — Variables, call stack and last event at the pause point.
  • abort_runtime { runtimeId } — Abort with clean teardown.
  • list_runtimes — — All active runtimes + status.

Collaborative debugging

You debug in the viewer, the agent joins the SAME browser. It never opens its own copy and never closes yours — control passes back and forth through an explicit token.

  • attach_debug_session { runtimeId? } — Join the live browser of your debug run: snapshots and actions now target the page you are looking at, and the run’s dialogPolicy applies to the attached client too.
  • detach_debug_session — — Drop the connection and go back to its own browser. Your session stays open.
  • take_wheel { runtimeId? } — Take the control token so the agent may record actions. You see an “Agent driving” badge and can take it back at any time.
  • release_wheel { runtimeId? } — Hand control back to you; recording is blocked until the agent takes the wheel again.
  • record_action { ref, action, value?, ... } — Append a DSL line to the live draft: the agent picks an element by ref, the runner resolves it to a stable locator and the line appears in your editor.

Multi-context

Tabs and frames.

  • list_pages — — All open tabs (index + URL).
  • list_frames — — The frame stack.
  • get_active_context — — Which tab is active.
  • switch_page { index } — Switch the active tab.

Failure artifacts

Read the evidence bundled when a run fails.

  • list_failures — — All failure bundles with timestamps.
  • get_failure_trace { runId } — DSL execution trace.
  • get_failure_console { runId } — Browser console logs.
  • get_failure_a11y { runId } — ARIA tree at the failure (YAML).
  • get_failure_screenshot { runId } — Screenshot at the failure.
  • get_failure_network { runId } — HAR (only if tier3.network is enabled).

Reading a box

The other half of bundle push --run: it hands work to a box and returns a run id, and these read the answer back — no browser, no shell on the box. Authenticated with your PERSONAL read token (UNOTEST_BOX_READ_TOKEN), which is read-only whatever your role there and is not the project token that pushes bundles.

  • box_envs — — Environments this read token may look at, as <project>/<environment> — the form every other box_* tool takes.
  • box_runs { environment?, latest?, running?, scenario?, limit? } — Run history in one environment. latest collapses it to the last run per scenario with its failing streak; running shows only what is in flight.
  • box_run { environment?, runId } — What happened in one run: status, the failure, soft-step results, judge verdicts, notes and logs. The tool to call right after bundle push --run.
  • box_run_download { environment?, runId, screenshots?, dir? } — Fetch the run and unpack its failure bundle into .unotest/failures/, where get_failure_* and agent_fix already read — a box failure debugged like a local one. screenshots: false leaves the step frames on the box. Baseline candidates from assertScreenshot are unpacked next to the zip; they become baselines only when copied into the scenario’s .screenshots/ folder and committed.
  • box_queue { environment? } — Who is running, who is waiting, how many slots are in use — why an ordered run has not started.
  • box_screenshot { environment?, runId, step? } — One frame the run captured, returned as an image. Without step you get the last, which on a failed run is usually the state that failed.

Writing to a box

One tool that changes a box, on the project’s PUSH token — the one bundle push uses, read from UNOTEST_BOX_TOKEN or unotest/.secrets — not the personal read token above.

  • box_repo_add { url? } — Map the repository (default: the project’s git origin) to the token’s project on the box, so bundle push stops warning that it maps to no project — for a box rented before 0.54.0. Answers {state: 'mapped' | 'already-mapped', project, url}; box-too-old means Update the box in the dashboard first; refused carries the box’s code (repo-claimed: another project holds it, named in project). Repeating it changes nothing.

Cloud account

The account that rents boxes, as tools — the same operations as the box commands, through the same code. They need a cloud login on the machine (npx @unotest/web login); without one every tool answers {state: 'not-logged-in'} with that command, never an error. A key that may not spend by itself gets {state: 'approval-pending'} from a create, and so does a delete by a key that did not create the box: nothing happens until a person approves it in the cabinet, and cloud_approval_wait is the next call. The key that created a box deletes it at once. A key without the permission a tool needs gets {state: 'insufficient-scope'}.

  • cloud_boxes_list — — Every box on the account: name, state, size, the projects it serves and what it runs. Where to start when you do not know what the account has.
  • cloud_box_status { name } — One box in detail: state, address, paid-until, projects, whether the Picker is on.
  • cloud_box_create { name, envs, size?, picker? } — Rent a box — it costs money by the hour. envs lists what it serves (project, name, target); a box runs only your tests, so each target must be a public address the box reaches over the internet. Answers the box, approval-pending with the request to wait on, or top-up-needed when the balance does not cover the first hour — with the amounts the key may read and the dashboard page where a person adds credit.
  • cloud_box_extend { name, ttl } — Give a running box more lifetime: its end (expiresAt) moves ttl later — 4h, 1d — or none takes the lifetime away. Nothing is charged at once. A key that may not spend unattended gets approval-pending. cloud_box_create takes the same ttl for a new box; without it the box gets 24 hours.
  • cloud_box_destroy { name } — Delete a box and everything on it. No undo; the rest of the paid hour is not refunded. The key that created the box deletes it at once; any other key gets approval-pending.
  • cloud_box_access { name, project? } — A fresh push + read token pair for a box (thirty days), written into unotest/.env and unotest/.secrets the way box access does. No token is ever typed or copied: the answer names what was written where, never a value.
  • cloud_box_update { name, version } — Move a box to the version the cloud offers it. The box installs it and reports back on its next check.
  • cloud_box_logs { name, kind? } — The end of the box’s bootstrap or updater log. The first call may answer {kind: 'requested'} — the box sends it on its next five-minute check; call again then.
  • cloud_balance — — What is left on the account, in cents, and the currency. Needs a key that may read billing.
  • cloud_approval_wait { id?, timeoutSeconds? } — Wait for a person to answer a request and return what the call would have returned. Ends approved, declined, failed, or still-waiting after timeoutSeconds (default 120) — call it again. Without id, lists what is waiting.

Agent self-service & viewer

Diagnostics, the human-in-the-loop fix flow, and the viewer.

  • agent_fix { location, fix } — Composes failure context for a fix. No LLM call, no auto-apply — you review the diff.
  • get_last_mcp_log — — JSONL debug log of the last run (under UNOTEST_DEBUG).
  • audit_last_run — — Deterministic rubric audit of the run against the rules (no LLM).
  • open_viewer — — Launch (or reuse) the local viewer; returns its URL.