Skip to main content
Glama
PashaBoiko

Playwright Accessibility Testing MCP Server

by PashaBoiko
README.md
# 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.**

TDQS

A3.8/5.0

Scored across 2 tools

Disambiguation4/5

The two tools have clearly distinct purposes: a11y_scanInteractiveByText focuses on testing components via visible text/labels for natural language scenarios, while a11y_scanUrl handles URL-based or element-specific scanning. There is minor potential for confusion since both perform accessibility scans, but their different input methods (text vs. URL/element) and use cases (interactive components vs. general pages) make them mostly distinguishable.

Naming Consistency5/5

Both tools follow a consistent naming pattern with the prefix 'a11y_scan' followed by descriptive suffixes (InteractiveByText and Url). They use snake_case uniformly, and the names clearly indicate their distinct functionalities, making them predictable and easy to understand.

Tool Count3/5

With only two tools, the server feels thin for an accessibility testing domain, which might involve more operations like generating reports, filtering results, or testing specific WCAG criteria. However, the tools cover core scanning functionalities, so it's borderline but not severely lacking.

Completeness3/5

The server provides basic scanning capabilities for URLs and interactive components, but there are notable gaps. Missing operations include result analysis tools (e.g., summarize findings, export reports), configuration options (e.g., custom rule sets), and follow-up actions (e.g., retest after fixes). This limits agents to scanning without deeper workflow support.

Maintenance

ActivityInactive
ResponsivenessNo issues