Architecture
WCAG-Guide is a Node.js application with four services orchestrated by Docker Compose.
Service diagram
┌─────────────────────────────────────────────────────────────────────┐
│ MCP Client (Claude Code, OpenCode, etc.) │
│ ── stdio ──► wguide mcp ── HTTP ──┐ │
└────────────────────────────────────┼───────────────────────────────┘
│
┌────────────────────────────────────┼───────────────────────────────┐
│ Docker Compose ▼ │
│ ┌──────────────────────────────────────────┐ │
│ │ App Server (Node.js) :8080 │ │
│ │ ├─ REST API │ │
│ │ ├─ Dashboard (vanilla JS SPA) │ │
│ │ └─ Health check /healthz │ │
│ └──────────────────────┬───────────────────┘ │
│ │ SQL │
│ ┌──────────────────────▼───────────────────┐ │
│ │ PostgreSQL :5432 │ │
│ │ ├─ scan_targets, scan_runs │ │
│ │ ├─ findings, finding_instances │ │
│ │ └─ status_events, rule_metadata │ │
│ └──────────────────────▲───────────────────┘ │
│ │ SQL │
│ ┌──────────────────────┴───────────────────┐ │
│ │ Worker (Node.js) │ │
│ │ ├─ Polls for queued scan runs │ │
│ │ ├─ Playwright + axe-core scanner │ │
│ │ └─ Writes findings back to DB │ │
│ └──────────────────────────────────────────┘ │
│ │
│ ┌──────────────────────────────────────────┐ │
│ │ Demo Site (static) :8081 │ │
│ │ └─ Intentionally flawed HTML pages │ │
│ └──────────────────────────────────────────┘ │
└────────────────────────────────────────────────────────────────────┘
Data flow
- Scan target registration — A user or MCP agent creates a scan target via the REST API (site key + environment + branch + base URL).
- Scan run creation — A POST to
/scan-runsqueues a scan job. The job record includes scan options (max pages, depth, concurrency) and the compliance profile. - Worker picks up job — The worker polls for
queuedruns, transitions them torunning, and starts the Playwright browser. - Crawl + scan — The worker crawls from the base URL, discovers internal links, and runs axe-core on each page. Findings are fingerprinted for deduplication.
- Results written — Finding instances are written to PostgreSQL. The worker computes diff states (new, persistent, resolved) by comparing with prior runs.
- Triage + remediation — Users or agents review findings in the dashboard or via MCP tools. Findings can be marked as
in_progress,resolved, orignored(with expiration). - Rescan — Targeted rescans (page, path, or full) verify fixes without re-crawling the entire site.
Key design decisions
- Vanilla JS dashboard — No build step, no framework. The dashboard is a single HTML file, a CSS file, and a JS file served directly by the app server. This keeps the deployment simple and the bundle size near zero.
- Contract-driven MCP — Tool schemas are defined in
contracts/mcp-tools.schema.jsonand validated by tests. The MCP server reads this contract at startup, ensuring tools always match the spec. - Fingerprint-based deduplication — Each finding gets a stable fingerprint (rule + selector + URL path). This enables diff tracking across runs without requiring exact DOM matching.
- InMemoryRepository for testing — The same service layer backs both the production PostgreSQL repository and an in-memory repository used by unit tests and the screenshot generator.
Directory structure
wcag-guide/
├── bin/ CLI entry point
├── contracts/ OpenAPI spec, MCP tool schema
├── scripts/
│ ├── db/ Database migrations
│ ├── dev/ Docker Compose helpers
│ ├── screenshots/ Screenshot capture script
│ └── mcp/ MCP demo workflow
├── site/ GitHub Pages static site
├── src/
│ ├── app/ App server, REST API, dashboard
│ │ ├── public/ Dashboard HTML/CSS/JS
│ │ └── repositories/ PostgreSQL + InMemory repos
│ ├── cli/ CLI command parser
│ ├── mcp/ MCP server, adapter, catalog
│ └── worker/ Scan worker, crawler, scanners
├── test/ Unit + integration tests
└── test-support/ Shared fixtures