secufusion-mcp 1.0.46 → 1.0.48
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/.secufusion-project-spec.json +15 -0
- package/index.js +30 -2
- package/package.json +1 -1
|
@@ -963,6 +963,21 @@
|
|
|
963
963
|
"port": 8088,
|
|
964
964
|
"uri": "lb://PRODUCT-SERVICE",
|
|
965
965
|
"note": "Defined in the shared deployment env config (PRODUCT_SERVICE_NAME/PRODUCT_SERVICE_PORT/PRODUCT_SERVICE_URI) but NO corresponding repository exists anywhere in this 18-repo workspace, and no route to it was found in sfn-gateway-api's application.properties. See manual_review_flags."
|
|
966
|
+
},
|
|
967
|
+
"sfn-web-ui": {
|
|
968
|
+
"repo": "sfn-web-ui",
|
|
969
|
+
"type": "frontend",
|
|
970
|
+
"dev_port": 5173,
|
|
971
|
+
"framework": "React 19.1.1 (Vite 7.1.7 build tool, @vitejs/plugin-react + @vitejs/plugin-basic-ssl for local HTTPS)",
|
|
972
|
+
"ui_library": "MUI (Material UI) v7.3.5 + Emotion styling engine, custom theme in src/theme.js",
|
|
973
|
+
"state_management": "Redux Toolkit 2.10.1 + react-redux 9.2.0 (primary global state: src/store/slices/ — auth, feature, notification, tenant, ui); React Context used narrowly (src/contexts/TenantContextProvider.jsx)",
|
|
974
|
+
"api_base_url_pattern": "axios instance (src/utils/axiosConfig.js) with host-derived baseURL: relative/empty on localhost (routed through Vite dev proxy to VITE_API_TARGET env var), or https://<current-host>/ in production (same-origin)",
|
|
975
|
+
"auth_token_attachment": "axios request interceptor: tries dynamicKeycloakService.getToken() (silent refresh) first, falls back to Redux auth.token, sets Authorization: Bearer <token>. Matching response interceptor handles 401 via Keycloak refresh or POST /api/tenants/auth/token/refresh, with a failed-request queue to avoid parallel refresh storms.",
|
|
976
|
+
"tenant_id_in_requests": "Hybrid: normally tenantId is embedded as a URL path segment (derived from Redux auth.user.tenantId) per service's own routing convention. For MSSP 'acting tenant' impersonation, the same axios interceptor adds an X-Acting-Tenant header (for tenant-agnostic-path APIs) AND rewrites /api/tenants/... URL path segments to swap in the acting tenant's UUID (implemented in src/services/tenantImpersonation.js).",
|
|
977
|
+
"owns_tables": [],
|
|
978
|
+
"calls_services": ["sfn-gateway-api"],
|
|
979
|
+
"kafka_produces": [],
|
|
980
|
+
"kafka_consumes": []
|
|
966
981
|
}
|
|
967
982
|
},
|
|
968
983
|
"frontend": {
|
package/index.js
CHANGED
|
@@ -374,9 +374,37 @@ server.tool("manage_project_spec", "Manages the .secufusion-project-spec.json fi
|
|
|
374
374
|
}, inputChars);
|
|
375
375
|
}
|
|
376
376
|
const microservices = spec.microservices;
|
|
377
|
-
const
|
|
377
|
+
const svcNameLower = service_name.toLowerCase();
|
|
378
|
+
// 1. Direct match in microservices
|
|
379
|
+
let serviceBlock = microservices?.[service_name];
|
|
380
|
+
// 2. Case-insensitive match in microservices (e.g. "sfn-iam-api" vs "sfn-IAM-api")
|
|
381
|
+
if (!serviceBlock && microservices) {
|
|
382
|
+
const matchedKey = Object.keys(microservices).find(k => k.toLowerCase() === svcNameLower);
|
|
383
|
+
if (matchedKey)
|
|
384
|
+
serviceBlock = microservices[matchedKey];
|
|
385
|
+
}
|
|
386
|
+
// 3. Check spec.frontend by repo name (e.g. "sfn-web-ui")
|
|
387
|
+
const frontend = spec.frontend;
|
|
388
|
+
if (!serviceBlock && frontend) {
|
|
389
|
+
const repoName = String(frontend.repo ?? "").toLowerCase();
|
|
390
|
+
if (repoName === svcNameLower || svcNameLower.includes("web-ui") || svcNameLower.includes("frontend")) {
|
|
391
|
+
serviceBlock = frontend;
|
|
392
|
+
}
|
|
393
|
+
}
|
|
394
|
+
// 4. Check spec.chrome_extension by repo name
|
|
395
|
+
const chromeExt = spec.chrome_extension;
|
|
396
|
+
if (!serviceBlock && chromeExt) {
|
|
397
|
+
const repoName = String(chromeExt.repo ?? "").toLowerCase();
|
|
398
|
+
if (repoName === svcNameLower || svcNameLower.includes("chrome") || svcNameLower.includes("extension")) {
|
|
399
|
+
serviceBlock = chromeExt;
|
|
400
|
+
}
|
|
401
|
+
}
|
|
378
402
|
if (!serviceBlock) {
|
|
379
|
-
const available =
|
|
403
|
+
const available = [
|
|
404
|
+
...(microservices ? Object.keys(microservices) : []),
|
|
405
|
+
...(frontend?.repo ? [String(frontend.repo)] : []),
|
|
406
|
+
...(chromeExt?.repo ? [String(chromeExt.repo)] : []),
|
|
407
|
+
].join(", ");
|
|
380
408
|
return appendTelemetry({
|
|
381
409
|
isError: true,
|
|
382
410
|
content: [{ type: "text", text: `Service '${service_name}' not found in spec.\n\nAvailable services: ${available}` }],
|