nfunc-mcp 0.3.0 → 0.5.0
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 +121 -379
- package/dist/index.js +4 -0
- package/dist/index.js.map +1 -1
- package/dist/mappers/a11yDedupe.js +38 -7
- package/dist/mappers/a11yDedupe.js.map +1 -1
- package/dist/mappers/defectFormatter.d.ts +9 -1
- package/dist/mappers/defectFormatter.js +53 -13
- package/dist/mappers/defectFormatter.js.map +1 -1
- package/dist/mappers/labFieldComparator.d.ts +62 -0
- package/dist/mappers/labFieldComparator.js +134 -0
- package/dist/mappers/labFieldComparator.js.map +1 -0
- package/dist/mappers/priorityMapper.d.ts +42 -0
- package/dist/mappers/priorityMapper.js +58 -0
- package/dist/mappers/priorityMapper.js.map +1 -1
- package/dist/mappers/psiAggregator.d.ts +130 -0
- package/dist/mappers/psiAggregator.js +293 -0
- package/dist/mappers/psiAggregator.js.map +1 -0
- package/dist/mappers/runComparator.d.ts +85 -0
- package/dist/mappers/runComparator.js +165 -0
- package/dist/mappers/runComparator.js.map +1 -0
- package/dist/mappers/wcagLevels.d.ts +73 -0
- package/dist/mappers/wcagLevels.js +320 -0
- package/dist/mappers/wcagLevels.js.map +1 -0
- package/dist/mappers/webVitalsMapper.d.ts +52 -0
- package/dist/mappers/webVitalsMapper.js +131 -0
- package/dist/mappers/webVitalsMapper.js.map +1 -0
- package/dist/tools/accessibility.d.ts +1 -0
- package/dist/tools/accessibility.js +488 -63
- package/dist/tools/accessibility.js.map +1 -1
- package/dist/tools/lighthouse.js +370 -102
- package/dist/tools/lighthouse.js.map +1 -1
- package/dist/tools/performanceAudit.d.ts +2 -0
- package/dist/tools/performanceAudit.js +446 -0
- package/dist/tools/performanceAudit.js.map +1 -0
- package/dist/tools/performanceAuditPlan.d.ts +2 -0
- package/dist/tools/performanceAuditPlan.js +438 -0
- package/dist/tools/performanceAuditPlan.js.map +1 -0
- package/dist/utils/batchState.d.ts +75 -0
- package/dist/utils/batchState.js +128 -0
- package/dist/utils/batchState.js.map +1 -0
- package/dist/utils/csvReader.d.ts +20 -0
- package/dist/utils/csvReader.js +172 -0
- package/dist/utils/csvReader.js.map +1 -0
- package/dist/utils/httpClient.d.ts +84 -0
- package/dist/utils/httpClient.js +171 -0
- package/dist/utils/httpClient.js.map +1 -0
- package/dist/utils/outputParsers.js +26 -30
- package/dist/utils/outputParsers.js.map +1 -1
- package/dist/utils/psiAuth.d.ts +26 -0
- package/dist/utils/psiAuth.js +36 -0
- package/dist/utils/psiAuth.js.map +1 -0
- package/dist/utils/psiParser.d.ts +135 -0
- package/dist/utils/psiParser.js +200 -0
- package/dist/utils/psiParser.js.map +1 -0
- package/dist/utils/publicUrl.d.ts +17 -0
- package/dist/utils/publicUrl.js +115 -0
- package/dist/utils/publicUrl.js.map +1 -0
- package/dist/utils/sitemapReader.d.ts +27 -0
- package/dist/utils/sitemapReader.js +272 -0
- package/dist/utils/sitemapReader.js.map +1 -0
- package/dist/utils/urlClassifier.d.ts +45 -0
- package/dist/utils/urlClassifier.js +267 -0
- package/dist/utils/urlClassifier.js.map +1 -0
- package/dist/utils/urlInput.d.ts +30 -0
- package/dist/utils/urlInput.js +130 -0
- package/dist/utils/urlInput.js.map +1 -0
- package/docs/manual.md +769 -0
- package/docs/psi-report-spec.md +174 -0
- package/package.json +13 -3
package/README.md
CHANGED
|
@@ -2,38 +2,18 @@
|
|
|
2
2
|
|
|
3
3
|
[](https://www.npmjs.com/package/nfunc-mcp)
|
|
4
4
|
|
|
5
|
-
A local MCP server that gives Claude
|
|
5
|
+
A local MCP server that gives Claude a non-functional QA toolkit. Performance,
|
|
6
|
+
accessibility, SEO, code quality and real-user Core Web Vitals — run
|
|
7
|
+
individually or all at once, returned as prioritised findings you can act on
|
|
8
|
+
rather than raw tool output.
|
|
6
9
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
## Table of contents
|
|
10
|
-
|
|
11
|
-
1. [What it does](#what-it-does)
|
|
12
|
-
2. [Prerequisites](#prerequisites)
|
|
13
|
-
3. [Install & build](#install--build)
|
|
14
|
-
4. [Register with Claude Code](#register-with-claude-code)
|
|
15
|
-
5. [Tools](#tools)
|
|
16
|
-
6. [run_qa_gate — the main tool](#run_qa_gate--the-main-tool)
|
|
17
|
-
- [Inputs](#inputs)
|
|
18
|
-
- [Release readiness tiers](#release-readiness-tiers)
|
|
19
|
-
- [Composite score](#composite-score)
|
|
20
|
-
- [Scorecard](#scorecard)
|
|
21
|
-
- [Cross-tool corroboration](#cross-tool-corroboration)
|
|
22
|
-
- [HTML report](#html-report)
|
|
23
|
-
- [Output shape](#output-shape)
|
|
24
|
-
7. [Individual tools](#individual-tools)
|
|
25
|
-
- [Mobile vs desktop](#mobile-vs-desktop)
|
|
26
|
-
- [Choosing an accessibility engine](#choosing-an-accessibility-engine)
|
|
27
|
-
8. [Priority system](#priority-system)
|
|
28
|
-
9. [Project layout](#project-layout)
|
|
29
|
-
10. [How to prompt](#how-to-prompt)
|
|
10
|
+
> **[Operating manual →](docs/manual.md)** — installation options, per-tool
|
|
11
|
+
> reference, output shapes, troubleshooting.
|
|
30
12
|
|
|
31
13
|
---
|
|
32
14
|
|
|
33
15
|
## What it does
|
|
34
16
|
|
|
35
|
-
`nfunc-mcp` wires four QA tools into Claude's tool-use loop:
|
|
36
|
-
|
|
37
17
|
| Capability | Tools | What it checks |
|
|
38
18
|
|---|---|---|
|
|
39
19
|
| Performance | Lighthouse | LCP, TTI, TBT, CLS, bundle size, caching |
|
|
@@ -42,399 +22,161 @@ A local MCP server that gives Claude (or any MCP client) a full non-functional Q
|
|
|
42
22
|
| Best practices | Lighthouse | HTTPS, deprecated APIs, third-party cookies |
|
|
43
23
|
| Code quality | ESLint | Dead code, undeclared vars, swallowed errors |
|
|
44
24
|
| Security patterns | Semgrep | OWASP JS/TS patterns |
|
|
25
|
+
| Real-user vitals | PageSpeed Insights + CrUX | What actual visitors experienced, versus what the lab measures |
|
|
45
26
|
|
|
46
|
-
|
|
27
|
+
Findings arrive prioritised **P1 / P2 / P3**, written as defect-ticket prose
|
|
28
|
+
rather than audit jargon, with passing checks filtered out. Nothing that passes
|
|
29
|
+
is ever reported.
|
|
47
30
|
|
|
48
31
|
---
|
|
49
32
|
|
|
50
|
-
##
|
|
51
|
-
|
|
52
|
-
`nfunc-mcp` is a thin wrapper around four CLI tools. Install the ones you need before registering the server:
|
|
53
|
-
|
|
54
|
-
| Tool | Install | Used by |
|
|
55
|
-
|---|---|---|
|
|
56
|
-
| Lighthouse | `npm install -g lighthouse` | `run_lighthouse`, `run_qa_gate` (URL) |
|
|
57
|
-
| pa11y | `npm install -g pa11y` | `run_accessibility_check`, `run_qa_gate` (URL) |
|
|
58
|
-
| ESLint | `npm install -g eslint` | `run_static_analysis`, `run_qa_gate` (path) |
|
|
59
|
-
| Semgrep | `brew install semgrep` or `pip install semgrep` | `run_static_analysis`, `run_qa_gate` (path) |
|
|
60
|
-
|
|
61
|
-
Verify each is reachable:
|
|
62
|
-
|
|
63
|
-
```bash
|
|
64
|
-
lighthouse --version
|
|
65
|
-
pa11y --version
|
|
66
|
-
eslint --version
|
|
67
|
-
semgrep --version
|
|
68
|
-
```
|
|
69
|
-
|
|
70
|
-
**You don't need all four.** If a tool is missing or not installed, the gate still runs — that tool's scorecard entry shows `UNAVAILABLE` and its findings are skipped. URL-only runs only need Lighthouse and pa11y; path-only runs only need ESLint and Semgrep.
|
|
71
|
-
|
|
72
|
-
---
|
|
73
|
-
|
|
74
|
-
## Install & register
|
|
75
|
-
|
|
76
|
-
### Option A — npm (recommended, no cloning needed)
|
|
33
|
+
## Quick start
|
|
77
34
|
|
|
78
35
|
```bash
|
|
79
36
|
claude mcp add nfunc-mcp -- npx -y nfunc-mcp
|
|
80
37
|
```
|
|
81
38
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
Add to `~/.claude.json` under `mcpServers`:
|
|
87
|
-
|
|
88
|
-
```json
|
|
89
|
-
{
|
|
90
|
-
"mcpServers": {
|
|
91
|
-
"nfunc-mcp": {
|
|
92
|
-
"command": "npx",
|
|
93
|
-
"args": ["-y", "nfunc-mcp"]
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
}
|
|
97
|
-
```
|
|
98
|
-
|
|
99
|
-
### Option C — From source (contributors / local dev)
|
|
100
|
-
|
|
101
|
-
```bash
|
|
102
|
-
git clone https://github.com/Hiddensound/NFunc_MCP.git
|
|
103
|
-
cd NFunc_MCP
|
|
104
|
-
npm install
|
|
105
|
-
npm run build
|
|
106
|
-
claude mcp add nfunc-mcp -- node /absolute/path/to/NFunc_MCP/dist/index.js
|
|
107
|
-
```
|
|
108
|
-
|
|
109
|
-
### Scripts (source only)
|
|
110
|
-
|
|
111
|
-
| Script | Purpose |
|
|
112
|
-
|---|---|
|
|
113
|
-
| `npm run build` | Compile TypeScript → `dist/` |
|
|
114
|
-
| `npm start` | Run the compiled server |
|
|
115
|
-
| `npm run dev` | Run from source with hot reload (`tsx watch`) |
|
|
39
|
+
Then install whichever CLIs you need — `lighthouse`, `pa11y`, `eslint`,
|
|
40
|
+
`semgrep`. Missing tools are skipped rather than fatal, so start with what you
|
|
41
|
+
have. For real-user field data, add a
|
|
42
|
+
[PageSpeed Insights key](docs/manual.md#pagespeed-insights-api-key).
|
|
116
43
|
|
|
117
|
-
|
|
44
|
+
Verify with `/mcp`, then ask Claude to *"call the nfunc-mcp ping tool."*
|
|
118
45
|
|
|
119
|
-
|
|
120
|
-
2. Ask Claude: *"Call the nfunc-mcp ping tool."*
|
|
121
|
-
3. Expected response:
|
|
122
|
-
```json
|
|
123
|
-
{ "status": "ok", "timestamp": "2026-05-20T12:00:00.000Z" }
|
|
124
|
-
```
|
|
46
|
+
[Other install options →](docs/manual.md#install-and-register)
|
|
125
47
|
|
|
126
48
|
---
|
|
127
49
|
|
|
128
|
-
##
|
|
129
|
-
|
|
130
|
-
| Tool | Description | Inputs |
|
|
131
|
-
|---|---|---|
|
|
132
|
-
| `ping` | Health check — confirms the server is up | — |
|
|
133
|
-
| `run_lighthouse` | Full Lighthouse audit for a URL | `url`, `form_factor` (optional), `categories` (optional), `thresholds` (optional) |
|
|
134
|
-
| `run_accessibility_check` | pa11y WCAG audit for a URL | `url`, `runner` (optional), `standard` (optional), `ignore` (optional) |
|
|
135
|
-
| `run_static_analysis` | ESLint + Semgrep scan for a local codebase | `path` |
|
|
136
|
-
| `run_qa_gate` | All tools in parallel + correlation + HTML report | `url` and/or `path`, `form_factor` (optional), `a11y_runner` (optional) |
|
|
137
|
-
|
|
138
|
-
---
|
|
139
|
-
|
|
140
|
-
## run_qa_gate — the main tool
|
|
141
|
-
|
|
142
|
-
This is the tool to reach for in nearly every QA workflow. It replaces running tools individually and adds cross-tool intelligence on top.
|
|
143
|
-
|
|
144
|
-
### Inputs
|
|
145
|
-
|
|
146
|
-
Both inputs are **optional** — provide whichever you have. At least one is required.
|
|
147
|
-
|
|
148
|
-
| Input | Type | When to provide |
|
|
149
|
-
|---|---|---|
|
|
150
|
-
| `url` | string (URL) | You have a running page — production, staging, preview URL, or localhost. Enables Lighthouse and pa11y. |
|
|
151
|
-
| `path` | string (path) | You have a local codebase. Enables ESLint and Semgrep. |
|
|
152
|
-
| `context` | string | Optional. Free-text description of the project (e.g. `"React e-commerce checkout"`). Helps Claude interpret results. |
|
|
153
|
-
| `form_factor` | `mobile` \| `desktop` \| `both` | Optional, default `mobile`. Lighthouse device profile — see [Mobile vs desktop](#mobile-vs-desktop). |
|
|
154
|
-
| `a11y_runner` | `htmlcs` \| `axe` \| `both` | Optional, default `htmlcs`. pa11y engine — see [Choosing an accessibility engine](#choosing-an-accessibility-engine). |
|
|
155
|
-
|
|
156
|
-
**URL only** — browser-based checks, static analysis skipped:
|
|
157
|
-
```
|
|
158
|
-
QA snapshot — https://staging.myapp.com
|
|
159
|
-
```
|
|
160
|
-
|
|
161
|
-
**Path only** — static analysis only, browser checks skipped:
|
|
162
|
-
```
|
|
163
|
-
QA snapshot — /path/to/my-feature-branch
|
|
164
|
-
```
|
|
165
|
-
|
|
166
|
-
**Both** — full suite:
|
|
167
|
-
```
|
|
168
|
-
QA snapshot — https://staging.myapp.com, code at /path/to/repo
|
|
169
|
-
```
|
|
170
|
-
|
|
171
|
-
### Release readiness tiers
|
|
172
|
-
|
|
173
|
-
The `release_readiness` field replaces a binary pass/fail with four actionable tiers:
|
|
174
|
-
|
|
175
|
-
| Value | Meaning | Condition |
|
|
176
|
-
|---|---|---|
|
|
177
|
-
| `BLOCKED` | Cannot ship — P1 issues exist | Any P1 finding |
|
|
178
|
-
| `CONDITIONAL` | Shippable with caveats — track P2s before merging | P2 findings, no P1s |
|
|
179
|
-
| `ADVISORY` | Safe to ship — P3s are tech debt to log | Only P3 findings |
|
|
180
|
-
| `CLEAR` | No issues detected | Zero findings |
|
|
181
|
-
|
|
182
|
-
### Composite score
|
|
183
|
-
|
|
184
|
-
A single `composite_score` (0–100) gives a continuous health measure across all tools.
|
|
185
|
-
|
|
186
|
-
**Formula:** Start at 100, deduct by finding severity:
|
|
187
|
-
|
|
188
|
-
| Priority | Deduction |
|
|
189
|
-
|---|---|
|
|
190
|
-
| P1 | −15 per finding |
|
|
191
|
-
| P2 | −7 per finding |
|
|
192
|
-
| P3 | −3 per finding |
|
|
193
|
-
|
|
194
|
-
Score is floored at 0. Tracks improvement over time — a score trending upward sprint-over-sprint is a healthy signal.
|
|
195
|
-
|
|
196
|
-
### Scorecard
|
|
197
|
-
|
|
198
|
-
A compact `scorecard` array gives a one-line status per tool:
|
|
199
|
-
|
|
200
|
-
```json
|
|
201
|
-
[
|
|
202
|
-
{ "tool": "Lighthouse", "gate": "WARN", "score": 75,
|
|
203
|
-
"breakdown": { "performance": 52, "accessibility": 98, "seo": 100, "best-practices": 58 } },
|
|
204
|
-
{ "tool": "pa11y", "gate": "PASS", "issues": 0 },
|
|
205
|
-
{ "tool": "ESLint / Semgrep", "gate": "SKIPPED" }
|
|
206
|
-
]
|
|
207
|
-
```
|
|
208
|
-
|
|
209
|
-
Gate values:
|
|
210
|
-
|
|
211
|
-
| Gate | Meaning |
|
|
212
|
-
|---|---|
|
|
213
|
-
| `PASS` | No issues at this tool's threshold |
|
|
214
|
-
| `WARN` | Issues exist but below the FAIL threshold |
|
|
215
|
-
| `FAIL` | Issues at P1 level (or Lighthouse avg < 50) |
|
|
216
|
-
| `SKIPPED` | Input not provided (URL or path not supplied) |
|
|
217
|
-
| `UNAVAILABLE` | Tool was invoked but is not installed or errored |
|
|
218
|
-
|
|
219
|
-
### Cross-tool corroboration
|
|
220
|
-
|
|
221
|
-
When Lighthouse and pa11y independently flag the same accessibility gap, those findings are:
|
|
222
|
-
|
|
223
|
-
1. **Merged** into a single entry in `corroborated_findings`
|
|
224
|
-
2. **Priority-promoted** one tier (P3→P2, P2→P1)
|
|
225
|
-
3. **Annotated** with `confidence: "high"` and `confirmed_by: ["lighthouse", "pa11y"]`
|
|
226
|
-
|
|
227
|
-
These are the highest-confidence findings in any report — two independent tools agreeing is stronger evidence than either alone. They appear in their own dedicated section above all other findings, and bubble to the top of `top_issues`.
|
|
228
|
-
|
|
229
|
-
**Corroboration mapping (Rule 1):**
|
|
50
|
+
## The tools
|
|
230
51
|
|
|
231
|
-
|
|
|
52
|
+
| Tool | What it does |
|
|
232
53
|
|---|---|
|
|
233
|
-
|
|
|
234
|
-
| `
|
|
235
|
-
| `
|
|
236
|
-
| `
|
|
237
|
-
| `
|
|
238
|
-
| `
|
|
239
|
-
|
|
|
54
|
+
| **`run_qa_gate`** | **The one to reach for.** Runs everything applicable in parallel, correlates findings across tools, and returns a release verdict, a composite score, a per-tool scorecard and an HTML report. |
|
|
55
|
+
| `run_lighthouse` | Lighthouse for **one URL, a list, or a CSV**. `form_factor: "both"` finds device-specific defects the single profiles miss. |
|
|
56
|
+
| `run_accessibility_check` | pa11y WCAG audit for **one URL, a list, or a CSV**. Findings are priced by **WCAG 2.1 conformance** — Level A failures are P1 because they put an AA claim out of reach. `runner: "axe"` for ARIA and design systems. |
|
|
57
|
+
| `run_static_analysis` | ESLint + Semgrep over a local codebase. Uses your ESLint config if it finds one. |
|
|
58
|
+
| `plan_performance_audit` | Plans a PageSpeed Insights audit — finds your URLs, groups them into page templates, costs the run. **Spends no quota.** |
|
|
59
|
+
| `run_performance_audit` | Runs it. Lab scores, real-user field data, and the disagreements between them. |
|
|
60
|
+
| `ping` | Health check. |
|
|
61
|
+
|
|
62
|
+
### Which one when
|
|
63
|
+
|
|
64
|
+
- **Shipping something?** `run_qa_gate`. It is the default answer.
|
|
65
|
+
- **One dimension in depth?** The individual tool — `run_lighthouse` for a perf
|
|
66
|
+
regression, `run_accessibility_check` for an a11y pass.
|
|
67
|
+
- **"Is the site actually fast for real people?"** The PSI pair. This is the
|
|
68
|
+
only thing here that measures real visitors instead of a simulation, and it
|
|
69
|
+
routinely disagrees with the lab.
|
|
70
|
+
|
|
71
|
+
The PSI tools are **opt-in** — `run_qa_gate` never calls them, because they
|
|
72
|
+
spend API quota and take minutes rather than seconds.
|
|
240
73
|
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
### HTML report
|
|
74
|
+
---
|
|
244
75
|
|
|
245
|
-
|
|
76
|
+
## How to ask for it
|
|
246
77
|
|
|
247
|
-
```json
|
|
248
|
-
{
|
|
249
|
-
"report_file": "file:///tmp/qa-report-myapp-com-1234567890.html"
|
|
250
|
-
}
|
|
251
78
|
```
|
|
79
|
+
# Full suite
|
|
80
|
+
QA snapshot — https://myapp.com, code at /path/to/repo
|
|
252
81
|
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
- Release readiness banner (colour-coded)
|
|
256
|
-
- Composite score gauge (SVG arc, 0–100)
|
|
257
|
-
- Per-tool scorecard table
|
|
258
|
-
- Cross-confirmed findings section (highlighted)
|
|
259
|
-
- Collapsible finding cards grouped by P1 / P2 / P3
|
|
260
|
-
- Evidence and selector for each finding
|
|
261
|
-
|
|
262
|
-
No server required — the file is fully self-contained with inline CSS.
|
|
263
|
-
|
|
264
|
-
### Output shape
|
|
265
|
-
|
|
266
|
-
```jsonc
|
|
267
|
-
{
|
|
268
|
-
"release_readiness": "BLOCKED", // BLOCKED | CONDITIONAL | ADVISORY | CLEAR
|
|
269
|
-
"composite_score": 22, // 0–100
|
|
270
|
-
"report_file": "file:///tmp/qa-report-xxx.html",
|
|
271
|
-
"scorecard": [ ... ], // per-tool gate + score/issues
|
|
272
|
-
"eslint_config_used": "project", // present only when path was supplied
|
|
273
|
-
"summary": "110 findings (101 P1, 9 P2) across 2 tools. ...",
|
|
274
|
-
"corroborated_findings": [ ... ], // cross-confirmed, confidence: "high"
|
|
275
|
-
"top_issues": [ ... ], // top 3 findings (corroborated first)
|
|
276
|
-
"all_findings": [ ... ], // all findings sorted by priority
|
|
277
|
-
"correlations_found": 1,
|
|
278
|
-
"errors": [ ... ] // present only if a tool errored
|
|
279
|
-
}
|
|
280
|
-
```
|
|
82
|
+
# URL only (Lighthouse + pa11y)
|
|
83
|
+
QA snapshot — https://myapp.com
|
|
281
84
|
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
```jsonc
|
|
285
|
-
{
|
|
286
|
-
"priority": "P1", // P1 | P2 | P3
|
|
287
|
-
"title": "Largest Contentful Paint",
|
|
288
|
-
"description": "Users see main content 34s after navigation...",
|
|
289
|
-
"evidence": { "audit_id": "largest-contentful-paint", "value": "34.3 s" },
|
|
290
|
-
"source_tool": "lighthouse",
|
|
291
|
-
// corroborated findings also have:
|
|
292
|
-
"confirmed_by": ["lighthouse", "pa11y"],
|
|
293
|
-
"confidence": "high"
|
|
294
|
-
}
|
|
85
|
+
# Local branch only (ESLint + Semgrep)
|
|
86
|
+
QA snapshot — /path/to/my-feature-branch
|
|
295
87
|
```
|
|
296
88
|
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
## Individual tools
|
|
300
|
-
|
|
301
|
-
### run_lighthouse
|
|
302
|
-
|
|
303
|
-
Runs a full Lighthouse audit against a URL.
|
|
89
|
+
These all work too:
|
|
304
90
|
|
|
305
91
|
```
|
|
306
|
-
|
|
92
|
+
Health check on https://myapp.com
|
|
93
|
+
Is https://myapp.com ready to ship? Code at /path/to/repo
|
|
94
|
+
Any red flags? /path/to/repo
|
|
307
95
|
Run Lighthouse on https://myapp.com for mobile and desktop
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
Returns: `url`, `form_factor`, `scores` (per category), `ttfb_ms`, `findings` (priority-ordered).
|
|
311
|
-
|
|
312
|
-
#### Mobile vs desktop
|
|
313
|
-
|
|
314
|
-
`form_factor` accepts `desktop` (default), `mobile`, or `both`.
|
|
315
|
-
|
|
316
|
-
**This default deliberately differs from the Lighthouse CLI's**, which is mobile: a 412×823 screen, a mid-range Android user agent, simulated slow 4G, and a **4× CPU slowdown**. That profile is much harsher and reports substantially lower performance scores for the same page, so passing `mobile` here is not a like-for-like comparison with a default run — check `form_factor` in the response before comparing two reports.
|
|
317
|
-
|
|
318
|
-
**The two are not interchangeable.** They render different DOM, so they find different defects — not just different performance numbers. Measured against one commerce category page:
|
|
319
|
-
|
|
320
|
-
| | Mobile | Desktop |
|
|
321
|
-
|---|---|---|
|
|
322
|
-
| performance | 54 | 62 |
|
|
323
|
-
| accessibility | **87** | **73** |
|
|
324
|
-
| seo | 77 | 69 |
|
|
325
|
-
|
|
326
|
-
Five accessibility audits failed on desktop that mobile never reported — `image-alt`, `aria-required-children`, `aria-required-parent`, `aria-allowed-attr`, `aria-valid-attr-value` — while three others failed only on mobile. Neither profile is a superset of the other.
|
|
327
|
-
|
|
328
|
-
With `form_factor: "both"`, the two run concurrently (so it costs little more wall time than one), `scores` is keyed by form factor, and each finding carries `affects_form_factors` and `form_factor_specific` so device-only regressions are obvious at a glance.
|
|
329
|
-
|
|
330
|
-
### run_accessibility_check
|
|
331
|
-
|
|
332
|
-
Runs pa11y against a URL at WCAG 2 AA by default. Returns only violations (errors) — use the CLI directly with `--include-notices --include-warnings` for the full checklist.
|
|
333
|
-
|
|
334
|
-
```
|
|
335
|
-
Run an accessibility check on https://myapp.com
|
|
336
|
-
Run accessibility check at AAA standard on https://myapp.com
|
|
96
|
+
Run Lighthouse on these: https://a.com, https://b.com, https://c.com
|
|
337
97
|
Run an accessibility check on https://myapp.com using the axe runner
|
|
98
|
+
Run an accessibility check on every URL in ./top-pages.csv
|
|
99
|
+
Scan http://localhost:3000 and compare it to ./a11y-baseline
|
|
100
|
+
Plan a PageSpeed Insights audit for https://myapp.com
|
|
338
101
|
```
|
|
339
102
|
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
#### Choosing an accessibility engine
|
|
343
|
-
|
|
344
|
-
`runner` accepts `htmlcs` (default), `axe`, or `both`.
|
|
345
|
-
|
|
346
|
-
| Engine | Strongest at | Severity source |
|
|
347
|
-
|---|---|---|
|
|
348
|
-
| `htmlcs` | WCAG techniques, document structure, form labelling, duplicate ids | WCAG technique class |
|
|
349
|
-
| `axe` | **ARIA** — invalid roles, missing required parent/child relationships, prohibited and unsupported attributes — and computed colour contrast | axe's own `impact` rating |
|
|
350
|
-
|
|
351
|
-
**Reach for `axe` whenever the work under test involves ARIA, a component library, or a design system.** The overlap between the engines is smaller than you would expect. On the same page:
|
|
352
|
-
|
|
353
|
-
- htmlcs found unlabelled inputs, forms with no submit mechanism, and ten duplicate ids that axe did not report.
|
|
354
|
-
- axe found `aria-allowed-attr`, `aria-prohibited-attr`, `aria-required-parent`, `aria-required-children` and `image-alt` failures that htmlcs missed entirely.
|
|
355
|
-
|
|
356
|
-
`both` runs them concurrently and merges the results. Note that an element flagged by both engines appears twice, because they emit different rule codes for the same defect — that is deliberate, since two independent engines agreeing is corroboration worth seeing.
|
|
357
|
-
|
|
358
|
-
Findings from axe carry `axe_impact` in evidence, and `needs_manual_review: true` where axe wants a human to confirm (those are demoted one priority tier — a maybe should not gate a release as hard as a certainty).
|
|
359
|
-
|
|
360
|
-
### run_static_analysis
|
|
361
|
-
|
|
362
|
-
Runs ESLint and Semgrep in parallel against a local directory. Automatically uses the project's own ESLint config if one is found; otherwise falls back to a QA-focused baseline config.
|
|
363
|
-
|
|
364
|
-
```
|
|
365
|
-
Run static analysis on /path/to/repo
|
|
366
|
-
```
|
|
367
|
-
|
|
368
|
-
Returns: `path`, `tools_run`, `eslint_config_used`, `issue_count`, `findings`, `warnings`.
|
|
369
|
-
|
|
370
|
-
---
|
|
371
|
-
|
|
372
|
-
## Priority system
|
|
373
|
-
|
|
374
|
-
| Priority | Meaning | Lighthouse threshold | WCAG level | ESLint / Semgrep |
|
|
375
|
-
|---|---|---|---|---|
|
|
376
|
-
| P1 | Blocker — fix before shipping | Score < 50 | Level A | Semgrep security, ESLint error |
|
|
377
|
-
| P2 | Warning — track before merging | Score 50–79 | Level AA | ESLint warning |
|
|
378
|
-
| P3 | Advisory — log as tech debt | Score 80–89 | Level AAA | — |
|
|
379
|
-
| *(suppressed)* | Passing — not reported | Score ≥ 90 | — | — |
|
|
380
|
-
|
|
381
|
-
Corroborated findings are promoted one tier above where either tool would place them individually.
|
|
103
|
+
`run_qa_gate` returns a `report_file` path — open it in a browser for the
|
|
104
|
+
visual dashboard.
|
|
382
105
|
|
|
383
106
|
---
|
|
384
107
|
|
|
385
|
-
##
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
108
|
+
## What makes it different from running the CLIs yourself
|
|
109
|
+
|
|
110
|
+
**Findings, not output.** Every result is a prioritised defect with QA-native
|
|
111
|
+
prose and traceable evidence, not a wall of audit JSON.
|
|
112
|
+
|
|
113
|
+
**Cross-tool corroboration.** When Lighthouse and pa11y independently flag the
|
|
114
|
+
same accessibility gap, the finding is merged, promoted a tier and marked
|
|
115
|
+
`confidence: "high"`. Two tools agreeing is stronger evidence than either alone.
|
|
116
|
+
|
|
117
|
+
**Hand it a list, not a URL.** `run_lighthouse` and `run_accessibility_check`
|
|
118
|
+
both take a single URL, a comma-separated list, or a path to a CSV — and work
|
|
119
|
+
out which you gave them. Multiple URLs run as a resumable batch that writes each
|
|
120
|
+
report to disk and finishes with a cross-page rollup.
|
|
121
|
+
[How batching works →](docs/manual.md#auditing-several-urls-at-once)
|
|
122
|
+
|
|
123
|
+
**Systemic collapse, twice over.** One duplicate-id component failing on eleven
|
|
124
|
+
elements is reported as one defect, not eleven. And across a set of pages, a
|
|
125
|
+
rule failing on 4 of 4 is flagged as shared-layout — one fix in the header
|
|
126
|
+
clears every page, which is a different job from fixing one page's own bug.
|
|
127
|
+
[How that works →](docs/manual.md#run_accessibility_check)
|
|
128
|
+
|
|
129
|
+
**WCAG conformance, not a violation count.** Every accessibility finding maps
|
|
130
|
+
to a WCAG 2.1 success criterion and says what it costs your claim. A Level A
|
|
131
|
+
failure is P1 — not because it feels worse, but because while it stands, Level
|
|
132
|
+
AA conformance is *unreachable* no matter how the other criteria score. Each run
|
|
133
|
+
returns a conformance verdict counting failing **criteria**, not findings: "22
|
|
134
|
+
issues" and "5 criteria failing" are answers to different questions, and only
|
|
135
|
+
one of them goes in a compliance statement.
|
|
136
|
+
[How →](docs/manual.md#wcag-conformance-and-priority)
|
|
137
|
+
|
|
138
|
+
**Before versus after.** Point either tool at a previous run with
|
|
139
|
+
`baseline_dir` and it reports what you **fixed**, what **still fails**, and what
|
|
140
|
+
you **newly introduced** — plus score deltas. A violation count can't tell those
|
|
141
|
+
apart: on a real test, adding an `aria-label` fixed five P1s and introduced an
|
|
142
|
+
`aria-valid-attr-value` P2, because the `aria-labelledby` pointed at a missing
|
|
143
|
+
id. Works against `localhost`, so it's a pre-PR check.
|
|
144
|
+
[How →](docs/manual.md#comparing-two-runs-before-vs-after)
|
|
145
|
+
|
|
146
|
+
**Lab versus field.** A metric that passes in the lab but fails for real users
|
|
147
|
+
means your test environment is not reproducing production — and no local tool
|
|
148
|
+
can detect it. On one real homepage the lab reported a perfect CLS of 0 while
|
|
149
|
+
70% of real users were experiencing a rating of poor.
|
|
150
|
+
[More →](docs/manual.md#what-psi-adds-over-run_lighthouse)
|
|
414
151
|
|
|
415
152
|
---
|
|
416
153
|
|
|
417
|
-
##
|
|
154
|
+
## Priority levels
|
|
418
155
|
|
|
419
|
-
|
|
156
|
+
| | Meaning |
|
|
157
|
+
|---|---|
|
|
158
|
+
| **P1** | Blocker — fix before shipping |
|
|
159
|
+
| **P2** | Warning — track before merging |
|
|
160
|
+
| **P3** | Advisory — log as tech debt |
|
|
420
161
|
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
162
|
+
Priority is derived, not guessed. Accessibility findings follow **WCAG 2.1
|
|
163
|
+
conformance** — a Level A failure is P1 because it puts an AA claim out of
|
|
164
|
+
reach. Lighthouse findings are ranked by the category points an audit actually
|
|
165
|
+
costs, not by score alone. Semgrep `security` findings are P1 regardless of the
|
|
166
|
+
severity Semgrep assigned them.
|
|
424
167
|
|
|
425
|
-
|
|
426
|
-
|
|
168
|
+
Corroborated and field-confirmed findings are promoted a tier; lab-only
|
|
169
|
+
findings that real users don't experience are demoted.
|
|
170
|
+
[Full mapping →](docs/manual.md#priority-system)
|
|
427
171
|
|
|
428
|
-
|
|
429
|
-
QA snapshot — /path/to/my-feature-branch
|
|
430
|
-
```
|
|
172
|
+
---
|
|
431
173
|
|
|
432
|
-
|
|
174
|
+
## Docs
|
|
433
175
|
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
```
|
|
176
|
+
| | |
|
|
177
|
+
|---|---|
|
|
178
|
+
| [Operating manual](docs/manual.md) | Install, per-tool reference, output shapes, troubleshooting |
|
|
179
|
+
| [PSI report spec](docs/psi-report-spec.md) | How to turn a PSI audit into a written report |
|
|
439
180
|
|
|
440
|
-
|
|
181
|
+
MIT-compatible ISC licence. Issues and PRs welcome at
|
|
182
|
+
[Hiddensound/NFunc_MCP](https://github.com/Hiddensound/NFunc_MCP).
|
package/dist/index.js
CHANGED
|
@@ -7,6 +7,8 @@ import { registerLighthouseTool } from "./tools/lighthouse.js";
|
|
|
7
7
|
import { registerAccessibilityTool } from "./tools/accessibility.js";
|
|
8
8
|
import { registerStaticAnalysisTool } from "./tools/staticAnalysis.js";
|
|
9
9
|
import { registerQaGateTool } from "./tools/qaGate.js";
|
|
10
|
+
import { registerPerformanceAuditPlanTool } from "./tools/performanceAuditPlan.js";
|
|
11
|
+
import { registerPerformanceAuditTool } from "./tools/performanceAudit.js";
|
|
10
12
|
/**
|
|
11
13
|
* Identity comes from package.json rather than being written out here, so the
|
|
12
14
|
* two cannot drift — they already had, declaring qa-mcp/0.1.0 against
|
|
@@ -44,6 +46,8 @@ registerLighthouseTool(server);
|
|
|
44
46
|
registerAccessibilityTool(server);
|
|
45
47
|
registerStaticAnalysisTool(server);
|
|
46
48
|
registerQaGateTool(server);
|
|
49
|
+
registerPerformanceAuditPlanTool(server);
|
|
50
|
+
registerPerformanceAuditTool(server);
|
|
47
51
|
async function main() {
|
|
48
52
|
const transport = new StdioServerTransport();
|
|
49
53
|
await server.connect(transport);
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA,OAAO,EAAE,YAAY,EAAE,MAAM,IAAI,CAAC;AAClC,OAAO,EAAE,aAAa,EAAE,MAAM,KAAK,CAAC;AACpC,OAAO,EAAE,SAAS,EAAE,MAAM,yCAAyC,CAAC;AACpE,OAAO,EAAE,oBAAoB,EAAE,MAAM,2CAA2C,CAAC;AACjF,OAAO,EAAE,sBAAsB,EAAE,MAAM,uBAAuB,CAAC;AAC/D,OAAO,EAAE,yBAAyB,EAAE,MAAM,0BAA0B,CAAC;AACrE,OAAO,EAAE,0BAA0B,EAAE,MAAM,2BAA2B,CAAC;AACvE,OAAO,EAAE,kBAAkB,EAAE,MAAM,mBAAmB,CAAC;AACvD,OAAO,EAAE,gCAAgC,EAAE,MAAM,iCAAiC,CAAC;AACnF,OAAO,EAAE,4BAA4B,EAAE,MAAM,6BAA6B,CAAC;AAE3E;;;;;;;;;;GAUG;AACH,SAAS,YAAY;IACnB,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,IAAI,CAAC,KAAK,CACpB,YAAY,CAAC,aAAa,CAAC,IAAI,GAAG,CAAC,iBAAiB,EAAE,MAAM,CAAC,IAAI,CAAC,GAAG,CAAC,CAAC,EAAE,MAAM,CAAC,CAC1C,CAAC;QACzC,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,IAAI,IAAI,WAAW,EAAE,OAAO,EAAE,GAAG,CAAC,OAAO,IAAI,OAAO,EAAE,CAAC;IAC5E,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,EAAE,IAAI,EAAE,WAAW,EAAE,OAAO,EAAE,OAAO,EAAE,CAAC;IACjD,CAAC;AACH,CAAC;AAED,MAAM,MAAM,GAAG,IAAI,SAAS,CAAC,YAAY,EAAE,CAAC,CAAC;AAE7C,MAAM,CAAC,YAAY,CACjB,MAAM,EACN;IACE,WAAW,EAAE,iEAAiE;IAC9E,WAAW,EAAE,EAAE;CAChB,EACD,KAAK,IAAI,EAAE;IACT,MAAM,OAAO,GAAG;QACd,MAAM,EAAE,IAAI;QACZ,SAAS,EAAE,IAAI,IAAI,EAAE,CAAC,WAAW,EAAE;KACpC,CAAC;IACF,OAAO;QACL,OAAO,EAAE,CAAC,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,EAAE,IAAI,CAAC,SAAS,CAAC,OAAO,CAAC,EAAE,CAAC;KAC3D,CAAC;AACJ,CAAC,CACF,CAAC;AAEF,sBAAsB,CAAC,MAAM,CAAC,CAAC;AAC/B,yBAAyB,CAAC,MAAM,CAAC,CAAC;AAClC,0BAA0B,CAAC,MAAM,CAAC,CAAC;AACnC,kBAAkB,CAAC,MAAM,CAAC,CAAC;AAC3B,gCAAgC,CAAC,MAAM,CAAC,CAAC;AACzC,4BAA4B,CAAC,MAAM,CAAC,CAAC;AAErC,KAAK,UAAU,IAAI;IACjB,MAAM,SAAS,GAAG,IAAI,oBAAoB,EAAE,CAAC;IAC7C,MAAM,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,CAAC;AAClC,CAAC;AAED,IAAI,EAAE,CAAC,KAAK,CAAC,CAAC,GAAG,EAAE,EAAE;IACnB,OAAO,CAAC,KAAK,CAAC,qBAAqB,EAAE,GAAG,CAAC,CAAC;IAC1C,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,CAAC;AAClB,CAAC,CAAC,CAAC"}
|
|
@@ -5,13 +5,41 @@ const PRIORITY_ORDER = { P1: 0, P2: 1, P3: 2 };
|
|
|
5
5
|
* component used everywhere, not N unrelated bugs. At or below it, each
|
|
6
6
|
* element keeps its own line.
|
|
7
7
|
*
|
|
8
|
-
*
|
|
9
|
-
*
|
|
10
|
-
*
|
|
11
|
-
*
|
|
12
|
-
*
|
|
8
|
+
* **Lowered from 10 to 2 in Phase 26.** The original 10 was chosen on the
|
|
9
|
+
* reasoning that "ten duplicate ids on a page are ten ids to rename, and
|
|
10
|
+
* collapsing them hides the list a developer needs". That objection turned out
|
|
11
|
+
* to be about `sample_selectors` being capped at 10, not about collapsing —
|
|
12
|
+
* the ids are in the evidence either way, and the cap is now generous enough
|
|
13
|
+
* that nothing is hidden.
|
|
14
|
+
*
|
|
15
|
+
* What forced the change was a boundary artifact seen on a live site. Two pages
|
|
16
|
+
* sharing one WooCommerce sorting component reported the same duplicate-id
|
|
17
|
+
* defect completely differently: the homepage had 11 instances and collapsed to
|
|
18
|
+
* a single finding (3 violations total), while a category page had exactly 10
|
|
19
|
+
* and listed every one (12 violations total). Identical underlying bug, and one
|
|
20
|
+
* page looked four times worse than the other purely because of which side of
|
|
21
|
+
* the threshold it fell on. A boundary that distorts cross-page comparison that
|
|
22
|
+
* badly is in the wrong place.
|
|
23
|
+
*
|
|
24
|
+
* Two was chosen over an intermediate value after measuring the alternatives on
|
|
25
|
+
* one product page: threshold 5 left 22 findings and collapsed nothing (its
|
|
26
|
+
* duplicate-id group sat at exactly 5), threshold 3 gave 18, and threshold 2
|
|
27
|
+
* gave 10 — one finding per rule, with every selector attached. At 2 the same
|
|
28
|
+
* component is reported identically on every page, which is the property that
|
|
29
|
+
* was actually missing. Three gallery images with no alt text are one template
|
|
30
|
+
* to fix, not three authoring mistakes.
|
|
31
|
+
*
|
|
32
|
+
* The cost is real: two genuinely unrelated defects of the same rule on
|
|
33
|
+
* different components now merge into one finding. `sample_selectors` makes
|
|
34
|
+
* that recoverable, and the cross-page rollup in the batch aggregate answers
|
|
35
|
+
* "shared or page-specific" without depending on this number at all.
|
|
36
|
+
*/
|
|
37
|
+
const SYSTEMIC_THRESHOLD = 2;
|
|
38
|
+
/**
|
|
39
|
+
* How many selectors a systemic finding carries. Generous on purpose: the whole
|
|
40
|
+
* objection to collapsing was that it hid the list, so the list has to survive.
|
|
13
41
|
*/
|
|
14
|
-
const
|
|
42
|
+
const MAX_SAMPLE_SELECTORS = 25;
|
|
15
43
|
/**
|
|
16
44
|
* Collapses the two ways pa11y over-reports a single defect.
|
|
17
45
|
*
|
|
@@ -85,8 +113,11 @@ export function dedupeA11yFindings(findings) {
|
|
|
85
113
|
distinct_elements: group.length,
|
|
86
114
|
systemic: true,
|
|
87
115
|
sample_selectors: group
|
|
88
|
-
.slice(0,
|
|
116
|
+
.slice(0, MAX_SAMPLE_SELECTORS)
|
|
89
117
|
.map((f) => String(f.evidence["selector"] ?? "")),
|
|
118
|
+
...(group.length > MAX_SAMPLE_SELECTORS
|
|
119
|
+
? { selectors_truncated: group.length - MAX_SAMPLE_SELECTORS }
|
|
120
|
+
: {}),
|
|
90
121
|
},
|
|
91
122
|
});
|
|
92
123
|
}
|