accessibility_audit
Run automated accessibility audits to detect WCAG 2.1 Level A and AA violations, providing severity-based issue reports with specific fix instructions for web pages.
Instructions
Run an automated accessibility audit using axe-core. Checks for WCAG 2.1 Level A and AA violations, reporting issues by severity with specific fix instructions.
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| url | Yes | URL of the page to audit |
Implementation Reference
- src/tools/accessibility.ts:46-117 (handler)The main handler function for the accessibility_audit tool, which orchestrates browser navigation, axe-core injection, and audit execution.
export async function runAccessibilityAudit( url: string ): Promise<AccessibilityResult> { const page = await createPage(1440, 900); try { await navigateAndWait(page, url, 500); // Inject axe-core into the page const axeSource = await getAxeSource(); await page.evaluate(axeSource); // Run the audit const rawResults = await page.evaluate(async () => { // @ts-expect-error axe is injected at runtime const results = await window.axe.run(document, { runOnly: { type: "tag", values: ["wcag2a", "wcag2aa", "wcag21a", "wcag21aa", "best-practice"], }, }); return { violations: results.violations.map( (v: { id: string; impact: string; description: string; help: string; helpUrl: string; nodes: Array<{ target: string[]; html: string; failureSummary: string; }>; }) => ({ id: v.id, impact: v.impact, description: v.description, help: v.help, helpUrl: v.helpUrl, nodes: v.nodes.slice(0, 5).map( (n: { target: string[]; html: string; failureSummary: string; }) => ({ target: n.target, html: n.html.slice(0, 200), failureSummary: n.failureSummary, }) ), }) ), passCount: results.passes.length, incompleteCount: results.incomplete.length, inapplicableCount: results.inapplicable.length, }; }); return { url, timestamp: new Date().toISOString(), violations: rawResults.violations as readonly AccessibilityViolation[], passes: rawResults.passCount, incomplete: rawResults.incompleteCount, inapplicable: rawResults.inapplicableCount, }; } finally { await closePage(page); } } - src/tools/accessibility.ts:122-177 (helper)Helper function to format the audit results into a readable Markdown report.
export function formatAccessibilityReport( result: AccessibilityResult ): string { const lines: string[] = [ `## Accessibility Audit Results`, ``, `**URL:** ${result.url}`, `**Scanned:** ${result.timestamp}`, `**Violations:** ${result.violations.length}`, `**Passes:** ${result.passes}`, `**Incomplete:** ${result.incomplete}`, ``, ]; if (result.violations.length === 0) { lines.push("No accessibility violations found."); return lines.join("\n"); } // Group by impact const byImpact = { critical: [] as AccessibilityViolation[], serious: [] as AccessibilityViolation[], moderate: [] as AccessibilityViolation[], minor: [] as AccessibilityViolation[], }; for (const violation of result.violations) { const bucket = byImpact[violation.impact]; if (bucket) { bucket.push(violation); } } for (const [impact, violations] of Object.entries(byImpact)) { if (violations.length === 0) continue; lines.push(`### ${impact.toUpperCase()} (${violations.length})`); lines.push(``); for (const v of violations) { lines.push(`- **${v.id}**: ${v.help}`); lines.push(` ${v.description}`); lines.push(` [Learn more](${v.helpUrl})`); for (const node of v.nodes.slice(0, 3)) { lines.push(` - Element: \`${node.target.join(" > ")}\``); lines.push(` Fix: ${node.failureSummary}`); } lines.push(``); } } return lines.join("\n"); }