secufusion-mcp 1.0.5 → 1.0.7

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 +67 -207
  2. package/index.js +152 -166
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -14,7 +14,7 @@
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 (works for single-repo or cross-repo tasks) |
17
+ | `manage_branch_state` | Planning & Execution | Manages a `.secufusion-state.json` tracker tied to the active Git branch |
18
18
  | `log_rejected_pattern` | Course Correction | Records bad patterns to `.rejected-patterns.json` so they are never repeated |
19
19
  | `run_pre_pr_checks` | PR Handoff | Discovers modified microservices across the workspace and gates PRs via AST-level linters + structural checks |
20
20
 
@@ -102,69 +102,40 @@ Open Cline settings → MCP Servers → Add:
102
102
 
103
103
  ## Tools Reference
104
104
 
105
- ### 1. `manage_feature_spec`
105
+ ### 1. manage_branch_state
106
106
 
107
- Creates or updates the `.current-task-spec.md` file your single source of truth for every task. The AI reads this before writing any code.
107
+ Manages a structured JSON state file (`.secufusion-state.json`) keyed to the current Git branch. This replaces unstructured Markdown parsing and ensures the AI can resume perfectly.
108
108
 
109
109
  **Parameters:**
110
110
 
111
111
  | Parameter | Type | Required | Description |
112
112
  |---|---|---|---|
113
- | `action` | `"create"` \| `"update"` \| `"read"` | Yes | What to do with the spec |
114
- | `task_description` | string | When `action=create` | Full description of the task/feature |
115
- | `update_content` | string | When `action=update` | Markdown lines to merge (e.g. tick checkboxes) |
116
- | `work_item_id` | string | No | Azure DevOps work item ID |
117
- | `reference_file_path` | string | No | Path to an existing source file to extract coding patterns from |
118
-
119
- **Example — Create a spec:**
120
-
121
- ```
122
- Ask your AI assistant:
123
- "Start working on WI-1042: Add tenant-scoped audit log export to CSV.
124
- Reference file: src/services/AuditService.java"
125
- ```
126
-
127
- The AI will call:
113
+ | `action` | string | Yes | `initialize`, `read`, or `update` |
114
+ | `task_description` | string | No | (For `initialize`) Summary of the work |
115
+ | `pending_acs` | array | No | Array of strings for pending tasks (For `initialize`/`update`) |
116
+ | `completed_acs` | array | No | Array of completed AC strings (For `update`) |
117
+ | `next_step` | string | No | **CRITICAL for `update`**: A clear instruction on what to do next to allow instant resumption. |
118
+ | `reference_file_path` | string | No | Path to a reference file with standard coding patterns |
119
+
120
+ **Example — Starting a new task:**
128
121
  ```json
129
122
  {
130
- "action": "create",
131
- "task_description": "Add tenant-scoped audit log export to CSV...",
132
- "work_item_id": "1042",
133
- "reference_file_path": "src/services/AuditService.java"
123
+ "action": "initialize",
124
+ "task_description": "Implement DeviceActivitySummaryDTO with browserUsage map.",
125
+ "pending_acs": ["Add browserUsage map", "Add unit tests", "Update OpenAPI"]
134
126
  }
135
127
  ```
136
128
 
137
- This generates `.current-task-spec.md` with:
138
- - Task description
139
- - Pre-filled guardrails checklist
140
- - Acceptance criteria placeholders
141
- - Reference code snippet
142
- - Session log
143
-
144
- **Example — Tick off completed work:**
145
-
146
- ```
147
- Ask: "Mark the tenantId scoping and unit tests as done in the spec."
148
- ```
149
-
150
- The AI will call:
129
+ **Example Updating progress:**
151
130
  ```json
152
131
  {
153
132
  "action": "update",
154
- "update_content": "- [x] All DB queries / event payloads are scoped with `tenantId`\n- [x] Unit tests written / updated for new business logic"
133
+ "pending_acs": ["Update OpenAPI"],
134
+ "completed_acs": ["Add browserUsage map", "Add unit tests"],
135
+ "next_step": "Generate the OpenAPI YAML for DeviceActivitySummaryDTO and test generation."
155
136
  }
156
137
  ```
157
138
 
158
- **Example — Read current spec:**
159
-
160
- ```
161
- Ask: "Where did we leave off? Read the current spec."
162
- ```
163
-
164
- ```json
165
- { "action": "read" }
166
- ```
167
-
168
139
  ---
169
140
 
170
141
  ### 2. `log_rejected_pattern`
@@ -219,15 +190,16 @@ This writes to `.rejected-patterns.json`:
219
190
  |---|---|---|---|
220
191
  | `work_item_id` | string | Yes | Azure DevOps work item ID |
221
192
  | `root_dir` | string | No | Directory to scan (defaults to cwd) |
222
- | `skip_checks` | array | No | `spec_boxes`, `console_logs`, `hardcoded_urls`, `flyway_migrations` |
193
+ | `skip_checks` | array | No | `branch_state`, `console_logs`, `tenant_isolation`, `hardcoded_urls`, `flyway_migrations` |
223
194
 
224
195
  **Guardrail checks:**
225
196
 
226
197
  | Check | Fails when |
227
198
  |---|---|
228
- | **SPEC** | `.current-task-spec.md` is missing or has unchecked `- [ ]` boxes |
199
+ | **STATE** | `.secufusion-state.json` has pending ACs for the active branch |
229
200
  | **LINTING** | AST linters (ESLint, Checkstyle) fail in any discovered microservice. Fix natively, do not suppress! (Falls back to regex if no linters exist) |
