Add a new cookbook recipe that generates WCAG accessibility reports using the Playwright MCP server. Includes streaming output, structured report formatting, and optional Playwright test generation. - C#, TypeScript, Python, and Go implementations - Markdown documentation for each language - Updated cookbook.yml, copilot-sdk README, and package.json
8.3 KiB
Generating Accessibility Reports
Build a CLI tool that analyzes web page accessibility using the Playwright MCP server and generates detailed WCAG-compliant reports with optional test generation.
Runnable example: recipe/accessibility-report.ts
cd recipe && npm install npx tsx accessibility-report.ts # or: npm run accessibility-report
Example scenario
You want to audit a website's accessibility compliance. This tool navigates to a URL using Playwright, captures an accessibility snapshot, and produces a structured report covering WCAG criteria like landmarks, heading hierarchy, focus management, and touch targets. It can also generate Playwright test files to automate future accessibility checks.
Prerequisites
npm install @github/copilot-sdk
npm install -D typescript tsx @types/node
You also need npx available (Node.js installed) for the Playwright MCP server.
Usage
npx tsx accessibility-report.ts
# Enter a URL when prompted
Full example: accessibility-report.ts
#!/usr/bin/env npx tsx
import { CopilotClient } from "@github/copilot-sdk";
import * as readline from "node:readline";
// ============================================================================
// Main Application
// ============================================================================
async function main() {
console.log("=== Accessibility Report Generator ===\n");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const askQuestion = (query: string): Promise<string> =>
new Promise((resolve) => rl.question(query, (answer) => resolve(answer.trim())));
let url = await askQuestion("Enter URL to analyze: ");
if (!url) {
console.log("No URL provided. Exiting.");
rl.close();
return;
}
// Ensure URL has a scheme
if (!url.startsWith("http://") && !url.startsWith("https://")) {
url = "https://" + url;
}
console.log(`\nAnalyzing: ${url}`);
console.log("Please wait...\n");
// Create Copilot client with Playwright MCP server
const client = new CopilotClient();
const session = await client.createSession({
model: "claude-opus-4.6",
streaming: true,
mcpServers: {
playwright: {
type: "local",
command: "npx",
args: ["@playwright/mcp@latest"],
tools: ["*"],
},
},
});
// Set up streaming event handling
let idleResolve: (() => void) | null = null;
session.on((event) => {
if (event.type === "assistant.message.delta") {
process.stdout.write(event.data.deltaContent ?? "");
} else if (event.type === "session.idle") {
idleResolve?.();
} else if (event.type === "session.error") {
console.error(`\nError: ${event.data.message}`);
idleResolve?.();
}
});
const waitForIdle = (): Promise<void> =>
new Promise((resolve) => {
idleResolve = resolve;
});
const prompt = `
Use the Playwright MCP server to analyze the accessibility of this webpage: ${url}
Please:
1. Navigate to the URL using playwright-browser_navigate
2. Take an accessibility snapshot using playwright-browser_snapshot
3. Analyze the snapshot and provide a detailed accessibility report
Format the report with emoji indicators:
- 📊 Accessibility Report header
- ✅ What's Working Well (table with Category, Status, Details)
- ⚠️ Issues Found (table with Severity, Issue, WCAG Criterion, Recommendation)
- 📋 Stats Summary (links, headings, focusable elements, landmarks)
- ⚙️ Priority Recommendations
Use ✅ for pass, 🔴 for high severity issues, 🟡 for medium severity, ❌ for missing items.
Include actual findings from the page analysis.
`;
let idle = waitForIdle();
await session.send({ prompt });
await idle;
console.log("\n\n=== Report Complete ===\n");
// Prompt user for test generation
const generateTests = await askQuestion(
"Would you like to generate Playwright accessibility tests? (y/n): "
);
if (generateTests.toLowerCase() === "y" || generateTests.toLowerCase() === "yes") {
const detectLanguagePrompt = `
Analyze the current working directory to detect the primary programming language.
Respond with ONLY the detected language name and a brief explanation.
If no project is detected, suggest "TypeScript" as the default.
`;
console.log("\nDetecting project language...\n");
idle = waitForIdle();
await session.send({ prompt: detectLanguagePrompt });
await idle;
let language = await askQuestion("\n\nConfirm language for tests (or enter a different one): ");
if (!language) language = "TypeScript";
const testGenerationPrompt = `
Based on the accessibility report you just generated for ${url},
create Playwright accessibility tests in ${language}.
Include tests for: lang attribute, title, heading hierarchy, alt text,
landmarks, skip navigation, focus indicators, and touch targets.
Use Playwright's accessibility testing features with helpful comments.
Output the complete test file.
`;
console.log("\nGenerating accessibility tests...\n");
idle = waitForIdle();
await session.send({ prompt: testGenerationPrompt });
await idle;
console.log("\n\n=== Tests Generated ===");
}
rl.close();
await session.destroy();
await client.stop();
}
main().catch(console.error);
How it works
- Playwright MCP server: Configures a local MCP server running
@playwright/mcpto provide browser automation tools - Streaming output: Uses
streaming: trueandassistant.message.deltaevents for real-time token-by-token output - Accessibility snapshot: Playwright's
browser_snapshottool captures the full accessibility tree of the page - Structured report: The prompt engineers a consistent WCAG-aligned report format with emoji severity indicators
- Test generation: Optionally detects the project language and generates Playwright accessibility tests
Key concepts
MCP server configuration
The recipe configures a local MCP server that runs alongside the session:
const session = await client.createSession({
mcpServers: {
playwright: {
type: "local",
command: "npx",
args: ["@playwright/mcp@latest"],
tools: ["*"],
},
},
});
This gives the model access to Playwright browser tools like browser_navigate, browser_snapshot, and browser_click.
Streaming with events
Unlike sendAndWait, this recipe uses streaming for real-time output:
session.on((event) => {
if (event.type === "assistant.message.delta") {
process.stdout.write(event.data.deltaContent ?? "");
} else if (event.type === "session.idle") {
idleResolve?.();
}
});
Sample interaction
=== Accessibility Report Generator ===
Enter URL to analyze: github.com
Analyzing: https://github.com
Please wait...
📊 Accessibility Report: GitHub (github.com)
✅ What's Working Well
| Category | Status | Details |
|----------|--------|---------|
| Language | ✅ Pass | lang="en" properly set |
| Page Title | ✅ Pass | "GitHub" is recognizable |
| Heading Hierarchy | ✅ Pass | Proper H1/H2 structure |
| Images | ✅ Pass | All images have alt text |
⚠️ Issues Found
| Severity | Issue | WCAG Criterion | Recommendation |
|----------|-------|----------------|----------------|
| 🟡 Medium | Some links lack descriptive text | 2.4.4 | Add aria-label to icon-only links |
📋 Stats Summary
- Total Links: 47
- Total Headings: 8 (1× H1, proper hierarchy)
- Focusable Elements: 52
- Landmarks Found: banner ✅, navigation ✅, main ✅, footer ✅
=== Report Complete ===
Would you like to generate Playwright accessibility tests? (y/n): y
Detecting project language...
TypeScript detected (package.json found)
Confirm language for tests (or enter a different one):
Generating accessibility tests...
[Generated test file output...]
=== Tests Generated ===