nfunc-mcp 0.1.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 (45) hide show
  1. package/README.md +395 -0
  2. package/dist/config/qa-mcp-baseline.eslint.config.js +18 -0
  3. package/dist/index.d.ts +2 -0
  4. package/dist/index.js +36 -0
  5. package/dist/index.js.map +1 -0
  6. package/dist/mappers/correlator.d.ts +28 -0
  7. package/dist/mappers/correlator.js +132 -0
  8. package/dist/mappers/correlator.js.map +1 -0
  9. package/dist/mappers/defectFormatter.d.ts +15 -0
  10. package/dist/mappers/defectFormatter.js +209 -0
  11. package/dist/mappers/defectFormatter.js.map +1 -0
  12. package/dist/mappers/priorityMapper.d.ts +6 -0
  13. package/dist/mappers/priorityMapper.js +37 -0
  14. package/dist/mappers/priorityMapper.js.map +1 -0
  15. package/dist/tools/accessibility.d.ts +2 -0
  16. package/dist/tools/accessibility.js +63 -0
  17. package/dist/tools/accessibility.js.map +1 -0
  18. package/dist/tools/lighthouse.d.ts +2 -0
  19. package/dist/tools/lighthouse.js +61 -0
  20. package/dist/tools/lighthouse.js.map +1 -0
  21. package/dist/tools/qaGate.d.ts +2 -0
  22. package/dist/tools/qaGate.js +383 -0
  23. package/dist/tools/qaGate.js.map +1 -0
  24. package/dist/tools/staticAnalysis.d.ts +2 -0
  25. package/dist/tools/staticAnalysis.js +172 -0
  26. package/dist/tools/staticAnalysis.js.map +1 -0
  27. package/dist/types.d.ts +7 -0
  28. package/dist/types.js +2 -0
  29. package/dist/types.js.map +1 -0
  30. package/dist/utils/eslintConfigDetector.d.ts +5 -0
  31. package/dist/utils/eslintConfigDetector.js +21 -0
  32. package/dist/utils/eslintConfigDetector.js.map +1 -0
  33. package/dist/utils/outputParsers.d.ts +56 -0
  34. package/dist/utils/outputParsers.js +184 -0
  35. package/dist/utils/outputParsers.js.map +1 -0
  36. package/dist/utils/reportGenerator.d.ts +21 -0
  37. package/dist/utils/reportGenerator.js +291 -0
  38. package/dist/utils/reportGenerator.js.map +1 -0
  39. package/dist/utils/shellRunner.d.ts +12 -0
  40. package/dist/utils/shellRunner.js +31 -0
  41. package/dist/utils/shellRunner.js.map +1 -0
  42. package/dist/utils/toolResponse.d.ts +12 -0
  43. package/dist/utils/toolResponse.js +26 -0
  44. package/dist/utils/toolResponse.js.map +1 -0
  45. package/package.json +55 -0
