MCP Server
The AudioEye MCP server (@audioeye/testing-sdk-mcp) is a Model Context Protocol
server that lets an AI coding agent scan a live page for accessibility issues and propose source-level fixes. It drives
a real browser, scans the page with the AudioEye Testing SDK, attaches a JSX source location to each issue where it can,
and exposes the results to your agent as MCP tools.
Use it with any MCP-capable coding agent: Claude Code, Cursor, VS Code, Codex, Gemini CLI, Windsurf, Zed, or Claude Desktop.
Setup takes three steps: install, sign in, scan. Unlike the rest of the SDK, the MCP server is published to the public npm registry and licensed through a browser sign-in, so it needs no private-registry configuration and no copied credentials on a developer machine. The other SDK packages keep their own setup; see Getting Started.
Requirements
- Node.js 22.12 or later. The SDK supports every Node.js LTS line that has not reached end of life, currently
22and24. Claude Desktop users installing the.mcpbbundle don't need Node.js; the bundle runs on Claude Desktop's own runtime. - An AudioEye account with Testing SDK access. If the Testing SDK Client Token card doesn't appear under Account settings in the AudioEye Customer Portal, contact your AudioEye representative.
- macOS or Linux for full file-write containment. The server also runs on Windows, where symlink containment is best-effort.
Step 1: Install and register
Install the package globally, then let setup register the server with every MCP host it finds on your machine:
npm install -g @audioeye/testing-sdk-mcp
audioeye-mcp setup
setup with no flags detects Claude Code, Codex, Gemini CLI, Cursor, VS Code, Windsurf, and Zed, and registers the
server with each one at user (machine-wide) scope under the key audioeye. Hosts prefix tool and prompt names with that
key (mcp__audioeye__scan, and so on). For Claude Code, setup also adds mcp__audioeye__* to permissions.allow so
the tools don't prompt on every call.
setup is safe to re-run, and audioeye-mcp setup --dry-run prints what it would detect and write without touching
anything. Restart your coding agent afterwards so it picks up the new server.
setup registers the absolute path of the installed audioeye-mcp binary. Registering the npx -y form instead makes
the host re-resolve the package from its own working directory on every launch, and a workspace whose package.json
carries a conflicting npm overrides block then kills the server at startup (the host just shows "Connection closed").
Install globally and let setup register the binary.
Register with a specific host
Pass one or more host flags to configure only those hosts. Each tab also shows the equivalent manual registration.
- Claude Code
- Codex
- Gemini CLI
- Cursor
- VS Code
- Windsurf / Zed
- Claude Desktop
audioeye-mcp setup --claude --global # user scope: `claude mcp add --scope user` + ~/.claude/settings.json
audioeye-mcp setup --claude # project scope: `claude mcp add` + ./.claude/settings.json
Both forms add the mcp__audioeye__* permissions rule. To register by hand instead:
claude mcp add audioeye -- audioeye-mcp
audioeye-mcp setup --codex
Or by hand:
codex mcp add audioeye -- audioeye-mcp
audioeye-mcp setup --gemini --global # `gemini mcp add --scope user`
audioeye-mcp setup --gemini # the CLI's default project scope (./.gemini/settings.json)
Or by hand (Gemini's grammar puts the command before the -- separator):
gemini mcp add audioeye audioeye-mcp
audioeye-mcp setup --cursor # ~/.cursor/mcp.json
Or use the one-click link, which registers the npx form:
Install in Cursor
audioeye-mcp setup --vscode --global # user-profile mcp.json, read by GitHub Copilot and other MCP clients
audioeye-mcp setup --vscode # project ./.vscode/mcp.json
The bare setup command uses the user-profile location. Or use the one-click link, which registers the npx form:
Install in VS Code
audioeye-mcp setup --windsurf # ~/.codeium/windsurf/mcp_config.json
audioeye-mcp setup --zed # ~/.config/zed/settings.json
Claude Desktop installs the server as an extension bundle, audioeye-mcp-<version>.mcpb, provided by AudioEye. Open
Settings → Extensions and drag the file in. The bundle carries its own dependencies and runs on Claude Desktop's
bundled Node.js, so no terminal or Node.js install is needed. Sign in from the chat with the audioeye_login tool; see
Signing in on Claude Desktop.
Step 2: Sign in
audioeye-mcp login
- The terminal prints a short pairing code and opens your browser to the AudioEye portal's device-approval page with the code pre-filled. If the browser doesn't open, use the printed link.
- Sign in to the portal if prompted, confirm the code matches your terminal, and click Approve. The page shows which user and account you're granting access as.
- The terminal reports
Signed in as you@example.com (Your Account). Credentials are stored with owner-only permissions at~/.config/audioeye/credentials.json(%APPDATA%\audioeye\credentials.jsonon Windows). No secret is displayed or pasted by hand.
Two companion commands:
audioeye-mcp whoamishows the active identity and whether it comes from the stored sign-in or from environment variables.audioeye-mcp logoutdeletes the stored credentials. Environment variables are left alone.
The sign-in is stored once per machine and shared by every AudioEye SDK package installed there.
The .mcpb install has no terminal. Sign in from the chat instead; see
Signing in on Claude Desktop.
Signing in on Claude Desktop
Ask Claude to sign you in to AudioEye. It calls the audioeye_login tool:
- The first call returns a pairing code and a link to the portal's device-approval page.
- Open the link, confirm the code matches, and click Approve.
- Ask Claude to check again. The tool reports
signed_inonce the approval lands. It polls in the background and writes the same credential store asaudioeye-mcp login, so every other MCP host on the machine is signed in too.
If you've already run audioeye-mcp login on this machine, Claude Desktop is signed in already. The license token is
never returned to the host or shown in the chat.
CI and other non-interactive environments
Where a browser sign-in isn't possible, provide the license credentials as the AUDIOEYE_TESTING_SDK_CLIENT_ID and
AUDIOEYE_TESTING_SDK_CLIENT_TOKEN environment variables instead. See
CI/CD integration in Getting Started for where to find them and how to store them.
A stored sign-in takes precedence. The SDK falls back to the environment variables when there is no sign-in or when the
stored credentials fail validation. If a host configuration needs the variables inline, reference them by name so the
committed file never carries the token (Claude Code's .mcp.json variable expansion shown):
{
"mcpServers": {
"audioeye": {
"command": "audioeye-mcp",
"env": {
"AUDIOEYE_TESTING_SDK_CLIENT_ID": "${AUDIOEYE_TESTING_SDK_CLIENT_ID}",
"AUDIOEYE_TESTING_SDK_CLIENT_TOKEN": "${AUDIOEYE_TESTING_SDK_CLIENT_TOKEN}"
}
}
}
}
Step 3: Scan
Start your app's development server, then ask your agent to run the bundled prompt against it:
/mcp__audioeye__scan-and-fix http://localhost:3000
The agent opens an AudioEye-controlled Chrome window (log in to your app there if needed), scans the page, and presents a deterministic summary of failures with JSX source locations. With your approval it proposes source-level fixes and re-scans to verify them.
Prefer smaller steps? /mcp__audioeye__scan only scans and summarizes; /mcp__audioeye__fix proposes fixes against the
scan persisted by a previous run.
Available tools
The server exposes these tools to your agent. Every tool that touches the AudioEye engine requires a valid license (sign-in or environment variables) and fails closed without one; rule metadata lookups are the exception.
audioeye_open_browserlaunches (or focuses) a Chrome window with a persistent profile at~/.cache/audioeye-mcp/profile/, so login state survives between scans.audioeye_scanscans the current page, attaches a JSX source location to each failing element where possible, and returns grouped results with rule metadata plus a server-renderedsummaryMarkdownreport. The agent presents the report verbatim, so the same scan produces the same summary on every host.audioeye_get_rule_metadatalooks up metadata for rule codes from an earlier scan.audioeye_get_source_contextreads the lines around a source location from a previous scan. Reads are confined to the workspace.audioeye_get_a11y_factscomputes assistive-technology facts (accessible name, role, resolved ARIA attributes, nearest landmark) for an element on the current page.audioeye_verify_fixre-scans after a fix and reports whether the targeted failure is resolved.audioeye_close_browsershuts the browser down while preserving the profile.audioeye_loginsigns in through browser device pairing for hosts with no terminal (see Signing in on Claude Desktop). It succeeds immediately when credentials are already present.
The Chrome window is owned by the server process your agent spawned. When the agent exits, Chrome exits with it; the persistent profile keeps your login state for the next session.
Bundled prompts
Three prompts ship with the server, registered through the standard MCP prompts API. Every host that surfaces MCP prompts exposes them automatically under its own naming. In Claude Code they appear as:
/mcp__audioeye__scan(optionalurlargument) scans a live URL and summarizes the failures. No code edits./mcp__audioeye__fixproposes source-level fixes against an existing scan result. Does not run a scan./mcp__audioeye__scan-and-fix(optionalurlargument) runs the full flow: scan, summarize, propose fixes (only with your approval), and verify.
Prompt updates arrive with the package. There are no per-repository command files to maintain.
Configuration
The server reads these optional environment variables from the host that launches it:
| Variable | Purpose |
|---|---|
AUDIOEYE_MCP_WORKSPACE | Repository root used for .audioeye-mcp.json discovery, persisted scans, and source reads. Defaults to the server's working directory. Set it when the host's working directory is not the repository root. |
AUDIOEYE_MCP_CONFIG_PATH | Absolute path to an ignore config file, bypassing the workspace-root search. |
AUDIOEYE_MCP_PROFILE_DIR | Location of the persistent Chrome profile. Defaults to ~/.cache/audioeye-mcp/profile. |
AUDIOEYE_MCP_HEADLESS | Set to 1 or true to run Chrome headless. Defaults to headed so you can log in to your app interactively. |
When source-level fixes are limited
The server resolves a source location for failing elements in React projects running a development build. In some situations it reports a failure but cannot point to the line that authored it:
- Non-React projects (Vue, Svelte, Angular, plain HTML): failures are reported without a source location.
- Production or minified builds: source mapping works only when the build ships source maps.
- CMS-rendered content: alt text, headings, and link names authored in a CMS live in the CMS, not in your source, so the fix is a content change.
- React Server Components without
'use client': server-rendered markup is opaque to the in-browser scan. Mark the component's file with'use client'to restore precise mapping. - Third-party iframes and closed shadow roots: the DOM inside is rendered by code you don't own or can't traverse.
In these cases the scan still surfaces the issue; it just cannot always propose a code diff.
Ignoring specific issues
Real pages carry noise you can't fix at source: third-party widgets (Chargebee, Stripe, Intercom), dev-only tooling
(TanStack Router Devtools, React Query Devtools), and runtime-injected vendor iframes. Suppress these with a checked-in
.audioeye-mcp.json at your repository root. The server reads it on every scan and filters matching failures out of
the results before the agent summarizes or proposes fixes.
{
"ignore": [
{
"cssSelector": "footer.TanStackRouterDevtools",
"comment": "Dev-only widget; not shipped to prod."
},
{
"ruleCode": "Iframe_Name_Missing",
"cssSelector": "iframe#cb-master-frame",
"comment": "Vendor SDK iframe — vendor-owned."
},
{
"fileNameContains": "node_modules/",
"comment": "Don't try to patch dependencies."
}
]
}
Each entry under ignore is a matcher with one or more of these optional fields:
| Field | Matches when… |
|---|---|
ruleCode | the failing rule code equals this value exactly |
cssSelector | the failing element — or any of its ancestors — matches this CSS selector |
fileNamePrefix | the resolved source file path starts with this string |
fileNameContains | the resolved source file path contains this string |
comment | (not a matcher) free-text note explaining why the entry exists |
cssSelector is a real CSS selector evaluated against the live DOM during the scan, so attribute selectors
([data-devtools]) and structural selectors (footer:has(> .TanStackRouterDevtoolsPanel)) work. Because ancestors
match too, a single selector suppresses everything inside a container.
An entry must specify at least one matcher field. Fields within an entry are AND'd (all must match); separate
entries are OR'd (an issue matching any entry is suppressed). Every scan response reports configPath, totalIgnored,
and per-rule hit counts, so reviewers can audit exactly what was filtered.
The agent manages this file for you: when it notices noise you repeatedly dismiss, it suggests an ignore entry, but
it treats .audioeye-mcp.json like source code and won't write to it without your approval. Because the file is checked
in, the same suppressions apply for every developer and in CI.
By default the server looks for .audioeye-mcp.json (or audioeye-mcp.config.json) by walking up from the workspace
root. Set AUDIOEYE_MCP_CONFIG_PATH to point at a config file in a non-standard location.
Troubleshooting
Tools fail with a license message
Not signed in to AudioEye. Call the audioeye_login tool to sign in… means the server found neither a stored sign-in
nor both environment variables. Run audioeye-mcp login (or ask the agent to call audioeye_login), then retry.
The AudioEye testing SDK token is invalid or inactive. means credentials were found but rejected. Run
audioeye-mcp whoami to see which identity is active, then:
- if it reports a stored sign-in, run
audioeye-mcp loginagain to refresh it - if it reports environment variables, confirm both are exported in the environment that launched your agent and that the Client Token has not been revoked by a refresh in the portal
See How licensing works for the license check and the offline grace window.
The server is not listed in my agent
Restart your agent after registering the server, then confirm the registration landed: claude mcp list in Claude Code,
or audioeye-mcp setup --dry-run to see what setup detects. Re-running audioeye-mcp setup repairs a missing or
drifted entry.
The server starts and immediately disconnects
If the host shows "Connection closed" as soon as it launches the server, the registration is probably using the
npx -y @audioeye/testing-sdk-mcp form inside a workspace with a conflicting npm overrides block. Install the package
globally and re-run audioeye-mcp setup so the host launches the installed binary instead.
Other issues
For browser-runtime problems shared with the rest of the SDK, see the main Troubleshooting guide.