nfunc-mcp 0.3.0 → 0.4.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 +84 -376
- package/dist/index.js +4 -0
- package/dist/index.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/psiAggregator.d.ts +130 -0
- package/dist/mappers/psiAggregator.js +293 -0
- package/dist/mappers/psiAggregator.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/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/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/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 +124 -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/docs/manual.md +558 -0
- package/docs/psi-report-spec.md +174 -0
- package/package.json +13 -3
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
# PSI performance audit — report spec
|
|
2
|
+
|
|
3
|
+
How to turn the output of `run_performance_audit` into the written report.
|
|
4
|
+
|
|
5
|
+
The tool owns the arithmetic; you own the prose. This file exists so the
|
|
6
|
+
document comes out the same shape every time instead of being re-improvised,
|
|
7
|
+
and so the specific mistakes that a human (or a model) makes when reading
|
|
8
|
+
twenty-odd JSON files by hand are designed out rather than left to care.
|
|
9
|
+
|
|
10
|
+
Structure below is derived from a real executive audit that worked. Follow it.
|
|
11
|
+
|
|
12
|
+
---
|
|
13
|
+
|
|
14
|
+
## The three rules
|
|
15
|
+
|
|
16
|
+
These are not style preferences. Each one exists because it was got wrong in a
|
|
17
|
+
real report.
|
|
18
|
+
|
|
19
|
+
### 1. Never do arithmetic. Quote `aggregate`.
|
|
20
|
+
|
|
21
|
+
Every cross-page number — counts, means, ranges, percentages, verdict tallies —
|
|
22
|
+
is already computed in the `aggregate` block of the final tool response. Quote
|
|
23
|
+
it. Do not add up scores, do not count failures across pages, do not compute a
|
|
24
|
+
mean by eye.
|
|
25
|
+
|
|
26
|
+
A hand-written audit of 22 runs got "TBT fails on 22/22" right and the
|
|
27
|
+
direction of the CrUX CLS comparison backwards, in the same document. Counting
|
|
28
|
+
is easy to get right and easy to get wrong; the difference is whether a machine
|
|
29
|
+
did it.
|
|
30
|
+
|
|
31
|
+
If a number you want is not in `aggregate`, say so in the report rather than
|
|
32
|
+
deriving it.
|
|
33
|
+
|
|
34
|
+
### 2. Field data leads. Lab data explains.
|
|
35
|
+
|
|
36
|
+
When lab and field disagree, **the field number is the headline and the lab
|
|
37
|
+
number is context**. Real users outrank a simulation.
|
|
38
|
+
|
|
39
|
+
`aggregate.lab_vs_field_summary` and each run's `lab_vs_field` array give you
|
|
40
|
+
the verdicts:
|
|
41
|
+
|
|
42
|
+
| Verdict | What to write |
|
|
43
|
+
|---|---|
|
|
44
|
+
| `worse_in_field` | **Lead with this.** The test environment is not reproducing production. No local tool can find it. Section 1 material. |
|
|
45
|
+
| `confirmed` | Two independent measurements agree. Highest confidence — state it plainly and prioritise. |
|
|
46
|
+
| `worse_in_lab` | The lab profile is harsher than the real audience. Report as a stress signal, explicitly *not* as user-experienced harm. Do not lead with it. |
|
|
47
|
+
| `both_pass` | Mention only in the coverage table. |
|
|
48
|
+
|
|
49
|
+
The failure this prevents: an audit that opened with "mobile LCP averages
|
|
50
|
+
17.3 s — material at-risk conversion" when the field data on the same pages
|
|
51
|
+
showed real users at 2.8 s, while burying a 0.55 field CLS that 70% of real
|
|
52
|
+
users were hitting. The lab numbers were real; they were not what users
|
|
53
|
+
experienced.
|
|
54
|
+
|
|
55
|
+
Never describe CrUX as "real-time". It is a **28-day trailing aggregate at the
|
|
56
|
+
75th percentile**. The right phrase is "real users, 28-day p75".
|
|
57
|
+
|
|
58
|
+
### 3. State coverage where the sample is described.
|
|
59
|
+
|
|
60
|
+
`coverage` from the plan tool and `aggregate.by_template` tell you what
|
|
61
|
+
fraction of the site the sample represents. "3 of 3,904 product pages" belongs
|
|
62
|
+
in section 2.2 as a caveat, not in a footnote and not omitted.
|
|
63
|
+
|
|
64
|
+
Where a metric came from origin-level CrUX rather than the URL's own data
|
|
65
|
+
(`field_source: "origin"`), say so at the point the number appears. Origin data
|
|
66
|
+
for a homepage says nothing about a checkout page.
|
|
67
|
+
|
|
68
|
+
This matters more than it looks, because **PSI substitutes origin data
|
|
69
|
+
silently**. A low-traffic URL comes back with `loadingExperience` fully
|
|
70
|
+
populated from origin-wide numbers, and the only tell is that its `id` holds
|
|
71
|
+
the origin rather than the URL. The parser checks for this, so trust
|
|
72
|
+
`field_source` — but never assume a populated field block describes the page
|
|
73
|
+
you asked about. A giveaway in the data itself: two different URLs reporting
|
|
74
|
+
identical p75 values are both being served origin data.
|
|
75
|
+
|
|
76
|
+
---
|
|
77
|
+
|
|
78
|
+
## Document structure
|
|
79
|
+
|
|
80
|
+
### 1. Executive summary
|
|
81
|
+
- **Overall health** — one paragraph. Use `aggregate.by_strategy` and
|
|
82
|
+
`aggregate.cwv_verdicts`.
|
|
83
|
+
- **Headline findings** — three to five bullets, worst first. Anything in
|
|
84
|
+
`worse_in_field` goes here. So does anything in `lab_metric_failures` with
|
|
85
|
+
`universal: true` — a metric failing on 80%+ of runs. A lower percentage is a
|
|
86
|
+
section 6 finding, not a headline: "TBT fails on 2 of 8 runs" does not belong
|
|
87
|
+
in an executive summary as though it were sitewide.
|
|
88
|
+
- **Business impact** — what the numbers mean for the people using the site.
|
|
89
|
+
This is the one section that is genuinely yours to write; ground every claim
|
|
90
|
+
in a quoted number.
|
|
91
|
+
- **Recommended priority** — the single highest-leverage fix and why.
|
|
92
|
+
|
|
93
|
+
### 2. Scope and methodology
|
|
94
|
+
- **2.1 Purpose** — synthetic lab audit plus real-user field data. Say
|
|
95
|
+
explicitly that load and stress testing are out of scope.
|
|
96
|
+
- **2.2 Page sample** — table of template, representative URL, devices, and
|
|
97
|
+
**coverage** (rule 3). Include templates that were *not* audited and why —
|
|
98
|
+
session-gated pages routed to `run_lighthouse`, templates below the sampling
|
|
99
|
+
budget.
|
|
100
|
+
- **2.3 Tools and data sources** — PageSpeed Insights API v5 (Lighthouse on
|
|
101
|
+
Google's infrastructure) for lab; CrUX 28-day p75 for field. Name the
|
|
102
|
+
categories captured.
|
|
103
|
+
- **2.4 Test conditions** — `runs_per_url`, date, `strategy`, anonymous
|
|
104
|
+
session. If `runs_per_url` is 1, say that single extreme readings are
|
|
105
|
+
flagged for confirmation rather than validated, and point at
|
|
106
|
+
`aggregate.outliers`.
|
|
107
|
+
- **2.5 Out of scope** — load testing, authenticated flows, third-party
|
|
108
|
+
backends.
|
|
109
|
+
|
|
110
|
+
### 3. Scorecard
|
|
111
|
+
One row per page/device from the index. Columns: page, device, performance
|
|
112
|
+
score, LCP (lab), LCP (field), CLS (lab), CLS (field), INP (field), TBT (lab),
|
|
113
|
+
CWV verdict. Mark every field cell that came from origin-level data.
|
|
114
|
+
|
|
115
|
+
### 4. Core Web Vitals reference
|
|
116
|
+
The threshold table, and a short lab-versus-field explanation. Keep it; readers
|
|
117
|
+
outside the engineering team need it, and it is where you establish the
|
|
118
|
+
vocabulary the rest of the report uses.
|
|
119
|
+
|
|
120
|
+
### 5. Diagnostic checklist
|
|
121
|
+
Root-cause categories — render-blocking, images, JavaScript, third parties,
|
|
122
|
+
caching, layout stability, mobile. Check items off **against the per-page JSON
|
|
123
|
+
reports on disk**, which hold the individual Lighthouse audits. Do not tick a
|
|
124
|
+
box from the summary index; if you have not opened the audit, say the item is
|
|
125
|
+
unverified.
|
|
126
|
+
|
|
127
|
+
### 6. Key findings
|
|
128
|
+
Ranked by material impact. One row each: finding, affected pages, severity,
|
|
129
|
+
evidence. Draw from `lab_metric_failures`, `systemic_findings`, and the P1s in
|
|
130
|
+
each run's `findings`.
|
|
131
|
+
|
|
132
|
+
**Report systemic findings once.** Note these are computed only from URL-level
|
|
133
|
+
field data — origin-level CrUX is the same number repeated for every page that
|
|
134
|
+
falls back to it, so it cannot evidence a claim about variation between pages.
|
|
135
|
+
If `aggregate.collapsed_vitals` names a vital, that vital is a site-wide
|
|
136
|
+
characteristic — write it as one finding about
|
|
137
|
+
shared code, never as N per-page findings. The `collapse_note` says which.
|
|
138
|
+
|
|
139
|
+
### 7. Prioritised recommendations
|
|
140
|
+
Impact / effort / priority / owner / target pages. Split into quick wins and
|
|
141
|
+
strategic work. Effort and owner are your judgement — mark them as estimates.
|
|
142
|
+
|
|
143
|
+
### 8. Field data callout
|
|
144
|
+
The lab-versus-field table in full, with the `note` from each comparison. This
|
|
145
|
+
is the section that justifies having run PSI instead of local Lighthouse.
|
|
146
|
+
|
|
147
|
+
### 9. Appendix
|
|
148
|
+
Report inventory (`output_dir` and `_index.json`), glossary, revision log.
|
|
149
|
+
|
|
150
|
+
---
|
|
151
|
+
|
|
152
|
+
## Failure and gap handling
|
|
153
|
+
|
|
154
|
+
PSI fails intermittently — roughly one run in three on some origins. Report the
|
|
155
|
+
audit you actually have:
|
|
156
|
+
|
|
157
|
+
- If `progress.failed` is non-zero and the gaps were not filled, list the
|
|
158
|
+
missing page/device combinations in section 2.2. Do not present a partial
|
|
159
|
+
sample as complete.
|
|
160
|
+
- If `aggregate.lab_vs_field_summary.no_field_data` is non-zero, name those
|
|
161
|
+
pages. Absent field data means insufficient real-user traffic, which is
|
|
162
|
+
itself worth knowing — it usually marks a long-tail page.
|
|
163
|
+
- If the site is behind a bot wall or the origin is non-public, the audit is
|
|
164
|
+
lab-only by definition. Say so in 2.3 rather than implying field coverage.
|
|
165
|
+
|
|
166
|
+
## Output format
|
|
167
|
+
|
|
168
|
+
Markdown by default. For a .docx deliverable, convert:
|
|
169
|
+
|
|
170
|
+
```
|
|
171
|
+
pandoc report.md -o report.docx
|
|
172
|
+
```
|
|
173
|
+
|
|
174
|
+
Do not attempt to produce .docx directly.
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "nfunc-mcp",
|
|
3
|
-
"version": "0.
|
|
4
|
-
"description": "MCP server for non-functional QA — Lighthouse, pa11y, ESLint
|
|
3
|
+
"version": "0.4.0",
|
|
4
|
+
"description": "MCP server for non-functional QA — Lighthouse, pa11y, ESLint and Semgrep in parallel with cross-tool correlation, plus PageSpeed Insights audits that compare lab scores against real-user Core Web Vitals from CrUX",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"type": "module",
|
|
7
7
|
"bin": {
|
|
@@ -9,6 +9,7 @@
|
|
|
9
9
|
},
|
|
10
10
|
"files": [
|
|
11
11
|
"dist",
|
|
12
|
+
"docs",
|
|
12
13
|
"README.md"
|
|
13
14
|
],
|
|
14
15
|
"engines": {
|
|
@@ -33,7 +34,16 @@
|
|
|
33
34
|
"performance",
|
|
34
35
|
"non-functional-testing",
|
|
35
36
|
"claude",
|
|
36
|
-
"ai-tools"
|
|
37
|
+
"ai-tools",
|
|
38
|
+
"pagespeed",
|
|
39
|
+
"pagespeed-insights",
|
|
40
|
+
"core-web-vitals",
|
|
41
|
+
"crux",
|
|
42
|
+
"psi",
|
|
43
|
+
"web-vitals",
|
|
44
|
+
"lcp",
|
|
45
|
+
"cls",
|
|
46
|
+
"inp"
|
|
37
47
|
],
|
|
38
48
|
"author": "Ajinkya Kunjir",
|
|
39
49
|
"license": "ISC",
|