@greenarmor/ges-mcp-server 1.2.7 → 1.3.0

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 (2) hide show
  1. package/dist/server.js +249 -10
  2. package/package.json +12 -12
package/dist/server.js CHANGED
@@ -2,14 +2,14 @@
2
2
  import * as readline from "node:readline";
3
3
  import * as fs from "node:fs";
4
4
  import * as path from "node:path";
5
- import { getAllPacks, getPacksForProjectType, getPack, listPackIds } from "@greenarmor/ges-policy-engine";
5
+ import { getAllPacks, getPacksForProjectType, getPack, listPackIds, PRIVACY_COUNTRIES, getCountryByCode } from "@greenarmor/ges-policy-engine";
6
6
  const PE = ["process", "env"].join(".");
7
7
  const HT = ["http", "//"].join(":");
8
8
  import { generateScoreFile, formatScoreOutput, computeGrade, generateBadgeSvg, injectBadgeIntoReadme, generateScoreExplainer } from "@greenarmor/ges-scoring-engine";
9
9
  import { runAudit, deduplicateFindings } from "@greenarmor/ges-audit-engine";
10
10
  import { GESF_VERSION, GES_DIR, COMPLIANCE_DIR, SECURITY_DIR, CONTROLS_DIR, POLICIES_DIR, CHECKLISTS_DIR, DOCS_DIR, REPORTS_DIR, DEFAULT_FRAMEWORKS } from "@greenarmor/ges-core";
11
11
  import { appendFixHistory, createFixHistoryEntry } from "@greenarmor/ges-core";
12
- import { addFrameworkToConfig, removeFrameworkFromConfig, loadControlsFromDisk, recordActivity, recordAIRecommendation } from "@greenarmor/ges-core";
12
+ import { addFrameworkToConfig, removeFrameworkFromConfig, loadControlsFromDisk, getInstalledPackIds, recordActivity, recordAIRecommendation } from "@greenarmor/ges-core";
13
13
  import { ProjectConfigSchema } from "@greenarmor/ges-core";
14
14
  import { generateComplianceDocs, generateSecurityDocs, generateConfigJson, generateMetadataJson, generateFrameworkVersionJson, generateScoreJson } from "@greenarmor/ges-doc-generator";
15
15
  import { generateAllWorkflows } from "@greenarmor/ges-cicd-generator";
@@ -246,7 +246,7 @@ const TOOLS = [
246
246
  },
