qa-mcp
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., "@qa-mcp open localhost:5173, click the checkout button, and capture a screenshot"
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.
qa-mcp
A local MCP server that lets an agent see a running app instead of reading its source.
It opens the app in a real browser, does exactly what it is told, takes pictures, and reports measurements. It makes no decisions: no model calls, no severity scores, no self-healing, no "is this fixed" logic. The calling agent does the judging.
What it gives an agent
Tool | Returns | Never does |
|
| choose a URL or size |
| window lifecycle and state | decide what to look at |
| per-action ok/error/timing | add steps, retry, heal selectors |
| a PNG plus timestamp | judge the frame |
| whether an explicit condition was met | guess a "ready" heuristic |
| rule results with definitions and thresholds | rank or triage them |
| run id, per-step facts, artifact paths | invent or repair steps |
| changed pixels, bounding box, diff image | call a change good or bad |
| stored ids; rejects findings with no evidence | grade or dedupe findings |
| annotation id, opened overlay | interpret the user's words |
| marks, elements, crops, status | paraphrase or fix the note |
Rules measured: horizontal overflow, broken images, missing alt text, tap targets under
24×24, clipped text, plus console errors, page errors and failed requests. Every rule reports its
own definition and threshold.
Baseline promotion is deliberately not a tool; accepting a new "correct" look is a human decision and stays in the CLI.
Related MCP server: vouch
Requirements
Node.js 20 or newer
Chromium for Playwright:
npx playwright install --with-deps chromiumOptional:
electronfor the invisible local window engine
Install
npm install qa-mcp
npx playwright install --with-deps chromiumOr from source:
git clone https://github.com/contraliz/qa-mcp.git
cd qa-mcp
npm install
npm run buildQuick start
npx qa-mcp init --url http://localhost:5173/ # writes .qa/config.json + .qa/scenarios/smoke.json
npx qa-mcp doctor # node, config, browser, allow-list, artifacts
npx qa-mcp run smoke # executes the scenario, writes artifactsrun exits 0 when every step executed, 2 when a step could not (missing selector, timeout).
Measurement counts are printed, not judged, so your gate stays your gate.
Register the MCP server
OpenCode (opencode.json):
{
"mcp": {
"qa": {
"type": "local",
"command": ["npx", "qa-mcp", "serve"],
"environment": { "QA_HOME": "./qa" }
}
}
}Claude Code (.mcp.json):
{
"mcpServers": {
"qa": {
"command": "npx",
"args": ["qa-mcp", "serve"],
"env": { "QA_HOME": "./qa" }
}
}
}Codex (~/.codex/config.toml):
[mcp_servers.qa]
command = "npx"
args = ["qa-mcp", "serve"]
env = { QA_HOME = "./qa" }Any stdio MCP client works the same way. Without an MCP client, the CLI covers everything
except live captures and findings_store.
Scenarios
// .qa/scenarios/checkout.json
{
"name": "checkout",
"url": "http://localhost:5173/",
"steps": [
{ "action": "goto", "value": "/" },
{ "action": "fill", "selector": "#email", "value": "sam@example.com" },
{ "action": "click", "selector": "#place-order" },
{ "action": "wait_for", "selector": "#status" },
{ "action": "capture", "name": "after-order" }
]
}Steps are literal: goto, click, fill, press, hover, select, scroll, wait_for,
capture. Capture names are what baseline promote and diff use.
Engines
| Window | Use it for |
| Chromium parked far off-screen | CI, servers, and local runs with no extra setup |
| Frameless Electron window, never shown until asked | local work: it exists, paints, stays out of the taskbar, and takes no focus |
Under CI=true or QA_HEADLESS=1 the server always uses Playwright headless; Electron needs a
desktop session. The Electron helper starts on demand, binds loopback with a per-process token,
and exits after 15 idle minutes.
Configuration
.qa/config.json (commit it) with environment variables taking precedence:
{
"baseUrl": "http://localhost:5173/",
"engine": "electron",
"allowHosts": ["localhost:5173", "127.0.0.1:5173"],
"scenariosDir": ".qa/scenarios",
"viewport": { "width": 1280, "height": 800 }
}Variable | Default | Meaning |
|
| artifacts root |
|
|
|
| auto on | headed vs headless |
| loopback only | hosts the server may open |
|
| page load timeout |
|
| action and wait timeout |
| bundled Chromium |
|
|
| config file location |
Safety defaults
Loopback only.
stage_open,scenario_run,gotosteps andannotate_startrefuse any host outside the allow-list.No credentials in URLs. Embedded
user:pass@is rejected; secret-looking query values (token,key,session, …) are masked in stored URLs.No writes to your source. Runs land in
qa/; the tool never edits code, commits, or promotes baselines.Artifacts may contain customer data. Screenshots are of your app. Keep them local and set retention if you publish them.
Storage
qa/
runs/<run_id>/ meta, steps, measurements, captures, diffs
findings/<run_id>.json findings with cited evidence
annotations/<id>/ annotation.json, crops, full frame
baselines/<scenario>/ promoted reference frames (commit these)CLI
qa-mcp serve stdio MCP server
qa-mcp init [--url ...] [--engine ...] write config + a smoke scenario
qa-mcp doctor check the machine before blaming the app
qa-mcp run <scenario|name> CI entrypoint
qa-mcp gc [--keep 20] delete the oldest runs
qa-mcp baseline promote|list human baseline management
qa-mcp --versionLimits
Web apps only. Native desktop automation is not implemented.
No visual AI anywhere: "does this look right" is the agent's job.
Detector set is small and reviewable; contrast and z-order checks are not implemented.
One browser instance per server; one active scenario at a time.
Pixel diffs are only comparable across machines inside the pinned browser image (
Dockerfilematches theplaywrightversion).
Development
npm run build
npm test # 21 tests, headless
npm run typecheck
npm run pack:checkSource: src/config.ts (env, config file, allow-list, redaction), src/stage.ts (Playwright
engine), src/electron-stage.ts + electron/main.cjs (Electron engine), src/inpage.ts
(measurement rules), src/inpage-annotation.ts (annotation overlay), src/scenario.ts,
src/diff.ts, src/annotate.ts, src/store.ts, src/server.ts (MCP), src/cli.ts.
License
MIT
Available Tools
15 toolsactPerform actionsB
Execute the given click/fill/press/hover/select/scroll actions in order. No extra steps, no retries, no healing. Returns per-action ok/error/timing.
Inputs: actions (required), stage_id (required).
annotate_startStart an annotation sessionA
Open a visible window with a drawing overlay so the user can mark parts of the app and attach notes. Returns immediately; poll annotations_list until status is ready.
Inputs: url (required), width, height.
annotations_getGet one annotationB
Return an annotation bundle with each mark's element, rect, note, and the cropped images of the marked regions.
Inputs: annotation_id (required).
annotations_listList annotationsC
List annotation sessions and their status. Marks and notes are returned verbatim.
Inputs: status.
annotations_resolveResolve an annotationC
Mark an annotation as handled and record how it was addressed.
Inputs: note (required), annotation_id (required).
captureCapture the stageB
Take a PNG of the current stage and return it as an image block plus metadata. Reports when it was taken; it does not judge the frame.
Inputs: stage_id (required), clip, full_page.
diffDiff a capture against its baselineB
Compare a named capture from a run with the promoted baseline for that scenario. Returns changed pixel counts and a bounding box, and writes a diff image. Does not decide if the change is acceptable.
Inputs: name (required), run_id (required).
findings_storeStore findingsB
Persist findings supplied by the caller. Each finding needs a claim and at least one evidence reference. This stores; it does not grade, dedupe, or resolve.
Inputs: run_id (required), findings (required), verdict.
measureMeasure the rendered pageB
Run rule-based measurements on the live page: horizontal overflow, broken images, missing alt, small tap targets, clipped text, plus collected console/page/network errors. Every rule returns its definition and threshold.
Inputs: stage_id (required).
scenario_runRun a named scenarioB
Open a URL, execute the supplied steps exactly as written, capture the requested frames, and store a run under qa/runs/<run_id>. Steps are never invented, repaired, or retried.
Inputs: url (required), name (required), steps (required), width, +1 more.
stage_closeClose a stageC
Close a stage and its browser context.
Inputs: stage_id (required).
stage_focusBring a stage on screenB
Move a stage window from off-screen to the visible desktop and focus it.
Inputs: stage_id (required).
stage_openOpen a stageA
Open a URL in a real browser window owned by this server. The window is parked off-screen unless hidden=false. Returns a stage_id. Does not choose what to test.
Inputs: url (required), width, height, hidden.
stage_stateStage stateB
Report URL, viewport, hidden flag, last capture time, capture staleness, and runtime error counts for a stage.
Inputs: stage_id (required).
wait_forWait for an explicit conditionB
Wait until a selector is visible or a text appears, up to timeout_ms. There is no implicit 'ready' heuristic.
Inputs: stage_id (required), text, selector, timeout_ms.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 15 tools
Most tools have clearly distinct roles: stage lifecycle, act/capture/wait, annotation lifecycle, measurement, scenario runs, and diffing. There is minor overlap between act and scenario_run for executing steps, and between stage_state and measure for error reporting, but descriptions clarify the different scopes.
Prefixes like stage_* and annotations_* help group related tools, but conventions are mixed: noun_verb (stage_open, annotations_get), verb_noun (annotate_start), and bare verbs (act, capture, diff, measure). The names remain readable but are not predictable throughout.
Fifteen tools map cleanly to distinct QA capabilities—browser staging, interaction, capture, waiting, annotations, findings, measurement, scenario runs, and diffing. There is no obvious filler, and the count fits the server's apparent scope.
The core QA loop is covered: open/focus/close a stage, act, capture, wait, measure, run scenarios, diff, annotate, and store findings. However diff references a promoted baseline with no visible baseline promotion/management tool, and findings_store has no retrieval counterpart, leaving some workflow gaps.
Maintenance
Related MCP Connectors
Control a real browser from AI agents: navigate, click, type, screenshot, read the page.
Read-only web QA agent: scans a site in a real browser and returns evidence-backed findings
Run multi-step tasks in a real Chrome browser: persistent environments, live view, human takeover.
Test a web app in a real browser from plain English. Passed, failed or cannot verify, with evidence.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables AI agents to perform end-to-end web testing by controlling a real browser through tools for navigation, interaction, state inspection, screenshots, and script evaluation.MIT
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to verify application changes by running browser workflows, executing independent state assertions, and producing replayable evidence.3 npmMIT
- AlicenseNot gradedqualityBmaintenanceEnables coding agents to validate web pages and run repeatable JSON browser scenarios, returning pass/fail results with local Playwright evidence such as screenshots, traces, and JUnit XML.7 npm1Apache 2.0
- AlicenseNot gradedqualityBmaintenanceEnables AI agents to drive real Chromium, WebKit, or Firefox browsers — opening named sessions, running ordered steps like clicks, typing, and assertions, and reading back text, console/network logs, and screenshots — so web apps can be tested and explored end to end. It starts and shares dev servers, attaches to already-running browsers, and can fake API responses or device settings.2 npmMIT