Record browser actions as a replayable Trace. Replay them deterministically — no agent, no LLM — and assert on network requests.
English · 简体中文
Drive a browser once (by an agent, a script, or by hand). Every action is captured to a Trace (JSONL). Transpile that Trace into a declarative Replay Spec (YAML), then replay it deterministically forever — for regression checks, smoke tests, or reproducing a bug.
- Record — every command (navigate / click / fill / …) auto-writes one Trace line with a full locator descriptor (all candidate selectors + role/text + rect) and the network requests it produced.
- Transpile — mechanical Trace → Replay Spec. Drops failed attempts, carries the locator fallback chain, turns
wait_for_requeststeps into assertions. Marks anything a human/LLM should review with_review. - Replay — reads the spec, locates each target by the fallback chain (
selector → role+text → rect), and verifies assertions against the request store. Deterministic and cheap.
Ships three ways to use it: a CLI, an MCP server (for agents), and a Skill.
- Bun — build tool + dev runtime (runs the TypeScript directly).
- Node ≥ 18 — runtime for the shipped
dist/*.mjsartifacts. - Chrome / Chromium — the CLI launches and owns a managed instance.
The plugin bundles an MCP server whose tools an agent calls to drive + record.
git clone <this-repo> browser-trace
cd browser-trace
bun install
bun run build # produces dist/cli.mjs + dist/mcp.mjs.claude-plugin/plugin.json registers the MCP server as node ${CLAUDE_PLUGIN_ROOT}/dist/mcp.mjs. Install the plugin via your marketplace/local plugin path, or register the server directly:
claude mcp add browser-trace --scope local -- node /abs/path/to/browser-trace/dist/mcp.mjsbun run build
node dist/cli.mjs help# 1. launch a managed browser (headful; add --headless for CI)
node dist/cli.mjs launch --session demo
# 2. drive it — each action records one Trace line
node dist/cli.mjs navigate "https://example.com" --intent "open page"
node dist/cli.mjs snapshot # read-only: see clickable elements
node dist/cli.mjs click --role link --text "Learn more" --intent "open docs"
node dist/cli.mjs wait --url-pattern "*/api/track*" --intent "verify beacon"
# 3. stop (kills the browser)
node dist/cli.mjs stop
# 4. mechanical Trace → Replay Spec
node dist/cli.mjs transpile output/demo.trace.jsonl
# 5. deterministic replay (launches its own browser, checks assertions)
node dist/cli.mjs replay output/demo.replay.yamlReplay writes a <spec>.compare.json with recorded_ms vs replay_ms (record-time command execution excludes agent think time, so a real agent recording is far slower than replay).
| Command | Records? | Purpose |
|---|---|---|
launch / stop / status |
— | own the browser lifecycle (random port, temp profile) |
snapshot / screenshot |
no (read-only) | perceive the page to pick a locator |
navigate <url> |
yes | go to a URL |
click / fill |
yes | act via --selector or --role+--text (never coordinates) |
clear-cookies --patterns "reA,reB" |
yes | delete cookies by name regex (e.g. force a deduped beacon to re-fire) |
evaluate <js> |
yes | escape hatch |
wait --url-pattern P |
yes | sync point / assertion — did a matching request fire this session |
transpile <trace.jsonl> |
— | Trace → Replay Spec (YAML) |
replay <spec.yaml> |
— | deterministic replay + assertions + timing |
Same surface as the CLI, prefixed mcp__browser-trace__: trace_start / trace_stop / snapshot / screenshot / navigate / click / fill / clear_cookies / evaluate / wait_for_request. The MCP server is a thin forwarder — every tool shells out to the CLI, so MCP-driven and CLI-driven recording share one code path.
record (any driver, all via one entry)
→ Trace (JSONL, one line per command)
→ transpile (mechanical; _review flags for LLM/human)
→ Replay Spec (YAML)
→ replay (locator fallback chain + assertions) → pass/fail + timing
Trust boundary. The CLI is the sole entry that launches and owns the browser (random debugging port, private profile). Whatever drives it — agent, script, human — can only act through these commands, so the Trace is complete by construction. Don't run another browser-driving tool in the same session, or you get holes in the Trace.
bun install
bun run typecheck # tsc --noEmit
bun run cli help # run the CLI from TypeScript (no build)
bun run mcp # run the MCP server from TypeScript (stdio)
bun run build # bundle to dist/*.mjs for shippingDev runs the TypeScript directly with bun; the MCP server auto-detects .ts (dev, spawns bun) vs bundled .mjs (prod, spawns node). Runtime output (traces, request stores, session state) lands under output/ (gitignored).