Analyze Tool
The analyze tool performs comprehensive accessibility analysis on web pages by running a scan through the Axe DevTools Browser Extension in a real browser environment. It works seamlessly with both local development URLs (e.g., localhost:3000) and remote production URLs.
What It Does
- Authentication - Validates the user's credentials (either an API key or an OAuth 2.0 access token) to ensure authorized access
- Configuration Retrieval - Fetches the user's organization-specific Axe Configuration settings, including:
- Accessibility Testing Standard (e.g., WCAG 2.2 AA)
- axe-core version
- Needs review / best practices
- Advanced Rules preset
- Browser-Based Analysis - Spins up a browser instance in the background with the Axe DevTools Extension mounted
- Page Navigation - Navigates to the URL provided by the user in their prompt to the AI agent
- Accessibility Scan - Runs a full accessibility analysis on the rendered page using the Axe DevTools Browser Extension, ensuring that the actual user experience is tested (not just static HTML)
- Results Delivery - Returns comprehensive analysis results back to the agent in a structured format
Responsive Testing
The analyze tool supports optional viewportWidth and viewportHeight parameters, allowing you to test pages at specific viewport dimensions. This is useful for catching accessibility issues that only appear at certain screen sizes, such as mobile or tablet breakpoints.
Analyze http://localhost:3000 for accessibility issues at a mobile viewport of 375x812When both parameters are omitted, the scan runs at 1000×1080. Passing viewportWidth alone defaults the height to 1080; viewportHeight requires viewportWidth to be set. Either dimension may be up to 7680 pixels.
Partial Page Scans
By default, the analyze tool scans the entire page. To scope the scan to a specific region, pass the optional selector parameter — useful for focusing on a single component or excluding noisy, unrelated parts of the page from the results.
-
A single CSS selector string targets an element in the top frame:
{ "url": "http://localhost:3000", "selector": "#main" } -
An array of CSS selectors steps through iframe or shadow-DOM boundaries — each segment selects the host for the next. Use an array only when the target lives inside an iframe or shadow root:
{ "url": "http://localhost:3000", "selector": ["iframe#checkout", "#payment-form"] }
An array supports up to 10 segments. If the selector matches no element on the page, the scan returns an error. When selector is omitted, the whole page is scanned.
Prompt your AI agent in natural language — the agent translates your intent into the tool call:
Scan only the #main region of http://localhost:3000 for accessibility issuesBrowser Interactions Before Scanning
The analyze tool supports an optional before array of interaction steps that run after the page loads but before the accessibility scan. This unlocks several real-world testing scenarios:
- Login-gated pages — fill in credentials and submit before scanning the post-login page
- Cookie/consent banners — dismiss banners that would otherwise overlay or obscure page content
- Dynamic content — wait for client-rendered content (route changes, late-injected DOM) to appear before scanning
Steps execute in array order, in the same browser context as the scan, so cookies, localStorage, and any route changes triggered by click or fill persist into the scan.
The before array supports up to 20 steps. Each step gets its own timeout of BROWSER_TIMEOUT_MS (default 30000 ms); there is no per-step override.
Supported Actions
| Action | Required fields | Optional fields | Purpose |
|---|---|---|---|
click |
selector |
Click the element matching the CSS selector (e.g., a submit button, a "Dismiss" button on a banner). |
|
fill |
selector, value |
Fill an input matching selector with value. Use for credentials, search queries, or form fields. An empty string clears the input. |
|
waitFor |
selector |
state — one of "visible" (default), "attached", "hidden", "detached" |
Wait for the element matching selector to reach state. Use to gate the next step or the scan itself. Pick a selector that exists only in the post-interaction state (e.g., a logout button or dashboard heading) — generic selectors like body or #app already exist before the interaction and resolve instantly, so they won't gate anything. |
wait |
ms |
Pause for ms milliseconds (1–5000), then continue. Use only when nothing on the page marks readiness — a CSS transition finishing, a debounce timer firing, a canvas drawing itself. If an element appears or changes, use waitFor instead: it's faster and doesn't guess. Requires v1.5.0 or later. |
Prefer waitFor over wait. A fixed pause either waits longer than needed or not long enough, and slows every scan by its full duration. The total of all wait steps in one before array is capped at 10000 ms; a request over the cap is rejected. The pause is added on top of the short automatic settle after each interaction; it doesn't replace it.
Example: Logging in before scanning
Prompt your AI agent in natural language — the agent translates your intent into the tool call:
Analyze http://localhost:3000 for accessibility issues. Before running
the analysis, fill in the #username and #password fields with USERNAME
and PASSWORD from ./.env.local, click the button[type=submit] button,
and wait for #main-content to appear.The agent resolves the prompt and calls the analyze tool with a payload similar to:
{
"url": "http://localhost:3000",
"before": [
{
"action": "fill",
"selector": "#username",
"value": "<resolved-from-.env.local>"
},
{
"action": "fill",
"selector": "#password",
"value": "<resolved-from-.env.local>"
},
{ "action": "click", "selector": "button[type=submit]" },
{ "action": "waitFor", "selector": "#main-content" }
]
}fill.value is treated as sensitive. The Axe MCP Server never logs fill.value, never echoes it in error messages, and never sends it to telemetry. Use fill for any user-supplied or secret input (passwords, API tokens, etc.) so secrets stay redacted across the entire pipeline — and never embed sensitive values in a selector, which does appear in logs and error messages.
The agent resolves value, not the server. The Axe MCP Server treats value as a literal string — it does not read files, expand environment variables, or interpret placeholder syntax like ${VAR}, $VAR, or {{VAR}}. Your AI agent (Claude, Copilot, Cursor, etc.) is responsible for resolving the user's intent into a concrete string before calling the tool.
In practice, this means:
- Phrase prompts naturally — "use USERNAME/PASSWORD from
.env.local" works. The agent reads the file with its own filesystem tools and substitutes the values. - Don't paste placeholder syntax — writing
value: "${USERNAME}"in a prompt will cause the literal string${USERNAME}to be typed into the input. - Be explicit about ambiguous sources — if you say "use my saved credentials" without pointing the agent at a file or env var, a well-behaved agent will ask rather than guess. Tell it where to look.
Some authentication flows are not supported. before actions drive the page through Playwright-style interactions in a Dockerized Chromium instance. The following are intentionally out of scope:
- Captcha challenges (reCAPTCHA, hCaptcha, etc.)
- 2FA / TOTP / SMS verification codes
- Third-party SSO redirect chains (e.g., "Sign in with Google", Okta-hosted login pages)
When your real login flow requires any of the above, scan an alternative entry point:
- A pre-authenticated session cookie injected with Cookie Injection — authenticate once in a real browser, then pass the resulting session cookie so the scan starts already logged in
- A session token or bypass URL your team uses for automated testing
- A staging URL with auth disabled for accessibility testing
Cookie Injection
The analyze tool supports an optional cookies array that sets cookies on the browser context before navigation — so they ride the very first request to the page. This is distinct from before actions, which run after navigation and therefore cannot influence how the initial request is routed. Two common uses:
- Environment routing — set a staging or feature-branch selector cookie that an edge or CDN layer reads to decide which version of the site to serve.
- Pre-authenticated sessions — inject a valid session cookie so the scan starts already logged in, without driving a login form through
before.
The cookies array supports up to 20 cookies.
Cookie fields
| Field | Required | Description |
|---|---|---|
name |
Yes | Cookie name. Appears in logs and error messages — never put secret values here. |
value |
Yes | Cookie value. Treated as sensitive: never logged, echoed in errors, or sent to telemetry. Up to 10,000 characters (long enough for JWTs and session tokens). |
domain |
Yes | Cookie domain. Required so scope is explicit. Use a leading dot (.example.com) to share the cookie across subdomains. |
path |
No | Cookie path. Defaults to /. |
sameSite |
No | One of "Strict", "Lax", or "None". "None" requires secure: true. |
secure |
No | Boolean. |
httpOnly |
No | Boolean. |
expires |
No | Expiry as a Unix timestamp in seconds. Omit for a session cookie. |
Example: Landing on a pre-authenticated page
Prompt your AI agent in natural language — the agent translates your intent into the tool call:
Analyze https://app.example.com for accessibility issues. Set the session
cookie for app.example.com from ./.env.local so the scan starts already
logged in.The agent resolves the cookie value and calls the analyze tool with a payload similar to:
{
"url": "https://app.example.com",
"cookies": [
{
"name": "session",
"value": "<resolved-from-.env.local>",
"domain": "app.example.com"
}
]
}cookies[*].value is treated as sensitive. As with fill.value, the Axe MCP Server never logs a cookie's value, never echoes it in error messages, and never sends it to telemetry. A cookie's name, however, does appear in logs and error messages — keep secrets in value, never in name.
The agent resolves value, not the server. Cookie values follow the same rule as fill.value in before actions: the server treats value as a literal string and does not read files, expand environment variables, or interpret placeholder syntax like ${VAR}. Your AI agent resolves the user's intent into a concrete string before calling the tool.
Screenshots
The analyze tool can return a screenshot of the page alongside the violation report, so you can see what was scanned. Pass the optional screenshot parameter to opt in — an empty object is enough:
{
"url": "http://localhost:3000",
"screenshot": {}
}PNG is the default. Set format to "jpeg" for a smaller image on photo-heavy pages:
{
"url": "http://localhost:3000",
"screenshot": { "format": "jpeg" }
}The image comes back as a standard MCP image content block, after the violation report.
What the screenshot shows
- The visible viewport, not the full page. Content below the fold is not included. To capture more of the page, pass a tall
viewportHeight(e.g.4096) so the visible area covers what you want to see. - The page as it was immediately before the scan started. Capture happens just before
axe.run(), so DOM changes that occur during the scan — SPA re-renders,useEffectupdates, animations, in-flight requests — are not reflected. On single-page apps this skew is common.
Don't treat the screenshot as the source of truth for what Axe saw. Because of the timing skew above, an element visible in the image may not be what Axe evaluated. Ask your agent not to narrate visible-but-unflagged elements as if they were scan results — the violation report is authoritative.
Cost and client support
Request screenshots deliberately. An image content block costs image-input tokens on your agent's next turn — roughly an order of magnitude more than the equivalent text. Ask for a screenshot when you actually want to see the page, rather than adding it to every scan.
Whether the image renders inline is up to your MCP client. The server always returns a spec-valid image block, but some clients collapse tool results or omit image previews — VS Code with Copilot displays it, while Cursor and Claude Desktop may not. A missing preview is a client-side display limitation, not a failed capture.
Saving screenshots to disk
The screenshot can also be written to a file, which is the reliable way to see a capture in a client that doesn't render inline images. Set saveTo to an absolute path:
{
"url": "http://localhost:3000",
"screenshot": { "saveTo": "/Users/me/Desktop/home.png" }
}Or set save: true to let the server choose the filename:
{
"url": "http://localhost:3000",
"screenshot": { "save": true }
}| Field | Type | Purpose |
|---|---|---|
saveTo |
string |
Absolute path to write the image to. If it points at an existing directory, a generated filename is written inside it. Implies saving, so save is not needed alongside it. |
save |
boolean |
Write the image under a generated filename in the server's screenshot directory (AXE_SCREENSHOT_DIR, default your OS temp directory). Ignored when saveTo is set. |
inline |
boolean |
Whether to also attach the image as an inline block (default true). Set false to skip the inline image and return only the saved path. |
The absolute path that was written comes back in the response's messages array, so your agent can tell you where to find the file.
Pair a save with inline: false to avoid paying for the image twice. If your client can't render the inline image anyway, { "save": true, "inline": false } writes the file and skips the image content block — saving the image-input tokens it would otherwise cost on your agent's next turn.
inline: false only takes effect once the save actually succeeds. If the write fails, the image is still returned inline so the capture isn't lost.
Under the Docker distribution, the file is written inside the container. To reach it from your host, mount a volume over the target directory and point saveTo (or AXE_SCREENSHOT_DIR) at the container-side path. The server does not detect whether a mount exists — without one, the file is written and then discarded with the container.
Saving applies to successful scans only. If the scan fails after the screenshot was captured, the image is returned inline alongside the error regardless of inline, and is never written to disk.
When capture fails
Screenshot capture is best-effort and never fails a scan. If the capture times out, the scan still returns its results with a note in the response's messages array:
Screenshot capture failed: <reason>If the scan itself fails after the screenshot was taken, the image is returned with the error response anyway — the visual state of the page at the moment things went wrong is usually the most useful debugging evidence you have.
Screenshots you request are not sent to Deque. The image is captured locally and returned directly to your agent. This is separate from the full-page screenshot that Advanced Rules upload for server-side evaluation; see What gets sent to Deque.
Advanced Rules
Beyond the standard axe-core ruleset, the analyze tool can run Advanced Rules — automated tests that use screenshots, computer vision, and large language models to catch issues axe-core alone cannot, such as headings that only look like headings or informative images with unhelpful alternative text.
Which preset runs is governed by your organization's Axe Configuration, and — where your administrator allows it — can be overridden per server with AXE_ADVANCED_RULES or per scan with the advancedRules argument:
{
"url": "http://localhost:3000",
"advancedRules": "thorough"
}Every response reports the preset that actually ran and where it came from:
{
"advancedRules": {
"value": "thorough",
"source": "tool_arg"
}
}Advanced Rules come with your Axe DevTools for Web subscription — the same one that gives you the Axe MCP Server. They add roughly 15–20 seconds to a scan, consume AI credits, and are the one case where analyze sends page data (a full-page screenshot plus page structure) to Deque for evaluation. See Advanced Rules for presets, precedence, degradation messages, and privacy details.
Key Benefits
- Real Browser Testing - Tests the actual rendered page, not just source code, ensuring accurate results
- Organization Standards - Respects your team's Axe Configuration settings for consistent testing across all users
- Comprehensive Coverage - Leverages the industry-leading Axe Platform
- Responsive Testing - Test at specific viewport dimensions to catch breakpoint-specific accessibility issues
- Targeted Scans - Scope a scan to a specific region, iframe, or shadow root with the
selectorparameter - Authenticated & Interactive Pages - Scan pages behind a login, dismiss cookie banners, or wait for dynamic content using
beforeactions - Session & Environment Cookies - Land already authenticated, or route to a specific environment, by injecting cookies before navigation with the
cookiesparameter - Visual Context - Return a screenshot of the page alongside the report with the
screenshotparameter, including when a scan fails - Advanced Rules - Catch issues that require visual or contextual reasoning, at a confidence threshold your organization controls
- Intelligent Guided Tests - Run the Keyboard, Interactive Elements, and Modal Dialog IGTs against the same page in the same call with the
igtToolsparameter
Output
The tool returns a structured JSON response containing:
- All accessibility violations found
- Violation severity levels (critical, serious, moderate, minor)
- Specific element selectors and source code
- Rule IDs and descriptions
- An
advancedRulesblock reporting the Advanced Rules preset that ran and where it came from - A
messagesarray carrying any notes about the run (for example, a failed screenshot capture, a degraded advanced rules run, or the path a screenshot was saved to)
When screenshot is set, an image content block follows the report. When igtTools is set, IGT results are returned alongside the Axe results, keyed by IGT name.
