secufusion-mcp 1.0.4 → 1.0.5

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 (3) hide show
  1. package/README.md +34 -37
  2. package/index.js +141 -21
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,9 +14,9 @@
14
14
 
15
15
  | Tool | Phase | What it does |
16
16
  |---|---|---|
17
- | `manage_feature_spec` | Planning & Execution | Creates/updates a `.current-task-spec.md` blueprint for every feature |
18
- | `log_rejected_pattern` | Course Correction | Records bad patterns to `.rejected-patterns.json` so they're never repeated |
19
- | `run_pre_pr_checks` | PR Handoff | Scans the codebase and gates PRs with automated guardrail validation |
17
+ | `manage_feature_spec` | Planning & Execution | Creates/updates a `.current-task-spec.md` blueprint (works for single-repo or cross-repo tasks) |
18
+ | `log_rejected_pattern` | Course Correction | Records bad patterns to `.rejected-patterns.json` so they are never repeated |
19
+ | `run_pre_pr_checks` | PR Handoff | Discovers modified microservices across the workspace and gates PRs via AST-level linters + structural checks |
20
20
 
21
21
  ---
22
22
 
@@ -213,26 +213,22 @@ This writes to `.rejected-patterns.json`:
213
213
 
214
214
  ---
215
215
 
216
- ### 3. `run_pre_pr_checks`
217
-
218
- Scans your entire codebase and produces a pass/fail report. **Must pass before raising a PR.**
219
-
220
- **Parameters:**
216
+ ### 3. run_pre_pr_checks
221
217
 
222
218
  | Parameter | Type | Required | Description |
223
219
  |---|---|---|---|
224
- | `work_item_id` | string | Yes | Azure DevOps work item ID for the PR summary |
220
+ | `work_item_id` | string | Yes | Azure DevOps work item ID |
225
221
  | `root_dir` | string | No | Directory to scan (defaults to cwd) |
226
- | `skip_checks` | array | No | Checks to bypass: `spec_boxes`, `console_logs`, `hardcoded_urls`, `flyway_migrations` |
222
+ | `skip_checks` | array | No | `spec_boxes`, `console_logs`, `hardcoded_urls`, `flyway_migrations` |
227
223
 
228
- **Checks performed:**
224
+ **Guardrail checks:**
229
225
 
230
- | Check | What fails it |
226
+ | Check | Fails when |
231
227
  |---|---|
232
- | **SPEC** | `.current-task-spec.md` is missing, or has any unchecked `- [ ]` boxes |
233
- | **LOGGING** | Any `console.log()` in `.ts/.tsx/.js/.jsx` files, or `System.out.println()` in `.java` files |
234
- | **SECURITY** | Hardcoded IP addresses or `uat.*` / `prod.*` / `staging.*` URLs in source or config files |
235
- | **FLYWAY** | `@Entity`-annotated Java files exist but no `V*__.sql` Flyway migration files are found |
228
+ | **SPEC** | `.current-task-spec.md` is missing or has unchecked `- [ ]` boxes |
229
+ | **LINTING** | AST linters (ESLint, Checkstyle) fail in any discovered microservice. Fix natively, do not suppress! (Falls back to regex if no linters exist) |
230
+ | **SECURITY** | Hardcoded IPs or `uat.*` / `prod.*` / `staging.*` URLs in source or config files |
231
+ | **FLYWAY** | `@Entity`-annotated Java files exist but no `V*__.sql` Flyway migrations found |
236
232
 
237
233
  **Example — Run checks before PR:**
238
234
 
@@ -538,9 +534,9 @@ Timestamp: 2026-08-26T16:00:00.000Z
538
534
  • Flyway SQL migration created for every modified JPA @Entity
539
535
  • API contract (OpenAPI / TS types) updated if endpoints changed
540
536
 
541
- ❌ [LOGGING] Found 2 prohibited log statement(s):
542
- console.log() found at src/components/MfaSetup.tsx:47
543
- System.out.println() found at src/main/java/MfaService.java:112
537
+ ❌ [LINTING] 2 linter error(s) across workspace — fix natively in their respective microservices:
538
+ [ESLint] apps/admin-dashboard/src/components/MfaSetup.tsx:47 — Unexpected console statement (no-console)
539
+ [Checkstyle/Maven] Build failed: [ERROR] AuditService.java:[112] Line contains System.out.println
544
540
 
