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.
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, })), }; }