@@ -0,0 +1,383 @@
1
+ import { z } from "zod";
2
+ import { fileURLToPath } from "url";
3
+ import { resolve } from "path";
4
+ import { runShell } from "../utils/shellRunner.js";
5
+ import { parseLighthouseJSON, parsePa11yJSON, parseESLintJSON, parseSemgrepJSON, } from "../utils/outputParsers.js";
6
+ import { formatLighthouseFinding, formatA11yFinding, formatStaticAnalysisFinding, } from "../mappers/defectFormatter.js";
7
+ import { detectESLintConfig } from "../utils/eslintConfigDetector.js";
8
+ import { correlate, } from "../mappers/correlator.js";
9
+ import { generateHtmlReport } from "../utils/reportGenerator.js";
10
+ const BASELINE_CONFIG_PATH = fileURLToPath(new URL("../config/qa-mcp-baseline.eslint.config.js", import.meta.url));
11
+ const PRIORITY_ORDER = { P1: 0, P2: 1, P3: 2 };
12
+ // ---------------------------------------------------------------------------
13
+ // Per-tool runners — each returns a structured result and never throws.
14
+ // Errors are surfaced as the `error` field so a partial failure in one tool
15
+ // doesn't prevent the other two from contributing findings.
16
+ // ---------------------------------------------------------------------------
17
+ async function runLighthouse(url) {
18
+ const result = await runShell("lighthouse", [url, "--output=json", "--quiet", "--chrome-flags=--headless"], { timeoutMs: 120_000 });
19
+ if (result.exitCode === -1 && !result.stdout && !result.stderr) {
20
+ return { scores: {}, ttfb_ms: null, findings: [], error: "Lighthouse is not installed or not found in PATH." };
21
+ }
22
+ if (!result.stdout) {
23
+ return { scores: {}, ttfb_ms: null, findings: [], error: `Lighthouse failed (exit ${result.exitCode}): ${result.stderr.slice(0, 300)}` };
24
+ }
25
+ try {
26
+ const parsed = parseLighthouseJSON(result.stdout);
27
+ const findings = [];
28
+ for (const audit of parsed.failedAudits) {
29
+ const f = formatLighthouseFinding(audit);
30
+ if (f)
31
+ findings.push(f);
32
+ }
33
+ return { scores: parsed.categoryScores, ttfb_ms: parsed.ttfbMs, findings };
34
+ }
35
+ catch (err) {
36
+ return {
37
+ scores: {}, ttfb_ms: null, findings: [],
38
+ error: `Lighthouse output could not be parsed: ${err instanceof Error ? err.message : String(err)}`,
39
+ };
40
+ }
41
+ }
42
+ async function runA11y(url) {
43
+ const result = await runShell("pa11y", [url, "--reporter", "json", "--standard", "WCAG2AA"], { timeoutMs: 120_000 });
44
+ if (result.exitCode === -1 && !result.stdout && !result.stderr) {
45
+ return { violation_count: 0, findings: [], error: "pa11y is not installed or not found in PATH." };
46
+ }
47
+ // pa11y exits 2 when violations are found — that is a successful run.
48
+ const ranOk = result.exitCode === 0 || result.exitCode === 2;
49
+ if (!ranOk || !result.stdout) {
50
+ return { violation_count: 0, findings: [], error: `pa11y failed (exit ${result.exitCode}): ${result.stderr.slice(0, 300)}` };
51
+ }
52
+ try {
53
+ const parsed = parsePa11yJSON(result.stdout);
54
+ const findings = [];
55
+ for (const v of parsed.violations) {
56
+ const f = formatA11yFinding(v);
57
+ if (f)
58
+ findings.push(f);
59
+ }
60
+ return { violation_count: findings.length, findings };
61
+ }
62
+ catch (err) {
63
+ return {
64
+ violation_count: 0, findings: [],
65
+ error: `pa11y output could not be parsed: ${err instanceof Error ? err.message : String(err)}`,
66
+ };
67
+ }
68
+ }
69
+ async function runStatic(absPath) {
70
+ const { hasConfig } = detectESLintConfig(absPath);
71
+ const eslintConfigUsed = hasConfig ? "project" : "qa-mcp-baseline";
72
+ const eslintArgs = [
73
+ ".", "--format", "json",
74
+ "--ignore-pattern", "node_modules",
75
+ "--ignore-pattern", "dist",
76
+ ];
77
+ if (!hasConfig)
78
+ eslintArgs.push("--config", BASELINE_CONFIG_PATH);
79
+ const semgrepArgs = [
80
+ "--config=p/javascript", "--config=p/typescript",
81
+ "--json", "--exclude", "node_modules", ".",
82
+ ];
83
+ // ESLint and Semgrep themselves run in parallel inside the static runner.
84
+ const [eslintResult, semgrepResult] = await Promise.all([
85
+ runShell("eslint", eslintArgs, { timeoutMs: 120_000, cwd: absPath }),
86
+ runShell("semgrep", semgrepArgs, { timeoutMs: 180_000, cwd: absPath }),
87
+ ]);
88
+ const allIssues = [];
89
+ const warnings = [];
90
+ if (eslintResult.exitCode === -1 && !eslintResult.stdout && !eslintResult.stderr) {
91
+ warnings.push("ESLint not installed — ESLint analysis skipped.");
92
+ }
93
+ else if (eslintResult.exitCode === 2) {
94
+ warnings.push(`ESLint configuration error: ${eslintResult.stderr.slice(0, 200)}`);
95
+ }
96
+ else if (eslintResult.stdout) {
97
+ try {
98
+ for (const issue of parseESLintJSON(eslintResult.stdout).issues) {
99
+ allIssues.push({ source: "eslint", file: issue.filePath, line: issue.line, column: issue.column, ruleId: issue.ruleId, message: issue.message, severity: issue.severity });
100
+ }
101
+ }
102
+ catch {
103
+ warnings.push("ESLint output could not be parsed.");
104
+ }
105
+ }
106
+ if (semgrepResult.exitCode === -1 && !semgrepResult.stdout && !semgrepResult.stderr) {
107
+ warnings.push("Semgrep not installed — Semgrep analysis skipped.");
108
+ }
109
+ else if (semgrepResult.stdout) {
110
+ try {
111
+ for (const f of parseSemgrepJSON(semgrepResult.stdout).findings) {
112
+ allIssues.push({ source: "semgrep", file: f.filePath, line: f.line, ruleId: f.ruleId, message: f.message, severity: f.severity, category: f.category });
113
+ }
114
+ }
115
+ catch {
116
+ warnings.push("Semgrep output could not be parsed.");
117
+ }
118
+ }
119
+ // Deduplicate by file:line (same logic as staticAnalysis.ts).
120
+ const dedupMap = new Map();
121
+ for (const issue of allIssues) {
122
+ const f = formatStaticAnalysisFinding(issue);
123
+ if (!f)
124
+ continue;
125
+ const key = `${String(f.evidence["file"])}:${String(f.evidence["line"])}`;
126
+ const existing = dedupMap.get(key);
127
+ if (!existing || PRIORITY_ORDER[f.priority] < PRIORITY_ORDER[existing.priority]) {
128
+ dedupMap.set(key, f);
129
+ }
130
+ }
131
+ const findings = Array.from(dedupMap.values());
132
+ return { issue_count: findings.length, eslint_config_used: eslintConfigUsed, findings, warnings };
133
+ }
134
+ // ---------------------------------------------------------------------------
135
+ // Report assembly helpers
136
+ // ---------------------------------------------------------------------------
137
+ // Option 1: four-tier readiness verdict
138
+ function buildVerdict(findings) {
139
+ if (findings.some((f) => f.priority === "P1"))
140
+ return "BLOCKED";
141
+ if (findings.some((f) => f.priority === "P2"))
142
+ return "CONDITIONAL";
143
+ if (findings.some((f) => f.priority === "P3"))
144
+ return "ADVISORY";
145
+ return "CLEAR";
146
+ }
147
+ // Option 5: severity-weighted composite score (100 = perfect health)
148
+ function buildCompositeScore(findings) {
149
+ let score = 100;
150
+ for (const f of findings) {
151
+ if (f.priority === "P1")
152
+ score -= 15;
153
+ else if (f.priority === "P2")
154
+ score -= 7;
155
+ else
156
+ score -= 3;
157
+ }
158
+ return Math.max(0, score);
159
+ }
160
+ // Option 3: compact per-tool scorecard
161
+ // Any argument may be null when the caller did not supply the corresponding
162
+ // input (url or path) — those tools show SKIPPED rather than UNAVAILABLE.
163
+ // UNAVAILABLE is reserved for tools that were attempted but failed or are not installed.
164
+ function buildScorecard(lhData, a11yData, staticData) {
165
+ const entries = [];
166
+ if (lhData === null) {
167
+ entries.push({ tool: "Lighthouse", gate: "SKIPPED" });
168
+ }
169
+ else if (lhData.error) {
170
+ entries.push({ tool: "Lighthouse", gate: "UNAVAILABLE" });
171
+ }
172
+ else {
173
+ const vals = Object.values(lhData.scores);
174
+ const avg = vals.length > 0
175
+ ? Math.round(vals.reduce((a, b) => a + b, 0) / vals.length)
176
+ : 0;
177
+ entries.push({
178
+ tool: "Lighthouse",
179
+ gate: avg < 50 ? "FAIL" : avg < 80 ? "WARN" : "PASS",
180
+ score: avg,
181
+ breakdown: lhData.scores,
182
+ });
183
+ }
184
+ if (a11yData === null) {
185
+ entries.push({ tool: "pa11y", gate: "SKIPPED" });
186
+ }
187
+ else if (a11yData.error) {
188
+ entries.push({ tool: "pa11y", gate: "UNAVAILABLE" });
189
+ }
190
+ else {
191
+ const hasP1 = a11yData.findings.some((f) => f.priority === "P1");
192
+ const hasP2 = a11yData.findings.some((f) => f.priority === "P2");
193
+ entries.push({
194
+ tool: "pa11y",
195
+ gate: hasP1 ? "FAIL" : hasP2 ? "WARN" : "PASS",
196
+ issues: a11yData.violation_count,
197
+ });
198
+ }
199
+ if (staticData === null) {
200
+ entries.push({ tool: "ESLint / Semgrep", gate: "SKIPPED" });
201
+ }
202
+ else {
203
+ const staticP1 = staticData.findings.some((f) => f.priority === "P1");
204
+ const staticP2 = staticData.findings.some((f) => f.priority === "P2");
205
+ entries.push({
206
+ tool: "ESLint / Semgrep",
207
+ gate: staticP1 ? "FAIL" : staticP2 ? "WARN" : "PASS",
208
+ issues: staticData.issue_count,
209
+ });
210
+ }
211
+ return entries;
212
+ }
213
+ function buildSummary(allFindings, toolsRan, readiness, correlationsFound) {
214
+ const n = allFindings.length;
215
+ const m = toolsRan.length;
216
+ // Priority breakdown — only mention tiers that have findings
217
+ const p1 = allFindings.filter((f) => f.priority === "P1").length;
218
+ const p2 = allFindings.filter((f) => f.priority === "P2").length;
219
+ const p3 = allFindings.filter((f) => f.priority === "P3").length;
220
+ const breakdown = ([p1 > 0 ? `${p1} P1` : "", p2 > 0 ? `${p2} P2` : "", p3 > 0 ? `${p3} P3` : ""]
221
+ .filter(Boolean)
222
+ .join(", ")) || "none";
223
+ const corrLine = correlationsFound > 0
224
+ ? ` ${correlationsFound} cross-confirmed by two tools (high confidence — see corroborated_findings).`
225
+ : "";
226
+ const readinessLine = {
227
+ BLOCKED: "Release is BLOCKED — resolve P1s before shipping.",
228
+ CONDITIONAL: "Conditionally shippable — P2 issues should be tracked.",
229
+ ADVISORY: "Advisory only — P3 issues are safe to ship, log as tech debt.",
230
+ CLEAR: "All clear — no issues detected.",
231
+ };
232
+ return `${n} finding${n !== 1 ? "s" : ""} (${breakdown}) across ${m} tool${m !== 1 ? "s" : ""}.${corrLine} ${readinessLine[readiness]}`;
233
+ }
234
+ // ---------------------------------------------------------------------------
235
+ // Tool registration
236
+ // ---------------------------------------------------------------------------
237
+ const inputShape = {
238
+ url: z
239
+ .string()
240
+ .url()
241
+ .optional()
242
+ .describe("URL of the running page to audit with Lighthouse and pa11y. " +
243
+ "Required for performance and accessibility checks. " +
244
+ "Omit when the user only has a local codebase and no running server. " +
245
+ "Accept localhost addresses, staging links, preview URLs, or production URLs."),
246
+ path: z
247
+ .string()
248
+ .optional()
249
+ .describe("Absolute or relative path to the local codebase to scan with ESLint and Semgrep. " +
250
+ "Required for static analysis and code quality checks. " +
251
+ "Omit when the user only provides a URL and has no local code to scan. " +
252
+ "Use the current working directory if the user doesn't specify one explicitly."),
253
+ context: z
254
+ .string()
255
+ .optional()
256
+ .describe("Optional free-text context about the project (e.g. 'React SPA', 'checkout flow', " +
257
+ "'marketing site'). Pass anything the user mentions about what the app is or does."),
258
+ };
259
+ export function registerQaGateTool(server) {
260
+ server.registerTool("run_qa_gate", {
261
+ description: "THE default tool to reach for whenever the user asks any of the following — " +
262
+ "or anything similar: 'health check', 'QA snapshot', 'smoke test', 'ready to ship', " +
263
+ "'any red flags', 'overall quality', 'non-functional testing', 'scan everything', " +
264
+ "'give me a report', 'how does this look', 'is this good enough to release'. " +
265
+ "url and path are BOTH OPTIONAL — provide whichever the user has. " +
266
+ "url-only: runs Lighthouse + pa11y, skips static analysis. " +
267
+ "path-only: runs ESLint + Semgrep, skips browser checks. " +
268
+ "Both: full suite. At least one must be supplied. " +
269
+ "\n\n" +
270
+ "What it does: runs Lighthouse (performance, accessibility, SEO, best-practices), " +
271
+ "pa11y (WCAG accessibility violations), ESLint (code bug-risk rules), and Semgrep " +
272
+ "(security patterns) ALL IN PARALLEL, then cross-correlates findings across tools. " +
273
+ "When two tools independently flag the same issue, that finding is promoted one " +
274
+ "priority tier (P3→P2, P2→P1) and marked as confirmed_by both tools — this is the " +
275
+ "'smoking gun' signal. " +
276
+ "\n\n" +
277
+ "Returns: release_readiness (BLOCKED / CONDITIONAL / ADVISORY / CLEAR), a composite_score " +
278
+ "(0–100 severity-weighted health score), a per-tool scorecard with gate status, a plain-English " +
279
+ "summary, top 3 issues, and a full prioritised finding list. " +
280
+ "If any individual tool is not installed, the report is still produced from the others — " +
281
+ "it never fails completely.",
282
+ inputSchema: inputShape,
283
+ }, async ({ url, path: targetPath }) => {
284
+ if (!url && !targetPath) {
285
+ return {
286
+ content: [{ type: "text", text: JSON.stringify({
287
+ error: "Provide at least one of url or path. " +
288
+ "url runs Lighthouse + pa11y; path runs ESLint + Semgrep; both runs the full suite.",
289
+ }) }],
290
+ isError: true,
291
+ };
292
+ }
293
+ // Only run the tools we have inputs for. null means deliberately skipped,
294
+ // not a failure — the scorecard will show SKIPPED for those entries.
295
+ const [lhData, a11yData, staticData] = await Promise.all([
296
+ url ? runLighthouse(url) : null,
297
+ url ? runA11y(url) : null,
298
+ targetPath ? runStatic(resolve(targetPath)) : null,
299
+ ]);
300
+ // Surface tool errors but don't abort — partial reports are still useful.
301
+ const errors = [];
302
+ if (lhData?.error)
303
+ errors.push(lhData.error);
304
+ if (a11yData?.error)
305
+ errors.push(a11yData.error);
306
+ if (staticData?.warnings)
307
+ errors.push(...staticData.warnings);
308
+ const reports = {
309
+ lighthouse: lhData && !lhData.error
310
+ ? { scores: lhData.scores, ttfb_ms: lhData.ttfb_ms, findings: lhData.findings }
311
+ : null,
312
+ a11y: a11yData && !a11yData.error
313
+ ? { violation_count: a11yData.violation_count, findings: a11yData.findings }
314
+ : null,
315
+ static: staticData
316
+ ? { issue_count: staticData.issue_count, findings: staticData.findings }
317
+ : null,
318
+ };
319
+ const { correlated_findings, unique_findings, correlations_count } = correlate(reports);
320
+ // Corroborated findings get a first-class section with an explicit
321
+ // confidence marker. Within the same priority, they sort before
322
+ // single-tool findings so top_issues naturally surfaces them first.
323
+ const corroboratedFindings = correlated_findings
324
+ .map((f) => ({ ...f, confidence: "high" }))
325
+ .sort((a, b) => PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority]);
326
+ const allFindings = [
327
+ ...correlated_findings,
328
+ ...unique_findings,
329
+ ].sort((a, b) => {
330
+ const pDiff = PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority];
331
+ if (pDiff !== 0)
332
+ return pDiff;
333
+ // Corroborated bubbles above single-tool within same priority tier
334
+ return (a.confirmed_by ? 0 : 1) - (b.confirmed_by ? 0 : 1);
335
+ });
336
+ const toolsRan = [
337
+ ...(lhData && !lhData.error ? ["lighthouse"] : []),
338
+ ...(a11yData && !a11yData.error ? ["pa11y"] : []),
339
+ ...(staticData ? ["eslint", "semgrep"] : []),
340
+ ];
341
+ const readiness = buildVerdict(allFindings);
342
+ const compositeScore = buildCompositeScore(allFindings);
343
+ const scorecard = buildScorecard(lhData, a11yData, staticData);
344
+ const summary = buildSummary(allFindings, toolsRan, readiness, correlations_count);
345
+ const report = {
346
+ release_readiness: readiness,
347
+ composite_score: compositeScore,
348
+ scorecard,
349
+ ...(staticData ? { eslint_config_used: staticData.eslint_config_used } : {}),
350
+ summary,
351
+ corroborated_findings: corroboratedFindings,
352
+ top_issues: allFindings.slice(0, 3),
353
+ all_findings: allFindings,
354
+ correlations_found: correlations_count,
355
+ };
356
+ if (errors.length > 0)
357
+ report["errors"] = errors;
358
+ // Generate a self-contained HTML report the user can open in a browser.
359
+ try {
360
+ const reportPath = await generateHtmlReport({
361
+ url,
362
+ path: targetPath,
363
+ release_readiness: readiness,
364
+ composite_score: compositeScore,
365
+ scorecard,
366
+ summary,
367
+ corroborated_findings: corroboratedFindings,
368
+ all_findings: allFindings,
369
+ correlations_found: correlations_count,
370
+ errors: errors.length > 0 ? errors : undefined,
371
+ generated_at: new Date().toISOString().replace("T", " ").slice(0, 19) + " UTC",
372
+ });
373
+ report["report_file"] = `file://${reportPath}`;
374
+ }
375
+ catch {
376
+ // Non-fatal — JSON report is still returned if HTML generation fails.
377
+ }
378
+ return {
379
+ content: [{ type: "text", text: JSON.stringify(report, null, 2) }],
380
+ };
381
+ });
382
+ }
383
+ //# sourceMappingURL=qaGate.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"qaGate.js","sourceRoot":"","sources":["../../src/tools/qaGate.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 registerStaticAnalysisTool(server: McpServer): void;
@@ -0,0 +1,172 @@
1
+ import { z } from "zod";
2
+ import { fileURLToPath } from "url";
3
+ import { resolve } from "path";
4
+ import { runShell } from "../utils/shellRunner.js";
5
+ import { parseESLintJSON, parseSemgrepJSON } from "../utils/outputParsers.js";
6
+ import { detectESLintConfig } from "../utils/eslintConfigDetector.js";
7
+ import { formatStaticAnalysisFinding, } from "../mappers/defectFormatter.js";
8
+ const BASELINE_CONFIG_PATH = fileURLToPath(new URL("../config/qa-mcp-baseline.eslint.config.js", import.meta.url));
9
+ const PRIORITY_ORDER = { P1: 0, P2: 1, P3: 2 };
10
+ function higherPriority(a, b) {
11
+ return PRIORITY_ORDER[a.priority] <= PRIORITY_ORDER[b.priority] ? a : b;
12
+ }
13
+ function isNotInstalled(stderr, stdout, exitCode) {
14
+ if (exitCode !== -1)
15
+ return false;
16
+ // When execa fails with ENOENT (command not found), the process never starts
17
+ // so stderr is an empty string — we can't rely on string-matching alone.
18
+ if (!stderr && !stdout)
19
+ return true;
20
+ return (stderr.includes("ENOENT") ||
21
+ stderr.includes("not found") ||
22
+ stderr.includes("command not found") ||
23
+ stderr.includes("No such file"));
24
+ }
25
+ const inputShape = {
26
+ path: z.string().describe("Absolute or relative path to the directory to analyse"),
27
+ ruleset: z
28
+ .string()
29
+ .optional()
30
+ .describe("Semgrep ruleset override (default: p/javascript + p/typescript)"),
31
+ language: z.enum(["js", "ts", "python"]).optional().describe("Language hint"),
32
+ };
33
+ export function registerStaticAnalysisTool(server) {
34
+ server.registerTool("run_static_analysis", {
35
+ description: "Runs ESLint and Semgrep in parallel against a local directory and returns a " +
36
+ "QA-style report with prioritised findings (P1/P2/P3). " +
37
+ "Requires ESLint and Semgrep to be installed on PATH. " +
38
+ "When the target directory has no ESLint config the QA MCP baseline is used as fallback; " +
39
+ "eslint_config_used in the response always states which config was applied.",
40
+ inputSchema: inputShape,
41
+ }, async ({ path: targetPath, ruleset }) => {
42
+ const absPath = resolve(targetPath);
43
+ // --- Decide ESLint config strategy ---
44
+ const { hasConfig } = detectESLintConfig(absPath);
45
+ const eslintConfigUsed = hasConfig ? "project" : "qa-mcp-baseline";
46
+ // ESLint flat config resolves `files` globs relative to cwd, not the
47
+ // config file location. Always lint "." and run from absPath so that
48
+ // "**/*.js" correctly matches files inside the target directory.
49
+ const eslintArgs = [".", "--format", "json"];
50
+ if (!hasConfig) {
51
+ eslintArgs.push("--config", BASELINE_CONFIG_PATH);
52
+ }
53
+ eslintArgs.push("--ignore-pattern", "node_modules", "--ignore-pattern", "dist");
54
+ // --- Semgrep args ---
55
+ const semgrepRulesets = ruleset
56
+ ? [`--config=${ruleset}`]
57
+ : ["--config=p/javascript", "--config=p/typescript"];
58
+ const semgrepArgs = [
59
+ ...semgrepRulesets,
60
+ "--json",
61
+ "--exclude",
62
+ "node_modules",
63
+ ".",
64
+ ];
65
+ // Run both tools in parallel, rooted at the target directory.
66
+ const [eslintResult, semgrepResult] = await Promise.all([
67
+ runShell("eslint", eslintArgs, { timeoutMs: 120_000, cwd: absPath }),
68
+ runShell("semgrep", semgrepArgs, { timeoutMs: 180_000, cwd: absPath }),
69
+ ]);
70
+ const toolsRun = [];
71
+ const warnings = [];
72
+ const allIssues = [];
73
+ // --- ESLint ---
74
+ // Exit 0 = no issues, exit 1 = issues found, exit 2 = config/fatal error.
75
+ // Exit -1 = tool not found or timeout.
76
+ if (isNotInstalled(eslintResult.stderr, eslintResult.stdout, eslintResult.exitCode)) {
77
+ warnings.push("ESLint is not installed or not found in PATH — skipping ESLint analysis.");
78
+ }
79
+ else if (eslintResult.exitCode === 2) {
80
+ warnings.push(`ESLint configuration error: ${eslintResult.stderr.slice(0, 500)}`);
81
+ }
82
+ else if (eslintResult.stdout) {
83
+ try {
84
+ const parsed = parseESLintJSON(eslintResult.stdout);
85
+ for (const issue of parsed.issues) {
86
+ allIssues.push({
87
+ source: "eslint",
88
+ file: issue.filePath,
89
+ line: issue.line,
90
+ column: issue.column,
91
+ ruleId: issue.ruleId,
92
+ message: issue.message,
93
+ severity: issue.severity,
94
+ });
95
+ }
96
+ toolsRun.push("eslint");
97
+ }
98
+ catch {
99
+ warnings.push("Failed to parse ESLint JSON output.");
100
+ }
101
+ }
102
+ else {
103
+ // Exit 0 with no stdout = no files matched (e.g. only TS files with JS-only baseline).
104
+ toolsRun.push("eslint");
105
+ }
106
+ // --- Semgrep ---
107
+ // Exit 0 = no matches, exit 1 = matches found, exit 2 = error.
108
+ if (isNotInstalled(semgrepResult.stderr, semgrepResult.stdout, semgrepResult.exitCode)) {
109
+ warnings.push("Semgrep is not installed or not found in PATH — skipping Semgrep analysis.");
110
+ }
111
+ else if (semgrepResult.exitCode === 2) {
112
+ warnings.push(`Semgrep error: ${semgrepResult.stderr.slice(0, 500)}`);
113
+ }
114
+ else if (semgrepResult.stdout) {
115
+ try {
116
+ const parsed = parseSemgrepJSON(semgrepResult.stdout);
117
+ for (const finding of parsed.findings) {
118
+ allIssues.push({
119
+ source: "semgrep",
120
+ file: finding.filePath,
121
+ line: finding.line,
122
+ ruleId: finding.ruleId,
123
+ message: finding.message,
124
+ severity: finding.severity,
125
+ category: finding.category,
126
+ });
127
+ }
128
+ toolsRun.push("semgrep");
129
+ }
130
+ catch {
131
+ warnings.push("Failed to parse Semgrep JSON output.");
132
+ }
133
+ }
134
+ else {
135
+ toolsRun.push("semgrep");
136
+ }
137
+ // --- Format findings ---
138
+ const formatted = [];
139
+ for (const issue of allIssues) {
140
+ const finding = formatStaticAnalysisFinding(issue);
141
+ if (finding)
142
+ formatted.push(finding);
143
+ }
144
+ // --- Deduplicate: same file:line → keep highest priority ---
145
+ const dedupMap = new Map();
146
+ for (const f of formatted) {
147
+ const key = `${String(f.evidence["file"])}:${String(f.evidence["line"])}`;
148
+ const existing = dedupMap.get(key);
149
+ dedupMap.set(key, existing ? higherPriority(existing, f) : f);
150
+ }
151
+ // --- Sort: priority first, then file path ---
152
+ const findings = Array.from(dedupMap.values()).sort((a, b) => {
153
+ const pDiff = PRIORITY_ORDER[a.priority] - PRIORITY_ORDER[b.priority];
154
+ if (pDiff !== 0)
155
+ return pDiff;
156
+ return String(a.evidence["file"]).localeCompare(String(b.evidence["file"]));
157
+ });
158
+ const report = {
159
+ path: absPath,
160
+ tools_run: toolsRun,
161
+ eslint_config_used: eslintConfigUsed,
162
+ issue_count: findings.length,
163
+ findings,
164
+ };
165
+ if (warnings.length > 0)
166
+ report["warnings"] = warnings;
167
+ return {
168
+ content: [{ type: "text", text: JSON.stringify(report, null, 2) }],
169
+ };
170
+ });
171
+ }
172
+ //# sourceMappingURL=staticAnalysis.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"staticAnalysis.js","sourceRoot":"","sources":["../../src/tools/staticAnalysis.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,7 @@
1
+ export type Priority = "P1" | "P2" | "P3";
2
+ export interface Finding {
3
+ priority: Priority;
4
+ title: string;
5
+ description: string;
6
+ evidence: Record<string, unknown>;
7
+ }
package/dist/types.js ADDED
@@ -0,0 +1,2 @@
1
+ export {};
2
+ //# sourceMappingURL=types.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"types.js","sourceRoot":"","sources":["../src/types.ts"],"names":[],"mappings":""}
@@ -0,0 +1,5 @@
1
+ export interface ESLintConfigResult {
2
+ hasConfig: boolean;
3
+ configFile: string | null;
4
+ }
5
+ export declare function detectESLintConfig(targetPath: string): ESLintConfigResult;
@@ -0,0 +1,21 @@
1
+ import { existsSync } from "fs";
2
+ import { join } from "path";
3
+ // Checked in priority order: flat config first, then legacy formats.
4
+ const CONFIG_FILES = [
5
+ "eslint.config.js",
6
+ "eslint.config.mjs",
7
+ ".eslintrc.js",
8
+ ".eslintrc.json",
9
+ ".eslintrc.yml",
10
+ ".eslintrc.yaml",
11
+ ".eslintrc",
12
+ ];
13
+ export function detectESLintConfig(targetPath) {
14
+ for (const file of CONFIG_FILES) {
15
+ if (existsSync(join(targetPath, file))) {
16
+ return { hasConfig: true, configFile: file };
17
+ }
18
+ }
19
+ return { hasConfig: false, configFile: null };
20
+ }
21
+ //# sourceMappingURL=eslintConfigDetector.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"eslintConfigDetector.js","sourceRoot":"","sources":["../../src/utils/eslintConfigDetector.ts"],"names":[],"mappings":"AAAA,OAAO,EAAE,UAAU,EAAE,MAAM,IAAI,CAAC;AAChC,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAE5B,qEAAqE;AACrE,MAAM,YAAY,GAAG;IACnB,kBAAkB;IAClB,mBAAmB;IACnB,cAAc;IACd,gBAAgB;IAChB,eAAe;IACf,gBAAgB;IAChB,WAAW;CACZ,CAAC;AAOF,MAAM,UAAU,kBAAkB,CAAC,UAAkB;IACnD,KAAK,MAAM,IAAI,IAAI,YAAY,EAAE,CAAC;QAChC,IAAI,UAAU,CAAC,IAAI,CAAC,UAAU,EAAE,IAAI,CAAC,CAAC,EAAE,CAAC;YACvC,OAAO,EAAE,SAAS,EAAE,IAAI,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;QAC/C,CAAC;IACH,CAAC;IACD,OAAO,EAAE,SAAS,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE,CAAC;AAChD,CAAC"}