accessibility-server-mcp 1.0.7 → 1.0.9
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +147 -6
- package/config/wcag-rules.json +22 -11
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +175 -31
- package/dist/index.js.map +1 -1
- package/dist/test-multi-standard.d.ts +6 -0
- package/dist/test-multi-standard.d.ts.map +1 -0
- package/dist/test-multi-standard.js +78 -0
- package/dist/test-multi-standard.js.map +1 -0
- package/dist/tools/accessibility-tester.d.ts +9 -1
- package/dist/tools/accessibility-tester.d.ts.map +1 -1
- package/dist/tools/accessibility-tester.js +75 -34
- package/dist/tools/accessibility-tester.js.map +1 -1
- package/dist/tools/wcag-validator.d.ts +19 -2
- package/dist/tools/wcag-validator.d.ts.map +1 -1
- package/dist/tools/wcag-validator.js +63 -1
- package/dist/tools/wcag-validator.js.map +1 -1
- package/dist/tools/website-accessibility-tester.d.ts +26 -4
- package/dist/tools/website-accessibility-tester.d.ts.map +1 -1
- package/dist/tools/website-accessibility-tester.js +122 -12
- package/dist/tools/website-accessibility-tester.js.map +1 -1
- package/dist/types/accessibility.d.ts +170 -5
- package/dist/types/accessibility.d.ts.map +1 -1
- package/dist/types/accessibility.js +67 -1
- package/dist/types/accessibility.js.map +1 -1
- package/dist/types/mcp.d.ts +13 -3
- package/dist/types/mcp.d.ts.map +1 -1
- package/dist/utils/ada-guidance.d.ts +22 -0
- package/dist/utils/ada-guidance.d.ts.map +1 -0
- package/dist/utils/ada-guidance.js +131 -0
- package/dist/utils/ada-guidance.js.map +1 -0
- package/dist/utils/browser-manager.d.ts +13 -0
- package/dist/utils/browser-manager.d.ts.map +1 -1
- package/dist/utils/browser-manager.js +67 -0
- package/dist/utils/browser-manager.js.map +1 -1
- package/dist/utils/index.d.ts +3 -0
- package/dist/utils/index.d.ts.map +1 -1
- package/dist/utils/index.js +3 -0
- package/dist/utils/index.js.map +1 -1
- package/dist/utils/report-generator.d.ts.map +1 -1
- package/dist/utils/report-generator.js +37 -2
- package/dist/utils/report-generator.js.map +1 -1
- package/dist/utils/standard-presets.d.ts +58 -0
- package/dist/utils/standard-presets.d.ts.map +1 -0
- package/dist/utils/standard-presets.js +188 -0
- package/dist/utils/standard-presets.js.map +1 -0
- package/dist/utils/standard-tag-metadata.d.ts +35 -0
- package/dist/utils/standard-tag-metadata.d.ts.map +1 -0
- package/dist/utils/standard-tag-metadata.js +416 -0
- package/dist/utils/standard-tag-metadata.js.map +1 -0
- package/dist/utils/website-crawler.d.ts +10 -0
- package/dist/utils/website-crawler.d.ts.map +1 -1
- package/dist/utils/website-crawler.js +40 -6
- package/dist/utils/website-crawler.js.map +1 -1
- package/package.json +1 -1
- package/dist/test-manual.d.ts +0 -6
- package/dist/test-manual.d.ts.map +0 -1
- package/dist/test-manual.js +0 -66
- package/dist/test-manual.js.map +0 -1
package/README.md
CHANGED
|
@@ -7,7 +7,8 @@ A comprehensive Model Context Protocol (MCP) server for web accessibility testin
|
|
|
7
7
|
- **🌐 URL Testing**: Test any public URL for accessibility issues using Puppeteer and axe-core
|
|
8
8
|
- **🕷️ Website-Wide Testing**: Crawl and test entire websites for comprehensive accessibility audits
|
|
9
9
|
- **📝 HTML Snippet Testing**: Analyze raw HTML strings for accessibility compliance
|
|
10
|
-
- **📋
|
|
10
|
+
- **📋 Multi-Standard Support**: Test against WCAG 2.0/2.1/2.2, Section 508, ADA, EN 301 549, and more using axe-core tags
|
|
11
|
+
- **🎯 Flexible Standard Combinations**: Use presets (wcag-aa, ada, section508) or specify individual axe-core tags
|
|
11
12
|
- **🎨 Color Contrast Analysis**: Check color combinations for WCAG contrast requirements
|
|
12
13
|
- **♿ ARIA Validation**: Validate proper usage of ARIA attributes and roles
|
|
13
14
|
- **📱 Mobile Testing**: Orientation lock detection and mobile accessibility issues
|
|
@@ -168,7 +169,69 @@ docker run -d --name accessibility-mcp \
|
|
|
168
169
|
accessibility-mcp
|
|
169
170
|
```
|
|
170
171
|
|
|
171
|
-
##
|
|
172
|
+
## � Multi-Standard Support
|
|
173
|
+
|
|
174
|
+
The server supports testing against multiple accessibility standards simultaneously:
|
|
175
|
+
|
|
176
|
+
### Standard Presets
|
|
177
|
+
|
|
178
|
+
Quick presets for common standard combinations:
|
|
179
|
+
|
|
180
|
+
- **`wcag-a`**: WCAG 2.0/2.1/2.2 Level A
|
|
181
|
+
- **`wcag-aa`**: WCAG 2.0/2.1/2.2 Level AA (default)
|
|
182
|
+
- **`wcag-aaa`**: WCAG 2.0/2.1/2.2 Level AAA
|
|
183
|
+
- **`section508`**: Section 508 compliance tags
|
|
184
|
+
- **`ada`**: ADA Title III compliance (WCAG 2.1 AA + legal guidance)
|
|
185
|
+
- **`en301549`**: EN 301 549 European standard
|
|
186
|
+
- **`all-standards`**: Comprehensive testing across all standards
|
|
187
|
+
|
|
188
|
+
### Individual Standard Tags
|
|
189
|
+
|
|
190
|
+
Specify individual axe-core tags for fine-grained control (~150+ available):
|
|
191
|
+
|
|
192
|
+
**WCAG Tags:**
|
|
193
|
+
- `wcag2a`, `wcag2aa`, `wcag2aaa` - WCAG 2.0 levels
|
|
194
|
+
- `wcag21a`, `wcag21aa`, `wcag21aaa` - WCAG 2.1 levels
|
|
195
|
+
- `wcag22aa` - WCAG 2.2 Level AA
|
|
196
|
+
- `wcag111`, `wcag131`, `wcag143`, `wcag244`, etc. - Specific success criteria
|
|
197
|
+
|
|
198
|
+
**Other Standards:**
|
|
199
|
+
- `section508`, `section508.22.a`, `section508.22.g` - Section 508
|
|
200
|
+
- `EN-301-549` - European accessibility standard
|
|
201
|
+
- `TTv5` - Trusted Tester v5
|
|
202
|
+
- `RGAAv4` - French accessibility standard
|
|
203
|
+
|
|
204
|
+
**Category Tags:**
|
|
205
|
+
- `cat.color`, `cat.forms`, `cat.keyboard`, `cat.aria`, `cat.text-alternatives`
|
|
206
|
+
- `cat.name-role-value`, `cat.semantics`, `cat.language`, etc.
|
|
207
|
+
|
|
208
|
+
### Standard Combination Logic
|
|
209
|
+
|
|
210
|
+
- **AND Logic** (default): Rules must match ALL specified standards (`requireAll: true`)
|
|
211
|
+
- **OR Logic**: Rules match ANY specified standard (`requireAll: false`)
|
|
212
|
+
|
|
213
|
+
### Usage Examples
|
|
214
|
+
|
|
215
|
+
```json
|
|
216
|
+
// Test against multiple presets
|
|
217
|
+
{
|
|
218
|
+
"standardPresets": ["wcag-aa", "section508", "ada"]
|
|
219
|
+
}
|
|
220
|
+
|
|
221
|
+
// Test against specific tags with AND logic
|
|
222
|
+
{
|
|
223
|
+
"standards": ["wcag21aa", "wcag244", "section508.22.a"],
|
|
224
|
+
"requireAll": true
|
|
225
|
+
}
|
|
226
|
+
|
|
227
|
+
// Test against category tags with OR logic
|
|
228
|
+
{
|
|
229
|
+
"standards": ["cat.color", "cat.forms"],
|
|
230
|
+
"requireAll": false
|
|
231
|
+
}
|
|
232
|
+
```
|
|
233
|
+
|
|
234
|
+
## �🛠️ Available MCP Tools
|
|
172
235
|
|
|
173
236
|
The server provides four main tools for accessibility testing, with two operational modes:
|
|
174
237
|
|
|
@@ -189,7 +252,7 @@ The server provides four main tools for accessibility testing, with two operatio
|
|
|
189
252
|
|
|
190
253
|
Test a website or HTML content for accessibility issues:
|
|
191
254
|
|
|
192
|
-
**For URLs:**
|
|
255
|
+
**For URLs with standard presets:**
|
|
193
256
|
```json
|
|
194
257
|
{
|
|
195
258
|
"name": "test_accessibility",
|
|
@@ -197,12 +260,25 @@ Test a website or HTML content for accessibility issues:
|
|
|
197
260
|
"target": "https://example.com",
|
|
198
261
|
"type": "url",
|
|
199
262
|
"level": "basic",
|
|
200
|
-
"
|
|
263
|
+
"standardPresets": ["wcag-aa", "section508"]
|
|
201
264
|
}
|
|
202
265
|
}
|
|
203
266
|
```
|
|
204
267
|
|
|
205
|
-
**For
|
|
268
|
+
**For URLs with specific standard tags:**
|
|
269
|
+
```json
|
|
270
|
+
{
|
|
271
|
+
"name": "test_accessibility",
|
|
272
|
+
"arguments": {
|
|
273
|
+
"target": "https://example.com",
|
|
274
|
+
"type": "url",
|
|
275
|
+
"level": "full",
|
|
276
|
+
"standards": ["wcag21aa", "wcag244", "section508.22.a", "EN-301-549"]
|
|
277
|
+
}
|
|
278
|
+
}
|
|
279
|
+
```
|
|
280
|
+
|
|
281
|
+
**For HTML snippets (legacy wcagLevel still supported):**
|
|
206
282
|
```json
|
|
207
283
|
{
|
|
208
284
|
"name": "test_accessibility",
|
|
@@ -215,16 +291,30 @@ Test a website or HTML content for accessibility issues:
|
|
|
215
291
|
}
|
|
216
292
|
```
|
|
217
293
|
|
|
294
|
+
**For ADA compliance testing:**
|
|
295
|
+
```json
|
|
296
|
+
{
|
|
297
|
+
"name": "test_accessibility",
|
|
298
|
+
"arguments": {
|
|
299
|
+
"target": "https://example.com",
|
|
300
|
+
"type": "url",
|
|
301
|
+
"level": "full",
|
|
302
|
+
"standardPresets": ["ada"]
|
|
303
|
+
}
|
|
304
|
+
}
|
|
305
|
+
```
|
|
306
|
+
|
|
218
307
|
#### 2. Test Website Accessibility
|
|
219
308
|
|
|
220
309
|
Test entire websites by crawling multiple pages:
|
|
221
310
|
|
|
311
|
+
**With standard presets:**
|
|
222
312
|
```json
|
|
223
313
|
{
|
|
224
314
|
"name": "test_website_accessibility",
|
|
225
315
|
"arguments": {
|
|
226
316
|
"baseUrl": "https://example.com",
|
|
227
|
-
"
|
|
317
|
+
"standardPresets": ["wcag-aa", "section508"],
|
|
228
318
|
"maxPages": 20,
|
|
229
319
|
"maxDepth": 3,
|
|
230
320
|
"concurrency": 3,
|
|
@@ -235,6 +325,32 @@ Test entire websites by crawling multiple pages:
|
|
|
235
325
|
}
|
|
236
326
|
```
|
|
237
327
|
|
|
328
|
+
**With specific standard tags:**
|
|
329
|
+
```json
|
|
330
|
+
{
|
|
331
|
+
"name": "test_website_accessibility",
|
|
332
|
+
"arguments": {
|
|
333
|
+
"baseUrl": "https://example.com",
|
|
334
|
+
"standards": ["wcag21aa", "wcag244", "EN-301-549"],
|
|
335
|
+
"maxPages": 10,
|
|
336
|
+
"maxDepth": 2
|
|
337
|
+
}
|
|
338
|
+
}
|
|
339
|
+
```
|
|
340
|
+
|
|
341
|
+
**Legacy wcagLevel parameter (still supported):**
|
|
342
|
+
```json
|
|
343
|
+
{
|
|
344
|
+
"name": "test_website_accessibility",
|
|
345
|
+
"arguments": {
|
|
346
|
+
"baseUrl": "https://example.com",
|
|
347
|
+
"wcagLevel": "AA",
|
|
348
|
+
"maxPages": 20,
|
|
349
|
+
"maxDepth": 3
|
|
350
|
+
}
|
|
351
|
+
}
|
|
352
|
+
```
|
|
353
|
+
|
|
238
354
|
**Key Features of Website Testing:**
|
|
239
355
|
- 🕷️ **Automatic Crawling**: Discovers pages by following links
|
|
240
356
|
- 📊 **Site-wide Reports**: Aggregated accessibility metrics across all pages
|
|
@@ -263,7 +379,32 @@ Validate color combinations for WCAG compliance:
|
|
|
263
379
|
#### 4. Get WCAG Rules (Full Mode Only)
|
|
264
380
|
|
|
265
381
|
Retrieve information about WCAG accessibility rules:
|
|
382
|
+
**Query rules by standard presets:**
|
|
383
|
+
```json
|
|
384
|
+
{
|
|
385
|
+
"name": "get_wcag_rules",
|
|
386
|
+
"arguments": {
|
|
387
|
+
"standardPresets": ["wcag-aa", "section508"],
|
|
388
|
+
"category": "color",
|
|
389
|
+
"search": "contrast",
|
|
390
|
+
"limit": 10
|
|
391
|
+
}
|
|
392
|
+
}
|
|
393
|
+
```
|
|
394
|
+
|
|
395
|
+
**Query rules by specific standard tags:**
|
|
396
|
+
```json
|
|
397
|
+
{
|
|
398
|
+
"name": "get_wcag_rules",
|
|
399
|
+
"arguments": {
|
|
400
|
+
"standards": ["wcag21aa", "wcag244", "EN-301-549"],
|
|
401
|
+
"requireAll": true,
|
|
402
|
+
"limit": 20
|
|
403
|
+
}
|
|
404
|
+
}
|
|
405
|
+
```
|
|
266
406
|
|
|
407
|
+
**Legacy wcagLevel parameter (still supported):**
|
|
267
408
|
```json
|
|
268
409
|
{
|
|
269
410
|
"name": "get_wcag_rules",
|
package/config/wcag-rules.json
CHANGED
|
@@ -158,67 +158,78 @@
|
|
|
158
158
|
"title": "Color Contrast",
|
|
159
159
|
"description": "Ensures the contrast between foreground and background colors meets WCAG 2 AA contrast ratio thresholds",
|
|
160
160
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/color-contrast",
|
|
161
|
-
"successCriteria": "1.4.3"
|
|
161
|
+
"successCriteria": "1.4.3",
|
|
162
|
+
"applicableStandards": ["wcag2aa", "wcag21aa", "wcag22aa", "wcag143", "section508", "section508.22.g", "EN-301-549", "TTv5", "cat.color"]
|
|
162
163
|
},
|
|
163
164
|
"image-alt": {
|
|
164
165
|
"title": "Images must have alternate text",
|
|
165
166
|
"description": "Ensures <img> elements have alternate text or a role of none or presentation",
|
|
166
167
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/image-alt",
|
|
167
|
-
"successCriteria": "1.1.1"
|
|
168
|
+
"successCriteria": "1.1.1",
|
|
169
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag111", "section508", "section508.22.a", "EN-301-549", "TTv5", "cat.text-alternatives"]
|
|
168
170
|
},
|
|
169
171
|
"label": {
|
|
170
172
|
"title": "Form elements must have labels",
|
|
171
173
|
"description": "Ensures every form element has a label",
|
|
172
174
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/label",
|
|
173
|
-
"successCriteria": "1.3.1, 3.3.2, 4.1.2"
|
|
175
|
+
"successCriteria": "1.3.1, 3.3.2, 4.1.2",
|
|
176
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag131", "wcag332", "wcag412", "section508", "section508.22.n", "EN-301-549", "TTv5", "cat.forms"]
|
|
174
177
|
},
|
|
175
178
|
"link-name": {
|
|
176
179
|
"title": "Links must have discernible text",
|
|
177
180
|
"description": "Ensures links have discernible text",
|
|
178
181
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/link-name",
|
|
179
|
-
"successCriteria": "2.4.4, 4.1.2"
|
|
182
|
+
"successCriteria": "2.4.4, 4.1.2",
|
|
183
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag244", "wcag412", "section508", "section508.22.a", "EN-301-549", "TTv5", "cat.name-role-value"]
|
|
180
184
|
},
|
|
181
185
|
"button-name": {
|
|
182
186
|
"title": "Buttons must have discernible text",
|
|
183
187
|
"description": "Ensures buttons have discernible text",
|
|
184
188
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/button-name",
|
|
185
|
-
"successCriteria": "4.1.2"
|
|
189
|
+
"successCriteria": "4.1.2",
|
|
190
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag412", "section508", "section508.22.a", "EN-301-549", "TTv5", "cat.name-role-value"]
|
|
186
191
|
},
|
|
187
192
|
"heading-order": {
|
|
188
193
|
"title": "Heading levels should only increase by one",
|
|
189
194
|
"description": "Ensures the order of headings is semantically correct",
|
|
190
195
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/heading-order",
|
|
191
|
-
"successCriteria": "1.3.1"
|
|
196
|
+
"successCriteria": "1.3.1",
|
|
197
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag131", "EN-301-549", "cat.semantics"]
|
|
192
198
|
},
|
|
193
199
|
"html-has-lang": {
|
|
194
200
|
"title": "<html> element must have a lang attribute",
|
|
195
201
|
"description": "Ensures every HTML document has a lang attribute",
|
|
196
202
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/html-has-lang",
|
|
197
|
-
"successCriteria": "3.1.1"
|
|
203
|
+
"successCriteria": "3.1.1",
|
|
204
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag311", "section508", "section508.22.q", "EN-301-549", "TTv5", "cat.language"]
|
|
198
205
|
},
|
|
199
206
|
"aria-allowed-attr": {
|
|
200
207
|
"title": "ARIA attributes must conform to valid names",
|
|
201
208
|
"description": "Ensures ARIA attributes are allowed for an element's role",
|
|
202
209
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/aria-allowed-attr",
|
|
203
|
-
"successCriteria": "4.1.2"
|
|
210
|
+
"successCriteria": "4.1.2",
|
|
211
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag412", "section508", "section508.22.a", "EN-301-549", "TTv5", "cat.aria"]
|
|
204
212
|
},
|
|
205
213
|
"aria-required-attr": {
|
|
206
214
|
"title": "Required ARIA attributes must be provided",
|
|
207
215
|
"description": "Ensures elements with ARIA roles have required ARIA attributes",
|
|
208
216
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/aria-required-attr",
|
|
209
|
-
"successCriteria": "4.1.2"
|
|
217
|
+
"successCriteria": "4.1.2",
|
|
218
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag412", "section508", "section508.22.a", "EN-301-549", "TTv5", "cat.aria"]
|
|
210
219
|
},
|
|
211
220
|
"region": {
|
|
212
221
|
"title": "All page content should be contained by landmarks",
|
|
213
222
|
"description": "Ensures all page content is contained by landmarks",
|
|
214
223
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/region",
|
|
215
|
-
"successCriteria": "1.3.1"
|
|
224
|
+
"successCriteria": "1.3.1",
|
|
225
|
+
"applicableStandards": ["wcag2a", "wcag21a", "wcag22a", "wcag131", "EN-301-549", "cat.keyboard"]
|
|
216
226
|
},
|
|
217
227
|
"target-size": {
|
|
218
228
|
"title": "Ensure touch targets have sufficient size and spacing",
|
|
219
229
|
"description": "Ensures touch targets are at least 24px by 24px",
|
|
220
230
|
"helpUrl": "https://dequeuniversity.com/rules/axe/4.6/target-size",
|
|
221
|
-
"successCriteria": "2.5.8"
|
|
231
|
+
"successCriteria": "2.5.8",
|
|
232
|
+
"applicableStandards": ["wcag22aa", "wcag258", "EN-301-549", "cat.sensory-and-visual-cues"]
|
|
222
233
|
}
|
|
223
234
|
},
|
|
224
235
|
"defaultSettings": {
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AA8BA,KAAK,UAAU,GAAG,QAAQ,GAAG,MAAM,CAAC;AAEpC,UAAU,YAAY;IACpB,IAAI,EAAE,UAAU,CAAC;IACjB,aAAa,EAAE,OAAO,CAAC;CACxB;AAuGD;;;GAGG;AACH,qBAAa,sBAAsB;IACjC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAS;IAChC,OAAO,CAAC,QAAQ,CAAC,cAAc,CAAiB;IAChD,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAe;IAGtC,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAClD,OAAO,CAAC,mBAAmB,CAAC,CAAsB;IAClD,OAAO,CAAC,aAAa,CAAC,CAAgB;IACtC,OAAO,CAAC,0BAA0B,CAAC,CAA6B;gBAEpD,MAAM,GAAE,YAAoD;IAwBxE;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAO9B;;OAEG;IACH,OAAO,CAAC,sBAAsB;IAO9B;;OAEG;IACH,OAAO,CAAC,gBAAgB;IAOxB;;OAEG;IACH,OAAO,CAAC,6BAA6B;IAOrC;;OAEG;YACW,wBAAwB;IA8CtC;;OAEG;IACH,OAAO,CAAC,aAAa;IA6VrB;;OAEG;IACH,OAAO,CAAC,qBAAqB;IA0B7B;;OAEG;IACG,GAAG,IAAI,OAAO,CAAC,IAAI,CAAC;IAW1B;;OAEG;IACG,OAAO,IAAI,OAAO,CAAC,IAAI,CAAC;CAS/B"}
|
package/dist/index.js
CHANGED
|
@@ -9,13 +9,56 @@ import { ColorContrastTester } from './tools/color-contrast.js';
|
|
|
9
9
|
import { WCAGValidator } from './tools/wcag-validator.js';
|
|
10
10
|
import { WebsiteAccessibilityTester } from './tools/website-accessibility-tester.js';
|
|
11
11
|
// Import utilities
|
|
12
|
-
import { Logger, BrowserManager } from './utils/index.js';
|
|
13
|
-
//
|
|
12
|
+
import { Logger, BrowserManager, combineStandardsAndPresets, shouldGenerateAdaGuidance, generateAdaGuidance } from './utils/index.js';
|
|
13
|
+
// Shared auth schemas
|
|
14
|
+
const CookieSchema = z.object({
|
|
15
|
+
name: z.string().min(1),
|
|
16
|
+
value: z.string().min(1),
|
|
17
|
+
domain: z.string().optional(),
|
|
18
|
+
path: z.string().default('/'),
|
|
19
|
+
expires: z.number().optional(),
|
|
20
|
+
httpOnly: z.boolean().default(false),
|
|
21
|
+
secure: z.boolean().default(true),
|
|
22
|
+
sameSite: z.enum(['Strict', 'Lax', 'None']).default('Lax')
|
|
23
|
+
});
|
|
24
|
+
const StorageStateSchema = z.object({
|
|
25
|
+
origin: z.string().url().optional(),
|
|
26
|
+
localStorage: z.record(z.string()).optional(),
|
|
27
|
+
sessionStorage: z.record(z.string()).optional()
|
|
28
|
+
});
|
|
29
|
+
const LoginStepSchema = z.object({
|
|
30
|
+
action: z.enum(['goto', 'type', 'click', 'waitForSelector', 'waitForNavigation', 'waitForTimeout']),
|
|
31
|
+
selector: z.string().optional(),
|
|
32
|
+
value: z.string().optional(),
|
|
33
|
+
timeout: z.number().optional()
|
|
34
|
+
});
|
|
35
|
+
const LoginFlowSchema = z.object({
|
|
36
|
+
loginUrl: z.string().url('Must be a valid login URL'),
|
|
37
|
+
steps: z.array(LoginStepSchema).min(1),
|
|
38
|
+
successSelector: z.string().optional(),
|
|
39
|
+
successUrlContains: z.string().optional()
|
|
40
|
+
});
|
|
41
|
+
const AuthSchema = z.object({
|
|
42
|
+
headers: z.record(z.string()).optional(),
|
|
43
|
+
cookies: z.array(CookieSchema).optional(),
|
|
44
|
+
storageState: z.array(StorageStateSchema).optional(),
|
|
45
|
+
loginFlow: LoginFlowSchema.optional(),
|
|
46
|
+
preNavigationScripts: z.array(z.string()).optional()
|
|
47
|
+
});
|
|
48
|
+
// Simplified validation schemas
|
|
14
49
|
const TestAccessibilitySchema = z.object({
|
|
15
50
|
target: z.string().min(1, 'Target URL or HTML is required'),
|
|
16
51
|
type: z.enum(['url', 'html']).default('url'),
|
|
17
52
|
level: z.enum(['basic', 'full']).default('basic'),
|
|
18
|
-
|
|
53
|
+
auth: AuthSchema.optional(),
|
|
54
|
+
standards: z.array(z.string()).optional(),
|
|
55
|
+
standardPresets: z.array(z.enum([
|
|
56
|
+
'wcag-a', 'wcag-aa', 'wcag-aaa',
|
|
57
|
+
'section508', 'section508-full',
|
|
58
|
+
'en301549', 'trustedtester', 'rgaa',
|
|
59
|
+
'ada', 'best-practice', 'all-standards'
|
|
60
|
+
])).optional(),
|
|
61
|
+
requireAll: z.boolean().default(true)
|
|
19
62
|
});
|
|
20
63
|
const ColorContrastSchema = z.object({
|
|
21
64
|
foreground: z.string().min(1, 'Foreground color is required'),
|
|
@@ -24,14 +67,20 @@ const ColorContrastSchema = z.object({
|
|
|
24
67
|
isBold: z.boolean().default(false)
|
|
25
68
|
});
|
|
26
69
|
const GetWCAGRulesSchema = z.object({
|
|
27
|
-
wcagLevel: z.enum(['A', 'AA', 'AAA']).optional(),
|
|
28
70
|
category: z.string().optional(),
|
|
29
71
|
search: z.string().optional(),
|
|
30
|
-
limit: z.number().min(1).max(50).default(20)
|
|
72
|
+
limit: z.number().min(1).max(50).default(20),
|
|
73
|
+
standards: z.array(z.string()).optional(),
|
|
74
|
+
standardPresets: z.array(z.enum([
|
|
75
|
+
'wcag-a', 'wcag-aa', 'wcag-aaa',
|
|
76
|
+
'section508', 'section508-full',
|
|
77
|
+
'en301549', 'trustedtester', 'rgaa',
|
|
78
|
+
'ada', 'best-practice', 'all-standards'
|
|
79
|
+
])).optional(),
|
|
80
|
+
requireAll: z.boolean().default(true)
|
|
31
81
|
});
|
|
32
82
|
const TestWebsiteSchema = z.object({
|
|
33
83
|
baseUrl: z.string().url('Must be a valid URL'),
|
|
34
|
-
wcagLevel: z.enum(['A', 'AA', 'AAA']).default('AA'),
|
|
35
84
|
fullAnalysis: z.boolean().default(false),
|
|
36
85
|
maxPages: z.number().min(1).max(100).default(20),
|
|
37
86
|
maxDepth: z.number().min(1).max(5).default(3),
|
|
@@ -41,7 +90,17 @@ const TestWebsiteSchema = z.object({
|
|
|
41
90
|
continueOnError: z.boolean().default(true),
|
|
42
91
|
respectRobots: z.boolean().default(true),
|
|
43
92
|
delay: z.number().min(0).max(10000).default(1000),
|
|
44
|
-
stayOnDomain: z.boolean().default(true)
|
|
93
|
+
stayOnDomain: z.boolean().default(true),
|
|
94
|
+
allowedDomains: z.array(z.string()).optional(),
|
|
95
|
+
auth: AuthSchema.optional(),
|
|
96
|
+
standards: z.array(z.string()).optional(),
|
|
97
|
+
standardPresets: z.array(z.enum([
|
|
98
|
+
'wcag-a', 'wcag-aa', 'wcag-aaa',
|
|
99
|
+
'section508', 'section508-full',
|
|
100
|
+
'en301549', 'trustedtester', 'rgaa',
|
|
101
|
+
'ada', 'best-practice', 'all-standards'
|
|
102
|
+
])).optional(),
|
|
103
|
+
requireAll: z.boolean().default(true)
|
|
45
104
|
});
|
|
46
105
|
/**
|
|
47
106
|
* Unified MCP server class for accessibility testing
|
|
@@ -65,7 +124,7 @@ export class AccessibilityMCPServer {
|
|
|
65
124
|
this.browserManager = new BrowserManager();
|
|
66
125
|
this.server = new Server({
|
|
67
126
|
name: 'accessibility-mcp-server',
|
|
68
|
-
version: '1.0.
|
|
127
|
+
version: '1.0.9',
|
|
69
128
|
}, {
|
|
70
129
|
capabilities: {
|
|
71
130
|
tools: {},
|
|
@@ -157,7 +216,7 @@ export class AccessibilityMCPServer {
|
|
|
157
216
|
const basicTools = [
|
|
158
217
|
{
|
|
159
218
|
name: 'test_accessibility',
|
|
160
|
-
description: 'Test URL or HTML content for accessibility issues',
|
|
219
|
+
description: 'Test URL or HTML content for accessibility issues using multiple standards (WCAG, Section 508, EN 301 549, etc.)',
|
|
161
220
|
inputSchema: {
|
|
162
221
|
type: 'object',
|
|
163
222
|
properties: {
|
|
@@ -177,11 +236,27 @@ export class AccessibilityMCPServer {
|
|
|
177
236
|
default: 'basic',
|
|
178
237
|
description: 'Testing depth level'
|
|
179
238
|
},
|
|
180
|
-
|
|
181
|
-
type: '
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
239
|
+
standards: {
|
|
240
|
+
type: 'array',
|
|
241
|
+
items: { type: 'string' },
|
|
242
|
+
description: 'Specific standard tags to test (e.g., ["wcag2aa", "section508", "EN-301-549"])'
|
|
243
|
+
},
|
|
244
|
+
standardPresets: {
|
|
245
|
+
type: 'array',
|
|
246
|
+
items: {
|
|
247
|
+
type: 'string',
|
|
248
|
+
enum: ['wcag-a', 'wcag-aa', 'wcag-aaa', 'section508', 'section508-full', 'en301549', 'trustedtester', 'rgaa', 'ada', 'best-practice', 'all-standards']
|
|
249
|
+
},
|
|
250
|
+
description: 'Standard presets to test against (defaults to wcag-aa if not specified)'
|
|
251
|
+
},
|
|
252
|
+
requireAll: {
|
|
253
|
+
type: 'boolean',
|
|
254
|
+
default: true,
|
|
255
|
+
description: 'Whether all standards must match (AND logic) or any standard (OR logic)'
|
|
256
|
+
},
|
|
257
|
+
auth: {
|
|
258
|
+
type: 'object',
|
|
259
|
+
description: 'Optional authentication context for protected pages'
|
|
185
260
|
}
|
|
186
261
|
},
|
|
187
262
|
required: ['target']
|
|
@@ -197,12 +272,6 @@ export class AccessibilityMCPServer {
|
|
|
197
272
|
type: 'string',
|
|
198
273
|
description: 'Base URL of the website to test'
|
|
199
274
|
},
|
|
200
|
-
wcagLevel: {
|
|
201
|
-
type: 'string',
|
|
202
|
-
enum: ['A', 'AA', 'AAA'],
|
|
203
|
-
default: 'AA',
|
|
204
|
-
description: 'WCAG compliance level'
|
|
205
|
-
},
|
|
206
275
|
fullAnalysis: {
|
|
207
276
|
type: 'boolean',
|
|
208
277
|
default: false,
|
|
@@ -260,6 +329,33 @@ export class AccessibilityMCPServer {
|
|
|
260
329
|
type: 'boolean',
|
|
261
330
|
default: true,
|
|
262
331
|
description: 'Whether to restrict crawl to the main domain'
|
|
332
|
+
},
|
|
333
|
+
allowedDomains: {
|
|
334
|
+
type: 'array',
|
|
335
|
+
items: { type: 'string' },
|
|
336
|
+
description: 'Additional domains allowed during crawl (e.g., IdP)'
|
|
337
|
+
},
|
|
338
|
+
standards: {
|
|
339
|
+
type: 'array',
|
|
340
|
+
items: { type: 'string' },
|
|
341
|
+
description: 'Specific standard tags to test (e.g., [\"wcag2aa\", \"section508\"])'
|
|
342
|
+
},
|
|
343
|
+
standardPresets: {
|
|
344
|
+
type: 'array',
|
|
345
|
+
items: {
|
|
346
|
+
type: 'string',
|
|
347
|
+
enum: ['wcag-a', 'wcag-aa', 'wcag-aaa', 'section508', 'section508-full', 'en301549', 'trustedtester', 'rgaa', 'ada', 'best-practice', 'all-standards']
|
|
348
|
+
},
|
|
349
|
+
description: 'Standard presets to test against (defaults to wcag-aa)'
|
|
350
|
+
},
|
|
351
|
+
requireAll: {
|
|
352
|
+
type: 'boolean',
|
|
353
|
+
default: true,
|
|
354
|
+
description: 'Whether all standards must match (AND logic)'
|
|
355
|
+
},
|
|
356
|
+
auth: {
|
|
357
|
+
type: 'object',
|
|
358
|
+
description: 'Optional authentication context for protected sites'
|
|
263
359
|
}
|
|
264
360
|
},
|
|
265
361
|
required: ['baseUrl']
|
|
@@ -299,15 +395,10 @@ export class AccessibilityMCPServer {
|
|
|
299
395
|
...basicTools,
|
|
300
396
|
{
|
|
301
397
|
name: 'get_wcag_rules',
|
|
302
|
-
description: 'Get information about WCAG
|
|
398
|
+
description: 'Get information about accessibility rules across multiple standards (WCAG, Section 508, EN 301 549, etc.)',
|
|
303
399
|
inputSchema: {
|
|
304
400
|
type: 'object',
|
|
305
401
|
properties: {
|
|
306
|
-
wcagLevel: {
|
|
307
|
-
type: 'string',
|
|
308
|
-
enum: ['A', 'AA', 'AAA'],
|
|
309
|
-
description: 'Filter rules by WCAG level'
|
|
310
|
-
},
|
|
311
402
|
category: {
|
|
312
403
|
type: 'string',
|
|
313
404
|
description: 'Filter rules by category'
|
|
@@ -322,6 +413,24 @@ export class AccessibilityMCPServer {
|
|
|
322
413
|
maximum: 50,
|
|
323
414
|
default: 20,
|
|
324
415
|
description: 'Maximum number of rules to return'
|
|
416
|
+
},
|
|
417
|
+
standards: {
|
|
418
|
+
type: 'array',
|
|
419
|
+
items: { type: 'string' },
|
|
420
|
+
description: 'Specific standard tags to filter by'
|
|
421
|
+
},
|
|
422
|
+
standardPresets: {
|
|
423
|
+
type: 'array',
|
|
424
|
+
items: {
|
|
425
|
+
type: 'string',
|
|
426
|
+
enum: ['wcag-a', 'wcag-aa', 'wcag-aaa', 'section508', 'section508-full', 'en301549', 'trustedtester', 'rgaa', 'ada', 'best-practice', 'all-standards']
|
|
427
|
+
},
|
|
428
|
+
description: 'Standard presets to filter by'
|
|
429
|
+
},
|
|
430
|
+
requireAll: {
|
|
431
|
+
type: 'boolean',
|
|
432
|
+
default: true,
|
|
433
|
+
description: 'Whether all standards must match (AND logic)'
|
|
325
434
|
}
|
|
326
435
|
}
|
|
327
436
|
}
|
|
@@ -334,12 +443,22 @@ export class AccessibilityMCPServer {
|
|
|
334
443
|
// Handle tool calls with centralized error handling
|
|
335
444
|
this.server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
336
445
|
const { name, arguments: args } = request.params;
|
|
337
|
-
|
|
446
|
+
const safeArgs = args && typeof args === 'object'
|
|
447
|
+
? { ...args, auth: args?.auth ? '[redacted]' : undefined }
|
|
448
|
+
: args;
|
|
449
|
+
this.logger.info(`Executing tool: ${name}`, { args: safeArgs });
|
|
338
450
|
let result;
|
|
339
451
|
switch (name) {
|
|
340
452
|
case 'test_accessibility': {
|
|
341
453
|
const params = TestAccessibilitySchema.parse(args);
|
|
342
|
-
result = await this.executeWithErrorHandling(() =>
|
|
454
|
+
result = await this.executeWithErrorHandling(async () => {
|
|
455
|
+
const testResult = await this.getAccessibilityTester().testTarget(params);
|
|
456
|
+
// Generate ADA guidance if ada preset is used
|
|
457
|
+
if (params.standardPresets && shouldGenerateAdaGuidance(params.standardPresets)) {
|
|
458
|
+
testResult.adaGuidance = generateAdaGuidance(testResult);
|
|
459
|
+
}
|
|
460
|
+
return testResult;
|
|
461
|
+
}, 'Accessibility test');
|
|
343
462
|
break;
|
|
344
463
|
}
|
|
345
464
|
case 'check_color_contrast': {
|
|
@@ -357,7 +476,32 @@ export class AccessibilityMCPServer {
|
|
|
357
476
|
throw new McpError(ErrorCode.MethodNotFound, 'WCAG rules not available in simple mode');
|
|
358
477
|
}
|
|
359
478
|
const params = GetWCAGRulesSchema.parse(args || {});
|
|
360
|
-
result = await this.executeWithErrorHandling(() =>
|
|
479
|
+
result = await this.executeWithErrorHandling(async () => {
|
|
480
|
+
const validator = this.getWCAGValidator();
|
|
481
|
+
// If standards or presets provided, use standards-based filtering
|
|
482
|
+
if (params.standards || params.standardPresets) {
|
|
483
|
+
const standards = combineStandardsAndPresets(params.standards, params.standardPresets);
|
|
484
|
+
const rules = await validator.getRulesByStandards(standards, params.requireAll ?? true);
|
|
485
|
+
// Apply additional filters
|
|
486
|
+
let filteredRules = rules;
|
|
487
|
+
if (params.category) {
|
|
488
|
+
filteredRules = filteredRules.filter(rule => rule.categories.some(cat => cat.toLowerCase().includes(params.category.toLowerCase())));
|
|
489
|
+
}
|
|
490
|
+
if (params.search) {
|
|
491
|
+
const searchLower = params.search.toLowerCase();
|
|
492
|
+
filteredRules = filteredRules.filter(rule => rule.title.toLowerCase().includes(searchLower) ||
|
|
493
|
+
rule.description.toLowerCase().includes(searchLower) ||
|
|
494
|
+
rule.id.toLowerCase().includes(searchLower));
|
|
495
|
+
}
|
|
496
|
+
if (params.limit && filteredRules.length > params.limit) {
|
|
497
|
+
filteredRules = filteredRules.slice(0, params.limit);
|
|
498
|
+
}
|
|
499
|
+
return filteredRules;
|
|
500
|
+
}
|
|
501
|
+
// Fallback to legacy getRules method
|
|
502
|
+
const response = await validator.getRules(params);
|
|
503
|
+
return response.data;
|
|
504
|
+
}, 'WCAG rules retrieval');
|
|
361
505
|
break;
|
|
362
506
|
}
|
|
363
507
|
default:
|
|
@@ -434,7 +578,7 @@ const args = process.argv.slice(2);
|
|
|
434
578
|
// Show help information
|
|
435
579
|
if (args.includes('--help') || args.includes('-h')) {
|
|
436
580
|
console.log(`
|
|
437
|
-
Accessibility MCP Server v1.0.
|
|
581
|
+
Accessibility MCP Server v1.0.9
|
|
438
582
|
A Model Context Protocol server for web accessibility testing with WCAG compliance.
|
|
439
583
|
|
|
440
584
|
Usage:
|
|
@@ -469,7 +613,7 @@ For more information, visit: https://github.com/your-repo/accessibility-mcp
|
|
|
469
613
|
}
|
|
470
614
|
// Show version
|
|
471
615
|
if (args.includes('--version') || args.includes('-v')) {
|
|
472
|
-
console.log('1.0.
|
|
616
|
+
console.log('1.0.9');
|
|
473
617
|
process.exit(0);
|
|
474
618
|
}
|
|
475
619
|
// Set MCP mode environment variable when running as MCP server
|