The MCP tools
Hover's authoring engine is an MCP server (@hover-dev/mcp) you add to the coding agent you already run. Once it's wired in, your agent gains a small set of grounded browser tools plus a test_app prompt. The agent drives your app through these tools — and because every action targets a control by role+name → testId → text read off the page snapshot, the selector that drove an action is the exact one Hover saves. That is what makes record == replay hold.
Install
Install the MCP globally, then register the bin (Claude Code shown — any MCP-capable agent works):
npm i -g @hover-dev/mcp
claude mcp add hover -- hover-mcp
Adding the server brings both the tools and the /mcp__hover__test_app command. See Install for other agents.
Want the agent to talk to you in another language? Register with -e HOVER_LANG=zh (a code or a language name) — the workflows then ask their questions and give their summaries in that language, while code and spec names stay English. See Install → Prefer another language?.
The MCP prompts
The server ships its workflows as MCP prompts, surfaced by Claude Code as slash commands:
/mcp__hover__test_app # map the app and crystallize a suite
/mcp__hover__test_app login # …or scope it to one area
/mcp__hover__guard "guests can't reach checkout" # declare a regression BEFORE you build it
/mcp__hover__build checkout # drive a declared guard to green, CI-verified
/mcp__hover__heal # replay every saved spec, fix any drift
/mcp__hover__heal login.spec.ts # …or heal one spec
/mcp__hover__optimize checkout # enrich one spec with observed assertions
/mcp__hover__optimize # …or enrich every spec (one candidate each)
/mcp__hover__lint # health-check the .hover/ test wiki
/mcp__hover__ask "does a guest see the cart?" # ask the wiki, with citations
test_app [scope] walks the agent through a scale-aware loop: map the business lines (the agent reads your routes + nav with its own file tools), pick a scope, cover each flow one at a time (navigate → snapshot → exercise → assert → crystallize), optionally add an API-layer check (Phase 3.5) and lift shared flows into Page Objects (Phase 5), then update coverage in .hover/hover-map.md. A large app doesn't have to finish in one pass — covering a batch and updating the map is a resumable unit.
heal [spec] runs the self-heal loop: replay a saved spec's recorded steps against the live app, find the first step that no longer locates, and re-ground just that step. Omit the argument to scan every spec.
ask <question> answers a question about your app from the .hover/ wiki — the business map, the remembered rules, the crystallized specs, and the run log — and cites what it drew from. It's read-only (no browser), and when an answer establishes a durable rule the wiki was missing, it can file that back with record_fact so the next question is cheaper.
lint health-checks your .hover/ test wiki. lint_map runs the mechanical checks: a business line that points at a deleted spec, a covered line whose spec last ran fail or flaky (a heal candidate), and a spec no line maps. The prompt layers the judged checks your agent does well — contradictory rules in .hover/memory/, and routes in your code that the map never mentions — then offers to fix each: heal the regression, add the missing line, correct the rule. It keeps the map honest as your app grows.
Guard-first development
Two prompts turn the loop around: declare the regression first, then let the agent build the feature until it passes. This is the define-the-behavior-first loop.
guard <intent> declares a regression before you implement the feature. The agent interviews the ambiguous edges of your intent, records the answers as line-anchored business rules (record_fact), and writes a pending - [ ] line with acceptance criteria onto the business map (via the declare_guard tool). Nothing is fabricated: it's a declared red, and the real executable spec is still crystallized later from the actual flow, so record == replay holds.
build <line> drives a declared guard to green. The agent implements, verifies each acceptance criterion in the live app with the grounded tools, crystallize_specs the flow, runs the full local regression, pushes, then polls Hover Cloud's per-spec verdicts and dispatches each one: a bug → fix the code, drift → heal the outdated spec, an unclear / weak-judge call → stop and escalate to you. It's budgeted (~10 local rounds, ~3 CI rounds), never weakens an assertion to pass, and leaves the merge to a human.
optimize [spec] enriches a crystallized spec. It hands your agent the spec, the outcome the recording session observed, and your existing Page Objects, and asks it to add assertions for that observed feedback, replace this-run values (a generated id, an order number) with stable ones, and reuse a Page Object where a step sequence matches. Your agent does the thinking; Hover picks no model. The result goes through save_optimized_spec, which validates it and writes a candidate under .hover/cache/optimized/. Your spec stays as it was until you review the diff and promote the candidate. Pass a slug to optimize one spec, or omit it to optimize every spec — each gets its own review candidate via the optimize_brief tool. Once you've reviewed a candidate's diff and want it, promote_optimized_spec applies it in one step (no manual mv).
The tools
| Tool | Purpose |
|---|---|
browser_navigate(url) | Open a URL in the app under test (the debug Chrome). |
browser_snapshot() | Read the page as an ARIA snapshot (role + accessible-name tree). The agent calls this before actuating, to get the exact role+name for the grounded tools. |
click_control(target) | Click a control by a grounded target (role+name preferred → testId → text). |
fill_control(target, value) | Type a value into a textbox / field by a grounded target. |
select_control(target, value) | Choose an option in a <select> by a grounded target. |
check_control(target, checked?) | Check or uncheck a checkbox / radio by a grounded target (handles sr-only / hidden inputs). |
assert_visible(target) | Assert a control / text is visible now — captures an expect(...).toBeVisible() into the saved spec. |
recall_business_knowledge() | Recall what earlier runs learned about this app (business rules, expected behaviours, access policies). The agent calls this at the start. Once an app has many remembered rules this returns an index (one line per rule) rather than every body — progressive disclosure, so recall stays cheap as the wiki compounds. |
recall_fact(name) | Read one remembered rule's full text by name, when a rule from the index is relevant to what's being tested. The on-demand tier behind the recall index. |
record_fact(title, rule, type?) | Remember a durable business rule so neither this run nor a future one re-asks it. Rules only — never secrets, passwords, tokens, or personal data. |
crystallize_spec(name, description?) | Save the flow the agent just performed (the grounded actions since the last crystallize) as a plain @playwright/test spec in __vibe_tests__/. |
API-layer tools
While the agent drives the UI, Hover passively captures the app's browser traffic off the same CDP connection — no MITM proxy, no cert. These tools turn that traffic into a plain *.api-test.spec.ts. See API testing.
| Tool | Purpose |
|---|---|
capture_requests(urlContains?, method?) | Return the app's xhr / fetch calls observed while driving — method, url, status, content-type, request body, response shape. Optional urlContains / method filters narrow the list. |
replay_request(...) | Re-send a (possibly mutated) request to verify a check before crystallizing, so no status code is confabulated. authenticated: false uses a fresh context with no session — the way to prove a "requires auth" boundary returns 401 / 403. |
crystallize_api_spec(name, description?, checks[]) | Write the verified checks as a plain @playwright/test *.api-test.spec.ts using Playwright's request fixture. |
Self-heal tools
| Tool | Purpose |
|---|---|
replay_spec(slug) | Replay a saved spec's recorded grounded steps against the live app and report the first step that no longer locates — its index and the role+name it was looking for. Drift detection without running playwright test. See Self-heal. |
lint_map() | Health-check the .hover/ test wiki: cross-check the business map against the real spec files and the run ledger. Reports deleted spec refs, regressed coverage (a candidate for heal), and unmapped specs. Deterministic — no LLM. |
Suite tools
| Tool | Purpose |
|---|---|
detect_shared_flows() | Report NON-login flows repeated across your saved specs. |
extract_page_objects() | Lift those shared flows into pages/ + fixtures.ts and fold the specs onto them. Deterministic — no LLM. See Page Object extraction. |
optimize_brief(slug) | Get one spec's improvement brief (its code + observed outcome + your Page Objects + the rules). Used by /mcp__hover__optimize with no arg to enrich every spec in a loop; a single-spec invocation gets the brief inline. |
save_optimized_spec(slug, code) | File an agent-improved spec (from the optimize prompt) as a review candidate. Hover validates it (semantic selectors, no waitForTimeout / XPath, parses), soft-batches trailing assertions, and writes .hover/cache/optimized/<slug>.spec.ts.draft. Never overwrites your spec — you promote the candidate after reviewing the diff. |
promote_optimized_spec(slug) | Apply a reviewed candidate: overwrite __vibe_tests__/<slug>.spec.ts with its draft and remove the draft. So once you've seen the diff and approve, "promote it" is one step — no manual mv. The VS Code cockpit's candidate review does the same visually. |
Cloud tools (signed in)
Available once the agent is linked to a Hover Cloud project. They read Cloud; they never run browsers.
| Tool | Purpose |
|---|---|
cloud_context() | One-call orientation: who the agent is connected as, whether this repo is a Cloud project (org, environments + URLs + accounts), and which environment is active in the editor — what a drive or heal will target. |
cloud_failures() | The drifted specs Cloud has queued from CI, each with its environment + URL so the agent activates the right target before healing. |
cloud_run_result() | One ingested CI run and what each failure means: status, the drift / bug / unclear verdict, and the advisory judge score + rationale. Polls Cloud's /api/v1/runs; the repo is auto-detected from the git origin. |
declare_guard(...) | Write a pending guard line + acceptance criteria onto the business map (used by /mcp__hover__guard). Deterministic, idempotent — never flips a [x] back. |
The grounded target
The actuation tools take a small target shape read off the snapshot:
{
role?: string; // ARIA role, e.g. 'button', 'textbox', 'link' — pair with name
name?: string; // accessible name, exactly as shown — pair with role
testId?: string; // a data-testid, if there is no clean role+name
text?: string; // real visible text — last resort
within?: { role: string; name: string }; // scope to a container first
}
This shape mirrors the agent-facing schema, so what you see here is what the agent receives in its tool catalogue.
Why grounded actuation
A loose "click the Submit button" instruction doesn't round-trip to a replayable selector — it crystallizes as a confabulated getByText that may not match next time. Hover's grounded tools take a target read straight from the snapshot and run it through page.getByRole(...) / getByTestId(...) / getByText(...) over CDP. The selector that drives the action is the one that gets saved, so what you replay is what you recorded. No LLM rewrites the code at save time — crystallization is deterministic.
The agent never freehand-writes the spec
Only crystallize_spec writes files, and it doesn't ask the model to author Playwright — it translates the recorded grounded steps deterministically. The calling agent is the intelligence that explores; Hover guarantees fidelity at the output. Hover bundles no model and holds no key (BYO-CLI): the agent talks to its own provider.