230
201
  | **SECURITY** | Hardcoded IPs or `uat.*` / `prod.*` / `staging.*` URLs in source or config files |
202
+ | **TENANT ISOLATION** | `*Repository.java` files contain query methods or `@Query` annotations that do NOT include `tenant` filtering |
231
203
  | **FLYWAY** | `@Entity`-annotated Java files exist but no `V*__.sql` Flyway migrations found |
232
204
 
233
205
  **Example — Run checks before PR:**
@@ -247,14 +219,14 @@ The AI will call:
247
219
  ```
248
220
  # ✅ Pre-PR Checks PASSED — Work Item #1042
249
221
 
250
- ✅ [SPEC] All spec checkboxes are checked.
222
+ ✅ [STATE] All branch tasks completed.
251
223
  ✅ [LOGGING] No console.log / System.out.println found.
252
224
  ✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
253
225
  ✅ [FLYWAY] Flyway migration coverage looks good.
254
226
 
255
227
  ## PR Summary
256
228
  - Work Item: #1042
257
- - Spec: All acceptance criteria verified
229
+ - State: All pending ACs resolved
258
230
  - Ready to raise PR 🚀
259
231
  ```
260
232
 
@@ -262,7 +234,7 @@ The AI will call:
262
234
  ```
263
235
  # ❌ Pre-PR Checks FAILED — Work Item #1042
264
236
 
265
- ❌ [SPEC] 3 unchecked item(s) in the spec:
237
+ ❌ [STATE] 3 pending AC(s) remain in .secufusion-state.json:
266
238
  • Flyway SQL migration created for every modified JPA @Entity
267
239
  • Unit tests written / updated for new business logic
268
240
  • API contract updated if endpoints changed
@@ -283,7 +255,7 @@ These rules are enforced automatically — the AI will never violate them:
283
255
  ✅ No console.log() or System.out.println() in any source file
284
256
  ✅ No hardcoded UAT/Prod IPs or environment URLs
285
257
  ✅ Every JPA @Entity change accompanied by a Flyway .sql migration
286
- Spec must be fully checked before PR is raised
258
+ State in .secufusion-state.json must be fully resolved before PR is raised
287
259
  ```
288
260
 
289
261
  ---
@@ -292,7 +264,7 @@ These rules are enforced automatically — the AI will never violate them:
292
264
 
293
265
  | File | Description |
294
266
  |---|---|
295
- | `.current-task-spec.md` | Living task blueprint — created per feature, updated as work progresses |
267
+ | `.secufusion-state.json` | Living task blueprint — tracks pending/completed ACs per branch |
296
268
  | `.rejected-patterns.json` | Cumulative log of all rejected patterns across sessions |
297
269
 
298
270
  > **Tip:** Commit both files to your repo so the entire team benefits from the shared knowledge.
@@ -305,11 +277,11 @@ These rules are enforced automatically — the AI will never violate them:
305
277
  ┌─────────────────────────────────────────────────────┐
306
278
  │ SecuFusion MCP Workflow │
307
279
  ├──────────────┬──────────────────────────────────────┤
308
- │ Phase 1 │ manage_feature_spec (action=create)
309
- │ Planning │ → Generates .current-task-spec.md
280
+ │ Phase 1 │ manage_branch_state (action=init)
281
+ │ Planning │ → Initializes .secufusion-state.json
310
282
  ├──────────────┼──────────────────────────────────────┤
311
- │ Phase 2 │ manage_feature_spec (action=update) │
312
- │ Execution │ → Tick off ACs as you complete them
283
+ │ Phase 2 │ manage_branch_state (action=update) │
284
+ │ Execution │ → Resolve ACs & define next_step
313
285
  ├──────────────┼──────────────────────────────────────┤
314
286
  │ Phase 3 │ log_rejected_pattern │
315
287
  │ Correction │ → Record mistakes to avoid repeat │
@@ -372,46 +344,14 @@ Admin users must be forced through TOTP verification before
372
344
  accessing any dashboard route. Exempt service accounts.
373
345
  ```
374
346
 
375
- **The AI automatically calls `manage_feature_spec` and creates `.current-task-spec.md`:**
376
- ```markdown
377
- # SecuFusion Feature Spec
378
-
379
- **Generated:** 2026-08-26T14:35:00.000Z
380
- **Azure DevOps Work Item:** 2847 → https://dev.azure.com/secufusion/_workitems/edit/2847
381
-
382
- ## Task Description
383
- Add MFA enforcement for admin users on login...
384
-
385
- ## Guardrails Checklist
386
- - [ ] All DB queries / event payloads are scoped with `tenantId`
387
- - [ ] No `console.log` / `System.out.println` left in source files
388
- - [ ] No hardcoded UAT/Prod IPs or environment URLs
389
- - [ ] Flyway SQL migration created for every modified JPA `@Entity`
390
- - [ ] Unit tests written / updated for new business logic
391
- - [ ] API contract updated if endpoints changed
392
-
393
- ## Acceptance Criteria
394
- - [ ] AC-1: (fill in from ticket)
395
- - [ ] AC-2: (fill in from ticket)
396
-
397
- ## Session Log
398
- | 2026-08-26T14:35:00Z | Spec created |
399
- ```
400
-
401
- You can paste as little or as much as you want:
402
-
403
- ```
404
- # Minimal — one liner
405
- WI-2847: MFA for admin login with TOTP.
406
-
407
- # Full ticket paste — straight from Azure
408
- WI-2847
409
- Title: Add MFA enforcement for admin users
410
- Description: Admin users must complete TOTP verification...
411
- Acceptance Criteria:
412
- - Given admin logs in, When MFA not done, Then redirect to /mfa
413
- - Service accounts in GROUP_SERVICE_ACCOUNTS are exempt
414
- Priority: High
347
+ **The AI automatically calls `manage_branch_state` and creates/initializes `.secufusion-state.json`:**
348
+ ```json
349
+ {
350
+ "task": "WI-2847: Add MFA enforcement",
351
+ "pending_acs": ["AC-1: Auth flow", "AC-2: Redirects", "AC-3: Exemptions"],
352
+ "completed_acs": [],
353
+ "next_step": "Implement MFA logic in AuthController"
354
+ }
415
355
  ```
