Skip to content

@real-a11y-dev/mcp

Beta — preview page

Published on npm as a beta: the API and tool surface may still change before 1.0, and audit fidelity is bounded by known engine issues. Pin a version rather than tracking the latest tag if you build on it.

The Real A11y MCP server gives an AI assistant a real browser and the accessibility tree behind any page, so it can audit a URL and report the defects a screen reader would announce — not guess from pasted HTML (which hides computed roles and visibility) or a screenshot (which has no semantics). Point any Model Context Protocol client at it and ask it to audit a page in plain language.

Accessibility is a property of the rendered page — the roles, names, and visibility a browser actually computes, not what's in the HTML source. So the server drives a real browser (via Playwright) rather than parsing markup. You need:

  • Node.js 20+ — the server runs under Node and is fetched with npx.
  • A Chrome binary — install one with npx real-a11y install (downloads Chrome for Testing; npx playwright install chromium also works).
  • An MCP client — Claude Code, Claude Desktop, Cursor, VS Code, Windsurf, or any other MCP-capable tool.

Connect it to your client

The server speaks MCP over stdio — point your client at npx -y @real-a11y-dev/mcp. No install step is needed; npx fetches it on first run.

sh
claude mcp add real-a11y -- npx -y @real-a11y-dev/mcp
sh
code --add-mcp '{"name":"real-a11y","command":"npx","args":["-y","@real-a11y-dev/mcp"]}'
json
{
  "mcpServers": {
    "real-a11y": {
      "command": "npx",
      "args": ["-y", "@real-a11y-dev/mcp"]
    }
  }
}

For clients configured by file, add the mcpServers block above to:

  • Claude Desktopclaude_desktop_config.json (Settings → Developer → Edit Config).
  • Cursor~/.cursor/mcp.json, or a project-local .cursor/mcp.json.
  • Windsurf and others — that client's MCP config, using the same block.

Then install a browser once:

sh
npx real-a11y install

To pin a version instead of tracking the latest, add it to your project (npm install -D @real-a11y-dev/mcp playwright) and point command / args at the local install.

The server picks up an installed browser automatically. To point it at a specific binary instead (a system Chrome, say), set REAL_A11Y_CHROME_PATH; REAL_A11Y_BROWSERS_DIR overrides where real-a11y install caches its download if you don't want the platform default (~/.cache/real-a11y). A CDP connection (REAL_A11Y_MCP_CDP) ignores both — it reuses whatever browser is already running.

Your first audit

Once it's connected, ask in plain language — the assistant picks the tools:

You: "Audit example.com for accessibility problems."

Assistant: calls open_page("https://example.com")audit_page() → gets structured findings → explains them and proposes fixes.

Because it drives a real browser, JS-heavy SPAs render fully, and any URL the browser can reach works — public sites, a local dev server, or staging.

Every tool + parameters → /packages/mcp/tools

This is a guide. For the full tool reference — all twenty tools, their parameters, and when to reach for each — see the tools reference.

Auditing a page behind a login

To audit a page only a logged-in user can see, save a browser session once, then point the server at it — the assistant never touches credentials.

  1. Save a session with the CLI's login helper (a one-time human step):

    sh
    npx -y @real-a11y-dev/cli login https://app.example.com --save auth.json

    A real browser opens; sign in by hand (passwords, SSO, MFA, and passkeys all work), then press Enter. Playwright's storage state — cookies and origin storage — is written to auth.json. Keep that file out of version control; it holds live session tokens.

  2. Point the server at it with two environment variables in your MCP config:

    json
    {
      "mcpServers": {
        "real-a11y": {
          "command": "npx",
          "args": ["-y", "@real-a11y-dev/mcp"],
          "env": {
            "REAL_A11Y_MCP_STORAGE_STATE": "/absolute/path/to/auth.json",
            "REAL_A11Y_MCP_ALLOWED_ORIGINS": "https://app.example.com"
          }
        }
      }
    }
  3. Audit as usual. Every page now opens already authenticated, and open_page tells the assistant the session is active so it won't try to log in. REAL_A11Y_MCP_ALLOWED_ORIGINS pins auditing to that origin, so a redirect can't route your session to another site.

The session path is server configuration, never a tool argument, so tokens stay out of the assistant's context. See Authenticated pages for the full workflow and security rules.