545
541
  ## Passed
546
542
  ✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
@@ -585,32 +581,33 @@ Create `.agents/AGENTS.md` in your project root:
585
581
  ```markdown
586
582
  # SecuFusion MCP Workflow Rules
587
583
 
588
- You are an elite Senior Developer working on the SecuFusion platform.
589
- You have access to the `secufusion-mcp` MCP server. Follow these rules strictly.
584
+ You are an elite Senior Developer and Architect working on the SecuFusion workspace (containing multiple microservice repositories). You prioritize robust cross-service architecture, zero-trust security (tenant isolation), and flawless state management. You rely on standard AST-aware build tools (ESLint/Checkstyle) for code hygiene. You strictly follow project standards and always use the custom MCP tools provided.
590
585
 
591
586
  ## Phase 1 — Planning (TRIGGER: user assigns a task or work item)
592
- - Do NOT write code immediately.
593
- - Call `manage_feature_spec` with `action=create`, passing the task description and `work_item_id`.
587
+ - Do NOT write code immediately. First, call `manage_feature_spec`.
588
+ - Determine which microservices are likely affected. Pass a `reference_file_path` to observe exact coding patterns.
589
+ - Pass the user's requirements into `task_description` to generate the workspace-level `.current-task-spec.md` blueprint.
594
590
  - Wait for the spec to be generated before writing any code.
595
591
 
596
592
  ## Phase 2 — Execution (TRIGGER: as you complete work)
597
- - Keep `.current-task-spec.md` updated as your source of truth.
598
- - When you finish a logical chunk, call `manage_feature_spec` with `action=update`.
599
- - If resuming a session, always call `manage_feature_spec` with `action=read` first.
593
+ - Keep the `.current-task-spec.md` file updated as your source of truth.
594
+ - Whenever you finish a logical chunk, call `manage_feature_spec` with `update_content` to check off the `[ ]` boxes to `[x]`.
595
+ - If you are resuming a session, always call `manage_feature_spec` with `action=read` first to know exactly where you left off.
600
596
 
601
597
  ## Phase 3 — Course Correction (TRIGGER: user corrects you or rejects an approach)
602
598
  - Immediately call `log_rejected_pattern`.
603
- - Pass the bad `pattern` and the `reason` the user provided.
604
-
605
- ## Phase 4 — PR Handoff (TRIGGER: user says "prepare PR", "run checks", "ready to ship")
606
- - Call `run_pre_pr_checks` with the `work_item_id`.
607
- - Fix any errors and run again until all checks are green.
608
-
609
- ## Guardrails (enforce always)
610
- - Always scope DB queries and event payloads with `tenantId`.
611
- - Never use `console.log` (JS/TS) or `System.out.println` (Java).
612
- - Never hardcode UAT/Prod IPs or environment URLs.
613
- - If you modify a JPA `@Entity`, create the corresponding Flyway `.sql` migration.
599
+ - Pass the bad `pattern` you used and the `reason`/correction the user provided.
600
+ - Always check `.rejected-patterns.json` implicitly before suggesting architectural choices to ensure you never repeat past mistakes.
601
+
602
+ ## Phase 4 PR Handoff (TRIGGER: user says "prepare PR", "finish up", or "run checks")
603
+ - Call `run_pre_pr_checks` with the Azure DevOps `work_item_id`. The tool will automatically discover all modified microservices in the workspace and run their native linters.
604
+ - If the tool throws an error for a specific repository (e.g., missing Flyway SQL migration, or a linter failing due to console logs/bad formatting), YOU MUST navigate to that specific microservice, FIX THE ERROR in the codebase, and run the tool again until the entire workspace passes. Do not suppress linter warnings.
605
+
606
+ ## Guardrails (enforce always, no exceptions)
607
+ - All DB queries and event payloads MUST be scoped with `tenantId` unless the spec explicitly notes it as a global/system operation.
608
+ - Do not ignore linter errors. AST-level tools (ESLint, Checkstyle/Maven) are the source of truth for hygiene. Fix them natively.
609
+ - Never hardcode UAT/Prod IPs or URLs. Use environment variables or configuration properties.
610
+ - If you modify a JPA `@Entity` in any backend repo, you MUST create the corresponding Flyway `.sql` migration script before running PR checks.
614
611
  ```
615
612
 