416
356
 
417
357
  ---
@@ -434,23 +374,20 @@ As you finish pieces of the feature, tell the AI:
434
374
 
435
375
  ```
436
376
  I've finished the tenantId scoping on all queries and written the unit tests.
437
- Update the spec.
377
+ Update the state.
438
378
  ```
439
379
 
440
- The AI calls `manage_feature_spec` with `action=update` and the spec updates:
441
-
442
- ```markdown
443
- # Before
444
- - [ ] All DB queries / event payloads are scoped with `tenantId`
445
- - [ ] Unit tests written / updated for new business logic
380
+ The AI calls `manage_branch_state` with `action=update` and the state updates:
446
381
 
447
- # After
448
- - [x] All DB queries / event payloads are scoped with `tenantId`
449
- - [x] Unit tests written / updated for new business logic
382
+ ```json
383
+ {
384
+ "task": "WI-2847: Add MFA enforcement",
385
+ "pending_acs": ["AC-3: Exemptions"],
386
+ "completed_acs": ["Add tenantId scoping", "Add unit tests"],
387
+ "next_step": "Implement exemption logic for service accounts"
388
+ }
450
389
  ```
451
390
 
452
- A new row is also appended to the Session Log automatically, so you always have an audit trail of progress across sessions.
453
-
454
391
  ---
455
392
 
456
393
  ### Step 5 — Course correction (if the AI does something wrong)
@@ -473,23 +410,6 @@ The AI **immediately** calls `log_rejected_pattern`:
473
410
  }
474
411
  ```
475
412
 
476
- This gets appended to `.rejected-patterns.json`:
477
-
478
- ```json
479
- [
480
- {
481
- "id": 1,
482
- "timestamp": "2026-08-26T15:10:00.000Z",
483
- "category": "security",
484
- "pattern": "Hardcoded staging URL https://staging.secufusion.io in source files",
485
- "reason": "Must use @Value from application.properties...",
486
- "file_context": "src/services/NotificationService.java"
487
- }
488
- ]
489
- ```
490
-
491
- From this point on, the AI checks this file before every suggestion — the mistake will never be repeated, even in future sessions.
492
-
493
413
  ---
494
414
 
495
415
  ### Step 6 — Pre-PR checks (PR Handoff phase)
@@ -507,104 +427,44 @@ The AI scans your entire repo and produces a report.
507
427
  # ✅ Pre-PR Checks PASSED — Work Item #2847
508
428
 
509
429
  Scanned directory: C:\projects\secufusion-backend
510
- Timestamp: 2026-08-26T16:00:00.000Z
511
430
 
512
- ✅ [SPEC] All spec checkboxes are checked.
431
+ ✅ [STATE] All tasks resolved.
513
432
  ✅ [LOGGING] No console.log / System.out.println found.
514
433
  ✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
515
434
  ✅ [FLYWAY] Flyway migration coverage looks good.
516
435
 
517
436
  ## PR Summary
518
437
  - Work Item: #2847
519
- - Spec: All acceptance criteria verified
520
- - Guardrails: tenantId scoping, no debug logs, no hardcoded URLs, Flyway covered ✅
438
+ - State: All pending ACs resolved
439
+ - Guardrails: tenant isolation secured, no debug logs, no hardcoded URLs, Flyway covered ✅
521
440
  - Ready to raise PR 🚀
522
-
523
- 🚫 Rejected Patterns Reminder (1 on record)
524
- - [security] Hardcoded staging URL https://staging.secufusion.io in source files
525
- ```
526
-
527
- **❌ Checks fail — you must fix before PR:**
528
- ```
529
- # ❌ Pre-PR Checks FAILED — Work Item #2847
530
-
531
- ## Errors (must fix before PR)
532
-
533
- ❌ [SPEC] 2 unchecked item(s) in the spec:
534
- • Flyway SQL migration created for every modified JPA @Entity
535
- • API contract (OpenAPI / TS types) updated if endpoints changed
536
-
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
540
-
541
- ## Passed
542
- ✅ [SECURITY] No hardcoded UAT/Prod IPs or environment URLs found.
543
- ✅ [FLYWAY] Flyway migration coverage looks good.
544
441
  ```
545
442
 
546
- Fix the issues, run checks again, and only raise the PR once it's fully green.
547
-
548
- ---
549
-
550
- ### Quick cheat sheet
551
-
552
- | What you want to do | What to say to the AI |
553
- |---|---|
554
- | Start a new task | `WI-XXXX: [paste description from Azure]` |
555
- | Check where you left off | `Read the current spec` |
556
- | Mark work as done | `Mark the tenantId scoping as complete in the spec` |
557
- | Record a mistake | `Never do [X] again because [Y]` |
558
- | Gate the PR | `Run pre-PR checks for WI-XXXX` |
559
- | Resume after a break | `What's left on the current task?` |
560
- | Use a reference file | `WI-XXXX: [desc]. Reference: src/services/MyService.java` |
561
-
562
443
  ---
