SceneScout
SceneScout is an MCP server that turns a coding agent into an exploratory QA tester for running web apps, with safety-enforced browser exploration, persistent findings, and shareable evidence.
Attach to a web app and drive it with browser tools: crawl routes, snapshot pages, click, type, select, scroll, press keys, hover, upload files, navigate/back, and run multi-step action plans.
Choose enforced write modes (
observe,read-only,safe-write,destructive), configure browser/viewport/timeouts, use saved sign-ins, and run multiple authenticated sessions or roles concurrently.Detect and record defects: console/page errors, failed or suspicious HTTP/network responses, dead ends, layout/overlap issues, broken images, accessibility and design problems, double-submit bugs, and permission leaks via direct API calls.
Run guided checks: collect the person's choices, build/confirm plans, track check areas, re-check open findings, save/reuse/delete checks, and produce recaps.
Maintain persistent project memory and written assumptions across runs; track coverage, gaps, open findings, resolutions, regressions, and verification verdicts.
Generate reports and evidence: markdown/HTML reports, team-ready reports with screenshots and SHA-256 manifests, coverage summaries, acceptance-criteria results, and optional GitHub/Jira ticket preview/filing/status.
Support parallel/lane-based testing, finding cards, user feedback labels, ticket workflows, and status monitoring/live view.
Enforce safety at the network layer and keep data local where configured, with evidence capture, redaction/egress settings, and no telemetry.
Enables exploratory UI testing of Angular applications, including route discovery from Angular router configuration to plan coverage and measure completeness.
Enables exploratory UI testing of Next.js applications, including route discovery from file-based routing to plan coverage and measure completeness.
Enables exploratory UI testing of Nuxt applications, including route discovery from file-based routing to plan coverage and measure completeness.
Enables exploratory UI testing of applications using React Router, including route discovery from its router configuration to plan coverage and measure completeness.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@SceneScout Explore our staging app at staging.example.com and report any broken flows or coverage gaps"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
SceneScout
Your coding agent, turned into an exploratory QA tester for any web app.
Works with Claude Code · Cursor · VS Code (Copilot) · Codex CLI · Gemini CLI · Copilot CLI · Windsurf · any MCP client
🌐 Website · 📖 Guide · 🐛 What it catches · 🎬 QA with evidence · 🚀 Get started · 🚦 CI · 🔒 Safety · 📚 Docs
Scripted end-to-end tests answer one question: does this exact flow still work? They say nothing about the rest of the app. SceneScout lets the agent you already use explore a running web app like a curious, thorough tester. It clicks, fills forms, switches roles and calls the API behind a hidden button, then writes a report of what is broken and what could be better, with a picture and evidence for every line.
Try it on any app you are allowed to test. No account, no API key, no setup:
npx -y scenescout http://localhost:3000flowchart LR
A["🧠 Your coding agent<br/>Claude Code, Cursor, Copilot…"] -- MCP --> S["🔭 SceneScout<br/>browser · checks · memory"]
S --> App["🌐 Your running app"]
S --> R["📋 Report<br/>plain words + pictures"]
S --> E["🎬 Evidence<br/>replay, videos, test report"]👥 Who it's for
🛠️ Developers | 🧑💼 QA, product and non-technical teams |
Findings with the request that failed ( | Each problem in plain words first: what was done, what was expected, what happened, and a picture of the page |
Next to the source, the file behind the bug and a likely fix | Visible problems shown, not described: a covered button, a broken image, text too faint to read |
A deterministic gate for pull requests, with SARIF for code scanning | Journeys recorded step by step, as frames and video, so a pass is something you can watch |
Runs from your editor's agent, or unattended in CI | A test report laid out by your template, with expected and actual results, deviations and blank sign-off rows |
Related MCP server: visual-ui-debug-agent-mcp
🐛 What it catches
A real run against the small demo app in this repository, which has bugs planted on purpose. The two you can see are outlined:
It filed twelve findings. A few from the report:
🔴 [HIGH] A double-click on Create order creates two orders
2× click fired the same state-changing request 2× (POST /api/orders)🔴 [HIGH] A clerk can approve an order by calling the endpoint the page hides from them
POST /api/orders/1037/approve 200 as clerk: the button was hidden, the server did not agree.🔴 [HIGH] Filtering orders by Archived fails, and the page shows an empty table instead of an error
GET /api/orders?status=archived → HTTP 500🟠 [MEDIUM] The "New: bulk import" badge sits on top of the All orders button (callout 1)
"All orders" overlaps "New: bulk import" (81%), measured from layout boxes, no screenshot needed.
What it looks for, on every page, after every action:
📐 Broken layout, from geometry: controls that overlap, sit off-screen, hide under a sticky bar or can never be scrolled into view. The CSS bugs a person spots at a glance, found without one.
🧨 Real breakage: console errors, crashes, failed requests, 4xx and 5xx responses, broken images, dead-end pages.
🔓 Permission leaks: it calls the app's own API as each role, so "the button is hidden" becomes "the server refuses it", or doesn't.
🤥 Pages that lie: "Saved!" after the server refused the save, or an empty table after the request failed.
👆 Impatient users: a double-click that sends the same order twice.
♿ Accessibility and craft: contrast, focus, labels, target sizes, spacing and type, with a 0 to 100 score per page.
🧭 Friction: how many steps a task takes, and where a user had to go back.
💉 Security smells: typed markup that comes back as an element, and tokens posted to any window.
🎬 Automated QA with evidence
Save the journeys that matter, the happy paths and the ones that must fail politely, and scenescout check replays them on every pull request with no model involved. Recorded, each run leaves proof you can watch and hand to someone who never opens a terminal.
flowchart LR
PR["🔀 Pull request"] --> C["🚦 scenescout check<br/>--record --video --template"]
F["📝 Saved journeys<br/>.scenescout/flows/*.json"] --> C
C --> G["✅ / ❌ A gate on the pull request"]
C --> RP["🖼️ replay.html<br/>every step, with frames"]
C --> V["🎞️ A video per journey"]
C --> TR["📄 Test report<br/>your template, SHA-256 manifest,<br/>blank sign-off rows"]A journey is a few lines of JSON, written by hand or kept from a flow your agent just walked. This is the demo's happy path:
{
"name": "place an order",
"id": "TC-01",
"requirements": ["REQ-ORD-1", "REQ-ORD-2"],
"steps": [
{ "action": "navigate", "target": "/orders-new.html", "expected": "The new-order form is shown" },
{ "action": "type", "target": "testid=new-order-customer", "value": "Harbour Bakery", "expected": "The customer is filled in" },
{ "action": "type", "target": "testid=new-order-items", "value": "4", "replace": true, "expected": "The item count is 4" },
{ "action": "click", "target": "testid=new-order-submit", "expected": "The order is created" },
{ "action": "expect-request", "request": "POST /api/orders", "status": "2xx" },
{ "action": "expect-element", "target": "testid=new-order-created-link", "state": "visible", "expected": "A link to the new order is shown" }
]
}Against the demo app, with the three journeys in examples/flows (one creates an order, so the check is allowed to send it):
npx scenescout check http://127.0.0.1:4173 --flows examples/flows --mode read-only --flow-writes allow \
--record --video --template examples/report-template.jsonThe journey, as it ran (one of the videos it filmed):
The replay page: each journey with a pass or fail badge. A journey that broke opens at the step that broke, with the page as it was after every step:
The test report, laid out by a template you write once: test IDs, the requirements each covers, expected and actual results, a screenshot per step, every deviation listed again for the reviewer, and the SHA-256 of each piece of evidence. SceneScout signs nothing; the sign-off rows are for your people.
Everything is one self-contained HTML page per report, with no scripts, nothing loaded from the network, and a layout that prints. Recording a check and a test report from a template have the details, including what to keep out of the pictures.
📺 Watch it work
A narrated eight-minute tour of every feature, on the demo app, is on the website.
Each exploratory run opens a live view on your machine, with one card per agent: what it is doing, the page it is on, and a feed of every action. Three agents are testing the demo app in parallel here:
You can read the report while the agents are still working, and scrub back through any session's timeline. Beside report.md, every run writes report.html, the whole run as one self-contained page; ask for a recorded run and it also keeps a frame after every action.
✨ Why it's different
🧠 Your agent is the brain, so no extra API key. The engine contains no model. It gives the agent you already pay for deterministic tools and the testing method to use them.
📐 It reads structure, not pixels. The agent sees every element with its role, state and layout box, so overlap and broken images are measured, not guessed from a screenshot.
🛡️ Safety is enforced on the network, not requested in a prompt. Every request the page sends is judged against the session's write mode, and a request the mode refuses never reaches your server.
✅ "Done" is a contract. The report lists everything not tested, and at the
extensivelevel refuses to finish while any known page is unvisited.🧠 It remembers. Each run starts from what the last one learned, and re-tests the bugs earlier runs left open.
📊 It is measured, not asserted. Every change to how it explores is scored against an app with planted bugs and a held-out app it is never tuned on (the log):
xychart-beta
title "Planted defects found per run, demo app (13 planted)"
x-axis "Run" ["0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "10", "11", "12"]
y-axis "Found" 0 --> 13
bar [11, 12, 9, 10, 11, 11, 10, 10, 11, 13, 11, 11, 12]Each bar is one run of eight parallel agents, re-scored against today's answer key. One run is noisy: read the trend across runs, not a single bar.
🚀 Get started
1. Install for your agent (Node 22.19 or newer):
npx -y scenescout install # Claude Code: skill, MCP server and the test browser
npx -y scenescout install --client cursor # or vscode, codex, gemini, copilot, windsurf
npx -y scenescout doctor # every line should be a ✓Claude Code plugin:
/plugin marketplace add brunoboto96/SceneScout, then/plugin install scenescout@scenescout-marketplace. The command becomes/scenescout:scenescout.Claude Desktop: download
scenescout-X.Y.Z.mcpbfrom the latest release and open it. No terminal needed. In the chat, pick Check my site from its prompts to start a guided check without writing a prompt (guided prompts).Any other MCP client: add a stdio server whose command is
npx -y scenescout@3.35.0 serve. Each client's config.
2. Start your app, then a fresh session of your agent, and ask:
Use SceneScout to test http://localhost:3000In Claude Code there is a command too: /scenescout --url http://localhost:3000 --level medium. On its own, /scenescout asks instead: where your client can show a form, one guided form (the address, what to check, how you sign in, how careful to be and how thorough), and otherwise four plain questions in chat. A care level that writes on a site that is not on your computer, or that creates test data, asks you to confirm it in a second form the agent cannot answer (care levels). While it runs, scout_status follows the check in plain words, says what went wrong in one sentence with one thing to do (the site does not open, the sign-in ran out, the check ran out of time), and at the end the problems come back as cards you can mark "not a bug". After a fix, Re-check after a fix checks them again in the care level they were found in, never a looser one, and a check you want to repeat can be saved by name (never with a password or token) and run again. Is it safe to run on my live site? Rather press buttons than write prompts? The optional mod's /scenescout-guided panel does the same from a form in the Claude Code CLI or the desktop Code tab (the mod).
3. Read the report in .scenescout/report.md. It opens with a Summary in plain words (what was checked, what was found, the problems as a table, and each problem's steps, what was expected and what happened), then the Evidence (each problem's picture, on a run that has them), with the Technical details and, in report.html, the Steps folded one click away. A complete example. To hand it round, ask for a team report (scout_team_report, or Make a team report in the chat's menu or the guided panel): .scenescout/team-report.html has a cover, each problem as a card with its picture, what was not checked and blank sign-off lines, with an evidence folder beside it holding the pictures and a manifest of every file's SHA-256. To file problems as GitHub or Jira tickets, pick File problems as tickets (or File as tickets on the guided panel): you see a preview first, and SceneScout files only after you say yes in its own form (how).
No app handy? Clone this repository and run npm run demo:serve: the demo app starts on http://127.0.0.1:4173, and its README lists every planted bug.
Behind a sign-in? Sign in once yourself, SSO and MFA included, and every session reuses it:
npx -y scenescout login http://localhost:3000 --role admin # then: /scenescout --role adminSigning in covers roles, expiry and scripted sign-in for CI.
🚦 In CI
What it does | Needs a model? | |
A deterministic gate: measures every page, replays your saved journeys and visual baselines, fails only on what it can prove; recorded, it leaves the evidence above | No | |
An unattended exploratory run, driven by the Anthropic or OpenAI API. It reports and never fails the build | An API key | |
A comment on a pull request that tests its preview deploy and replies with the results; | An API key ( | |
Files the findings as GitHub or Jira issues, each once | No | |
Checks in seconds, with no browser, that the runner can reach the app, that a saved sign-in has not expired and, when asked, the model's API; each cause has its own exit code | No |
A gate on every pull request that keeps its evidence, as a GitHub Action:
- uses: brunoboto96/SceneScout@v3
with:
url: http://127.0.0.1:3000
record: on
video: on
template: tests/report-template.jsondocs/ci.md has complete workflows, every option, and the same check on GitLab CI, CircleCI or any shell.
Ask for it on a pull request
Type a command in a pull request comment and SceneScout answers in the thread, with pictures taken by a real browser:
Comment | Reply |
| Explores the pull request's preview and replies with what it found |
| A picture of that element |
| The element on the base and on the pull request, with the changed pixels highlighted |
| Runs your saved journeys on the branch and replies with each one's result; the video of every journey is in the run's files |
The preview commands test a deployed preview, so the pull request's code never runs beside the model's key; qa check runs your own workflow and uses no key. Setting it up.
🔒 Safe by default
Mode | What may leave the page |
🔵 |
|
🟢 | Also ordinary form posts, which can create a record or change one that was there. |
🟡 | Also |
🔴 | Everything. Only when you say the data is disposable: the agent is told never to choose it, and the config file below can rule it out |
A team can commit .scenescout/config.json to cap what any agent may choose, for example {"preset": "strict"} to keep every run in observe and the browser on the app's own host (the config file).
SceneScout sends no telemetry. Every path by which data leaves the machine (the agent's model, scenescout ci, export, /scenescout qa, CI artifacts, downloads), what each carries and how to turn it off is in What leaves the machine. {"egress": "none"} in the config file makes SceneScout refuse every one it sends itself; it cannot stop your coding agent from sending what it reads to its own model. {"redactPageText": "pii"} masks email addresses, phone numbers and card numbers in what the tools return to the agent, at the cost of hiding bugs that are about that content.
What the policy cannot stop: a link is opened like any page, so a link that changes something just by being opened (an unsubscribe link) is followed, unless you turn on refusing those in observe and read-only; only HTTP is judged, so messages over a WebSocket are not; and in Firefox and WebKit the later hops of a redirect that keeps a write's method go out unjudged. Which refusals are guarantees and which are pattern matching is in the Safety model, and scenescout policy explain POST <url> says how any one request would be judged in each mode.
The policy sits on the network, so a blocked request never reaches your server. The page gets a refusal instead, which is how SceneScout catches a page that claims success anyway. A saved journey that creates something, like the order above, runs only when the check is told --flow-writes allow, against a test environment. Findings, memory and reports stay in a .scenescout/ folder that keeps itself out of git.
Only test sites you own or are allowed to test.Safety model.
📚 Documentation
A first look, install, a first run, reading the report, the live view | |
Interactive runs, parallel agents, CI, pull-request QA, filing issues | |
Every check and every | |
Roles and saved logins; what each mode refuses and why | |
Every path data leaves by, to whom, and the | |
Every host each command needs, | |
Setups for seven kinds of project | |
Every option, environment variable and action input | |
Symptoms and fixes, upgrading and uninstalling | |
Saved journeys, recording, test reports from templates, every workflow | |
Diagrams of a run, an action, the write policy, lanes | |
How runs are scored against answer keys, and runs on public apps | |
Why the rules are what they are |
🔧 Contributing
Start with VISION.md (what is in scope) and CONTRIBUTING.md (local setup and how changes land). AGENTS.md holds the house rules for people and coding agents alike.
Found a way past the write policy, or another security problem? Report it privately: SECURITY.md.
MIT licensed.
Available Tools
48 toolsscout_attachA
Launch a browser and attach to a running web app. First attach in this conversation and you have read neither the SceneScout skill nor scout_playbook? Call scout_playbook before this. Write policy is enforced at the NETWORK layer: mode='observe' lets out only GET, HEAD and OPTIONS, plus signing in, signing out and token refresh, and POSTs the user named in readPosts; every other request is blocked — choose it for a target that holds real data, where even an ordinary form submission would create a record; mode='read-only' (default) blocks destructive-labeled elements AND all PUT/PATCH/DELETE + destructive POSTs, but lets ordinary form POSTs through; mode='safe-write' allows creating data and permits updates/deletes ONLY on resources this session created (use when the user wants create/edit flows tested); mode='destructive' allows everything — ONLY when the user explicitly confirmed a disposable/seeded environment. Pass role to sign in with a login the user saved by scenescout login <url> --role <name>, or a Playwright storage-state JSON as storageStatePath. Pass session to keep MULTIPLE roles alive at once (one browser each, genuinely concurrent) for collaboration testing — target each directly with every tool's session param, or use scout_session to set which one is the default; coverage and findings merge into one project memory.
Inputs: url (required), mode, open, role, +18 more.
scout_backB
Go back in browser history (tests back-button resilience).
Inputs: task, leave, session, objective.
scout_captureA
Save a PNG of ONE element — its bounds plus a margin, from a real screenshot — under .scenescout/captures/, to show a person how it looks. Give the element's ref from the latest scout_snapshot. Not a way to judge design: scout_design_audit measures it.
Inputs: key, ref, name, margin, +1 more.
scout_cardsFinding cardsARead-onlyIdempotent
Show the project's findings as plain cards, worst first: impact (Blocks users, Annoying, Cosmetic, Worth a look), title, what kind of problem, the page, numbered steps, what was expected, what happened, and the finding id. Use it at the end of a guided check, or whenever the person wants to read what was found in plain words; post each card's text as it is. By default fixed findings and the ones the person marked not a bug are left out (the count of the second is given; filter {feedback: ["not-a-bug"]} lists them). Pass ids to show exactly those. pictures: "inline" puts each card's picture in the result as an image (at most 10, at most 1024 px on the longer side), "files" (default) names each picture's file, "none" leaves them out. Touches no browser.
Inputs: ids, limit, filter, pictures. Returns: cards, total, missing, notABug
scout_clickA
Click an element by its ref from the latest scout_snapshot. Returns the outcome plus any oracle violations triggered. clicks=2 (or 3) probes IMPATIENT-USER behaviour: a rapid multi-click that fires the same state-changing request twice means the control is not guarded against double submission (button stays enabled, endpoint not idempotent) — use it on every important submit/create button once; the result says explicitly whether duplicates fired.
Inputs: ref (required), task, leave, clicks, +2 more.
scout_closeA
Close a session's browser (memory persists on disk). Default: the DEFAULT session. Pass session to close a specific one, or all=true to close every live session at the end of a multi-role run. A session that is a lane of a parallel run (named by scout_lane_brief or scout_lane_report) is not closed until its lane report has been accepted by scout_lane_report, since folding needs the session attached; the refusal names every such lane.
Inputs: all, force, session.
scout_coverageA
Show exploration coverage: states visited, which elements remain unexercised, which options of a dropdown used this run no session has chosen yet, and which forms seen this run no session has submitted with every text field blank. Use to decide where to explore next and when the level's budget is satisfied. In a parallel run it shows this session's own work by default — the routes it reached this run and the forms it saw — so one lane is not handed another's gaps; scope:"project" shows every session's, each form and route tagged with the sessions that saw it.
Inputs: scope, session.
scout_crawlA
Engine-side route sweep in ONE call: visits each path (default: all known routes not yet visited), records states into coverage memory, and returns a per-route health summary (HTTP status, element count, what the main area holds, oracle violations, dead-ends, auth-redirects, and ERROR-VIEW or STILL-LOADING for a main area showing only an alert or a loading placeholder). Navigation-only — safe in read-only mode. Use this FIRST for broad coverage; explore interactively only where it flags problems or where journeys matter.
Inputs: paths, session.
scout_criterionA
Record whether one acceptance criterion of a ticket read with scout_tickets passed, failed or was not tested, with how sure you are. The link from a criterion to the findings that show it is YOUR judgement, stated with a confidence — never matched on words. A "fail" names the findings that show it (file them with scout_finding first); "not-tested" says why in untestedBecause. Recording the same criterion again from the same session replaces your earlier verdict. Touches no browser.
Inputs: reason (required), ticket (required), verdict (required), criterion (required), +4 more.
scout_design_auditA
Computed-style design audit of the current page — a design connoisseur's read WITHOUT screenshots. Measurable defects (⚠): WCAG contrast, tiny targets, clipped text, aspect-distorted images, horizontal overflow, sibling cards touching with no gap (naming their parent), missing keyboard-focus indicators (sampled with real Tab presses). Craft suggestions (→): line measure and line-height rhythm, spacing-scale adherence, typography entropy, palette discipline (gray census, accent hue families, pure-#000 body text), elevation/control consistency, heading structure, indistinguishable links, and AI-slop tells (gradient text, glassmorphism, side-stripe borders, neon glows, violet gradients, identical card grids). Ends with a SYSTEM SUMMARY of design-system coherence and a PAGE SCORE whose why: line lists the largest deductions behind it; once four pages are audited, a SITE CONSISTENCY block sets this page's button heights, radii, headers, tabs and grays beside the other pages' and names the values only one page uses. Run once per representative page; the → tier is improvement feedback — file genuine opportunities as ux-polish findings with the concrete numbers, not just defects.
Inputs: session.
scout_feedbackLabel a findingA
Record the person's own verdict on a finding: "not-a-bug" or "confirmed", with their reason if they gave one (at most 300 characters). Call it only with what the person said; never label a finding yourself. The finding is kept either way, keeps its id, and a later filing of the same problem still merges into it; cards and the status pane list a not-a-bug one apart. export: true instead lists every label in the project as candidates for a maintainer to review: they never change an answer key or the dedup by themselves. Touches no browser.
Inputs: id, export, reason, verdict. Returns: verdict, findingId, candidates
scout_findingA
Record a structured finding (bug, UX issue, or improvement). Deduplicates across runs; automatically captures the recent action trace as the repro, and a picture of what it is about (the element ref names, else the viewport), kept for report.html and returned in this result as an image. Use for anything worth reporting: crashes, oracle violations you confirmed, dead ends, confusing UX, permission leaks, missing testids — and design-audit improvement opportunities (ux-polish) with their concrete measurements.
Inputs: title (required), detail (required), category (required), severity (required), +4 more.
scout_guided_confirmAsk the person to confirm a guided planA
Ask the PERSON, through a form this server shows them, whether a guided plan whose care level writes may start: May submit forms on a site that is not on this computer, or May create test data anywhere (which also asks them to type a code the form shows). You may make this call; you cannot answer it, and nothing you pass accepts a plan. The plan is accepted only when the person says yes in the form. When they decline, close it, go back, mistype the code, or this client cannot show forms, the plan stays unaccepted and the result says so: tell the person, and offer the same check in Look only. Never retry it on their behalf. Touches no browser.
Inputs: planId (required). Returns: mode, planId, outcome, accepted, +1 more
scout_guided_planBuild a guided planARead-only
Turn a person's guided choices into a plan in plain words: the write mode its care level is, what each area does and holds back, what to tell the person, the calls to make in order, and its time and page limits. Pass choices to build a new plan, or planId to read one already built (a panel builds it and hands you only the id). A plan whose care level writes on a site that is not on this computer, or that creates test data anywhere, is built unaccepted: scout_guided_confirm asks the person, and until they accept, scout_attach above observe is refused. Once any plan is built, every scout_attach above observe must name an accepted plan's planId. A care level above the SceneScout config's maxMode is refused. Touches no browser.
Inputs: planId, choices. Returns: mode, areas, calls, level, +8 more
scout_hoverA
Hover an element by ref like a user pausing the pointer on it, and report what it reveals: tooltips/popovers (diffed against pre-hover state), any other new page text that appeared (labelled as possibly unrelated on busy pages), the title attribute, and aria-describedby text — each item truncated to 300 chars. Hovering does not count as exercising the element. Use on badges, icons, truncated text, and error indicators BEFORE concluding an element 'does nothing' — hover-gated UI is invisible to snapshots and clicks.
Inputs: ref (required), session.
scout_intakeStart a guided checkA
Ask how the person wants their site checked when they gave no settings. Where this client can show a form, the person chooses there: the site's address, what to check, a journey or tickets, how they sign in, how careful to be and how thorough. This then builds a guided plan, asks the person to confirm it in a second form when its care level needs that, and returns the plan: what to tell them, then the calls in order. Otherwise, or when they decline or close the form, it returns four plain questions for you to ask in chat. Never asks for a password. Touches no browser.
Inputs: url.
scout_journeyA
Measure how EASY a real task is, not just whether it works — the question pass/fail e2e suites never answer. Wrap one user goal: scout_journey {action:'start', goal:'Create an order'}, perform it the way a first-time user would (navigate by CLICKING through the UI, not by jumping to a known deep URL — a shortcut invalidates the measurement), then scout_journey {action:'end', completed:true|false}. Returns interaction cost (clicks, navigations, distinct screens, elapsed), the actual path taken, and friction signals: BACKTRACKS (returning to a screen already left — the clearest sign the next step wasn't discoverable), screen count, and over-interaction. Run it on each module's primary journey; an abandoned journey is a high-severity finding.
Inputs: action (required), goal, note, session, +1 more.
scout_lane_briefA
For a run split across parallel agents (lanes). Divides the app's known routes between N lanes and returns each lane's session name, the objective to attach it with, and the routes it owns — whole modules per lane, balanced by route count, so no two lanes audit the same area and none is left unopened. Call it after the first crawl, when route knowledge is complete. Touches no browser; pass the briefs to your lane agents, then use scout_lane_report for what they hand back.
Inputs: lanes (required), goal, routes, fromRun, +4 more.
scout_lane_reportA
For a run split across parallel agents (lanes). Without reply: returns the paragraph to put in a lane's prompt, telling it to hand back ONE typed JSON object (verdicts, severities and categories from closed sets, a calibrated confidence per decision, routes covered, what blocked it). With reply: parses what the lane handed back and returns the one-line fold (defects, highs, unsure, mean confidence, routes) or the reason it was refused, to relay to the lane once. Touches no browser.
Inputs: lane (required), reply.
scout_loginA
Open a visible browser window for the USER to sign in to the app as a role, and save that sign-in for scout_attach { role }. Use it when an attach is refused because no sign-in is saved for the role, or the saved one has expired. Tell the user first, in plain words: "A browser window is opening. Sign in there as you normally would; it closes by itself once you are in." The window saves once the user is back on the app with a new session (a round trip through a single sign-on provider is followed, not taken for the end) and closes. Never type credentials into it yourself. Returns once signed in and saved, or after waitSeconds with the window still open: then call scout_login again with the same role to keep waiting. Closing the window saves nothing. Needs a desktop: on a machine with no display, ask the user to run scenescout login <url> --role <name> where they can see the window.
Inputs: url (required), role (required), browser, successUrl, +2 more.
scout_networkA
List the data requests (fetch and XHR) the current page made since its document loaded: method, path, status and time, oldest first, each marked with the route it was sent from when a client-side route change moved the page since. Use it when the page and the server seem to disagree — an empty list where the API has data, a stale value after a save — to tell a request that failed, one still pending and one that never ran apart. Read-only: it lists what the browser already saw and sends nothing. Credentials in query strings are redacted. A full page load starts a new list; scout_request's own calls are marked.
Inputs: limit, session, contains.
scout_noteA
Cumulative WRITTEN knowledge about the tested app — .scenescout/ASSUMPTIONS.md, in prose a human can read and correct. memory.json stores coverage; this stores UNDERSTANDING, so every run starts smarter than the last. READ it at the start of every session ({action:'read'}). ADD durable learnings as you go ({action:'add', section, note}): what the app is for (app-model), who each role is and what they're FOR — infer the persona from what the role can see and do, e.g. 'qa-role = reviewer: approves orders, cannot administer' (roles), UI patterns the app follows (conventions), rules discovered the hard way like 'an order can only ship once approved' (constraints), fragile areas worth re-testing every run (risks), domain terms (glossary), and how to get the app testable at all — the command that regenerates an expired login state, what has to be running (setup), which the engine reads back to you the next time a storage state has expired. Notes are dated, attributed to the acting role, and deduplicated. Do NOT record session-specific facts (ids, counts) — only durable knowledge.
Inputs: action (required), note, section, session.
scout_playbookA
Return the SceneScout testing method: setup order, write modes, how to explore, what counts as done, how to report. Call this ONCE before the first scout_attach in a conversation, then follow it. If this client offers a SceneScout skill, load that instead — it is the same text, so never read both. Takes no input and touches no browser.
Inputs: no named parameters.
scout_pressB
Press a keyboard key (e.g. Escape, Tab, Enter) — useful for closing modals and testing keyboard navigation.
Inputs: key (required), task, session, objective.
scout_recapEnd-of-check cardAIdempotent
The recap of the guided check that has just ended, for the person to share with their team: the address, pages checked, problems found by impact, problems a re-check confirmed fixed, and the time it took. Returns it as an SVG picture and as lines of text; with save: true it is also written to the project's .scenescout/recap.svg. Built from the check's figures only. Refuses while no guided check has ended. Touches no browser.
Inputs: save. Returns: svg, date, file, rows, +3 more
scout_recheck_planPlan a re-check after a fixARead-only
Build a guided plan that checks again the problems earlier checks left open, and says which are gone, still there or changed. It runs in the care level the problems were found in, never a looser one: problems found in a looser care level are left for a re-check of their own, and problems on another site for a re-check of that site. It is a new plan: where its care level needs the person's confirmation, scout_guided_confirm asks them again, whatever an earlier check was given. Follow it as you would scout_guided_plan's: attach with its planId, then scout_verify. By default every open problem but those the person marked not a bug, worst route first, at most 25; pass ids for exactly those. Touches no browser.
Inputs: ids, signIn, address. Returns: mode, areas, calls, level, +8 more
scout_reportA
Generate the final markdown report — findings, page quality scores (worst first), role capability matrix, oracle rollup, and the GAP LEDGER (an explicit list of what was NOT tested). Writes the full document to .scenescout/report.md and returns a bounded SUMMARY (full reports exceed client token limits). Gates by level: 'minimal' needs all routes visited + ≥1 design audit; 'medium' additionally needs several routes audited; 'extensive' REFUSES while the gap ledger is non-empty — that refusal is the completeness guarantee: an extensive report only generates when nothing known is left untested. force=true overrides (only when the user capped the budget).
Inputs: force, level, report, history, +1 more.
scout_requestA
Call the app's own API as this session, with the UI bypassed — the check that turns a hidden or disabled control into a proven refusal. A button that is not shown proves nothing; the same action refused by the server does. The fetch runs IN the page, so it carries the session's cookies and replays the Authorization header the app itself last sent, and it passes through the same interception the write policy is enforced on: in safe-write a mutation on a record this session did not create is refused here exactly as it would be for a click, and that refusal is the engine's safety net, not a finding. Returns the status line, the timing, the headers that decide whether two responses are truly identical (content-type, location, www-authenticate, retry-after, cache-control), and the body — its first 2000 characters, or the part named by select (one JSON value by path) or offset/limit (a window of characters). Unlike a shell call, every request is recorded in the run's trail and its signature is what a finding should quote. Paths are fenced to the attached origin: use another session to reach another host.
Inputs: path (required), body, task, limit, +6 more.
scout_resolveA
Mark a finding as resolved (by its id, shown when recorded and in the report). Resolved findings move to the report's green ✅ Resolved section, and reopen automatically as flagged REGRESSIONS if re-found later. Use when the user says a bug is fixed, or when re-testing shows the evidence no longer reproduces.
Inputs: id, session, findingId.
scout_run_planA
Execute up to 20 actions in ONE call — use for mechanical sequences (fill a form, walk a wizard) so each step doesn't cost a round-trip. Targets resolve at execution time by semantic locator: 'testid=…', 'text=…', 'label=…' or 'role=button[name=Save]' (never snapshot refs). The same steps, saved to .scenescout/flows/.json with expect-text / expect-url / expect-request steps added, are replayed by scenescout check on every pull request. An upload step attaches a file as scout_upload does (target required — the file input or the control that opens its chooser; value = a fixture kind or a project-relative path). The plan ABORTS at the first NEW oracle violation, policy refusal, or failed step, returning a transcript of how far it got; repeats of already-reported violations do not abort (they stay logged for the report). For a sweep of independent steps (tabs, filters, pages) pass onViolation "continue": a new error status or its console echo is listed on its step's line and the plan goes on; a failed step, a policy refusal or any other violation still stops it. A select step whose value names no option fails at once, listing the options.
Inputs: steps (required), task, session, objective, +1 more.
scout_saved_checksThe person's saved checksA
List, save, run or delete the person's saved guided checks. A saved check keeps the choices of a guided plan under a name (the address, what to check, the journey, how they sign in, the care level and how thorough), never a password, code, token or cookie, in the project's .scenescout/guided/ folder, readable by its owner only. "save" takes a name and the planId of a plan built from choices (a re-check is not saved). "run" builds a new plan from a saved check, unaccepted: where its care level needs the person's confirmation, scout_guided_confirm asks them again. Follow that plan as scout_guided_plan's. "delete" removes one saved check's file and nothing else. With no check open in this conversation, pass the site's address so SceneScout finds its folder. Touches no browser.
Inputs: action (required), name, planId, address. Returns: plan, saved, folder, refused
scout_scanA
Scan a project directory to discover the frontend workspace, framework, routes, dev command, Playwright auth storage states, and testid conventions. Run this first.
Inputs: projectPath (required).
scout_screenshotA
Take a JPEG screenshot of the current viewport. LAST RESORT: geometry issues are in scout_snapshot and style/contrast/spacing issues are in scout_design_audit — images that failed to load are listed in scout_snapshot under BROKEN IMAGES — use a screenshot only for pixel-native content (a canvas, visual gestalt) that computed data cannot capture.
Inputs: session.
scout_scrollA
Scroll like a user — real apps hide their bugs below the fold. Reports the resulting position (px and %), and explicitly flags SCROLL LOCKED: scrollable content exists but the page will not move (the classic leaked modal scroll-lock that silently cuts users off from everything below the fold — snapshots also detect this passively as an OVERLAY line). Without target it scrolls the page, falling back to the largest scrollable pane on app-shell layouts. Pass target to scroll ONE region instead (a sidebar nav, a dialog body, a table pane): the page-level pick is the LARGEST scroll port, so a smaller region beside it never moves and its content looks truncated when it is only scrolled away — never call a nav item missing without scrolling its own container first. Use before judging a long page: the design audit measures at the current scroll position, so scroll + re-snapshot/re-audit deep sections; scroll also triggers lazy-loaded content whose failures then surface as oracle violations.
Inputs: by, to, target, session.
scout_selectA
Select an option in a by ref. The value is matched against the options before anything is picked: an exact value, an exact label, either ignoring case, then a label it starts with. A value matching no option, or several, is refused at once with the options listed.
Inputs: ref (required), value (required), task, session, +1 more.
scout_sessionA
List live sessions, or set which one is the DEFAULT (used by any tool call that omits session). Prefer passing session directly on each tool call for multi-role work — that's what lets concurrent dispatch happen; scout_session is for sequential convenience (skip repeating session on every call) and for checking what's live. Both browsers stay live and authenticated regardless of which is default — re-snapshot a session after a break to see what changed while it was away.
Inputs: area, name, paceMs, session.
scout_snapshotA
Capture the current page state: URL, state fingerprint, a one-line summary of the main area's heading and text, interactable elements with refs (e1, e2, …) and their state (pressed, selected, checked, expanded, current), what the page announces (alert and status regions, by their text), geometry issues, coverage, and oracle violations since the last action. Re-snapshotting a route returns a DIFF against its last snapshot, even after visiting elsewhere, and another tab of the same screen diffs against that screen's last tab; refs stay stable, including across a search or filter that rewrites only the query string. On a dense page, says what past the element cap was cut. Cheap — prefer this over screenshots.
Inputs: full, session.
scout_statusRun statusA
Show the person running you how the run stands: each session's objective and current task, open findings by severity, coverage, and the live view's address. In a guided check it also shows the check's progress in plain words: what is happening now, each area's state, pages and time against the plan's limits, problems so far and the newest cards, and the clicking it saved with what that figure is based on. Call it when the user wants to watch or asks how the run is going. A host that renders MCP Apps shows a pane that keeps itself up to date; every other host gets the same as text, and you pass the Live view: address on. Takes no input and touches no browser.
Inputs: no named parameters.
scout_status_pollRun status (for the pane)A
Called by the scout_status pane every few seconds to refresh itself. Not for the agent: call scout_status instead.
Inputs: no named parameters.
scout_team_reportMake a team reportAIdempotent
Write the team report: the plain report made for sharing, as one page that opens in any browser. It has a cover (the site, the date, what was checked, the care level and how thorough), a short summary, each problem this check found as a plain card with its picture, the ones the person marked not a bug listed apart, what the check did not test, and blank sign-off lines. Beside it, an evidence folder holds the pictures, the report as Markdown and manifest.json with every file's SHA-256, the SceneScout version and the check's times. Writes only inside the project's .scenescout/ folder (team-report.html and team-report/), replacing the last team report; sends nothing anywhere and touches no browser. Tell the person where the page is.
Inputs: no named parameters. Returns: file, path, folder, manifest, +4 more
scout_ticket_fileFile previewed ticketsA
File the tickets a preview from scout_ticket_preview listed. Before anything is sent, this server asks the PERSON in its own form, listing the tickets; it files only on their yes there. You may make this call; you cannot answer the form, and nothing you pass confirms it. It files only the previewed problems, only while the preview is under 10 minutes old and only if those problems have not changed since; otherwise it refuses and a new preview is needed. A preview is used once: after the person goes back, turns the form down or closes it, or this client cannot show forms, nothing is filed: tell them, and never call it again on their behalf. Returns each filed ticket's link. Touches no browser.
Inputs: previewId (required). Returns: filed, outcome, tickets, problems, +1 more
scout_ticket_previewPreview problems as ticketsARead-only
Preview filing the problems the person chose as GitHub or Jira tickets. Reads the tracker to find which are already filed (by the marker scenescout export writes) and sends nothing. Returns each ticket that would be filed, the ones already filed, any over the limit of one filing, and a previewId that lasts 10 minutes. Show the person the list; to file it, call scout_ticket_file with the previewId. to: "github" with target the repository as owner/name, or "jira" with target the project key (the Jira site is JIRA_BASE_URL in the server's settings). The tracker's sign-in comes from the server's environment or the desktop extension's settings only (GH_TOKEN or GITHUB_TOKEN; JIRA_EMAIL and JIRA_API_TOKEN): never ask the person for a token and never pass one. Touches no browser.
Inputs: to (required), ids (required), target (required). Returns: to, cap, later, where, +6 more
scout_ticketsA
Read the tickets or acceptance criteria the person gave this run, pasted (text) or from a file (path), and keep them so the report answers each criterion: passed, failed or not tested. Recognises Given/When/Then scenarios, checklists, numbered and "AC1:" criteria, and lists under an "Acceptance criteria" heading; several tickets may be given at once. A ticket with no recognisable criteria is reported as such, never guessed at. Returns each criterion's id (AC1, AC2, …) to judge it by with scout_criterion. Touches no browser.
Inputs: path, text, session.
scout_ticket_statusRead filed tickets' stateARead-onlyIdempotent
List the tickets this project filed (with scout_ticket_file or scenescout export) and whether each is still open in GitHub or Jira, with whether its problem is still open in this project. Reads each tracker at most once every 5 minutes per project; a call sooner answers what the last read found (fresh: false, with when the next read is due). Only a tracker the server's settings point at is read, with the sign-in from its environment. Sends nothing but reads; touches no browser. With no check open in this conversation, pass the site's address so SceneScout finds its folder.
Inputs: address. Returns: fresh, readAt, tickets, problems, +1 more
scout_typeA
Type into a text input/textarea/composer by ref, the way a real user does: if the field already holds content (e.g. an @-mention chip a menu click inserted), the text is APPENDED at the end — preserving that content — and the result reports what was already there (a separating space is added only at a word-to-word boundary). Pass replace=true to clear the field first (correcting a previous entry); an empty textValue always clears. Appending fires input events but not keydown, so keydown-driven triggers (slash/mention menus) will not react to appended text. Use for both valid values and boundary/fuzz values (empty, very long, unicode, script tags). A value typed into a secret field (a password, a one-time code, a card number, a field labelled secret or token) is masked in the result and the log, as is what the field held.
Inputs: ref (required), task, value, replace, +4 more.
scout_uploadA
Attach a file to an upload control the way a user does. ref is either a visible (snapshots list these with role file) or the button/label/dropzone that opens the file chooser — the chooser is intercepted and answered, which is how the hidden input behind a styled 'Choose file' control is reached. Omit ref to target the page's only file input, hidden or not (snapshots disclose hidden ones on a FILE INPUTS line). Nothing needs to exist on disk: a small VALID fixture (real PDF/PNG structure) is generated in memory, its kind inferred from the input's accept attribute or chosen with fixture; filePath uploads a real file but must live inside the attached project (fenced like navigation is fenced to the origin), and a credential-shaped one (a dotfile or anything in a hidden folder, .env*, an SSH key, *.pem/*.key/*.p8/*.p12/*.pfx/*.ppk/*.jks/*.keystore/*.asc/*.kdbx, anything under .ssh, .aws, .gnupg, .config, .kube or .docker) is refused unless the operator listed it; name overrides the filename for boundary tests (wrong extension vs accept, very long, unicode). The result names the input, how the file reached it, flags a file that violates accept (a mismatch the app then accepts is a validation finding), warns if the app cleared the input after selection, and says whether a state-changing request fired on selection — if none did, click the form's submit, or check the next snapshot for a client-side rejection.
Inputs: ref, name, task, fixture, +3 more.
scout_verifyA
Re-test findings earlier runs left open. With no arguments, returns the open findings in the order to re-test them — worst route first, grouped so a route is walked once — each with its evidence and repro steps. Pass ids to narrow it to specific findings. After re-testing one, call again with id and verdict to record what you saw: "gone" resolves it, "present" stamps it confirmed so the report stops calling it unverified, "changed" keeps it open and says the behaviour differs. Use after a fix wave, or at the start of a run against an app this project has tested before.
Inputs: id, ids, note, session, +1 more.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 48 tools
Core interaction tools (click, type, select, press, hover, scroll, navigate, back) are cleanly separated, and the finding-lifecycle tools (finding, cards, feedback, resolve, verify) are distinguishable. But the planning surface — scout_intake, scout_guided_plan, scout_guided_confirm, scout_recheck_plan, scout_saved_checks, scout_lane_brief/report — and the four reporting tools (report, team_report, recap, cards) overlap heavily in intent; only very long descriptions keep them apart. scout_run_plan vs. the single-action tools is another soft overlap an agent could misselect on.
Every tool uses the scout_ prefix and snake_case, with a predictable verb_noun or resource_noun shape. Minor deviations (noun-only names like scout_playbook, scout_journey, scout_recap, scout_coverage) break the verb pattern but remain readable and unambiguous within the prefix scheme.
48 tools is well past the 'heavy' band and sits in the too-many range for a single server. Much of the bulk comes from peripheral surfaces (guided plans, saved checks, lanes, ticket filing/status, three report formats) that could be consolidated or split into separate servers.
The surface is unusually thorough for browser-based QA: interaction, network, design audit, journeys, coverage, findings with dedup/verification, guided plans, and reporting are all present with lifecycle coverage. Gaps are minor — no explicit console-log retrieval, viewport/device emulation, or waiting/synchronization primitive — which agents can mostly work around.
Maintenance
Related MCP Connectors
MCP server for building and testing AI agents with multi-model experimentation and insights.
MCP server for Mint — AI-powered QA that runs your app in a real browser on every PR.
Official Browserless MCP server: browser automation, scraping, screenshots and PDFs for AI agents.
Web scraping, browser automation, screenshots and AI data extraction for AI agents via MCP.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceAn MCP server that gives AI agents real browser capabilities including screenshotting, action execution, data extraction, and multi-persona auditing for frontend validation.63 npm6MIT
- AlicenseCqualityCmaintenanceAn MCP server that enables AI agents to autonomously test, debug, and analyze web interfaces visually using Playwright, with 30 tools for screenshots, workflows, performance, and visual comparison.3048 npm84ISC
- FlicenseNot gradedqualityDmaintenanceMCP server that enables AI agents to automate browser testing via Chromium, providing tools for navigation, interaction, and inspection.-
- AlicenseNot gradedqualityBmaintenanceMCP server that provides AI coding agents with real-time visual access to web apps, enabling them to see screens, run accessibility and layout inspections, and interact with pages through a shared live panel.95 npm130MIT