616
613
  ### How it all wires together
package/index.js CHANGED
@@ -5,11 +5,11 @@
5
5
  * Provides developer-workflow tooling for the SecuFusion platform:
6
6
  * - manage_feature_spec : create / update task blueprints (.current-task-spec.md)
7
7
  * - log_rejected_pattern : record bad patterns so they are never repeated (.rejected-patterns.json)
8
- * - run_pre_pr_checks : gate PRs with automated guardrail validation
8
+ * - run_pre_pr_checks : gate PRs via AST-level linters (ESLint / Maven Checkstyle) + structural checks
9
9
  *
10
10
  * Guardrails enforced:
11
11
  * 1. All DB queries and event payloads must carry tenantId.
12
- * 2. No console.log (TS/JS) or System.out.println (Java) in source files.
12
+ * 2. Code hygiene (console.log, unused vars, formatting) enforced via ESLint / Checkstyle — not regex.
13
13
  * 3. No hardcoded UAT/Prod IPs or environment URLs.
14
14
  * 4. Every modified JPA @Entity must have a matching Flyway SQL migration.
15
15
  */
@@ -18,6 +18,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
18
18
  import { z } from "zod";
19
19
  import fs from "fs";
20
20
  import path from "path";
21
+ import { execSync } from "child_process";
21
22
  // ─────────────────────────────────────────────
22
23
  // Constants / helpers
23
24
  // ─────────────────────────────────────────────