563
444
 
564
- ## How It Actually Works The Invoker
565
-
566
- There are two layers that need to be in place for the MCP server to work automatically.
567
-
568
- ### Layer 1 — `mcp_config.json` (makes tools available)
569
-
570
- This tells the IDE to start the MCP server process when it launches. The tools become registered and ready, but nothing calls them yet.
571
-
572
- ```
573
- IDE starts → reads mcp_config.json → spawns node index.js → tools registered
574
- ```
575
-
576
- ### Layer 2 — `AGENTS.md` (the invoker — tells AI when to call each tool)
577
-
578
- Without this, the tools sit idle. The AI doesn't know when to invoke them.
579
- Create `.agents/AGENTS.md` in your project root:
445
+ ## How It Works (The 2 Layers)
580
446
 
581
- ```markdown
582
- # SecuFusion MCP Workflow Rules
447
+ To prevent AI amnesia or generic bad habits across your projects, the SecuFusion MCP relies on structured branch-aware JSON and native AST tooling.
583
448
 
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.
449
+ ## Phase 0 Resume (TRIGGER: new session, switching branches, or user says "resume" or "continue")
450
+ - Call `manage_branch_state` with `action: "read"`. The tool will automatically detect the current Git branch and return the structured JSON state for that specific branch. Do not re-read requirements; immediately begin executing the item explicitly listed in the `next_step` field.
585
451
 
586
452
  ## Phase 1 — Planning (TRIGGER: user assigns a task or work item)
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.
590
- - Wait for the spec to be generated before writing any code.
453
+ - Call `manage_branch_state` with `action: "initialize"`. Pass the `task_description` and `reference_file_path`. The tool will generate a branch-bound JSON state file tracking the Acceptance Criteria (ACs) as a structured array of pending tasks, keyed to your active Git branch.
591
454
 
592
- ## Phase 2 — Execution (TRIGGER: as you complete work)
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.
455
+ ## Phase 2 — Execution (TRIGGER: as you complete ACs, or before ending a session/response)
456
+ - Call `manage_branch_state` with `action: "update"`. Move completed items from `pending_acs` to `completed_acs`.
457
+ - CRITICAL: You must explicitly write a clear, actionable instruction into the `next_step` parameter (e.g., "Implement browserUsage map in DeviceActivitySummaryDTO"). This ensures your future self can resume instantly without parsing the full spec.
596
458
 
597
459
  ## Phase 3 — Course Correction (TRIGGER: user corrects you or rejects an approach)
598
- - Immediately call `log_rejected_pattern`.
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.
460
+ - Immediately call `log_rejected_pattern`. Pass the bad `pattern` and the `reason`. Always check `.rejected-patterns.json` implicitly before suggesting architectural choices.
601
461
 
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.
462
+ ## Phase 4 — PR Handoff (TRIGGER: user says "prepare PR" or "run checks")
463
+ - Call `run_pre_pr_checks` with the Azure DevOps `workItemId`. The tool checks the structured branch state to ensure `pending_acs` is empty, then discovers modified microservices to run native linters, Flyway checks, and the Tenant-Isolation scanner.
464
+ - If an error is thrown, YOU MUST navigate to that microservice, FIX THE ERROR natively, and rerun until the workspace passes.
605
465
 
606
466
  ## 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.
467
+ - TENANT SAFETY IS AUTOMATED: The PR Gatekeeper actively scans all `*Repository.java` files. If you write a database query (derived method or `@Query`) that does not explicitly filter by `tenantId` or contain the word `tenant`, the PR will be blocked. Write secure, tenant-isolated queries on your first attempt.
608
468
  - Do not ignore linter errors. AST-level tools (ESLint, Checkstyle/Maven) are the source of truth for hygiene. Fix them natively.
609
469
  - Never hardcode UAT/Prod IPs or URLs. Use environment variables or configuration properties.
610
470
  - If you modify a JPA `@Entity` in any backend repo, you MUST create the corresponding Flyway `.sql` migration script before running PR checks.
@@ -619,9 +479,9 @@ mcp_config.json → starts the server (tools available)
619
479
 
620
480
  You say: "WI-1042: Add audit log export"
621
481
 
622
- AI reads AGENTS.md → Phase 1 triggered → calls manage_feature_spec
482
+ AI reads AGENTS.md → Phase 1 triggered → calls manage_branch_state
623
483
 
624
- .current-task-spec.md created in your project root
484
+ .secufusion-state.json initialized for your Git branch
625
485
  ```
626
486
 
627
487
  ### Reusing across projects
@@ -647,14 +507,14 @@ Once both layers are in place, you interact completely naturally:
647
507
  | Situation | What you say |
648
508
  |---|---|
649
509
  | 🆕 New task | `WI-XXXX: [paste description from Azure]` |
650
- | ✅ Done a chunk | `Done with the tenantId scoping, update the spec` |
510
+ | ✅ Done a chunk | `Done with the tenantId scoping, update the state` |
651
511
  | ❌ AI did something wrong | `Don't do X, do Y instead` |