247
247
  {
248
248
  name: "init_project",
249
- description: "Initialize GESF in a project directory. Creates the .ges/ directory structure, compliance/security documentation, controls, CI/CD workflows, and configuration files.",
249
+ description: "Initialize GESF in a project directory. Creates the .ges/ directory structure, compliance/security documentation, controls, CI/CD workflows, and configuration files. Automatically installs the privacy pack for the specified country of origin.",
250
250
  inputSchema: {
251
251
  type: "object",
252
252
  properties: {
@@ -254,11 +254,26 @@ const TOOLS = [
254
254
  project_name: { type: "string", description: "Project name (defaults to directory name)." },
255
255
  project_type: { type: "string", description: "Project type (saas, ai-application, mcp-server, blockchain, wallet, government-system, healthcare-system, event-platform, photo-storage-platform, vulnerability-scanner, generic-web-application, api-backend, mobile-application)." },
256
256
  frameworks: { type: "string", description: "Comma-separated framework names (default: GDPR,OWASP,CIS,NIST)." },
257
+ country: { type: "string", description: "Country/region code of origin (e.g., BR, CA, US-CA, GB, CH, SG, PH, JP, KR, CN, IN, ZA, AE, SA, EU). Determines which privacy pack is auto-installed." },
258
+ additional_packs: { type: "string", description: "Comma-separated additional privacy pack IDs to install (e.g., uk-gdpr,br-lgpd). Optional." },
257
259
  force: { type: "boolean", description: "Re-initialize even if GESF is already set up (default: false)." },
258
260
  },
259
261
  required: ["project_path"],
260
262
  },
261
263
  },
264
+ {
265
+ name: "suggest_packs",
266
+ description: "Analyze the project and recommend which privacy/compliance policy packs to install. Examines project type, country of origin, installed packs, codebase patterns (dependencies, file structure), and data processing indicators. Returns ranked recommendations with rationale. Does NOT install packs — use policy_install to act on suggestions.",
267
+ inputSchema: {
268
+ type: "object",
269
+ properties: {
270
+ project_path: { type: "string", description: "Absolute path to the project root." },
271
+ country: { type: "string", description: "Optional country/region code to factor into recommendations (e.g., BR, US-CA, EU)." },
272
+ project_type: { type: "string", description: "Optional project type override (if not set, reads from config or infers from codebase)." },
273
+ },
274
+ required: ["project_path"],
275
+ },
276
+ },
262
277
  {
263
278
  name: "run_scans",
264
279
  description: "Run security scanner integrations on a project. Detects the ecosystem (Node.js, Python, etc.) and runs available scanners (npm audit, Trivy, Gitleaks, Semgrep, etc.) plus SBOM generation.",
@@ -2726,11 +2741,17 @@ export function handleRequest(request) {
2726
2741
  const projectType = (args.project_type || "generic-web-application");
2727
2742
  const frameworksStr = args.frameworks || DEFAULT_FRAMEWORKS.join(",");
2728
2743
  const frameworks = frameworksStr.split(",").map(f => f.trim());
2744
+ const countryCode = (args.country || "").toUpperCase();
2745
+ const countryInfo = countryCode ? getCountryByCode(countryCode) : undefined;
2746
+ const additionalPackIds = args.additional_packs
2747
+ ? String(args.additional_packs).split(",").map((s) => s.trim()).filter(Boolean)
2748
+ : [];
2729
2749
  const now = new Date().toISOString();
2730
2750
  const config = {
2731
2751
  project_name: projectName,
2732
2752
  project_type: projectType,
2733
2753
  frameworks,
2754
+ country: countryCode || undefined,
2734
2755
  requirements: {
2735
2756
  encryption: { required: true },
2736
2757
  mfa: { required: true },
@@ -2788,11 +2809,38 @@ export function handleRequest(request) {
2788
2809
  fs.mkdirSync(path.dirname(filePath), { recursive: true });
2789
2810
  fs.writeFileSync(filePath, doc.content);
2790
2811
  }
2791
- const packs = getPacksForProjectType(projectType);
2792
- for (const pack of packs) {
2793
- const packDir = path.join(projectPath, CONTROLS_DIR, pack.id);
2794
- fs.mkdirSync(packDir, { recursive: true });
2795
- fs.writeFileSync(path.join(packDir, "controls.json"), JSON.stringify(pack.controls, null, 2));
2812
+ const installedPackIds = new Set();
2813
+ // Domain packs from project type
2814
+ const projectPacks = getPacksForProjectType(projectType);
2815
+ const fwLower = new Set(frameworks.map((f) => f.toLowerCase()));
2816
+ const DOMAIN_PACKS = new Set(["ai", "blockchain", "government"]);
2817
+ for (const pack of projectPacks) {
2818
+ if (DOMAIN_PACKS.has(pack.id.toLowerCase()) || fwLower.has(pack.id.toLowerCase())) {
2819
+ installedPackIds.add(pack.id);
2820
+ }
2821
+ }
2822
+ // Privacy core (always installed)
2823
+ installedPackIds.add("privacy-core");
2824
+ // Country privacy pack (auto-selected)
2825
+ if (countryInfo) {
2826
+ installedPackIds.add(countryInfo.packId);
2827
+ }
2828
+ else if (countryCode === "EU") {
2829
+ installedPackIds.add("gdpr");
2830
+ }
2831
+ // Additional user-selected packs
2832
+ for (const packId of additionalPackIds) {
2833
+ installedPackIds.add(packId);
2834
+ }
2835
+ const packs = [];
2836
+ for (const packId of installedPackIds) {
2837
+ const pack = getPack(packId);
2838
+ if (pack) {
2839
+ const packDir = path.join(projectPath, CONTROLS_DIR, pack.id);
2840
+ fs.mkdirSync(packDir, { recursive: true });
2841
+ fs.writeFileSync(path.join(packDir, "controls.json"), JSON.stringify(pack.controls, null, 2));
2842
+ packs.push(pack);
2843
+ }
2796
2844
  }
2797
2845
  const workflows = generateAllWorkflows(config);
2798
2846
  const workflowsDir = path.join(projectPath, ".github", "workflows");
@@ -2805,6 +2853,12 @@ export function handleRequest(request) {
2805
2853
  lines.push(`**Project**: ${projectName}`);
2806
2854
  lines.push(`**Type**: ${projectType}`);
2807
2855
  lines.push(`**Frameworks**: ${frameworks.join(", ")}`);
2856
+ if (countryInfo) {
2857
+ lines.push(`**Country**: ${countryInfo.name} — ${countryInfo.lawName}`);
2858
+ }
2859
+ else if (countryCode === "EU") {
2860
+ lines.push(`**Region**: European Union (EU GDPR)`);
2861
+ }
2808
2862
  lines.push(`**Path**: ${projectPath}\n`);
2809
2863
  lines.push(`## Created Structure`);
2810
2864
  lines.push(`- \`.ges/\` — Configuration and score files`);
@@ -2823,11 +2877,196 @@ export function handleRequest(request) {
2823
2877
  source: "mcp",
2824
2878
  action: "init",
2825
2879
  title: `Project initialized: ${projectName}`,
2826
- description: `Initialized GESF for ${projectType} project with frameworks: ${frameworks.join(", ")}. Installed ${packs.length} policy packs.`,
2827
- details: { packs_affected: packs.map(p => p.id), frameworks_added: frameworks.map(f => String(f)) },
2880
+ description: `Initialized GESF for ${projectType} project${countryInfo ? ` in ${countryInfo.name}` : ""} with frameworks: ${frameworks.join(", ")}. Installed ${packs.length} policy packs: ${packs.map(p => p.id).join(", ")}.`,
2881
+ details: { packs_affected: packs.map(p => p.id), frameworks_added: frameworks.map(f => String(f)), country: countryCode || undefined },
2828
2882
  });
2829
2883
  break;
2830
2884
  }
2885
+ case "suggest_packs": {
2886
+ const projectPath = resolveProjectPath(args.project_path);
2887
+ if (!fs.existsSync(projectPath)) {
2888
+ resultText = `Project path does not exist: ${projectPath}`;
2889
+ break;
2890
+ }
2891
+ const allPacks = getAllPacks();
2892
+ const allPackIds = new Set(allPacks.map(p => p.id));
2893
+ // Read existing config
2894
+ const configPath = path.join(projectPath, GES_DIR, "config.json");
2895
+ let existingConfig = null;
2896
+ let detectedCountry = "";
2897
+ let detectedProjectType = "";
2898
+ if (fs.existsSync(configPath)) {
2899
+ try {
2900
+ const parsed = JSON.parse(fs.readFileSync(configPath, "utf-8"));
2901
+ existingConfig = parsed;
2902
+ detectedCountry = parsed.country || "";
2903
+ detectedProjectType = parsed.project_type;
2904
+ }
2905
+ catch { /* ignore parse errors */ }
2906
+ }
2907
+ // Override with args
2908
+ if (args.country)
2909
+ detectedCountry = String(args.country).toUpperCase();
2910
+ if (args.project_type)
2911
+ detectedProjectType = String(args.project_type);
2912
+ // Already installed packs
2913
+ const installedPackIdsSet = getInstalledPackIds(projectPath);
2914
+ const installedPackIds = [...installedPackIdsSet];
2915
+ const installedSet = installedPackIdsSet;
2916
+ // Codebase analysis
2917
+ const codebaseIndicators = [];
2918
+ const recommendations = [];
2919
+ // Check for package.json (Node.js)
2920
+ const pkgJsonPath = path.join(projectPath, "package.json");
2921
+ let hasAI = false, hasBlockchain = false, hasHealthcare = false;
2922
+ if (fs.existsSync(pkgJsonPath)) {
2923
+ codebaseIndicators.push("Node.js project (package.json detected)");
2924
+ try {
2925
+ const pkg = JSON.parse(fs.readFileSync(pkgJsonPath, "utf-8"));
2926
+ const allDeps = Object.keys({ ...(pkg.dependencies || {}), ...(pkg.devDependencies || {}) });
2927
+ const depLower = allDeps.join(" ").toLowerCase();
2928
+ if (depLower.includes("openai") || depLower.includes("langchain") || depLower.includes("anthropic") || depLower.includes("llm") || depLower.includes("ai-sdk")) {
2929
+ hasAI = true;
2930
+ codebaseIndicators.push("AI/LLM dependencies detected (openai/langchain/anthropic)");
2931
+ }
2932
+ if (depLower.includes("ethers") || depLower.includes("web3") || depLower.includes("solana") || depLower.includes("hardhat")) {
2933
+ hasBlockchain = true;
2934
+ codebaseIndicators.push("Blockchain dependencies detected (ethers/web3/hardhat)");
2935
+ }
2936
+ if (depLower.includes("fhir") || depLower.includes("hl7") || depLower.includes("medical")) {
2937
+ hasHealthcare = true;
2938
+ codebaseIndicators.push("Healthcare dependencies detected");
2939
+ }
2940
+ }
2941
+ catch { /* ignore */ }
2942
+ }
2943
+ // Check for Python
2944
+ const reqPath = path.join(projectPath, "requirements.txt");
2945
+ const pyprojPath = path.join(projectPath, "pyproject.toml");
2946
+ if (fs.existsSync(reqPath) || fs.existsSync(pyprojPath)) {
2947
+ codebaseIndicators.push("Python project detected");
2948
+ try {
2949
+ const pyContent = fs.existsSync(reqPath)
2950
+ ? fs.readFileSync(reqPath, "utf-8").toLowerCase()
2951
+ : fs.readFileSync(pyprojPath, "utf-8").toLowerCase();
2952
+ if (pyContent.includes("openai") || pyContent.includes("langchain") || pyContent.includes("anthropic") || pyContent.includes("torch")) {
2953
+ hasAI = true;
2954
+ codebaseIndicators.push("AI/ML Python dependencies detected");
2955
+ }
2956
+ }
2957
+ catch { /* ignore */ }
2958
+ }
2959
+ // Check for mobile
2960
+ if (fs.existsSync(path.join(projectPath, "android")) || fs.existsSync(path.join(projectPath, "ios"))) {
2961
+ codebaseIndicators.push("Mobile project detected (android/ios directories)");
2962
+ }
2963
+ // Check for Docker/deployment (indicates production data processing)
2964
+ if (fs.existsSync(path.join(projectPath, "Dockerfile")) || fs.existsSync(path.join(projectPath, "docker-compose.yml"))) {
2965
+ codebaseIndicators.push("Docker deployment detected (likely processes personal data in production)");
2966
+ }
2967
+ // --- Build recommendations ---
2968
+ // 1. Privacy core (always recommended)
2969
+ if (!installedSet.has("privacy-core")) {
2970
+ recommendations.push({ packId: "privacy-core", reason: "Universal baseline privacy controls (10 domains, 40 controls). Applicable to any project processing personal data.", priority: "critical" });
2971
+ }
2972
+ // 2. Country pack
2973
+ if (detectedCountry) {
2974
+ const countryInfo = getCountryByCode(detectedCountry);
2975
+ if (countryInfo) {
2976
+ if (!installedSet.has(countryInfo.packId)) {
2977
+ recommendations.push({ packId: countryInfo.packId, reason: `${countryInfo.name} privacy law: ${countryInfo.lawName}. Required for compliance with ${countryInfo.regulator}.`, priority: "critical" });
2978
+ }
2979
+ }
2980
+ else if (detectedCountry === "EU") {
2981
+ if (!installedSet.has("gdpr")) {
2982
+ recommendations.push({ packId: "gdpr", reason: "EU GDPR is the baseline for all European Economic Area data protection.", priority: "critical" });
2983
+ }
2984
+ }
2985
+ }
2986
+ // 3. AI pack
2987
+ if (hasAI && !installedSet.has("ai")) {
2988
+ recommendations.push({ packId: "ai", reason: "AI/LLM application detected. The AI policy pack adds controls for prompt logging, output validation, PII detection, and rate limiting.", priority: "high" });
2989
+ }
2990
+ // 4. Blockchain pack
2991
+ if (hasBlockchain && !installedSet.has("blockchain")) {
2992
+ recommendations.push({ packId: "blockchain", reason: "Blockchain application detected. Adds controls for cryptographic signatures, key rotation, and on-chain data protection.", priority: "high" });
2993
+ }
2994
+ // 5. HIPAA for healthcare
2995
+ if (hasHealthcare && !installedSet.has("hipaa")) {
2996
+ recommendations.push({ packId: "hipaa", reason: "Healthcare indicators detected. HIPAA controls are required for protected health information (PHI).", priority: "high" });
2997
+ }
2998
+ // 6. ISO 27001/27701 for production deployments
2999
+ if (!installedSet.has("iso27001") && codebaseIndicators.some(i => i.includes("Docker") || i.includes("production"))) {
3000
+ recommendations.push({ packId: "iso27001", reason: "Production deployment detected. ISO 27001 provides internationally recognized information security management controls.", priority: "medium" });
3001
+ }
3002
+ // 7. ISO 27701 (privacy extension)
3003
+ if (!installedSet.has("iso27701")) {
3004
+ recommendations.push({ packId: "iso27701", reason: "ISO 27701 extends ISO 27001 with privacy information management. Recommended for any organization acting as a data controller or processor.", priority: "medium" });
3005
+ }
3006
+ // --- Build output ---
3007
+ const lines = [];
3008
+ lines.push("# Policy Pack Recommendations\n");
3009
+ if (codebaseIndicators.length > 0) {
3010
+ lines.push("## Codebase Analysis");
3011
+ for (const indicator of codebaseIndicators) {
3012
+ lines.push(`- ${indicator}`);
3013
+ }
3014
+ lines.push("");
3015
+ }
3016
+ if (detectedCountry) {
3017
+ const countryInfo = getCountryByCode(detectedCountry);
3018
+ lines.push(`## Project Context`);
3019
+ lines.push(`- **Country/Region**: ${countryInfo ? `${countryInfo.name} (${detectedCountry})` : detectedCountry === "EU" ? "European Union" : detectedCountry}`);
3020
+ if (detectedProjectType)
3021
+ lines.push(`- **Project Type**: ${detectedProjectType}`);
3022
+ if (existingConfig)
3023
+ lines.push(`- **Frameworks Configured**: ${existingConfig.frameworks.join(", ")}`);
3024
+ lines.push("");
3025
+ }
3026
+ if (installedSet.size > 0) {
3027
+ lines.push(`## Already Installed (${installedPackIds.length} packs)`);
3028
+ lines.push(installedPackIds.map((id) => `- \`${id}\``).join("\n"));
3029
+ lines.push("");
3030
+ }
3031
+ if (recommendations.length === 0) {
3032
+ lines.push("## ✅ All Recommended Packs Installed");
3033
+ lines.push("No additional packs are recommended at this time. Your project has comprehensive coverage.");
3034
+ }
3035
+ else {
3036
+ const priorityOrder = { critical: 0, high: 1, medium: 2, low: 3 };
3037
+ recommendations.sort((a, b) => priorityOrder[a.priority] - priorityOrder[b.priority]);
3038
+ lines.push(`## Recommended Packs (${recommendations.length})\n`);
3039
+ lines.push("| Priority | Pack ID | Reason |");
3040
+ lines.push("|----------|---------|--------|");
3041
+ for (const rec of recommendations) {
3042
+ const pack = allPacks.find(p => p.id === rec.packId);
3043
+ const packName = pack ? pack.name : rec.packId;
3044
+ lines.push(`| ${rec.priority.toUpperCase()} | \`${rec.packId}\` (${packName}) | ${rec.reason} |`);
3045
+ }
3046
+ lines.push("");
3047
+ lines.push("### How to Install");
3048
+ lines.push("Use `policy_install` with the pack ID:");
3049
+ for (const rec of recommendations.slice(0, 3)) {
3050
+ lines.push(`- \`policy_install\` with pack_id: \`${rec.packId}\``);
3051
+ }
3052
+ if (recommendations.length > 3) {
3053
+ lines.push(`- ... and ${recommendations.length - 3} more`);
3054
+ }
3055
+ }
3056
+ lines.push("");
3057
+ lines.push("### Available Country Packs");
3058
+ const grouped = PRIVACY_COUNTRIES.reduce((acc, c) => {
3059
+ if (!acc[c.region])
3060
+ acc[c.region] = [];
3061
+ acc[c.region].push(c);
3062
+ return acc;
3063
+ }, {});
3064
+ for (const [region, countries] of Object.entries(grouped)) {
3065
+ lines.push(`- **${region}**: ${countries.map(c => `\`${c.packId}\` (${c.name})`).join(", ")}`);
3066
+ }
3067
+ resultText = lines.join("\n");
3068
+ break;
3069
+ }
2831
3070
  case "run_scans": {
2832
3071
  const projectPath = resolveProjectPath(args.project_path);
2833
3072
  if (!fs.existsSync(projectPath)) {
package/package.json CHANGED
@@ -3,17 +3,17 @@
3
3
  "ges-mcp": "dist/server.js"
4
4
  },
5
5
  "dependencies": {
6
- "@greenarmor/ges-audit-engine": "1.2.7",
7
- "@greenarmor/ges-cicd-generator": "1.2.7",
8
- "@greenarmor/ges-compliance-engine": "1.2.7",
9
- "@greenarmor/ges-core": "1.2.7",
10
- "@greenarmor/ges-doc-generator": "1.2.7",
11
- "@greenarmor/ges-policy-engine": "1.2.7",
12
- "@greenarmor/ges-report-generator": "1.2.7",
13
- "@greenarmor/ges-rules-engine": "1.2.7",
14
- "@greenarmor/ges-scanner-integration": "1.2.7",
15
- "@greenarmor/ges-scoring-engine": "1.2.7",
16
- "@greenarmor/ges-web-dashboard": "1.2.7"
6
+ "@greenarmor/ges-audit-engine": "1.3.0",
7
+ "@greenarmor/ges-cicd-generator": "1.3.0",
8
+ "@greenarmor/ges-compliance-engine": "1.3.0",
9
+ "@greenarmor/ges-core": "1.3.0",
10
+ "@greenarmor/ges-doc-generator": "1.3.0",
11
+ "@greenarmor/ges-policy-engine": "1.3.0",
12
+ "@greenarmor/ges-report-generator": "1.3.0",
13
+ "@greenarmor/ges-rules-engine": "1.3.0",
14
+ "@greenarmor/ges-scanner-integration": "1.3.0",
15
+ "@greenarmor/ges-scoring-engine": "1.3.0",
16
+ "@greenarmor/ges-web-dashboard": "1.3.0"
17
17
  },
18
18
  "description": "GESF MCP Server - AI Compliance Assistant for GDPR, OWASP, NIST, CIS. Check compliance, generate policies, assess risks via MCP protocol.",
19
19
  "devDependencies": {
@@ -67,7 +67,7 @@
67
67
  },
68
68
  "type": "module",
69
69
  "types": "./dist/index.d.ts",
70
- "version": "1.2.7",
70
+ "version": "1.3.0",
71
71
  "scripts": {
72
72
  "build": "tsc",
73
73
  "clean": "rm -rf dist bundle tsconfig.tsbuildinfo",