@@ -93,7 +94,7 @@ function appendSessionLog(existing, update) {
93
94
  // ─────────────────────────────────────────────
94
95
  // run_pre_pr_checks helpers
95
96
  // ─────────────────────────────────────────────
96
- const CONSOLE_LOG_PATTERNS = [
97
+ const CONSOLE_LOG_REGEX = [
97
98
  { regex: /console\.log\s*\(/g, label: "console.log()", ext: [".ts", ".tsx", ".js", ".jsx"] },
98
99
  { regex: /System\.out\.println\s*\(/g, label: "System.out.println()", ext: [".java"] },
99
100
  ];
@@ -127,9 +128,71 @@ function checkSpecFile() {
127
128
  }
128
129
  return { unchecked, missing: false };
129
130
  }
130
- function checkConsoleLogs(rootDir) {
131
+ // ── AST-level linting via ESLint (preferred over regex for JS/TS) ─────────────
132
+ function runEslint(rootDir) {
133
+ // Check if ESLint is configured in the project
134
+ const eslintConfigs = [".eslintrc", ".eslintrc.js", ".eslintrc.json", ".eslintrc.yml",
135
+ ".eslintrc.yaml", "eslint.config.js", "eslint.config.mjs"];
136
+ const hasEslintConfig = eslintConfigs.some((f) => fs.existsSync(path.join(rootDir, f)));
137
+ const hasPkgEslint = (() => {
138
+ const pkg = readFileSafe(path.join(rootDir, "package.json"));
139
+ if (!pkg)
140
+ return false;
141
+ try {
142
+ return !!(JSON.parse(pkg).eslintConfig);
143
+ }
144
+ catch {
145
+ return false;
146
+ }
147
+ })();
148
+ if (!hasEslintConfig && !hasPkgEslint)
149
+ return { violations: [], available: false };
150
+ try {
151
+ execSync("npx eslint . --format json --max-warnings=0 --no-error-on-unmatched-pattern", { cwd: rootDir, stdio: "pipe" });
152
+ return { violations: [], available: true };
153
+ }
154
+ catch (e) {
155
+ const raw = e.stdout?.toString() ?? "";
156
+ try {
157
+ const results = JSON.parse(raw);
158
+ const violations = [];
159
+ for (const file of results) {
160
+ for (const msg of file.messages) {
161
+ if (msg.severity > 0) {
162
+ violations.push(`[ESLint] ${path.relative(rootDir, file.filePath)}:${msg.line} — ${msg.message}${msg.ruleId ? ` (${msg.ruleId})` : ""}`);
163
+ }
164
+ }
165
+ }
166
+ return { violations, available: true };
167
+ }
168
+ catch {
169
+ // ESLint output not JSON — return raw stderr summary
170
+ const stderr = e.stderr?.toString() ?? "";
171
+ return { violations: stderr ? [`[ESLint] ${stderr.slice(0, 400)}`] : [], available: true };
172
+ }
173
+ }
174
+ }
175
+ // ── Maven + Checkstyle for Java/Spring projects ───────────────────────────────
176
+ function runCheckstyle(rootDir) {
177
+ const hasPom = fs.existsSync(path.join(rootDir, "pom.xml"));
178
+ if (!hasPom)
179
+ return { violations: [], available: false };
180
+ try {
181
+ execSync("mvn checkstyle:check -q --no-transfer-progress", { cwd: rootDir, stdio: "pipe" });
182
+ return { violations: [], available: true };
183
+ }
184
+ catch (e) {
185
+ const output = ((e.stdout?.toString() ?? "") + (e.stderr?.toString() ?? "")).slice(0, 1200);
186
+ return {
187
+ violations: [`[Checkstyle/Maven] Build failed:\n${output}`],
188
+ available: true,
189
+ };
190
+ }
191
+ }
192
+ // ── Regex fallback — used only when no linter is configured ──────────────────
193
+ function checkConsoleLogsRegex(rootDir) {
131
194
  const violations = [];
132
- for (const { regex, label, ext } of CONSOLE_LOG_PATTERNS) {
195
+ for (const { regex, label, ext } of CONSOLE_LOG_REGEX) {
133
196
  const files = walkDir(rootDir, ext);
134
197
  for (const file of files) {
135
198
  const content = readFileSafe(file);
@@ -161,6 +224,28 @@ function checkHardcodedUrls(rootDir) {
161
224
  }
162
225
  return violations;
163
226
  }
227
+ // ── Workspace / Microservice Discovery ───────────────────────────────────────
228
+ function findProjectRoots(dir, depth = 0) {
229
+ if (depth > 3)
230
+ return []; // Limit depth to avoid scanning massive trees
231
+ if (!fs.existsSync(dir))
232
+ return [];
233
+ const entries = fs.readdirSync(dir, { withFileTypes: true });
234
+ const hasPackageJson = entries.some(e => e.name === "package.json");
235
+ const hasPomXml = entries.some(e => e.name === "pom.xml");
236
+ const roots = [];
237
+ if (hasPackageJson || hasPomXml) {
238
+ roots.push(dir);
239
+ }
240
+ for (const entry of entries) {
241
+ if (entry.isDirectory()) {
242
+ if (["node_modules", ".git", "dist", "build", "target", ".idea"].includes(entry.name))
243
+ continue;
244
+ roots.push(...findProjectRoots(path.join(dir, entry.name), depth + 1));
245
+ }
246
+ }
247
+ return [...new Set(roots)];
248
+ }
164
249
  function checkFlywayMigrations(rootDir) {
165
250
  const warnings = [];
166
251
  const javaFiles = walkDir(rootDir, [".java"]);
@@ -195,10 +280,9 @@ const server = new McpServer({
195
280
  version: "1.0.0",
196
281
  });
197
282
  // ─── Tool 1: manage_feature_spec ─────────────────────────────────────────────
198
- server.tool("manage_feature_spec", "Create or update the .current-task-spec.md task blueprint. " +
199
- "Pass `task_description` to create a new spec, or `update_content` to patch the existing one " +
200
- "(e.g., checking off completed acceptance criteria). Optionally provide `reference_file_path` " +
201
- "so the spec generator can observe existing coding patterns.", {
283
+ server.tool("manage_feature_spec", "Create or update the .current-task-spec.md blueprint for the current feature. " +
284
+ "Use this during the Planning and Execution phases. " +
285
+ "Works for both single-repo and cross-repo workspace tasks.", {
202
286
  task_description: z
203
287
  .string()
204
288
  .optional()
@@ -379,10 +463,11 @@ server.tool("log_rejected_pattern", "Record a coding pattern that was rejected b
379
463
  };
380
464
  });
381
465
  // ─── Tool 3: run_pre_pr_checks ───────────────────────────────────────────────
382
- server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks against the current working directory. " +
383
- "Validates: spec checkboxes, console.log/System.out.println usage, hardcoded IPs/URLs, " +
384
- "and Flyway migration coverage for JPA entities. " +
385
- "MUST pass (exit with no errors) before a PR is raised.", {
466
+ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks across the workspace. " +
467
+ "Automatically discovers modified microservices and runs their native linters (ESLint, Maven Checkstyle). " +
468
+ "Also validates: spec checkboxes, hardcoded IPs/URLs, and Flyway migration coverage for JPA entities. " +
469
+ "Linter errors must be fixed natively do not suppress warnings to pass the gate. " +
470
+ "MUST pass with zero errors before a PR is raised.", {
386
471
  work_item_id: z
387
472
  .string()
388
473
  .describe("Azure DevOps work item ID to include in the PR summary."),
@@ -422,21 +507,56 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks aga
422
507
  else {
423
508
  warnings.push("⚠️ [SPEC] Spec checkbox check SKIPPED (manually bypassed).");
424
509
  }
425
- // ── 2. Console log check ───────────────────────────────────────────────
510
+ // ── 2. AST-level linting (Workspace Discovery → ESLint/Checkstyle → regex fallback)
426
511
  if (!skip_checks.includes("console_logs")) {
427
- const logViolations = checkConsoleLogs(scanRoot);
428
- if (logViolations.length > 0) {
429
- errors.push(`❌ [LOGGING] Found ${logViolations.length} prohibited log statement(s):\n` +
430
- logViolations.map((v) => ` • ${v}`).join("\n"));
512
+ let projectRoots = findProjectRoots(scanRoot);
513
+ if (projectRoots.length === 0)
514
+ projectRoots.push(scanRoot);
515
+ let anyLinterAvailable = false;
516
+ const allLintViolations = [];
517
+ const usedLinters = new Set();
518
+ for (const pRoot of projectRoots) {
519
+ const eslint = runEslint(pRoot);
520
+ const checkstyle = runCheckstyle(pRoot);
521
+ if (eslint.available) {
522
+ anyLinterAvailable = true;
523
+ usedLinters.add("ESLint");
524
+ allLintViolations.push(...eslint.violations);
525
+ }
526
+ if (checkstyle.available) {
527
+ anyLinterAvailable = true;
528
+ usedLinters.add("Checkstyle");
529
+ allLintViolations.push(...checkstyle.violations);
530
+ }
531
+ }
532
+ if (anyLinterAvailable) {
533
+ // AST linters found and ran — they are the source of truth
534
+ if (allLintViolations.length > 0) {
535
+ errors.push(`❌ [LINTING] ${allLintViolations.length} linter error(s) across workspace — fix natively in their respective microservices:\n` +
536
+ allLintViolations.map((v) => ` • ${v}`).join("\n"));
537
+ }
538
+ else {
539
+ passed.push(`✅ [LINTING] AST-level linting passed across workspace (${Array.from(usedLinters).join(" + ")}).`);
540
+ }
431
541
  }
432
542
  else {
433
- passed.push("✅ [LOGGING] No console.log / System.out.println found.");
543
+ // No linters configured fall back to regex scan with a warning
544
+ const regexViolations = checkConsoleLogsRegex(scanRoot);
545
+ if (regexViolations.length > 0) {
546
+ errors.push(`❌ [LOGGING] ${regexViolations.length} prohibited log statement(s) found via regex fallback\n` +
547
+ ` (No ESLint/Checkstyle config detected in any workspace project — configure a linter for AST-level accuracy):\n` +
548
+ regexViolations.map((v) => ` • ${v}`).join("\n"));
549
+ }
550
+ else {
551
+ warnings.push("⚠️ [LOGGING] No ESLint/Checkstyle config found in workspace — used regex fallback. " +
552
+ "Consider adding a linter for AST-level hygiene.");
553
+ }
434
554
  }
435
555
  }
436
556
  else {
437
- warnings.push("⚠️ [LOGGING] Console log check SKIPPED (manually bypassed).");
557
+ warnings.push("⚠️ [LINTING] Linter check SKIPPED (manually bypassed).");
438
558
  }
439
- // ── 3. Hardcoded URL check ─────────────────────────────────────────────
559
+ // ── 3. Hardcoded URL check (regex — no AST equivalent needed here) ───────
440
560
  if (!skip_checks.includes("hardcoded_urls")) {
441
561
  const urlViolations = checkHardcodedUrls(scanRoot);
442
562
  if (urlViolations.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secufusion-mcp",
3
- "version": "1.0.4",
3
+ "version": "1.0.5",
4
4
  "type": "module",
5
5
  "description": "SecuFusion MCP server - developer workflow tooling with guardrails",
6
6
  "main": "index.js",