axe-core Accessibility Audit
axe_auditRun axe-core accessibility audits on HTML strings or live URLs. Returns violations grouped by impact with fix guidance and help links.
Instructions
Run the axe-core accessibility ruleset against an HTML string (jsdom) or a live URL (Playwright). Returns violations grouped by impact with fix guidance and helpUrl links.
Tool call template
This tool declares no required parameters. Add optional parameters from the input schema as needed.
{
"id": 1,
"jsonrpc": "2.0",
"method": "tools/call",
"params": {
"arguments": {},
"name": "axe_audit"
}
}Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | No | Fully qualified URL to audit. Requires playwright installed. | |
| html | No | Raw HTML string to audit (jsdom, no JS execution). | |
| tags | No | axe rule tags to include. Defaults to ['wcag2a','wcag2aa','wcag21a','wcag21aa','wcag22aa','best-practice']. | |
| selector | No | CSS selector to scope the audit (URL mode only). | |
| timeoutMs | No | Default 15000. |
Implementation Reference
- src/index.ts:24-25 (registration)Registration of the axe_audit tool in the main server setup. The function 'registerAxeAudit' is called with the MCP server instance.
registerAxeAudit(server); registerPageScreenshot(server); - src/tools/axe-audit.ts:6-27 (schema)Input schema for the axe_audit tool defined using Zod: url (optional), html (optional), tags (optional array of strings), selector (optional), timeoutMs (optional positive int).
const InputShape = { url: z .string() .url() .optional() .describe("Fully qualified URL to audit. Requires playwright installed."), html: z .string() .optional() .describe("Raw HTML string to audit (jsdom, no JS execution)."), tags: z .array(z.string()) .optional() .describe( "axe rule tags to include. Defaults to ['wcag2a','wcag2aa','wcag21a','wcag21aa','wcag22aa','best-practice']." ), selector: z .string() .optional() .describe("CSS selector to scope the audit (URL mode only)."), timeoutMs: z.number().int().positive().optional().describe("Default 15000."), }; - src/tools/axe-audit.ts:164-191 (handler)Main registration & handler for the 'axe_audit' tool. Registers the tool with title, description, inputSchema, and the async handler function that dispatches to auditUrl or auditHtml based on input, returning summarized results.
export function registerAxeAudit(server: McpServer) { server.registerTool( "axe_audit", { title: "axe-core Accessibility Audit", description: "Run the axe-core accessibility ruleset against an HTML string (jsdom) or a live URL (Playwright). Returns violations grouped by impact with fix guidance and helpUrl links.", inputSchema: InputShape, }, async (args) => { const tags = args.tags && args.tags.length ? args.tags : DEFAULT_TAGS; const timeoutMs = args.timeoutMs ?? 15000; if (!args.url && !args.html) { return errorResult("Provide either `url` or `html`."); } try { if (args.url) { const res = await auditUrl(args.url, tags, args.selector, timeoutMs); return jsonResult(summarize(res, "url", args.url)); } const res = await auditHtml(args.html!, tags); return jsonResult(summarize(res, "html", `<${args.html!.length} chars>`)); } catch (err) { return errorResult(err instanceof Error ? err.message : String(err)); } } ); } - src/tools/axe-audit.ts:38-57 (helper)TypeScript interfaces (AxeViolation, AxeResults) defining the shape of axe-core audit results.
interface AxeViolation { id: string; impact?: string | null; help: string; helpUrl: string; tags: string[]; nodes: Array<{ target: string[]; html: string; failureSummary?: string; }>; } interface AxeResults { violations: AxeViolation[]; passes: Array<{ id: string }>; incomplete: AxeViolation[]; url?: string; timestamp: string; } - src/tools/axe-audit.ts:59-101 (helper)Helper to summarize axe-core results: counts of violations by impact, passes, incomplete, and truncated violation details with up to 5 nodes each.
function summarize(results: AxeResults, mode: "url" | "html", target: string) { const byImpact: Record<string, number> = { critical: 0, serious: 0, moderate: 0, minor: 0, unknown: 0, }; for (const v of results.violations) { const k = v.impact ?? "unknown"; byImpact[k] = (byImpact[k] ?? 0) + 1; } const totalNodes = results.violations.reduce((n, v) => n + v.nodes.length, 0); return { mode, target, summary: { violationRules: results.violations.length, violatingNodes: totalNodes, passedRules: results.passes.length, incompleteRules: results.incomplete.length, byImpact, }, violations: results.violations.map((v) => ({ id: v.id, impact: v.impact ?? null, help: v.help, helpUrl: v.helpUrl, tags: v.tags, nodes: v.nodes.slice(0, 5).map((n) => ({ target: n.target, html: n.html.slice(0, 400), failureSummary: n.failureSummary, })), nodeCount: v.nodes.length, })), incomplete: results.incomplete.map((v) => ({ id: v.id, help: v.help, nodeCount: v.nodes.length, })), }; }