652
512
  | 🚀 Ready for PR | `Run checks for WI-XXXX` or `Prepare PR` |
653
513
  | 🔄 Resuming after a break | `What's left?` or `Resume the current task` |
654
514
 
655
515
  **Before `AGENTS.md`:** You had to remember which tool to invoke and when.
656
516
 
657
- **After `AGENTS.md`:** You just describe work. The AI follows the 4-phase workflow automatically — no commands, no syntax, no manual tool calls.
517
+ **After `AGENTS.md`:** You just describe work. The AI follows the 5-phase workflow automatically — no commands, no syntax, no manual tool calls.
658
518
 
659
519
  ---
660
520
 
package/index.js CHANGED
@@ -8,7 +8,7 @@
8
8
  * - run_pre_pr_checks : gate PRs via AST-level linters (ESLint / Maven Checkstyle) + structural checks
9
9
  *
10
10
  * Guardrails enforced:
11
- * 1. All DB queries and event payloads must carry tenantId.
11
+ * 1. TENANT SAFETY AUTOMATED: Active scanning of *Repository.java for tenantId scoping.
12
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.
@@ -22,7 +22,7 @@ import { execSync } from "child_process";
22
22
  // ─────────────────────────────────────────────
23
23
  // Constants / helpers
24
24
  // ─────────────────────────────────────────────
25
- const SPEC_FILE = ".current-task-spec.md";
25
+ const STATE_FILE = ".secufusion-state.json";
26
26
  const REJECTED_FILE = ".rejected-patterns.json";
27
27
  /** Resolve a path relative to cwd (where the MCP server is invoked). */
