nfunc-mcp 0.2.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.
Files changed (65) hide show
  1. package/README.md +85 -335
  2. package/dist/index.js +27 -4
  3. package/dist/index.js.map +1 -1
  4. package/dist/mappers/a11yDedupe.d.ts +25 -0
  5. package/dist/mappers/a11yDedupe.js +95 -0
  6. package/dist/mappers/a11yDedupe.js.map +1 -0
  7. package/dist/mappers/compositeScore.d.ts +23 -0
  8. package/dist/mappers/compositeScore.js +107 -0
  9. package/dist/mappers/compositeScore.js.map +1 -0
  10. package/dist/mappers/correlator.js +94 -33
  11. package/dist/mappers/correlator.js.map +1 -1
  12. package/dist/mappers/defectFormatter.js +129 -15
  13. package/dist/mappers/defectFormatter.js.map +1 -1
  14. package/dist/mappers/labFieldComparator.d.ts +62 -0
  15. package/dist/mappers/labFieldComparator.js +134 -0
  16. package/dist/mappers/labFieldComparator.js.map +1 -0
  17. package/dist/mappers/priorityMapper.d.ts +38 -0
  18. package/dist/mappers/priorityMapper.js +126 -0
  19. package/dist/mappers/priorityMapper.js.map +1 -1
  20. package/dist/mappers/psiAggregator.d.ts +130 -0
  21. package/dist/mappers/psiAggregator.js +293 -0
  22. package/dist/mappers/psiAggregator.js.map +1 -0
  23. package/dist/mappers/webVitalsMapper.d.ts +52 -0
  24. package/dist/mappers/webVitalsMapper.js +131 -0
  25. package/dist/mappers/webVitalsMapper.js.map +1 -0
  26. package/dist/tools/accessibility.js +72 -31
  27. package/dist/tools/accessibility.js.map +1 -1
  28. package/dist/tools/lighthouse.d.ts +14 -0
  29. package/dist/tools/lighthouse.js +134 -34
  30. package/dist/tools/lighthouse.js.map +1 -1
  31. package/dist/tools/performanceAudit.d.ts +2 -0
  32. package/dist/tools/performanceAudit.js +446 -0
  33. package/dist/tools/performanceAudit.js.map +1 -0
  34. package/dist/tools/performanceAuditPlan.d.ts +2 -0
  35. package/dist/tools/performanceAuditPlan.js +438 -0
  36. package/dist/tools/performanceAuditPlan.js.map +1 -0
  37. package/dist/tools/qaGate.js +109 -34
  38. package/dist/tools/qaGate.js.map +1 -1
  39. package/dist/utils/csvReader.d.ts +20 -0
  40. package/dist/utils/csvReader.js +172 -0
  41. package/dist/utils/csvReader.js.map +1 -0
  42. package/dist/utils/httpClient.d.ts +84 -0
  43. package/dist/utils/httpClient.js +171 -0
  44. package/dist/utils/httpClient.js.map +1 -0
  45. package/dist/utils/outputParsers.d.ts +25 -0
  46. package/dist/utils/outputParsers.js +42 -0
  47. package/dist/utils/outputParsers.js.map +1 -1
  48. package/dist/utils/psiAuth.d.ts +26 -0
  49. package/dist/utils/psiAuth.js +36 -0
  50. package/dist/utils/psiAuth.js.map +1 -0
  51. package/dist/utils/psiParser.d.ts +124 -0
  52. package/dist/utils/psiParser.js +200 -0
  53. package/dist/utils/psiParser.js.map +1 -0
  54. package/dist/utils/publicUrl.d.ts +17 -0
  55. package/dist/utils/publicUrl.js +115 -0
  56. package/dist/utils/publicUrl.js.map +1 -0
  57. package/dist/utils/sitemapReader.d.ts +27 -0
  58. package/dist/utils/sitemapReader.js +272 -0
  59. package/dist/utils/sitemapReader.js.map +1 -0
  60. package/dist/utils/urlClassifier.d.ts +45 -0
  61. package/dist/utils/urlClassifier.js +267 -0
  62. package/dist/utils/urlClassifier.js.map +1 -0
  63. package/docs/manual.md +558 -0
  64. package/docs/psi-report-spec.md +174 -0
  65. package/package.json +14 -4
@@ -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.2.0",
4
- "description": "MCP server for non-functional QA — Lighthouse, pa11y, ESLint, and Semgrep in parallel with cross-tool correlation and HTML reports",
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,13 +34,22 @@
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",
40
50
  "repository": {
41
51
  "type": "git",
42
- "url": "https://github.com/Hiddensound/NFunc_MCP.git"
52
+ "url": "git+https://github.com/Hiddensound/NFunc_MCP.git"
43
53
  },
44
54
  "homepage": "https://github.com/Hiddensound/NFunc_MCP#readme",
45
55
  "dependencies": {