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,446 @@
1
+ import { mkdir, readFile, writeFile } from "node:fs/promises";
2
+ import { join, resolve } from "node:path";
3
+ import { z } from "zod";
4
+ import { httpGetJson } from "../utils/httpClient.js";
5
+ import { parsePsiResponse, PsiRuntimeError } from "../utils/psiParser.js";
6
+ import { checkPublicReachability } from "../utils/publicUrl.js";
7
+ import { resolveApiKey, keylessWarning, KEY_ENV_VAR, KEYLESS_RUN_CAP } from "../utils/psiAuth.js";
8
+ import { formatLighthouseFinding } from "../mappers/defectFormatter.js";
9
+ import { formatFieldFindings } from "../mappers/webVitalsMapper.js";
10
+ import { sortFindingsByPriority } from "../mappers/priorityMapper.js";
11
+ import { compareLabField, adjustmentFor, promote, demote, AUDIT_TO_VITAL, } from "../mappers/labFieldComparator.js";
12
+ import { aggregate, collapseSystemicFindings, suppressComponentFindings, } from "../mappers/psiAggregator.js";
13
+ /**
14
+ * Overridable so the tool can be exercised end to end against a captured
15
+ * response without spending quota, and so anyone behind an egress proxy can
16
+ * point at it. Defaults to the real API.
17
+ */
18
+ const PSI_ENDPOINT = process.env.PAGESPEED_API_ENDPOINT ??
19
+ "https://www.googleapis.com/pagespeedonline/v5/runPagespeed";
20
+ /**
21
+ * Per-attempt ceiling, set from measurement rather than from the documentation.
22
+ *
23
+ * Across every successful live run observed, latency fell between 10 s and
24
+ * 57 s. A longer timeout therefore buys no additional successes — it only makes
25
+ * a hang more expensive. 75 s covers the slowest observed success with margin
26
+ * and leaves room inside a 150 s chunk for a second attempt, which is worth
27
+ * more than waiting: in a 6-run sample the attempt immediately after a timeout
28
+ * succeeded in 10 s.
29
+ */
30
+ const PSI_TIMEOUT_MS = 75_000;
31
+ /** Typical successful run, measured against a live key. Used to reserve budget. */
32
+ const TYPICAL_RUN_MS = 45_000;
33
+ /** Spacing between calls. Sequencing beats fanning out — see the tool description. */
34
+ const INTER_CALL_DELAY_MS = 1_500;
35
+ const DEFAULT_OUTPUT_DIR = "./psi-reports";
36
+ /**
37
+ * PSI calls take 10-30 s. Six is roughly 90 seconds of work, which fits inside
38
+ * a typical MCP client timeout with room to spare; the caller loops on the
39
+ * cursor until the batch is done.
40
+ */
41
+ const DEFAULT_MAX_RUNS_PER_CALL = 2;
42
+ /**
43
+ * Wall-clock ceiling for one call, the real protection against an MCP timeout.
44
+ *
45
+ * A run count alone cannot bound the time, because per-call latency varies far
46
+ * more than expected: fivebelow.com's homepage took 47 s per run while its
47
+ * beauty PLP returned HTTP 500 and burned the retry budget. Counting runs let
48
+ * a two-run chunk exceed five minutes. The loop now stops starting new work
49
+ * once the budget is spent and hands back a cursor, so a chunk returns on time
50
+ * whatever the API does.
51
+ */
52
+ const DEFAULT_MAX_SECONDS_PER_CALL = 150;
53
+ const pageShape = z.object({
54
+ template: z.string(),
55
+ label: z.string(),
56
+ url: z.string().url(),
57
+ slug: z.string().optional(),
58
+ });
59
+ const inputShape = {
60
+ pages: z.array(pageShape).min(1).describe("Approved pages from plan_performance_audit."),
61
+ strategy: z.enum(["mobile", "desktop", "both"]).optional(),
62
+ runs_per_url: z.number().int().min(1).max(5).optional(),
63
+ categories: z.array(z.string()).optional(),
64
+ output_dir: z.string().optional(),
65
+ origin_fallback: z.boolean().optional(),
66
+ api_key: z.string().optional(),
67
+ cursor: z.string().optional().describe("Resume token from a previous call. Omit on the first call."),
68
+ max_runs_per_call: z.number().int().min(1).max(20).optional(),
69
+ skip_completed: z
70
+ .boolean()
71
+ .optional()
72
+ .describe("Skip page/strategy pairs already present in the output index (default true). " +
73
+ "PSI fails intermittently, so re-running to fill gaps is normal — this makes " +
74
+ "that cheap instead of re-spending quota on pages that already succeeded. " +
75
+ "Set false to force a fresh measurement."),
76
+ max_seconds_per_call: z
77
+ .number()
78
+ .int()
79
+ .min(30)
80
+ .max(900)
81
+ .optional()
82
+ .describe(`Wall-clock ceiling for one call (default ${DEFAULT_MAX_SECONDS_PER_CALL}s). Must stay ` +
83
+ "below your MCP client's tool timeout; raise MCP_TOOL_TIMEOUT to use a bigger chunk."),
84
+ };
85
+ const sleep = (ms) => new Promise((r) => setTimeout(r, ms));
86
+ function slugFor(page) {
87
+ if (page.slug)
88
+ return page.slug;
89
+ try {
90
+ const url = new URL(page.url);
91
+ const path = url.pathname.replace(/^\/|\/$/g, "").replace(/\//g, "-");
92
+ const query = url.search ? `-${url.search.slice(1).replace(/[^a-z0-9]+/gi, "-")}` : "";
93
+ return (path || "homepage") + query;
94
+ }
95
+ catch {
96
+ return page.template;
97
+ }
98
+ }
99
+ function encodeCursor(index) {
100
+ return Buffer.from(JSON.stringify({ i: index })).toString("base64url");
101
+ }
102
+ function decodeCursor(cursor) {
103
+ if (!cursor)
104
+ return 0;
105
+ try {
106
+ const parsed = JSON.parse(Buffer.from(cursor, "base64url").toString("utf8"));
107
+ return typeof parsed.i === "number" && parsed.i >= 0 ? parsed.i : 0;
108
+ }
109
+ catch {
110
+ return 0;
111
+ }
112
+ }
113
+ function buildRequestUrl(target, strategy, categories, key) {
114
+ const url = new URL(PSI_ENDPOINT);
115
+ url.searchParams.set("url", target);
116
+ url.searchParams.set("strategy", strategy);
117
+ if (key)
118
+ url.searchParams.set("key", key);
119
+ for (const category of categories)
120
+ url.searchParams.append("category", category);
121
+ return url;
122
+ }
123
+ /**
124
+ * Apply the lab-vs-field verdicts to the findings for one run.
125
+ *
126
+ * The lab finding is the one that moves. A confirmed failure is promoted
127
+ * because two independent measurements agree; a lab-only failure is demoted
128
+ * and tagged, because the simulation saw something real users do not. Field
129
+ * findings that the lab missed entirely are promoted and tagged `field_only` —
130
+ * those are the ones no local tool can produce.
131
+ */
132
+ function applyComparisons(findings, comparisons) {
133
+ for (const comparison of comparisons) {
134
+ const { direction, tag } = adjustmentFor(comparison.verdict);
135
+ if (direction === "none")
136
+ continue;
137
+ const labAuditId = Object.entries(AUDIT_TO_VITAL).find(([, vital]) => vital === comparison.metric)?.[0];
138
+ const target = comparison.verdict === "worse_in_field"
139
+ ? findings.find((f) => f.evidence.audit_id === `crux.${comparison.metric}`)
140
+ : findings.find((f) => f.evidence.audit_id === labAuditId);
141
+ if (!target)
142
+ continue;
143
+ target.priority =
144
+ direction === "promote"
145
+ ? promote(target.priority, comparison.metric)
146
+ : demote(target.priority);
147
+ target.evidence = { ...target.evidence, lab_field_verdict: comparison.verdict, adjustment: tag };
148
+ target.description = `${target.description} ${comparison.note}`;
149
+ }
150
+ return findings;
151
+ }
152
+ /** Merge into the running index rather than overwriting, so batches accumulate. */
153
+ async function mergeIndex(dir, results) {
154
+ const indexPath = join(dir, "_index.json");
155
+ let existing = [];
156
+ try {
157
+ existing = JSON.parse(await readFile(indexPath, "utf8"));
158
+ }
159
+ catch {
160
+ existing = [];
161
+ }
162
+ const byKey = new Map(existing.map((r) => [`${r.url}|${r.strategy}`, r]));
163
+ for (const result of results)
164
+ byKey.set(`${result.url}|${result.strategy}`, result);
165
+ await writeFile(indexPath, JSON.stringify([...byKey.values()], null, 2), "utf8");
166
+ return indexPath;
167
+ }
168
+ export function registerPerformanceAuditTool(server) {
169
+ server.registerTool("run_performance_audit", {
170
+ description: "Runs a PageSpeed Insights audit over an approved set of pages and " +
171
+ "returns lab scores, CrUX real-user field data, and the disagreements " +
172
+ "between them. " +
173
+ "\n\n" +
174
+ "**Call plan_performance_audit first** and get the user's approval on " +
175
+ "the page list — this tool spends real API quota and takes 10-30 " +
176
+ "seconds per page/device. " +
177
+ "\n\n" +
178
+ "Runs in chunks: each call performs at most `max_runs_per_call` PSI " +
179
+ "requests and returns a `cursor`. Keep calling with that cursor until " +
180
+ "`complete` is true. Full raw reports are written to `output_dir` " +
181
+ "(one JSON per page/device) and merged into a running `_index.json`, " +
182
+ "so a failure partway through never costs the completed runs. The " +
183
+ "final call also returns an `aggregate` block containing every " +
184
+ "cross-page number — quote those rather than recomputing them." +
185
+ "\n\n" +
186
+ "The unique value here is `lab_vs_field`: a metric that passes in the " +
187
+ "lab but fails for real users means the test environment is not " +
188
+ "reproducing production, which no local tool can detect.",
189
+ inputSchema: inputShape,
190
+ }, async ({ pages, strategy, runs_per_url, categories, output_dir, origin_fallback, api_key, cursor, max_runs_per_call, max_seconds_per_call, skip_completed, }) => {
191
+ const warnings = [];
192
+ const resolvedStrategy = strategy ?? "both";
193
+ const runsPerUrl = runs_per_url ?? 1;
194
+ const originFallback = origin_fallback ?? true;
195
+ const maxRuns = max_runs_per_call ?? DEFAULT_MAX_RUNS_PER_CALL;
196
+ const budgetMs = (max_seconds_per_call ?? DEFAULT_MAX_SECONDS_PER_CALL) * 1000;
197
+ const startedAt = Date.now();
198
+ const requestedCategories = categories ?? [
199
+ "performance", "accessibility", "best-practices", "seo",
200
+ ];
201
+ const dir = resolve(output_dir ?? DEFAULT_OUTPUT_DIR);
202
+ const { key, source } = resolveApiKey(api_key);
203
+ const strategies = resolvedStrategy === "both" ? ["mobile", "desktop"] : [resolvedStrategy];
204
+ const units = [];
205
+ for (const page of pages)
206
+ for (const s of strategies)
207
+ units.push({ page, strategy: s });
208
+ /**
209
+ * Drop work that already succeeded.
210
+ *
211
+ * PSI fails intermittently on real sites — roughly one run in three
212
+ * against fivebelow.com timed out or 500'd, and the same URL succeeded
213
+ * minutes later. Filling those gaps is the normal workflow, not an edge
214
+ * case, so a second pass must not re-measure and re-charge for the pages
215
+ * that worked. Ordering is preserved, so a cursor from a previous call
216
+ * would not line up; re-running to fill gaps means starting without one.
217
+ */
218
+ let skipped = 0;
219
+ if ((skip_completed ?? true) && !cursor) {
220
+ try {
221
+ const existing = JSON.parse(await readFile(join(dir, "_index.json"), "utf8"));
222
+ const done = new Set(existing.map((r) => `${r.url}|${r.strategy}`));
223
+ const before = units.length;
224
+ const remaining = units.filter((u) => !done.has(`${u.page.url}|${u.strategy}`));
225
+ skipped = before - remaining.length;
226
+ units.length = 0;
227
+ units.push(...remaining);
228
+ }
229
+ catch {
230
+ // No index yet — nothing to skip.
231
+ }
232
+ }
233
+ if (skipped > 0) {
234
+ warnings.push(`Skipped ${skipped} page/strategy pair(s) already in ${join(dir, "_index.json")}. ` +
235
+ "Pass skip_completed:false to re-measure them.");
236
+ }
237
+ if (units.length === 0) {
238
+ return {
239
+ content: [{
240
+ type: "text",
241
+ text: JSON.stringify({
242
+ complete: true,
243
+ progress: { done: 0, total: 0, failed: 0 },
244
+ message: "Every requested page/strategy is already in the index. Nothing to run.",
245
+ index_file: join(dir, "_index.json"),
246
+ warnings,
247
+ }, null, 2),
248
+ }],
249
+ };
250
+ }
251
+ const totalRuns = units.length * runsPerUrl;
252
+ if (!key && totalRuns > KEYLESS_RUN_CAP) {
253
+ return {
254
+ content: [{
255
+ type: "text",
256
+ text: JSON.stringify({
257
+ error: "keyless_batch_too_large",
258
+ message: keylessWarning(totalRuns),
259
+ runs_requested: totalRuns,
260
+ keyless_cap: KEYLESS_RUN_CAP,
261
+ env_var: KEY_ENV_VAR,
262
+ }, null, 2),
263
+ }],
264
+ isError: true,
265
+ };
266
+ }
267
+ const unreachable = pages
268
+ .map((p) => ({ url: p.url, check: checkPublicReachability(p.url) }))
269
+ .filter((p) => !p.check.auditable);
270
+ if (unreachable.length > 0) {
271
+ return {
272
+ content: [{
273
+ type: "text",
274
+ text: JSON.stringify({
275
+ error: "pages_not_auditable",
276
+ pages: unreachable.map((p) => ({ url: p.url, reason: p.check.reason })),
277
+ }, null, 2),
278
+ }],
279
+ isError: true,
280
+ };
281
+ }
282
+ await mkdir(dir, { recursive: true });
283
+ const start = decodeCursor(cursor);
284
+ const results = [];
285
+ const failures = [];
286
+ let index = start;
287
+ let callsUsed = 0;
288
+ let budgetExhausted = false;
289
+ while (index < units.length && callsUsed + runsPerUrl <= Math.max(maxRuns, runsPerUrl)) {
290
+ // Check before starting a unit, never mid-unit: abandoning a page
291
+ // half-way through its repeat runs would leave a median of one.
292
+ // Reserve room for the run about to start, rather than checking only
293
+ // whether the budget is already spent — otherwise a 40 s run at the
294
+ // 149 s mark starts a second and overshoots by a full run.
295
+ const reserve = TYPICAL_RUN_MS * runsPerUrl;
296
+ if (callsUsed > 0 && Date.now() - startedAt + reserve >= budgetMs) {
297
+ budgetExhausted = true;
298
+ break;
299
+ }
300
+ const unit = units[index];
301
+ // Raw bodies are kept alongside the parsed form: the parsed summary
302
+ // feeds the index, the raw response is what gets written to disk.
303
+ const attempts = [];
304
+ let lastError = "";
305
+ for (let run = 0; run < runsPerUrl; run++) {
306
+ if (callsUsed > 0)
307
+ await sleep(INTER_CALL_DELAY_MS);
308
+ const requestUrl = buildRequestUrl(unit.page.url, unit.strategy, requestedCategories, key);
309
+ // Clamp this run's timeout to the budget it has left. Without it the
310
+ // budget is only advisory: a single hang costs the full PSI timeout
311
+ // on top of whatever was already spent, and a 150 s chunk ran 188 s.
312
+ // The reserve check above guarantees at least TYPICAL_RUN_MS remains,
313
+ // so this never squeezes a run into an unwinnable window.
314
+ // The whole call, retries included, is bounded by what is left of the
315
+ // chunk budget — so a retry can never push the chunk past its ceiling.
316
+ const remainingMs = budgetMs - (Date.now() - startedAt);
317
+ const response = await httpGetJson(requestUrl, {
318
+ timeoutMs: PSI_TIMEOUT_MS,
319
+ totalBudgetMs: Math.max(TYPICAL_RUN_MS, remainingMs),
320
+ // One retry, and timeouts are included: measurement showed timeout
321
+ // is the dominant failure mode here and that it usually recovers,
322
+ // while 5xx is rare and cheap. Two attempts, not three.
323
+ retries: 1,
324
+ retryOnTimeout: true,
325
+ retryDelayMs: 3_000,
326
+ redact: key ? [key] : [],
327
+ });
328
+ callsUsed++;
329
+ if (!response.ok || !response.data) {
330
+ lastError = response.error ?? `HTTP ${response.status}`;
331
+ continue;
332
+ }
333
+ try {
334
+ attempts.push({
335
+ parsed: parsePsiResponse(response.body, {
336
+ strategy: unit.strategy,
337
+ originFallback,
338
+ }),
339
+ raw: response.body,
340
+ });
341
+ }
342
+ catch (err) {
343
+ // A 200 carrying a runtimeError is a failed run, not a zero score.
344
+ lastError =
345
+ err instanceof PsiRuntimeError
346
+ ? `Lighthouse runtime error ${err.code}: ${err.message}`
347
+ : err.message;
348
+ }
349
+ }
350
+ if (attempts.length === 0) {
351
+ failures.push({ url: unit.page.url, strategy: unit.strategy, error: lastError });
352
+ index++;
353
+ continue;
354
+ }
355
+ // Median run by performance score. Averaging the metrics of different
356
+ // runs would invent a page that was never measured; picking the median
357
+ // run keeps every number internally consistent with one real load.
358
+ const ordered = [...attempts].sort((a, b) => (a.parsed.scores.performance ?? 0) - (b.parsed.scores.performance ?? 0));
359
+ const median = ordered[Math.floor(ordered.length / 2)];
360
+ const parsed = median.parsed;
361
+ // Write the **raw** PSI response, not the parsed summary.
362
+ //
363
+ // This originally saved the parsed object, which holds only failing
364
+ // audits — 9 KB where the real response is closer to a megabyte. The
365
+ // report spec sends the agent to these files to fill in the diagnostic
366
+ // checklist (unused JS, third-party weight, image formats), and none of
367
+ // that detail survived parsing. The parsed summary lives in _index.json;
368
+ // disk holds the full record.
369
+ const fileName = `${slugFor(unit.page)}_${unit.strategy}.json`;
370
+ await writeFile(join(dir, fileName), median.raw, "utf8");
371
+ const labFindings = parsed.lighthouse.failedAudits
372
+ .map(formatLighthouseFinding)
373
+ .filter((f) => f !== null);
374
+ const fieldFindings = parsed.field ? formatFieldFindings(parsed.field.metrics) : [];
375
+ const comparisons = compareLabField(parsed.lab, parsed.field);
376
+ let findings = suppressComponentFindings([...fieldFindings, ...labFindings]);
377
+ findings = sortFindingsByPriority(applyComparisons(findings, comparisons));
378
+ results.push({
379
+ template: unit.page.template,
380
+ label: unit.page.label,
381
+ url: unit.page.url,
382
+ strategy: unit.strategy,
383
+ runs: attempts.length,
384
+ scores: parsed.scores,
385
+ lab: parsed.lab,
386
+ field: parsed.field,
387
+ comparisons,
388
+ findings,
389
+ report_file: fileName,
390
+ });
391
+ index++;
392
+ }
393
+ if (budgetExhausted) {
394
+ warnings.push(`Stopped after ${Math.round((Date.now() - startedAt) / 1000)}s to stay inside the ` +
395
+ "per-call time budget. Call again with the cursor to continue.");
396
+ }
397
+ const indexPath = await mergeIndex(dir, results);
398
+ const complete = index >= units.length;
399
+ if (failures.length > 0) {
400
+ warnings.push(`${failures.length} run(s) failed. PSI fails intermittently — the same URL often ` +
401
+ "succeeds on a later attempt. Call this tool again with the same pages and no " +
402
+ "cursor: completed runs are skipped automatically, so only the gaps are retried.");
403
+ }
404
+ let aggregateBlock;
405
+ if (complete) {
406
+ // Aggregate over everything on disk, not just this chunk — the whole
407
+ // point of the running index is that the final numbers cover the batch.
408
+ let allRuns = results;
409
+ try {
410
+ allRuns = JSON.parse(await readFile(indexPath, "utf8"));
411
+ }
412
+ catch {
413
+ warnings.push("Could not re-read the index; aggregate covers this call's runs only.");
414
+ }
415
+ const systemic = collapseSystemicFindings(allRuns);
416
+ aggregateBlock = {
417
+ ...aggregate(allRuns),
418
+ systemic_findings: systemic.findings,
419
+ collapsed_vitals: systemic.collapsedVitals,
420
+ ...(systemic.collapsedVitals.length > 0
421
+ ? {
422
+ collapse_note: `Per-page findings for ${systemic.collapsedVitals.join(", ")} are superseded by ` +
423
+ "the systemic findings above; report them once, site-wide, not per page.",
424
+ }
425
+ : {}),
426
+ };
427
+ }
428
+ const payload = {
429
+ complete,
430
+ ...(complete ? {} : { cursor: encodeCursor(index) }),
431
+ progress: { done: index, total: units.length, failed: failures.length },
432
+ api_key: { present: Boolean(key), source },
433
+ output_dir: dir,
434
+ index_file: indexPath,
435
+ results,
436
+ ...(failures.length > 0 ? { failures } : {}),
437
+ ...(aggregateBlock ? { aggregate: aggregateBlock } : {}),
438
+ ...(complete
439
+ ? {}
440
+ : { next_step: "Call run_performance_audit again with this cursor and the same pages." }),
441
+ warnings,
442
+ };
443
+ return { content: [{ type: "text", text: JSON.stringify(payload, null, 2) }] };
444
+ });
445
+ }
446
+ //# sourceMappingURL=performanceAudit.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"performanceAudit.js","sourceRoot":"","sources":["../../src/tools/performanceAudit.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,2 @@
1
+ import type { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
2
+ export declare function registerPerformanceAuditPlanTool(server: McpServer): void;