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.
- package/README.md +34 -37
- package/index.js +141 -21
- 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
|
|
18
|
-
| `log_rejected_pattern` | Course Correction | Records bad patterns to `.rejected-patterns.json` so they
|
|
19
|
-
| `run_pre_pr_checks` | PR Handoff |
|
|
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.
|
|
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
|
|
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 |
|
|
222
|
+
| `skip_checks` | array | No | `spec_boxes`, `console_logs`, `hardcoded_urls`, `flyway_migrations` |
|
|
227
223
|
|
|
228
|
-
**
|
|
224
|
+
**Guardrail checks:**
|
|
229
225
|
|
|
230
|
-
| Check |
|
|
226
|
+
| Check | Fails when |
|
|
231
227
|
|---|---|
|
|
232
|
-
| **SPEC** | `.current-task-spec.md` is missing
|
|
233
|
-
| **
|
|
234
|
-
| **SECURITY** | Hardcoded
|
|
235
|
-
| **FLYWAY** | `@Entity`-annotated Java files exist but no `V*__.sql` Flyway
|
|
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
|
-
❌ [
|
|
542
|
-
•
|
|
543
|
-
•
|
|
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
|
|
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
|
-
-
|
|
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
|
-
-
|
|
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
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
-
|
|
612
|
-
-
|
|
613
|
-
-
|
|
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
|
|
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.
|
|
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
|
|
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
|
-
|
|
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
|
|
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
|
|
199
|
-
"
|
|
200
|
-
"
|
|
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
|
|
383
|
-
"
|
|
384
|
-
"and Flyway migration coverage for JPA entities. " +
|
|
385
|
-
"
|
|
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.
|
|
510
|
+
// ── 2. AST-level linting (Workspace Discovery → ESLint/Checkstyle → regex fallback)
|
|
426
511
|
if (!skip_checks.includes("console_logs")) {
|
|
427
|
-
|
|
428
|
-
if (
|
|
429
|
-
|
|
430
|
-
|
|
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
|
-
|
|
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("⚠️ [
|
|
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) {
|