secufusion-mcp 2.2.5 → 2.2.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.
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "secufusion-mcp",
3
3
  "description": "SecuFusion MSSP platform dev agent — project-level spec driven, tenant-isolation enforced, zero-trust guardrails. 5 Core Slash Commands: /sfn-init (dynamic DNA + architecture diagram + watcher), /sfn-plan (philosophy check + intent WHY + classify + plan), /sfn-code (spec-driven implementation), /sfn-review (zero-tolerance 3-tier PR gate), /sfn-explore (on-demand service graph or blast-radius analysis).",
4
- "version": "2.2.5",
4
+ "version": "2.2.7",
5
5
  "author": {
6
6
  "name": "Motivity Labs"
7
7
  },
@@ -679,7 +679,7 @@ server.tool("spec_create_intent", "Creates a structured Markdown intent file in
679
679
  })).optional().describe("Polyglot service map — lists every service involved regardless of language. This is the polyglot bridge."),
680
680
  notes: z.string().optional().describe("Any additional context, constraints, or dependencies.")
681
681
  }, async ({ work_item_id, title, business_goal, pm_owner, ticket_url, acceptance_criteria, out_of_scope, risk, services_involved, notes }) => {
682
- const inputChars = JSON.stringify(arguments).length;
682
+ const inputChars = JSON.stringify({ work_item_id, title, business_goal, pm_owner, ticket_url, acceptance_criteria, out_of_scope, risk, services_involved, notes }).length;
683
683
  const num = nextIntentNumber();
684
684
  const slug = slugify(title);
685
685
  const filename = `${num}-WI-${work_item_id}-${slug}.md`;
@@ -2044,10 +2044,20 @@ server.tool("classify_task", "MANDATORY FIRST STEP for every task without except
2044
2044
  ];
2045
2045
  if (spec) {
2046
2046
  if (spec.microservices) {
2047
- Object.keys(spec.microservices).forEach(k => BACKEND_SIGNALS.push(k.toLowerCase()));
2047
+ // FIX A: Only add service/table names when they appear as a whole word in the corpus.
2048
+ // Substring matching caused "events" or "incidents" in a bug description to
2049
+ // match sfn-events-api and its owned tables, inflating backendScore falsely.
2050
+ const wholeWord = (token) => new RegExp(`(?<![a-z0-9-])${token.replace(/[-]/g, '[\\-\\s]?')}(?![a-z0-9-])`, "i").test(corpus);
2051
+ Object.keys(spec.microservices).forEach(k => {
2052
+ if (wholeWord(k.toLowerCase()))
2053
+ BACKEND_SIGNALS.push(k.toLowerCase());
2054
+ });
2048
2055
  Object.values(spec.microservices).forEach(svc => {
2049
2056
  if (Array.isArray(svc.owns_tables)) {
2050
- svc.owns_tables.forEach(t => BACKEND_SIGNALS.push(t.toLowerCase()));
2057
+ svc.owns_tables.forEach(t => {
2058
+ if (wholeWord(t.toLowerCase()))
2059
+ BACKEND_SIGNALS.push(t.toLowerCase());
2060
+ });
2051
2061
  }
2052
2062
  });
2053
2063
  }
@@ -2316,10 +2326,14 @@ server.tool("classify_task", "MANDATORY FIRST STEP for every task without except
2316
2326
  classification = "EXTENSION_ONLY";
2317
2327
  }
2318
2328
  else if (backendScore > 0 && (frontendScore > 0 || extensionScore > 0)) {
2319
- if (frontendScore > backendScore * 2.5) {
2329
+ // FIX C: Threshold reduced from 2.5x to 1.5x. The original 2.5x was set
2330
+ // before spec signal injection existed; now that injection can inflate
2331
+ // backendScore with service/table name hits, a genuine frontend-only task
2332
+ // with a few domain words could never clear 2.5x and was trapped as FULL_STACK.
2333
+ if (frontendScore > backendScore * 1.5) {
2320
2334
  classification = "FRONTEND_ONLY";
2321
2335
  }
2322
- else if (backendScore > frontendScore * 2.5) {
2336
+ else if (backendScore > frontendScore * 1.5) {
2323
2337
  classification = "BACKEND_ONLY";
2324
2338
  }
2325
2339
  else {
@@ -2374,16 +2388,12 @@ server.tool("classify_task", "MANDATORY FIRST STEP for every task without except
2374
2388
  "column", "table", "migration", "entity", "@entity",
2375
2389
  "schema", "alter", "add column", "drop column", "rename column",
2376
2390
  ];
2377
- if (spec && spec.microservices) {
2378
- Object.values(spec.microservices).forEach(svc => {
2379
- if (Array.isArray(svc.owns_tables)) {
2380
- svc.owns_tables.forEach(t => DB_CHANGE_SIGNALS.push(t.toLowerCase()));
2381
- }
2382
- if (Array.isArray(svc.kafka_consumes)) {
2383
- svc.kafka_consumes.forEach(topic => KAFKA_CHANGE_SIGNALS.push(topic.toLowerCase()));
2384
- }
2385
- });
2386
- }
2391
+ // FIX B: Do NOT append Kafka topic names or DB table names from the spec into the
2392
+ // signal arrays. Topic names like "device-registration" or table names like "events"
2393
+ // appear in ordinary bug descriptions and trigger false breaking-change warnings.
2394
+ // The structural keywords already in KAFKA_CHANGE_SIGNALS / DB_CHANGE_SIGNALS
2395
+ // ("kafka", "migration", "alter", "add column", etc.) are sufficient — those only
2396
+ // fire when the description explicitly discusses infrastructure changes.
2387
2397
  const endpointRisk = ENDPOINT_CHANGE_SIGNALS.some(s => corpus.includes(s));
2388
2398
  const kafkaRisk = KAFKA_CHANGE_SIGNALS.some(s => corpus.includes(s));
2389
2399
  const dbRisk = DB_CHANGE_SIGNALS.some(s => corpus.includes(s));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "secufusion-mcp",
3
- "version": "2.2.5",
3
+ "version": "2.2.7",
4
4
  "type": "module",
5
5
  "description": "Project-level spec driven AI workflow tooling for SecuFusion, MCP server - developer workflow tooling with guardrails",
6
6
  "main": "mcp/dist/server.js",