28
28
  function resolve(file) {
@@ -40,56 +40,13 @@ function writeFile(filePath, content) {
40
40
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
41
41
  fs.writeFileSync(filePath, content, "utf-8");
42
42
  }
43
- // ─────────────────────────────────────────────
44
- // manage_feature_spec helpers
45
- // ─────────────────────────────────────────────
46
- function buildInitialSpec(taskDescription, workItemId, referenceSnippet) {
47
- const now = new Date().toISOString();
48
- const wiLine = workItemId
49
- ? `**Azure DevOps Work Item:** [${workItemId}](https://dev.azure.com/secufusion/_workitems/edit/${workItemId})\n`
50
- : "";
51
- const refSection = referenceSnippet
52
- ? `\n## Reference Code Pattern\n\n> Extracted from the provided reference file to guide implementation style.\n\n\`\`\`\n${referenceSnippet.slice(0, 800)}\n\`\`\`\n`
53
- : "";
54
- return `# SecuFusion Feature Spec
55
- <!-- AUTO-GENERATED — do not remove the checkboxes; they are used by run_pre_pr_checks -->
56
-
57
- **Generated:** ${now}
58
- ${wiLine}
59
- ## Task Description
60
-
61
- ${taskDescription}
62
- ${refSection}
63
- ## Guardrails Checklist
64
-
65
- - [ ] All DB queries / event payloads are scoped with \`tenantId\`
66
- - [ ] No \`console.log\` / \`System.out.println\` left in source files
67
- - [ ] No hardcoded UAT/Prod IPs or environment URLs
68
- - [ ] Flyway SQL migration created for every modified JPA \`@Entity\`
69
- - [ ] Unit tests written / updated for new business logic
70
- - [ ] API contract (OpenAPI / TS types) updated if endpoints changed
71
-
72
- ## Acceptance Criteria
73
-
74
- - [ ] AC-1: (fill in from ticket)
75
- - [ ] AC-2: (fill in from ticket)
76
- - [ ] AC-3: (fill in from ticket)
77
-
78
- ## Implementation Notes
79
-
80
- > Add architecture decisions, edge-cases, and external dependency notes here.
81
-
82
- ## Session Log
83
-
84
- | Timestamp | Update |
85
- |-----------|--------|
86
- | ${now} | Spec created |
87
- `;
88
- }
89
- function appendSessionLog(existing, update) {
90
- const ts = new Date().toISOString();
91
- const logRow = `| ${ts} | ${update} |`;
92
- return existing.replace(/(\| [^\n]+ \|)\s*$/, `$1\n${logRow}`);
43
+ function getCurrentBranch(cwd) {
44
+ try {
45
+ return execSync("git rev-parse --abbrev-ref HEAD", { cwd, stdio: "pipe" }).toString().trim();
46
+ }
47
+ catch {
48
+ return "unknown-branch";
49
+ }
93
50
  }
94
51
  // ─────────────────────────────────────────────
95
52
  // run_pre_pr_checks helpers
@@ -100,34 +57,29 @@ const CONSOLE_LOG_REGEX = [
100
57
  ];
101
58
  const HARDCODED_URL_PATTERN = /(?:https?:\/\/)?(?:\d{1,3}\.){3}\d{1,3}(?::\d+)?|(?:uat|prod|staging|preprod)\.[a-z0-9.-]+\.[a-z]{2,}/gi;
102
59
  function walkDir(dir, exts) {
103
- const results = [];
104
- if (!fs.existsSync(dir))
105
- return results;
106
- for (const entry of fs.readdirSync(dir, { withFileTypes: true })) {
107
- const full = path.join(dir, entry.name);
108
- if (entry.isDirectory()) {
109
- if (["node_modules", ".git", "dist", "build", "target", ".idea"].includes(entry.name))
60
+ let results = [];
61
+ try {
62
+ const list = fs.readdirSync(dir);
63
+ for (const file of list) {
64
+ if (file === "node_modules" || file === ".git" || file === "dist" || file === "target")
110
65
  continue;
111
- results.push(...walkDir(full, exts));
112
- }
113
- else if (exts.some((e) => entry.name.endsWith(e))) {
114
- results.push(full);
66
+ const filePath = path.join(dir, file);
67
+ const stat = fs.statSync(filePath);
68
+ if (stat && stat.isDirectory()) {
69
+ results = results.concat(walkDir(filePath, exts));
70
+ }
71
+ else {
72
+ if (exts.some(ext => file.endsWith(ext))) {
73
+ results.push(filePath);
74
+ }
75
+ }
115
76
  }
116
77
  }
78
+ catch (e) {
79
+ /* ignore */
80
+ }
117
81
  return results;
118
82
  }
119
- function checkSpecFile() {
120
- const content = readFileSafe(resolve(SPEC_FILE));
121
- if (!content)
122
- return { unchecked: [], missing: true };
123
- const unchecked = [];
124
- for (const line of content.split("\n")) {
125
- const match = line.match(/^- \[ \] (.+)/);
126
- if (match)
127
- unchecked.push(match[1].trim());
128
- }
129
- return { unchecked, missing: false };
130
- }
131
83
  // ── AST-level linting via ESLint (preferred over regex for JS/TS) ─────────────
132
84
  function runEslint(rootDir) {
133
85
  // Check if ESLint is configured in the project
@@ -246,6 +198,31 @@ function findProjectRoots(dir, depth = 0) {
246
198
  }
247
199
  return [...new Set(roots)];
248
200
  }
201
+ // ── Automated Tenant Isolation Scanner ────────────────────────────────────────
202
+ function checkTenantIsolation(rootDir) {
203
+ const violations = [];
204
+ const files = walkDir(rootDir, [".java"]).filter(f => f.endsWith("Repository.java"));
205
+ const queryMethodRegex = /\b(?:find|get|read|query|search|stream|count|exists|delete|remove)(?:All)?By[A-Z0-9]/;
206
+ const atQueryRegex = /@Query\s*\(/;
207
+ for (const file of files) {
208
+ const content = readFileSafe(file);
209
+ if (!content)
210
+ continue;
211
+ const lines = content.split("\n");
212
+ for (let i = 0; i < lines.length; i++) {
213
+ const line = lines[i];
214
+ if (queryMethodRegex.test(line) || atQueryRegex.test(line)) {
215
+ // Look at current line and the next to handle simple wrapping
216
+ const combinedContext = line + (lines[i + 1] || "");
217
+ if (!/tenant/i.test(combinedContext)) {
218
+ violations.push(`Missing tenant isolation in ${path.relative(rootDir, file)}:${i + 1} — query lacks 'tenantId':\n` +
219
+ ` ${line.trim()}`);
220
+ }
221
+ }
222
+ }
223
+ }
224
+ return violations;
225
+ }
249
226
  function checkFlywayMigrations(rootDir) {
250
227
  const warnings = [];
251
228
  const javaFiles = walkDir(rootDir, [".java"]);
@@ -279,128 +256,113 @@ const server = new McpServer({
279
256
  name: "secufusion-mcp",
280
257
  version: "1.0.0",
281
258
  });
282
- // ─── Tool 1: manage_feature_spec ─────────────────────────────────────────────
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.", {
259
+ // ─── Tool 1: manage_branch_state ─────────────────────────────────────────────
260
+ server.tool("manage_branch_state", "Manage the structured JSON state for the current Git branch. Use to initialize new tasks, track pending/completed ACs, and leave explicit next steps for resuming.", {
261
+ action: z
262
+ .enum(["initialize", "read", "update"])
263
+ .describe("Action to perform on the branch state."),
286
264
  task_description: z
287
265
  .string()
288
266
  .optional()
289
- .describe("Full description of the new feature/task (used when creating a spec from scratch)."),
290
- update_content: z
267
+ .describe("Used with 'initialize' to describe the task."),
268
+ reference_file_path: z
291
269
  .string()
292
270
  .optional()
293
- .describe("Partial markdown update / session-log entry to merge into the existing spec. " +
294
- "Use to tick checkboxes: replace '- [ ] AC-1' with '- [x] AC-1'."),
295
- work_item_id: z
296
- .string()
271
+ .describe("Used with 'initialize' to note a reference file."),
272
+ pending_acs: z
273
+ .array(z.string())
297
274
  .optional()
298
- .describe("Azure DevOps work item ID to embed in the spec header."),
299
- reference_file_path: z
275
+ .describe("Array of ACs. Used with 'initialize' or 'update' to set pending tasks."),
276
+ completed_acs: z
277
+ .array(z.string())
278
+ .optional()
279
+ .describe("Array of completed ACs. Used with 'update'."),
280
+ next_step: z
300
281
  .string()
301
282
  .optional()
302
- .describe("Absolute or cwd-relative path to an existing source file whose coding patterns should " +
303
- "be embedded as a reference snippet in the new spec."),
304
- action: z
305
- .enum(["create", "update", "read"])
306
- .default("create")
307
- .describe("'create' generates a new spec (overwrites if one exists), " +
308
- "'update' patches the existing spec, " +
309
- "'read' returns the current spec content without modification."),
310
- }, async ({ task_description, update_content, work_item_id, reference_file_path, action }) => {
311
- const specPath = resolve(SPEC_FILE);
312
- if (action === "read") {
313
- const content = readFileSafe(specPath);
314
- if (!content) {
315
- return {
316
- content: [
317
- {
318
- type: "text",
319
- text: `No spec file found at ${SPEC_FILE}. Run with action='create' to generate one.`,
320
- },
321
- ],
322
- };
283
+ .describe("Explicit actionable instruction for resuming. Required for 'update'."),
284
+ }, async ({ action, task_description, reference_file_path, pending_acs, completed_acs, next_step }) => {
285
+ const cwd = process.cwd();
286
+ const branch = getCurrentBranch(cwd);
287
+ const statePath = path.resolve(STATE_FILE);
288
+ let state = {};
289
+ if (fs.existsSync(statePath)) {
290
+ try {
291
+ state = JSON.parse(readFileSafe(statePath) || "{}");
292
+ }
293
+ catch {
294
+ state = {};
323
295
  }
324
- return { content: [{ type: "text", text: content }] };
325
296
  }
326
- if (action === "create") {
327
- if (!task_description) {
297
+ if (action === "read") {
298
+ const branchState = state[branch];
299
+ if (!branchState) {
328
300
  return {
329
301
  content: [
330
302
  {
331
303
  type: "text",
332
- text: "ERROR: `task_description` is required when action='create'.",
304
+ text: `No state found for branch '${branch}'. Please initialize first.`,
333
305
  },
334
306
  ],
335
- isError: true,
336
307
  };
337
308
  }
338
- let refSnippet = null;
339
- if (reference_file_path) {
340
- refSnippet = readFileSafe(path.isAbsolute(reference_file_path)
341
- ? reference_file_path
342
- : resolve(reference_file_path));
343
- }
344
- const spec = buildInitialSpec(task_description, work_item_id, refSnippet);
345
- writeFile(specPath, spec);
309
+ return {
310
+ content: [{ type: "text", text: JSON.stringify(branchState, null, 2) }],
311
+ };
312
+ }
313
+ if (action === "initialize") {
314
+ state[branch] = {
315
+ task_description: task_description || "",
316
+ reference_file_path: reference_file_path || "",
317
+ pending_acs: pending_acs || [],
318
+ completed_acs: [],
319
+ next_step: "Review requirements and begin implementation.",
320
+ last_updated: new Date().toISOString()
321
+ };
322
+ writeFile(statePath, JSON.stringify(state, null, 2));
346
323
  return {
347
324
  content: [
348
325
  {
349
326
  type: "text",
350
- text: `✅ Spec created at \`${SPEC_FILE}\`.\n\n` +
351
- `Review and fill in the Acceptance Criteria before starting implementation.\n\n` +
352
- `---\n\n${spec}`,
327
+ text: `✅ Branch state initialized for '${branch}'.\n\nState:\n${JSON.stringify(state[branch], null, 2)}`,
353
328
  },
354
329
  ],
355
330
  };
356
331
  }
357
332
  if (action === "update") {
358
- if (!update_content) {
333
+ if (!state[branch]) {
359
334
  return {
335
+ isError: true,
360
336
  content: [
361
- {
362
- type: "text",
363
- text: "ERROR: `update_content` is required when action='update'.",
364
- },
337
+ { type: "text", text: `No state found for branch '${branch}'. Initialize first.` },
365
338
  ],
366
- isError: true,
367
339
  };
368
340
  }
369
- const existing = readFileSafe(specPath);
370
- if (!existing) {
341
+ if (!next_step) {
371
342
  return {
343
+ isError: true,
372
344
  content: [
373
- {
374
- type: "text",
375
- text: `ERROR: No spec file found at ${SPEC_FILE}. Create one first with action='create'.`,
376
- },
345
+ { type: "text", text: "ERROR: 'next_step' is required when updating." },
377
346
  ],
378
- isError: true,
379
347
  };
380
348
  }
381
- let updated = existing;
382
- for (const line of update_content.split("\n")) {
383
- const checkedMatch = line.match(/^- \[x\] (.+)/);
384
- if (checkedMatch) {
385
- const label = checkedMatch[1].trim().replace(/[.*+?^${}()|[\]\\]/g, "\\$&");
386
- updated = updated.replace(new RegExp(`- \\[ \\] ${label}`, "g"), `- [x] ${checkedMatch[1].trim()}`);
387
- }
388
- }
389
- updated = appendSessionLog(updated, update_content.replace(/\n/g, " ").slice(0, 120));
390
- writeFile(specPath, updated);
349
+ if (pending_acs)
350
+ state[branch].pending_acs = pending_acs;
351
+ if (completed_acs)
352
+ state[branch].completed_acs = completed_acs;
353
+ state[branch].next_step = next_step;
354
+ state[branch].last_updated = new Date().toISOString();
355
+ writeFile(statePath, JSON.stringify(state, null, 2));
391
356
  return {
392
357
  content: [
393
358
  {
394
359
  type: "text",
395
- text: `✅ Spec updated at \`${SPEC_FILE}\`.\n\n---\n\n${updated}`,
360
+ text: `✅ Branch state updated for '${branch}'. Next step recorded:\n> ${next_step}`,
396
361
  },
397
362
  ],
398
363
  };
399
364
  }
400
- return {
401
- content: [{ type: "text", text: "Unknown action." }],
402
- isError: true,
403
- };
365
+ return { content: [{ type: "text", text: "Invalid action." }] };
404
366
  });
405
367
  // ─── Tool 2: log_rejected_pattern ────────────────────────────────────────────
406
368
  server.tool("log_rejected_pattern", "Record a coding pattern that was rejected by the team so it is never repeated. " +
@@ -464,9 +426,9 @@ server.tool("log_rejected_pattern", "Record a coding pattern that was rejected b
464
426
  });
465
427
  // ─── Tool 3: run_pre_pr_checks ───────────────────────────────────────────────
466
428
  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. " +
429
+ "Automatically discovers modified microservices and runs native linters, database script checks, and strict tenant-isolation scanners. " +
430
+ "Validates: spec checkboxes, hardcoded IPs/URLs, tenantId in Repositories, and Flyway migration coverage for JPA entities. " +
431
+ "Linter/isolation errors must be fixed natively — do not suppress warnings to pass the gate. " +
470
432
  "MUST pass with zero errors before a PR is raised.", {
471
433
  work_item_id: z
472
434
  .string()
@@ -479,6 +441,7 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks acr
479
441
  .array(z.enum([
480
442
  "spec_boxes",
481
443
  "console_logs",
444
+ "tenant_isolation",
482
445
  "hardcoded_urls",
483
446
  "flyway_migrations",
484
447
  ]))
@@ -489,23 +452,32 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks acr
489
452
  const errors = [];
490
453
  const warnings = [];
491
454
  const passed = [];
492
- // ── 1. Spec checkbox check ─────────────────────────────────────────────
455
+ // ── 1. Branch state check ─────────────────────────────────────────────
493
456
  if (!skip_checks.includes("spec_boxes")) {
494
- const { unchecked, missing } = checkSpecFile();
495
- if (missing) {
496
- errors.push(`❌ [SPEC] No \`.current-task-spec.md\` found. ` +
497
- `Call \`manage_feature_spec\` with action='create' before raising a PR.`);
457
+ const branch = getCurrentBranch(scanRoot);
458
+ const statePath = path.resolve(scanRoot, STATE_FILE);
459
+ let branchState = null;
460
+ if (fs.existsSync(statePath)) {
461
+ try {
462
+ const stateData = JSON.parse(readFileSafe(statePath) || "{}");
463
+ branchState = stateData[branch];
464
+ }
465
+ catch { }
466
+ }
467
+ if (!branchState) {
468
+ errors.push(`❌ [STATE] No branch state found for '${branch}' in \`${STATE_FILE}\`. ` +
469
+ `Call \`manage_branch_state\` with action='initialize' before raising a PR.`);
498
470
  }
499
- else if (unchecked.length > 0) {
500
- errors.push(`❌ [SPEC] ${unchecked.length} unchecked item(s) in the spec:\n` +
501
- unchecked.map((u) => ` • ${u}`).join("\n"));
471
+ else if (branchState.pending_acs && branchState.pending_acs.length > 0) {
472
+ errors.push(`❌ [STATE] ${branchState.pending_acs.length} pending AC(s) remain for branch '${branch}':\n` +
473
+ branchState.pending_acs.map((u) => ` • ${u}`).join("\n"));
502
474
  }
503
475
  else {
504
- passed.push("✅ [SPEC] All spec checkboxes are checked.");
476
+ passed.push(`✅ [STATE] Branch state for '${branch}' is clear (no pending ACs).`);
505
477
  }
506
478
  }
507
479
  else {
508
- warnings.push("⚠️ [SPEC] Spec checkbox check SKIPPED (manually bypassed).");
480
+ warnings.push("⚠️ [STATE] Branch state check SKIPPED (manually bypassed).");
509
481
  }
510
482
  // ── 2. AST-level linting (Workspace Discovery → ESLint/Checkstyle → regex fallback)
511
483
  if (!skip_checks.includes("console_logs")) {
@@ -556,7 +528,21 @@ server.tool("run_pre_pr_checks", "Run all SecuFusion pre-PR guardrail checks acr
556
528
  else {
557
529
  warnings.push("⚠️ [LINTING] Linter check SKIPPED (manually bypassed).");
558
530
  }
559
- // ── 3. Hardcoded URL check (regex — no AST equivalent needed here) ───────
531
+ // ── 3. Tenant Isolation Scanner ──────────────────────────────────────────
532
+ if (!skip_checks.includes("tenant_isolation")) {
533
+ const tenantViolations = checkTenantIsolation(scanRoot);
534
+ if (tenantViolations.length > 0) {
535
+ errors.push(`❌ [SECURITY] Tenant Isolation Failed: Found ${tenantViolations.length} query method(s) in *Repository.java without tenant scoping:\n` +
536
+ tenantViolations.map((v) => ` • ${v}`).join("\n"));
537
+ }
538
+ else {
539
+ passed.push("✅ [SECURITY] Tenant isolation active: All database queries correctly scoped with tenantId.");
540
+ }
541
+ }
542
+ else {
543
+ warnings.push("⚠️ [SECURITY] Tenant isolation check SKIPPED (manually bypassed).");
544
+ }
545
+ // ── 4. Hardcoded URL check (regex) ───────────────────────────────────────
560
546
  if (!skip_checks.includes("hardcoded_urls")) {
561
547
  const urlViolations = checkHardcodedUrls(scanRoot);
562
548
  if (urlViolations.length > 0) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secufusion-mcp",
3
- "version": "1.0.5",
3
+ "version": "1.0.7",
4
4
  "type": "module",
5
5
  "description": "SecuFusion MCP server - developer workflow tooling with guardrails",
6
6
  "main": "index.js",