Configuration

All server behavior is set through REAL_A11Y_MCP_* environment variables, in the "env" block of the config above — attaching to a running Chrome over CDP, headful mode, file:// access, a saved login session, origin pinning, and the named-session limits (REAL_A11Y_MCP_MAX_SESSIONS, REAL_A11Y_MCP_SESSION_IDLE_TIMEOUT_MS). Each one, with its values and security notes, is documented in Environment in the tools reference.

Every page tool also takes an optional session parameter — independent named live pages with their own checkpoints, serialized within a session and parallel across sessions. Omit it and the server behaves as a single-page tool; the tools reference has the full semantics.

Scripting audits without an MCP client

Same engine, no agent: the route is the CLI. @real-a11y-dev/cli runs these audits from a shell script or a CI job and prints them in machine formats, so nothing in the pipeline has to speak MCP.

sh
npx -y @real-a11y-dev/cli audit https://example.com --format json -o report.json
  • The audit itselfaudit exits 1 on error-severity findings, so it gates a build with no configuration; --format json with -o writes the same findings audit_page reports.
  • Multi-step flows--session <name> keeps one browser alive across invocations, so successive commands read the same live page instead of reloading it — the shell's version of the session parameter above.
  • Acting on the pageclick, type, and focus target a control by role + accessible name and print the tree diff the action caused; interact --step '<step>' chains several of them in one run.
  • Gating a PRsnapshot writes the diffable artifact, and diff base.json pr.json classifies findings as new / changed / fixed in pretty, json, or md (--format md -o comment.md is the PR-comment shape).

Every command and flag is in Commands & flags.

What the CLI cannot give you is an in-process Node API, and no other package does either now. The Playwright-backed session lives in @real-a11y-dev/browser, which was published through 0.1.0-beta.13 and is workspace-internal from mcp@0.1.0-beta.4 on: it is bundled into this server, the CLI, and the testing adapter, and there is nothing to install by that name. Two narrower doors stay open, neither a replacement for it:

  • You already have a Playwright Pageattach(page) from @real-a11y-dev/testing/playwright runs the same audit helpers against it, and stays public.
  • You are embedding this server — it re-exports BrowserSession along with A11ySession, BrowserSessionOptions, PageSnapshot, and SnapshotOptions, because buildServer takes an A11ySession and a custom SessionManager has to name that contract. That is an embedding surface, not a session package: it arrives with the whole MCP SDK dependency graph, which is exactly what installing browser on its own used to avoid.

How it compares to Playwright MCP

This complements a browser-automation MCP such as Playwright MCP — it doesn't try to replace it. Both can act on a page now, but they act for different reasons:

  • Playwright MCP drives a page for automation breadth — arbitrary selectors, hover, drag, file uploads, multiple tabs — using the accessibility snapshot as a means to act.
  • Real A11y acts through the accessibility tree and judges what changed. Its act tools target role + accessible name only — a control the tools can't reach is itself an accessibility smell — and every action pairs with diff_tree's answer to "what did that change for a screen reader?". Its audit reports what assistive technology would announce as broken.

For an accessibility flow — open the dialog, judge the dialog — Real A11y now stands alone. For automation verbs it doesn't ship (hover, drag, uploads, multi-tab), pair the two: drive with Playwright MCP, judge with Real A11y.

Two different "Playwrights"

This package is built on Playwright the library — the browser driver you install as a peer dependency. It complements Playwright MCP, the automation server above. Same underlying engine, different jobs.

Limitations

  • Scope. It runs five rules today — unlabeled interactive elements, images missing alt text, heading order, dialog labeling, and landmark structure — plus the full semantic tree. It is not a complete WCAG or axe-core suite; it is semantic-tree-based and tuned to "what a screen reader announces." For contrast, focus visibility, and other rendered/interactive checks, pair it with axe-core and manual testing.
  • Fidelity follows the engine. The tree is a reimplementation of accessible-name / role computation, not the browser's native accessibility tree. It is tuned to match what assistive tech announces, but can diverge on edge cases.
  • Requires a real browser. The assistant's environment must be able to launch Chromium or connect to one over CDP.

See also

Released under the MIT License. · Privacy · Accessibility