# Playwright Accessibility Testing MCP Server
A production-ready Model Context Protocol (MCP) server for comprehensive accessibility testing using Playwright and axe-core. Test web applications for WCAG 2.0/2.1 compliance with ease.
## Features
- **Comprehensive WCAG Testing**: Tests WCAG 2.0/2.1 Level A/AA compliance plus best practices
- **Natural Language Testing**: Find elements by visible text - no CSS selectors needed
- **Auto-Discovery**: Automatically discover and test all interactive elements
- **Interactive Component Testing**: Test across different states (dropdowns, modals, etc.)
- **Screenshot Capture**: Visual documentation of tested areas
- **Predefined Prompts**: Quick-start templates for common testing scenarios
- **Production-Ready**: Modular architecture, TypeScript, comprehensive error handling
## Installation
```bash
npm install
npm run build
```
## Configuration
Add to your MCP client configuration file:
**Claude Desktop** (`~/Library/Application Support/Claude/claude_desktop_config.json`):
```json
{
"mcpServers": {
"playwright-a11y": {
"command": "node",
"args": ["/absolute/path/to/playwright-axe-mcp/dist/server.js"]
}
}
}
```
**Claude Code** (`.claude/config.json` in your project):
```json
{
"mcpServers": {
"playwright-a11y": {
"command": "node",
"args": ["/absolute/path/to/playwright-axe-mcp/dist/server.js"]
}
}
}
```
Restart your MCP client after configuration.
## Quick Start
### Using the Predefined Prompt (Easiest!)
The fastest way to test is with the **comprehensive-a11y-test** prompt:
```
Use comprehensive-a11y-test prompt:
- url: https://example.com
- block: Rewards account
- steps: open menu, select provider, input value, click apply
```
**Parameters:**
- `url` (required): URL to test
- `block` (optional): Section name (e.g., "Navigation", "User Profile")
- `steps` (optional): Comma-separated interaction steps
**What it does:**
1. Navigates to the URL
2. Runs full accessibility audit
3. Tests specified section with auto-discovery or custom steps
4. Generates human-readable report with:
- Executive summary
- Critical/serious/moderate/minor issues
- Prioritized fix recommendations
## Available Tools
### 1. `a11y_scanUrl`
Scan a URL for accessibility violations.
**Parameters:**
```typescript
{
url: string; // Required: URL to scan
selector?: string; // Optional: CSS selector to scan specific element
waitForSelector?: string; // Optional: Wait for this element before scanning
captureScreenshot?: boolean; // Optional: Capture screenshot (default: false)
timeout?: number; // Optional: Navigation timeout in ms (default: 30000)
}
```
**Example:**
```json
{
"url": "https://example.com",
"selector": "header nav",
"captureScreenshot": true
}
```
### 2. `a11y_scanInteractiveByText`
Test components using natural language - no CSS selectors needed!
**Parameters:**
```typescript
{
url: string; // Required: URL to navigate to
containerText: string; // Required: Visible text to find section (e.g., "Rewards")
autoDiscover?: boolean; // Optional: Auto-discover interactions (default: true)
customInteractions?: Array<{
stateName: string; // Name for this interaction
elementText: string; // Visible text of element to interact with
action: "click" | "hover" | "focus";
waitAfter?: number; // Wait time in ms (default: 500)
}>;
captureScreenshots?: boolean; // Optional: Screenshot each state
timeout?: number; // Optional: Navigation timeout in ms
}
```
**Example: Auto-discover everything**
```json
{
"url": "https://example.com",
"containerText": "User Menu",
"autoDiscover": true,
"captureScreenshots": true
}
```
**Example: Custom interactions**
```json
{
"url": "https://example.com",
"containerText": "Rewards",
"customInteractions": [
{
"stateName": "After opening menu",
"elementText": "View Details",
"action": "click"
}
]
}
```
## Response Format
### Scan Result
```json
{
"url": "https://example.com",
"timestamp": "2025-01-28T...",
"summary": {
"violations": 3,
"passes": 12,
"incomplete": 1
},
"violations": [
{
"id": "color-contrast",
"impact": "serious",
"description": "Elements must have sufficient color contrast",
"help": "Ensure contrast ratio is at least 4.5:1",
"helpUrl": "https://dequeuniversity.com/rules/axe/4.4/color-contrast",
"tags": ["wcag2aa", "wcag21aa"],
"nodes": [
{
"html": "<button class=\"btn\">Submit</button>",
"target": [".btn"],
"failureSummary": "..."
}
]
}
],
"screenshotPath": "/path/to/screenshot.png"
}
```
### Interactive Scan Result
```json
{
"url": "https://example.com",
"containerText": "Rewards",
"totalStates": 3,
"states": [
{
"stateName": "Initial State",
"summary": {"violations": 0, "passes": 5, "incomplete": 0},
"violations": []
},
{
"stateName": "After interacting with buttons: \"View Details\"",
"action": "click",
"elementText": "View Details",
"summary": {"violations": 2, "passes": 8, "incomplete": 1},
"violations": [...]
}
]
}
```
## Project Structure
```
server/
├── constants/ # Configuration and constants
│ ├── config.ts # Application configuration
│ ├── selectors.ts # Interactive element selectors
│ └── wcag-tags.ts # WCAG compliance tags
├── types/ # TypeScript type definitions
│ └── scan-result.ts # Scan result interfaces
├── utils/ # Utility functions
│ ├── axe-scanner.ts # Axe configuration and scanning
│ ├── browser.ts # Browser management
│ ├── container-finder.ts # Find elements by text
│ └── screenshot.ts # Screenshot utilities
├── tools/ # Tool implementations
│ ├── scan-url.ts # URL scanning tool
│ └── scan-interactive-by-text.ts # Interactive testing tool
├── prompts/ # Prompt templates
│ └── comprehensive-a11y-test.ts # Predefined test prompt
└── server.ts # Main MCP server entry point
```
## WCAG Compliance
This server tests for:
- **WCAG 2.0 Level A** (`wcag2a`)
- **WCAG 2.0 Level AA** (`wcag2aa`)
- **WCAG 2.1 Level A** (`wcag21a`)
- **WCAG 2.1 Level AA** (`wcag21aa`)
- **Best Practices** (`best-practice`)
## Impact Levels
Violations are categorized by severity:
- **Critical**: Blocks access completely - fix immediately
- **Serious**: Creates significant barriers - fix before release
- **Moderate**: Noticeable issues - fix soon
- **Minor**: Small improvements - can be backlogged
## Output Files
All screenshots are saved to: `./accessibility-screenshots/`
- `scan-{timestamp}.png` - Full page screenshots
- `{name}-{timestamp}.png` - Named screenshots
## Usage Examples
### Test a simple page
```
User: "Check accessibility of https://example.com"
Claude: → Calls a11y_scanUrl
→ Returns: 3 violations (2 serious, 1 moderate)
```
### Test specific section by name
```
User: "Test the Navigation menu on https://example.com"
Claude: → Calls a11y_scanInteractiveByText with containerText="Navigation"
→ Auto-discovers all interactive elements
→ Returns: Issues found in different states
```
### Test with custom interactions
```
User: "Test the Rewards section - click 'View Details' then 'Redeem'"
Claude: → Parses your request
→ Calls a11y_scanInteractiveByText with custom interactions
→ Returns: Accessibility analysis for each state
```
## Development
```bash
# Build TypeScript
npm run build
# Watch mode for development
npm run watch
# Start server directly (for testing)
npm start
```
### Adding New Tools
1. Create tool file in `server/tools/`
2. Define schema and execution function
3. Register in `server/server.ts`
### Adding New Utilities
1. Create utility file in `server/utils/`
2. Export functions
3. Import where needed
## Best Practices
1. **Always test with screenshots** during initial development:
```json
{"captureScreenshot": true}
```
2. **Wait for dynamic content** on SPAs:
```json
{"waitForSelector": ".content-loaded"}
```
3. **Test interactive components** in different states:
- Initial state
- After user interaction
- Error states
- Success states
4. **Use natural language testing** when you don't know selectors:
```json
{"containerText": "User Menu", "autoDiscover": true}
```
## Troubleshooting
### Server not starting
```bash
npm run build
# Check config path is absolute
# Restart MCP client
```
### Element not found
- Verify the text exists on the page
- Try a shorter, more specific text
- Check if content is loaded (use `waitForSelector`)
### Timeout errors
```json
{"timeout": 60000} // Increase to 60 seconds
```
## Requirements
- Node.js 18+
- Playwright (browsers installed automatically)
## License
ISC
## Contributing
1. Follow the modular architecture
2. Add TypeScript types for new features
3. Document new tools in README
4. Update constants instead of hardcoding values
---
**Built for production use with modular, maintainable architecture.**