MCP server by vovaplanka
mcp-astound-a11y
MCP server for WCAG 2.1 accessibility analysis of ISML and HTML templates in Salesforce Commerce Cloud (SFCC) projects.
Powered by axe-core (64 WCAG rules) + 12 custom ISML-specific rules. Integrates into GitHub Copilot Chat and any MCP-compatible AI client. Also works as a standalone CLI tool.
Quick Start
git clone https://github.com/vovaplanka/mcp-astound-a11y.git
cd mcp-astound-a11y
npm install
Register in Your SFCC Project
Add .vscode/mcp.json to your project repo so every team member gets it automatically:
{
"servers": {
"astound-a11y": {
"type": "stdio",
"command": "node",
"args": ["/path/to/mcp-astound-a11y/isml-a11y-server.js"]
}
}
}
Or via VS Code user settings.json:
{
"mcp": {
"servers": {
"astound-a11y": {
"type": "stdio",
"command": "node",
"args": ["/path/to/mcp-astound-a11y/isml-a11y-server.js"]
}
}
}
}
The server resolves file paths relative to the working directory, which MCP clients set to your project root automatically.
Copilot Chat Usage (Prompts & Agent)
Copy the .github/ folder from this repo into your SFCC project to get the @a11y agent and prompt aliases.
@a11y Agent
Select a11y in the Copilot Chat agent picker. It formats MCP results as actionable checklists with code fixes — no raw JSON.
Prompt Aliases
Type / in Copilot Chat to see these:
| Command | What it does |
|---|---|
| /scan cart.isml | Scan a single file, show issues with fixes |
| /scan cartridges/templates/ | Scan a directory, highlight top 5 worst files |
| /fix-a11y cart.isml | Auto-fix all a11y issues in a file |
| /a11y-rules | List all 76 WCAG rules (axe + custom) |
Example — /scan:
Summary: 12 errors, 5 warnings across 8 files
🔴 cart.isml, line 42 —
axe-image-alt(WCAG 1.1.1)
- Problem:
<img src="promo.jpg">missing alt- Fix:
<img src="promo.jpg" alt="Spring promotion banner">Next steps: Fix
cart.ismlfirst (7 issues), thenshipping.isml(3 issues)
Example — /fix-a11y:
Copilot reads the file, applies all fixes automatically, re-scans to verify zero remaining issues, and shows a before/after summary.
CLI Usage
Run from your SFCC project root:
# Analyze a single file (axe-core engine by default)
node /path/to/mcp-astound-a11y/isml-a11y-server.js analyze cart.isml
# Use a specific engine
node .../isml-a11y-server.js analyze cart.isml --engine custom
node .../isml-a11y-server.js analyze cart.isml --engine both
# Works with plain HTML too
node .../isml-a11y-server.js analyze checkout.html
# Scan a directory (custom engine — faster for batch)
node .../isml-a11y-server.js scan cartridges/app_storefront/cartridge/templates/
# List all available rules
node .../isml-a11y-server.js rules
Supported File Types
| Extension | Include chain | axe-core | Custom rules |
|---|---|---|---|
| .isml | yes (parent + child templates) | yes | yes |
| .html / .htm | no | yes | yes |
Analysis Engines
engine: "axe" — axe-core via jsdom (default for single files)
ISML source is converted to clean HTML via isml-to-html.js:
- Injects
data-isml-line/data-isml-fileattributes into every HTML tag - Replaces
${...}expressions with[dynamic]placeholders - Strips ISML tags (
<isif>,<isloop>,<isset>,<isscript>,<iscomment>) - Converts
<isprint>to[dynamic],<islabel>to<label> - Feeds to axe-core → maps violations back to original ISML line numbers
64 WCAG Level A + AA rules. Page-level rules not applicable to fragments are auto-disabled.
engine: "custom" — ISML cross-file rules (default for batch)
12 rules that understand the ISML include chain:
| Rule | WCAG | Checks |
|---|---|---|
| a11y-img-alt | 1.1.1 | <img> missing alt |
| a11y-form-labels | 1.3.1 | Input without label |
| a11y-button-name | 4.1.2 | Button with no accessible name |
| a11y-link-name | 2.4.4 | Link with empty/generic text |
| a11y-target-blank | 3.2.2 | target="_blank" without rel="noopener" |
| a11y-no-div-button | 4.1.2 | <div onclick> without role="button" |
| a11y-tabindex | 2.4.3 | Positive tabindex |
| a11y-heading-order | 1.3.1 | Skipped heading levels across templates |
| a11y-aria-empty | 4.1.2 | Empty aria-label |
| a11y-aria-hidden-focus | 4.1.2 | aria-hidden on focusable element |
| a11y-duplicate-id | 4.1.1 | Duplicate id across included templates |
| a11y-role-valid | 4.1.2 | Invalid ARIA role |
engine: "both" — full audit
Runs both engines and merges results.
MCP Tools
analyze_isml_file
Analyze a single .isml or .html file.
| Parameter | Type | Default | Description |
|---|---|---|---|
| filePath | string | required | Path to .isml, .html, or .htm file |
| engine | "axe" \| "custom" \| "both" | "axe" | Analysis engine |
| parentLevels | number | 2 | Parent template levels to resolve (ISML only) |
| includeJs | boolean | true | Analyze js- class interactions |
| ruleIds | string[] | all | Custom rule IDs to run |
| disableAxeRules | string[] | [] | axe rule IDs to skip |
| workspaceRoot | string | cwd | Override project root |
analyze_isml_directory
Batch scan for all .isml and .html files in a directory.
| Parameter | Type | Default | Description |
|---|---|---|---|
| directory | string | required | Directory to scan |
| engine | "axe" \| "custom" \| "both" | "custom" | Analysis engine |
| maxFiles | number | 50 | Max files to analyze |
| ruleIds | string[] | all | Custom rule IDs |
| workspaceRoot | string | cwd | Override project root |
get_a11y_rules
List all rules — custom (12) + axe-core (64). No parameters.
check_js_interactions
Find JS files that use js- CSS class selectors and detect dynamic HTML injection.
| Parameter | Type | Description |
|---|---|---|
| jsClasses | string[] | required — e.g. ["js-cart-btn"] |
| directory | string | Directory to scan |
| workspaceRoot | string | Override project root |
Project Structure
mcp-astound-a11y/
├── .github/
│ ├── agents/
│ │ └── a11y.agent.md @a11y Copilot Chat agent
│ └── prompts/
│ ├── scan.prompt.md /scan — scan file or directory
│ ├── fix-a11y.prompt.md /fix-a11y — auto-fix issues
│ └── a11y-rules.prompt.md /a11y-rules — list all rules
├── isml-a11y-server.js MCP protocol server + CLI
├── axe-runner.js axe-core integration via jsdom
├── isml-to-html.js ISML → HTML converter (line-number preserving)
├── package.json
└── engine/ Zero-dependency analysis engine
├── isml-parser.js ISML/HTML tokenizer
├── include-resolver.js Include chain resolution
├── js-analyzer.js JS file scanner for js-* class DOM mutations
└── rules/
├── index.js Rule registry
├── images.js a11y-img-alt
├── forms.js a11y-form-labels
├── interactive.js a11y-button-name, link-name, target-blank, no-div-button, tabindex
├── headings.js a11y-heading-order
└── aria.js a11y-aria-empty, aria-hidden-focus, duplicate-id, role-valid
Workspace Root Resolution
Priority order:
workspaceRootparameter in tool callISML_A11Y_WORKSPACE_ROOTenvironment variableprocess.cwd()(set by MCP client — usually your project root)
Known Limitations
color-contrast— disabled (jsdom cannot compute CSS styles)<isif>branches — both branches treated as present (possible false positives)- Heading order — may false-positive when
parentLevels > 0 <iscomponent>/<isslot>— rendered output not analyzed
License
MIT