Skip to main content
Glama

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

stage_open

stage_id, URL, viewport

choose a URL or size

stage_close, stage_focus, stage_state

window lifecycle and state

decide what to look at

act

per-action ok/error/timing

add steps, retry, heal selectors

capture

a PNG plus timestamp

judge the frame

wait_for

whether an explicit condition was met

guess a "ready" heuristic

measure

rule results with definitions and thresholds

rank or triage them

scenario_run

run id, per-step facts, artifact paths

invent or repair steps

diff

changed pixels, bounding box, diff image

call a change good or bad

findings_store

stored ids; rejects findings with no evidence

grade or dedupe findings

annotate_start

annotation id, opened overlay

interpret the user's words

annotations_list, annotations_get, annotations_resolve

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 chromium

  • Optional: electron for the invisible local window engine

Install

npm install qa-mcp
npx playwright install --with-deps chromium

Or from source:

git clone https://github.com/contraliz/qa-mcp.git
cd qa-mcp
npm install
npm run build

Quick 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 artifacts

run 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

engine

Window

Use it for

playwright (default)

Chromium parked far off-screen

CI, servers, and local runs with no extra setup

electron

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

QA_HOME

./qa

artifacts root

QA_ENGINE

playwright

playwright or electron

QA_HEADLESS

auto on CI=true

headed vs headless

QA_ALLOW_HOSTS

loopback only

hosts the server may open

QA_NAV_TIMEOUT_MS

30000

page load timeout

QA_ACTION_TIMEOUT_MS

5000

action and wait timeout

QA_CHANNEL

bundled Chromium

chrome or msedge

QA_CONFIG

./.qa/config.json

config file location

Safety defaults

  • Loopback only. stage_open, scenario_run, goto steps and annotate_start refuse 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 --version

Limits

  • 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 (Dockerfile matches the playwright version).

Development

npm run build
npm test          # 21 tests, headless
npm run typecheck
npm run pack:check

Source: 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 tools
actPerform 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

    B3.3/5.0

    Scored across 15 tools

    Disambiguation4/5

    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.

    Naming Consistency3/5

    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.

    Tool Count5/5

    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.

    Completeness4/5

    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

    ActivityMaintained
    ResponsivenessNo recent community issues

    Related MCP Connectors

    Related MCP Servers

    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables coding agents to verify application changes by running browser workflows, executing independent state assertions, and producing replayable evidence.
      3 npm
      MIT
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables 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 npm
      1
      Apache 2.0
    • A
      license
      Not graded
      quality
      B
      maintenance
      Enables 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 npm
      MIT