@greenarmor/ges-mcp-server 1.3.0 → 1.4.1

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 +676 -0
  2. package/package.json +12 -12
package/dist/server.js CHANGED
@@ -10,6 +10,7 @@ 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
12
  import { addFrameworkToConfig, removeFrameworkFromConfig, loadControlsFromDisk, getInstalledPackIds, recordActivity, recordAIRecommendation } from "@greenarmor/ges-core";
13
+ import { loadGovernanceRecords, createGovernanceRecord, addGovernanceRecord, findGovernanceRecord, setGovernanceApproval, addGovernanceEvidence, createEvidenceRef, verifyGovernanceRecord, setGovernanceRiskAssessment, setGovernancePolicyBasis, setGovernanceReviewCycle, setGovernanceDataInventory, setGovernanceComplianceLinks, setGovernanceCommittee, } from "@greenarmor/ges-core";
13
14
  import { ProjectConfigSchema } from "@greenarmor/ges-core";
14
15
  import { generateComplianceDocs, generateSecurityDocs, generateConfigJson, generateMetadataJson, generateFrameworkVersionJson, generateScoreJson } from "@greenarmor/ges-doc-generator";
15
16
  import { generateAllWorkflows } from "@greenarmor/ges-cicd-generator";
@@ -25,6 +26,94 @@ export function stopDashboardServer() {
25
26
  activeDashboardServer = null;
26
27
  }
27
28
  }
29
+ function formatGovernanceRecordOutput(record, verification) {
30
+ const lines = [
31
+ `# Governance Record: ${record.system_name}\n`,
32
+ `**ID**: ${record.id}`,
33
+ `**Type**: ${record.system_type}`,
34
+ `**Version**: ${record.system_version || "(none)"}`,
35
+ `**Status**: ${record.status}`,
36
+ `**Risk Level**: ${record.risk_level}`,
37
+ `**Verification**: ${verification.valid ? "✓ VALID" : "✕ ISSUES"} | Approval: ${verification.approval_status}`,
38
+ ``,
39
+ `## Risk Assessment`,
40
+ ];
41
+ if (record.risk_assessment) {
42
+ const ra = record.risk_assessment;
43
+ lines.push(`- **Assessor**: ${ra.assessor}`);
44
+ lines.push(`- **Methodology**: ${ra.methodology}`);
45
+ lines.push(`- **Risk Score**: ${ra.risk_score} | **Residual**: ${ra.residual_risk}`);
46
+ lines.push(`- **Date**: ${ra.assessment_date}`);
47
+ }
48
+ else {
49
+ lines.push(`⚠ NOT RECORDED`);
50
+ }
51
+ lines.push(`\n## Policy Basis`);
52
+ if (record.policy_basis) {
53
+ const pb = record.policy_basis;
54
+ lines.push(`- **Policy**: ${pb.policy_name} (${pb.policy_id} v${pb.version})`);
55
+ lines.push(`- **Standard**: ${pb.standard}`);
56
+ if (pb.clauses.length)
57
+ lines.push(`- **Clauses**: ${pb.clauses.join(", ")}`);
58
+ }
59
+ else {
60
+ lines.push(`⚠ NOT RECORDED`);
61
+ }
62
+ lines.push(`\n## Approval Decision`);
63
+ if (record.approval) {
64
+ const a = record.approval;
65
+ lines.push(`- **Approver**: ${a.approver_name} (${a.approver_role})`);
66
+ lines.push(`- **Authority**: ${a.approval_authority}`);
67
+ lines.push(`- **Decision**: ${a.decision.toUpperCase()}`);
68
+ lines.push(`- **Date**: ${a.decision_date}`);
69
+ lines.push(`- **Validity**: ${a.valid_from} → ${a.valid_until || "indefinite"}`);
70
+ if (a.conditions.length)
71
+ lines.push(`- **Conditions**: ${a.conditions.join("; ")}`);
72
+ if (a.rationale)
73
+ lines.push(`- **Rationale**: ${a.rationale}`);
74
+ }
75
+ else {
76
+ lines.push(`⚠ NOT RECORDED`);
77
+ }
78
+ lines.push(`\n## Committee Approval`);
79
+ if (record.committee) {
80
+ const c = record.committee;
81
+ lines.push(`- **Committee**: ${c.committee_name}`);
82
+ lines.push(`- **Meeting**: ${c.meeting_date} (${c.meeting_reference})`);
83
+ if (c.attendees.length)
84
+ lines.push(`- **Attendees**: ${c.attendees.join(", ")}`);
85
+ }
86
+ else {
87
+ lines.push(`(not required or not recorded)`);
88
+ }
89
+ lines.push(`\n## Evidence Chain (${record.evidence.length})`);
90
+ if (record.evidence.length === 0) {
91
+ lines.push(`⚠ NO EVIDENCE REFERENCES`);
92
+ }
93
+ else {
94
+ record.evidence.forEach((e, i) => {
95
+ lines.push(`${i + 1}. **${e.title}** — ${e.source_system}: ${e.reference}`);
96
+ });
97
+ }
98
+ lines.push(`\n## Review Cycle`);
99
+ if (record.review_cycle) {
100
+ const rc = record.review_cycle;
101
+ lines.push(`- **Frequency**: ${rc.frequency}`);
102
+ lines.push(`- **Last**: ${rc.last_review} | **Next**: ${rc.next_review}`);
103
+ }
104
+ else {
105
+ lines.push(`⚠ NOT DEFINED`);
106
+ }
107
+ if (verification.issues.length > 0) {
108
+ lines.push(`\n## Blocking Issues`);
109
+ verification.issues.forEach(i => lines.push(`- ✕ ${i}`));
110
+ }
111
+ if (verification.warnings.length > 0) {
112
+ lines.push(`\n## Warnings`);
113
+ verification.warnings.forEach(w => lines.push(`- △ ${w}`));
114
+ }
115
+ return lines.join("\n");
116
+ }
28
117
  const TOOLS = [
29
118
  {
30
119
  name: "check_compliance",
@@ -386,6 +475,213 @@ const TOOLS = [
386
475
  required: ["project_path", "category", "title", "description", "suggested_action"],
387
476
  },
388
477
  },
478
+ {
479
+ name: "create_governance_record",
480
+ description: "Create a new governance provenance record for an AI system, application, data process, or model. Establishes the root of the approval provenance chain (System → Risk → Policy → Approval → Evidence → Review). The record starts in 'draft' status — use approve_governance_record to record an approval decision and add_governance_evidence to attach supporting evidence.",
481
+ inputSchema: {
482
+ type: "object",
483
+ properties: {
484
+ project_path: { type: "string", description: "Absolute path to the project root." },
485
+ system_name: { type: "string", description: "Name of the system being governed (e.g., 'Customer Support Chatbot')." },
486
+ system_type: { type: "string", description: "System type: ai-system, application, data-process, api, model, infrastructure, or third-party-service." },
487
+ system_description: { type: "string", description: "Description of what the system does." },
488
+ system_version: { type: "string", description: "Version of the system (e.g., '1.2.0')." },
489
+ risk_level: { type: "string", description: "Risk level: low, medium, high, or critical." },
490
+ created_by: { type: "string", description: "Who is creating this record (name or role)." },
491
+ actor_name: { type: "string", description: "Name of the person performing this action." },
492
+ actor_role: { type: "string", description: "Role of the person performing this action." },
493
+ },
494
+ required: ["project_path", "system_name"],
495
+ },
496
+ },
497
+ {
498
+ name: "approve_governance_record",
499
+ description: "Record an approval decision on a governance record. Captures who approved the system, under what authority, when, the validity period, and any conditions. This is a critical link in the provenance chain — it answers 'Who approved this AI system? Under which authority? When? Is it still valid?'",
500
+ inputSchema: {
501
+ type: "object",
502
+ properties: {
503
+ project_path: { type: "string", description: "Absolute path to the project root." },
504
+ record_id: { type: "string", description: "Governance record ID or system name." },
505
+ approver_name: { type: "string", description: "Full name of the approver." },
506
+ approver_role: { type: "string", description: "Role/title of the approver (e.g., 'DPO', 'CISO')." },
507
+ approver_email: { type: "string", description: "Email of the approver." },
508
+ approval_authority: { type: "string", description: "The authority under which approval is granted (e.g., 'AI Ethics Board', 'Data Protection Committee')." },
509
+ decision: { type: "string", description: "Decision: approved, rejected, or conditional." },
510
+ valid_from: { type: "string", description: "Approval start date (YYYY-MM-DD). Defaults to today." },
511
+ valid_until: { type: "string", description: "Approval expiry date (YYYY-MM-DD). Leave empty for indefinite validity." },
512
+ conditions: { type: "string", description: "Conditions attached to approval (comma-separated)." },
513
+ rationale: { type: "string", description: "Rationale for the decision." },
514
+ actor_name: { type: "string", description: "Name of the person performing this action." },
515
+ actor_role: { type: "string", description: "Role of the person performing this action." },
516
+ },
517
+ required: ["project_path", "record_id", "approver_name", "approver_role", "approval_authority", "decision"],
518
+ },
519
+ },
520
+ {
521
+ name: "add_governance_evidence",
522
+ description: "Add an evidence reference to a governance record. Evidence references point to supporting documentation in external systems (Jira, Confluence, ServiceNow, SharePoint, GRC platforms, etc.) WITHOUT duplicating their content. This maintains a single source of truth while providing a unified governance view. Each evidence entry answers 'What evidence exists to support the decision?'",
523
+ inputSchema: {
524
+ type: "object",
525
+ properties: {
526
+ project_path: { type: "string", description: "Absolute path to the project root." },
527
+ record_id: { type: "string", description: "Governance record ID or system name." },
528
+ title: { type: "string", description: "Title of the evidence (e.g., 'DPIA Report Q4 2026')." },
529
+ type: { type: "string", description: "Evidence type: document, ticket, meeting-record, email, report, certificate, log, dashboard, contract, or other." },
530
+ source_system: { type: "string", description: "Source system: jira, servicenow, confluence, sharepoint, grc-platform, email, git, file, url, or other." },
531
+ reference: { type: "string", description: "Reference identifier (ticket ID, URL, document name, or path)." },
532
+ location_description: { type: "string", description: "Human-readable description of where to find the evidence." },
533
+ added_by: { type: "string", description: "Who is adding this evidence." },
534
+ actor_name: { type: "string", description: "Name of the person performing this action." },
535
+ actor_role: { type: "string", description: "Role of the person performing this action." },
536
+ },
537
+ required: ["project_path", "record_id", "title", "source_system", "reference"],
538
+ },
539
+ },
540
+ {
541
+ name: "list_governance_records",
542
+ description: "List all governance provenance records in a project. Returns a summary of each record including system name, status, risk level, approval status, and evidence count. Use this to see all governed systems and their current compliance state.",
543
+ inputSchema: {
544
+ type: "object",
545
+ properties: {
546
+ project_path: { type: "string", description: "Absolute path to the project root." },
547
+ },
548
+ required: ["project_path"],
549
+ },
550
+ },
551
+ {
552
+ name: "get_governance_record",
553
+ description: "Get the full provenance chain for a single governance record. Returns all linked dimensions: system identity, risk assessment, policy basis, approval decision, committee approval, evidence chain, review cycle, data inventory, and compliance links. This provides the complete 'single defensible answer' for auditors — the full chain from AI System → Risk Assessment → Policy → Approval → Evidence → Review Cycle.",
554
+ inputSchema: {
555
+ type: "object",
556
+ properties: {
557
+ project_path: { type: "string", description: "Absolute path to the project root." },
558
+ record_id: { type: "string", description: "Governance record ID or system name." },
559
+ },
560
+ required: ["project_path", "record_id"],
561
+ },
562
+ },
563
+ {
564
+ name: "verify_governance_record",
565
+ description: "Verify the provenance chain completeness of a governance record. Checks all dimensions: approval exists and is not expired, risk assessment present, evidence attached, review cycle defined, data inventory complete. Returns a verification result with completeness checklist, blocking issues, warnings, and approval expiry status. This is the 'single defensible answer' function for auditors.",
566
+ inputSchema: {
567
+ type: "object",
568
+ properties: {
569
+ project_path: { type: "string", description: "Absolute path to the project root." },
570
+ record_id: { type: "string", description: "Governance record ID or system name." },
571
+ },
572
+ required: ["project_path", "record_id"],
573
+ },
574
+ },
575
+ {
576
+ name: "set_governance_risk_assessment",
577
+ description: "Link a risk assessment to a governance record. Captures assessor, methodology, risk score, identified risks, residual risk, and mitigation measures. This is a required dimension for provenance chain completeness — without it, verification will report a blocking issue.",
578
+ inputSchema: {
579
+ type: "object",
580
+ properties: {
581
+ project_path: { type: "string", description: "Absolute path to the project root." },
582
+ record_id: { type: "string", description: "Governance record ID or system name." },
583
+ assessor: { type: "string", description: "Name of the risk assessor." },
584
+ methodology: { type: "string", description: "Assessment methodology used (e.g., NIST RMF, ISO 27005)." },
585
+ risk_score: { type: "string", description: "Risk score (e.g., '7.5/10', 'High')." },
586
+ residual_risk: { type: "string", description: "Residual risk after mitigations." },
587
+ identified_risks: { type: "string", description: "Identified risks (comma-separated)." },
588
+ mitigation_measures: { type: "string", description: "Mitigation measures (comma-separated)." },
589
+ actor_name: { type: "string", description: "Name of the person performing this action." },
590
+ actor_role: { type: "string", description: "Role of the person performing this action." },
591
+ },
592
+ required: ["project_path", "record_id", "assessor", "methodology", "risk_score"],
593
+ },
594
+ },
595
+ {
596
+ name: "set_governance_policy_basis",
597
+ description: "Document the policy or standard under which a system is approved. Captures policy ID, name, version, applicable clauses, and the standard reference. This provides the regulatory basis for the approval decision.",
598
+ inputSchema: {
599
+ type: "object",
600
+ properties: {
601
+ project_path: { type: "string", description: "Absolute path to the project root." },
602
+ record_id: { type: "string", description: "Governance record ID or system name." },
603
+ policy_id: { type: "string", description: "Policy identifier." },
604
+ policy_name: { type: "string", description: "Policy name." },
605
+ version: { type: "string", description: "Policy version." },
606
+ standard: { type: "string", description: "Standard reference (e.g., GDPR, ISO 27001)." },
607
+ clauses: { type: "string", description: "Applicable clauses (comma-separated)." },
608
+ actor_name: { type: "string", description: "Name of the person performing this action." },
609
+ actor_role: { type: "string", description: "Role of the person performing this action." },
610
+ },
611
+ required: ["project_path", "record_id", "policy_name", "standard"],
612
+ },
613
+ },
614
+ {
615
+ name: "set_governance_review_cycle",
616
+ description: "Set up a periodic review cycle for a governance record. Defines how often the approval must be re-reviewed and when the next review is due. Enables continuous compliance monitoring.",
617
+ inputSchema: {
618
+ type: "object",
619
+ properties: {
620
+ project_path: { type: "string", description: "Absolute path to the project root." },
621
+ record_id: { type: "string", description: "Governance record ID or system name." },
622
+ frequency: { type: "string", description: "Review frequency: quarterly, semi-annual, annual, or biennial." },
623
+ next_review: { type: "string", description: "Next review date (YYYY-MM-DD)." },
624
+ actor_name: { type: "string", description: "Name of the person performing this action." },
625
+ actor_role: { type: "string", description: "Role of the person performing this action." },
626
+ },
627
+ required: ["project_path", "record_id", "frequency"],
628
+ },
629
+ },
630
+ {
631
+ name: "set_governance_data_inventory",
632
+ description: "Document the data inventory for a governance record. Captures personal data categories, processing purposes, data subjects, cross-border transfers, and retention period.",
633
+ inputSchema: {
634
+ type: "object",
635
+ properties: {
636
+ project_path: { type: "string", description: "Absolute path to the project root." },
637
+ record_id: { type: "string", description: "Governance record ID or system name." },
638
+ personal_data_categories: { type: "string", description: "Personal data categories (comma-separated, e.g., names, emails, IP addresses)." },
639
+ processing_purposes: { type: "string", description: "Processing purposes (comma-separated)." },
640
+ data_subjects: { type: "string", description: "Data subject types (comma-separated, e.g., customers, employees)." },
641
+ cross_border_transfers: { type: "string", description: "Cross-border transfer destinations (comma-separated)." },
642
+ retention_period: { type: "string", description: "Data retention period." },
643
+ actor_name: { type: "string", description: "Name of the person performing this action." },
644
+ actor_role: { type: "string", description: "Role of the person performing this action." },
645
+ },
646
+ required: ["project_path", "record_id"],
647
+ },
648
+ },
649
+ {
650
+ name: "set_governance_committee",
651
+ description: "Record committee approval details for a governance record. Captures committee name, meeting date, meeting reference, attendees, and decision summary.",
652
+ inputSchema: {
653
+ type: "object",
654
+ properties: {
655
+ project_path: { type: "string", description: "Absolute path to the project root." },
656
+ record_id: { type: "string", description: "Governance record ID or system name." },
657
+ committee_name: { type: "string", description: "Name of the approving committee." },
658
+ meeting_date: { type: "string", description: "Meeting date (YYYY-MM-DD)." },
659
+ meeting_reference: { type: "string", description: "Meeting reference or minutes ID." },
660
+ attendees: { type: "string", description: "Attendee names (comma-separated)." },
661
+ decision_summary: { type: "string", description: "Summary of the committee decision." },
662
+ actor_name: { type: "string", description: "Name of the person performing this action." },
663
+ actor_role: { type: "string", description: "Role of the person performing this action." },
664
+ },
665
+ required: ["project_path", "record_id", "committee_name"],
666
+ },
667
+ },
668
+ {
669
+ name: "set_governance_compliance_links",
670
+ description: "Map compliance frameworks and controls to a governance record. Links the record to GDPR, OWASP, CIS, NIST, etc., and references which controls are satisfied by this system's governance.",
671
+ inputSchema: {
672
+ type: "object",
673
+ properties: {
674
+ project_path: { type: "string", description: "Absolute path to the project root." },
675
+ record_id: { type: "string", description: "Governance record ID or system name." },
676
+ frameworks: { type: "string", description: "Compliance frameworks (comma-separated, e.g., GDPR,OWASP)." },
677
+ controls_satisfied: { type: "string", description: "Control IDs satisfied (comma-separated)." },
678
+ control_pack_ids: { type: "string", description: "Control pack IDs (comma-separated)." },
679
+ actor_name: { type: "string", description: "Name of the person performing this action." },
680
+ actor_role: { type: "string", description: "Role of the person performing this action." },
681
+ },
682
+ required: ["project_path", "record_id"],
683
+ },
684
+ },
389
685
  ];
390
686
  function send(message) {
391
687
  process.stdout.write(JSON.stringify(message) + "\n");
@@ -3481,6 +3777,386 @@ export function handleRequest(request) {
3481
3777
  ].join("\n");
3482
3778
  break;
3483
3779
  }
3780
+ case "create_governance_record": {
3781
+ const projectPath = resolveProjectPath(args.project_path);
3782
+ if (!fs.existsSync(projectPath)) {
3783
+ resultText = `Project path does not exist: ${projectPath}`;
3784
+ break;
3785
+ }
3786
+ const systemName = args.system_name || "";
3787
+ if (!systemName) {
3788
+ resultText = "Error: system_name is required.";
3789
+ break;
3790
+ }
3791
+ const systemType = (args.system_type || "ai-system");
3792
+ const riskLevel = (args.risk_level || "medium");
3793
+ const record = createGovernanceRecord({
3794
+ system_name: systemName,
3795
+ system_description: args.system_description || "",
3796
+ system_type: systemType,
3797
+ system_version: args.system_version || "",
3798
+ risk_level: riskLevel,
3799
+ created_by: args.created_by || "mcp",
3800
+ });
3801
+ addGovernanceRecord(projectPath, record);
3802
+ recordActivity(projectPath, {
3803
+ source: "mcp",
3804
+ action: "control_override",
3805
+ title: `Governance record created: ${systemName}`,
3806
+ description: `Created governance record for ${systemName} (${systemType}, risk: ${riskLevel}). Record ID: ${record.id}`,
3807
+ details: { governance_record_id: record.id },
3808
+ actor_name: args.actor_name,
3809
+ actor_role: args.actor_role,
3810
+ });
3811
+ resultText = [
3812
+ `# Governance Record Created\n`,
3813
+ `**ID**: ${record.id}`,
3814
+ `**System**: ${record.system_name}`,
3815
+ `**Type**: ${record.system_type}`,
3816
+ `**Risk Level**: ${record.risk_level}`,
3817
+ `**Status**: ${record.status}`,
3818
+ ``,
3819
+ `## Next Steps`,
3820
+ `- Use \`approve_governance_record\` to record an approval decision`,
3821
+ `- Use \`add_governance_evidence\` to attach supporting evidence references`,
3822
+ `- Use \`verify_governance_record\` to check provenance completeness`,
3823
+ ``,
3824
+ `The record is stored in \`.ges/governance-records.json\`.`,
3825
+ ].join("\n");
3826
+ break;
3827
+ }
3828
+ case "approve_governance_record": {
3829
+ const projectPath = resolveProjectPath(args.project_path);
3830
+ const recordId = args.record_id || "";
3831
+ if (!fs.existsSync(projectPath)) {
3832
+ resultText = `Project path does not exist: ${projectPath}`;
3833
+ break;
3834
+ }
3835
+ const record = findGovernanceRecord(projectPath, recordId);
3836
+ if (!record) {
3837
+ resultText = `Error: Governance record "${recordId}" not found. Use list_governance_records to see available records.`;
3838
+ break;
3839
+ }
3840
+ const approverName = args.approver_name || "";
3841
+ const approverRole = args.approver_role || "";
3842
+ if (!approverName || !approverRole) {
3843
+ resultText = "Error: approver_name and approver_role are required.";
3844
+ break;
3845
+ }
3846
+ const decision = (args.decision || "approved");
3847
+ const validFrom = args.valid_from || new Date().toISOString().split("T")[0];
3848
+ const updated = setGovernanceApproval(projectPath, record.id, {
3849
+ approver_name: approverName,
3850
+ approver_role: approverRole,
3851
+ approver_email: args.approver_email || "",
3852
+ approval_authority: args.approval_authority || "",
3853
+ decision,
3854
+ decision_date: new Date().toISOString(),
3855
+ valid_from: validFrom,
3856
+ valid_until: args.valid_until || null,
3857
+ conditions: args.conditions ? String(args.conditions).split(",").map((s) => s.trim()).filter(Boolean) : [],
3858
+ rationale: args.rationale || "",
3859
+ }, "mcp");
3860
+ if (!updated) {
3861
+ resultText = `Error: Failed to update record.`;
3862
+ break;
3863
+ }
3864
+ recordActivity(projectPath, {
3865
+ source: "mcp",
3866
+ action: "control_override",
3867
+ title: `Governance approval: ${updated.system_name} → ${decision}`,
3868
+ description: `${approverName} (${approverRole}) marked ${updated.system_name} as ${decision}. Valid until: ${args.valid_until || "indefinite"}.`,
3869
+ details: { governance_record_id: updated.id, decision },
3870
+ actor_name: args.actor_name,
3871
+ actor_role: args.actor_role,
3872
+ });
3873
+ resultText = [
3874
+ `# Approval Recorded\n`,
3875
+ `**System**: ${updated.system_name}`,
3876
+ `**Decision**: ${decision.toUpperCase()}`,
3877
+ `**Approver**: ${approverName} (${approverRole})`,
3878
+ `**Authority**: ${args.approval_authority || "(not specified)"}`,
3879
+ `**Valid**: ${validFrom} → ${args.valid_until || "indefinite"}`,
3880
+ ``,
3881
+ `The approval decision is now part of the provenance chain.`,
3882
+ ].join("\n");
3883
+ break;
3884
+ }
3885
+ case "add_governance_evidence": {
3886
+ const projectPath = resolveProjectPath(args.project_path);
3887
+ const recordId = args.record_id || "";
3888
+ if (!fs.existsSync(projectPath)) {
3889
+ resultText = `Project path does not exist: ${projectPath}`;
3890
+ break;
3891
+ }
3892
+ const record = findGovernanceRecord(projectPath, recordId);
3893
+ if (!record) {
3894
+ resultText = `Error: Governance record "${recordId}" not found.`;
3895
+ break;
3896
+ }
3897
+ const title = args.title || "";
3898
+ const sourceSystem = (args.source_system || "other");
3899
+ const reference = args.reference || "";
3900
+ if (!title || !reference) {
3901
+ resultText = "Error: title and reference are required.";
3902
+ break;
3903
+ }
3904
+ const evidenceType = (args.type || "document");
3905
+ const evidence = createEvidenceRef({
3906
+ type: evidenceType,
3907
+ title,
3908
+ source_system: sourceSystem,
3909
+ reference,
3910
+ location_description: args.location_description || "",
3911
+ added_by: args.added_by || "mcp",
3912
+ });
3913
+ const updated = addGovernanceEvidence(projectPath, record.id, evidence, "mcp");
3914
+ if (!updated) {
3915
+ resultText = `Error: Failed to add evidence.`;
3916
+ break;
3917
+ }
3918
+ recordActivity(projectPath, {
3919
+ source: "mcp",
3920
+ action: "control_override",
3921
+ title: `Evidence added: ${evidence.title}`,
3922
+ description: `Added evidence reference "${evidence.title}" (${evidence.source_system}: ${evidence.reference}) to governance record ${updated.system_name}.`,
3923
+ details: { governance_record_id: updated.id, evidence_id: evidence.id },
3924
+ actor_name: args.actor_name,
3925
+ actor_role: args.actor_role,
3926
+ });
3927
+ resultText = [
3928
+ `# Evidence Added\n`,
3929
+ `**System**: ${updated.system_name}`,
3930
+ `**Evidence**: ${evidence.title}`,
3931
+ `**Source**: ${evidence.source_system}`,
3932
+ `**Reference**: ${evidence.reference}`,
3933
+ `**Total Evidence**: ${updated.evidence.length} reference(s)`,
3934
+ ].join("\n");
3935
+ break;
3936
+ }
3937
+ case "list_governance_records": {
3938
+ const projectPath = resolveProjectPath(args.project_path);
3939
+ if (!fs.existsSync(projectPath)) {
3940
+ resultText = `Project path does not exist: ${projectPath}`;
3941
+ break;
3942
+ }
3943
+ const records = loadGovernanceRecords(projectPath);
3944
+ if (records.length === 0) {
3945
+ resultText = "No governance records found. Use create_governance_record to create one.";
3946
+ break;
3947
+ }
3948
+ const lines = [`# Governance Records (${records.length})\n`];
3949
+ records.forEach(r => {
3950
+ const approvalInfo = r.approval
3951
+ ? `${r.approval.decision} by ${r.approval.approver_name} → ${r.approval.valid_until || "indefinite"}`
3952
+ : "NOT RECORDED";
3953
+ lines.push(`- **${r.system_name}** (${r.id})`);
3954
+ lines.push(` - Type: ${r.system_type} | Risk: ${r.risk_level} | Status: ${r.status}`);
3955
+ lines.push(` - Approval: ${approvalInfo}`);
3956
+ lines.push(` - Evidence: ${r.evidence.length} reference(s)`);
3957
+ });
3958
+ resultText = lines.join("\n");
3959
+ break;
3960
+ }
3961
+ case "get_governance_record": {
3962
+ const projectPath = resolveProjectPath(args.project_path);
3963
+ const recordId = args.record_id || "";
3964
+ if (!fs.existsSync(projectPath)) {
3965
+ resultText = `Project path does not exist: ${projectPath}`;
3966
+ break;
3967
+ }
3968
+ const record = findGovernanceRecord(projectPath, recordId);
3969
+ if (!record) {
3970
+ resultText = `Error: Governance record "${recordId}" not found.`;
3971
+ break;
3972
+ }
3973
+ const verification = verifyGovernanceRecord(record);
3974
+ resultText = formatGovernanceRecordOutput(record, verification);
3975
+ break;
3976
+ }
3977
+ case "verify_governance_record": {
3978
+ const projectPath = resolveProjectPath(args.project_path);
3979
+ const recordId = args.record_id || "";
3980
+ if (!fs.existsSync(projectPath)) {
3981
+ resultText = `Project path does not exist: ${projectPath}`;
3982
+ break;
3983
+ }
3984
+ const record = findGovernanceRecord(projectPath, recordId);
3985
+ if (!record) {
3986
+ resultText = `Error: Governance record "${recordId}" not found.`;
3987
+ break;
3988
+ }
3989
+ const result = verifyGovernanceRecord(record);
3990
+ const lines = [
3991
+ `# Governance Verification: ${record.system_name}\n`,
3992
+ `**Overall**: ${result.valid ? "✓ VALID" : "✕ ISSUES FOUND"}`,
3993
+ `**Approval Status**: ${result.approval_status.toUpperCase()}`,
3994
+ ];
3995
+ if (result.days_until_expiry !== null) {
3996
+ const dayLabel = result.days_until_expiry < 0
3997
+ ? `${Math.abs(result.days_until_expiry)} days AGO (EXPIRED)`
3998
+ : `${result.days_until_expiry} days remaining`;
3999
+ lines.push(`**Expiry**: ${dayLabel}`);
4000
+ }
4001
+ lines.push(`\n## Completeness Checklist`);
4002
+ lines.push(`- ${result.completeness.has_approval ? "✓" : "✕"} Approval Decision`);
4003
+ lines.push(`- ${result.completeness.has_risk_assessment ? "✓" : "✕"} Risk Assessment`);
4004
+ lines.push(`- ${result.completeness.has_policy_basis ? "✓" : "✕"} Policy Basis`);
4005
+ lines.push(`- ${result.completeness.has_evidence ? "✓" : "✕"} Evidence Chain (${result.completeness.evidence_count} refs)`);
4006
+ lines.push(`- ${result.completeness.has_review_cycle ? "✓" : "△"} Review Cycle`);
4007
+ lines.push(`- ${result.completeness.has_data_inventory ? "✓" : "△"} Data Inventory`);
4008
+ lines.push(`- ${result.completeness.has_compliance_links ? "✓" : "△"} Compliance Links`);
4009
+ lines.push(`- ${result.completeness.is_current ? "✓" : "✕"} Currently Valid`);
4010
+ if (result.issues.length > 0) {
4011
+ lines.push(`\n## Blocking Issues`);
4012
+ result.issues.forEach(i => lines.push(`- ✕ ${i}`));
4013
+ }
4014
+ if (result.warnings.length > 0) {
4015
+ lines.push(`\n## Warnings`);
4016
+ result.warnings.forEach(w => lines.push(`- △ ${w}`));
4017
+ }
4018
+ resultText = lines.join("\n");
4019
+ break;
4020
+ }
4021
+ case "set_governance_risk_assessment": {
4022
+ const projectPath = resolveProjectPath(args.project_path);
4023
+ const recordId = args.record_id || "";
4024
+ const record = findGovernanceRecord(projectPath, recordId);
4025
+ if (!record) {
4026
+ resultText = `Error: Governance record "${recordId}" not found.`;
4027
+ break;
4028
+ }
4029
+ const assessor = args.assessor || "";
4030
+ const methodology = args.methodology || "";
4031
+ const riskScore = args.risk_score || "";
4032
+ const residualRisk = args.residual_risk || "";
4033
+ const identifiedRisks = args.identified_risks ? String(args.identified_risks).split(",").map((s) => s.trim()).filter(Boolean) : [];
4034
+ const mitigations = args.mitigation_measures ? String(args.mitigation_measures).split(",").map((s) => s.trim()).filter(Boolean) : [];
4035
+ const updated = setGovernanceRiskAssessment(projectPath, record.id, {
4036
+ id: `risk-${Date.now()}`,
4037
+ assessor,
4038
+ assessment_date: new Date().toISOString(),
4039
+ methodology,
4040
+ risk_score: riskScore,
4041
+ identified_risks: identifiedRisks,
4042
+ residual_risk: residualRisk,
4043
+ mitigation_measures: mitigations,
4044
+ evidence: [],
4045
+ }, args.actor_name || "mcp");
4046
+ if (!updated) {
4047
+ resultText = "Error: Failed to update record.";
4048
+ break;
4049
+ }
4050
+ recordActivity(projectPath, { source: "mcp", action: "control_override", title: `Risk assessment: ${updated.system_name}`, description: `Risk assessment by ${assessor} linked. Score: ${riskScore}, Residual: ${residualRisk}.`, details: { governance_record_id: updated.id }, actor_name: args.actor_name, actor_role: args.actor_role });
4051
+ resultText = `# Risk Assessment Linked\n\n**System**: ${updated.system_name}\n**Assessor**: ${assessor}\n**Methodology**: ${methodology}\n**Score**: ${riskScore} | **Residual**: ${residualRisk}`;
4052
+ break;
4053
+ }
4054
+ case "set_governance_policy_basis": {
4055
+ const projectPath = resolveProjectPath(args.project_path);
4056
+ const recordId = args.record_id || "";
4057
+ const record = findGovernanceRecord(projectPath, recordId);
4058
+ if (!record) {
4059
+ resultText = `Error: Governance record "${recordId}" not found.`;
4060
+ break;
4061
+ }
4062
+ const policyId = args.policy_id || "";
4063
+ const policyName = args.policy_name || "";
4064
+ const version = args.version || "1.0";
4065
+ const standard = args.standard || "";
4066
+ const clauses = args.clauses ? String(args.clauses).split(",").map((s) => s.trim()).filter(Boolean) : [];
4067
+ const updated = setGovernancePolicyBasis(projectPath, record.id, { policy_id: policyId, policy_name: policyName, version, clauses, standard, evidence: [] }, args.actor_name || "mcp");
4068
+ if (!updated) {
4069
+ resultText = "Error: Failed to update record.";
4070
+ break;
4071
+ }
4072
+ recordActivity(projectPath, { source: "mcp", action: "control_override", title: `Policy basis: ${updated.system_name}`, description: `Policy ${policyName} (${policyId} v${version}) documented.`, details: { governance_record_id: updated.id }, actor_name: args.actor_name, actor_role: args.actor_role });
4073
+ resultText = `# Policy Basis Documented\n\n**System**: ${updated.system_name}\n**Policy**: ${policyName} (${policyId} v${version})\n**Standard**: ${standard}`;
4074
+ break;
4075
+ }
4076
+ case "set_governance_review_cycle": {
4077
+ const projectPath = resolveProjectPath(args.project_path);
4078
+ const recordId = args.record_id || "";
4079
+ const record = findGovernanceRecord(projectPath, recordId);
4080
+ if (!record) {
4081
+ resultText = `Error: Governance record "${recordId}" not found.`;
4082
+ break;
4083
+ }
4084
+ const frequency = (args.frequency || "annual");
4085
+ const today = new Date().toISOString().split("T")[0];
4086
+ const nextReview = args.next_review || today;
4087
+ const updated = setGovernanceReviewCycle(projectPath, record.id, { frequency, last_review: today, next_review: nextReview, review_history: [] }, args.actor_name || "mcp");
4088
+ if (!updated) {
4089
+ resultText = "Error: Failed to update record.";
4090
+ break;
4091
+ }
4092
+ recordActivity(projectPath, { source: "mcp", action: "control_override", title: `Review cycle: ${updated.system_name}`, description: `Review cycle (${frequency}) set. Next: ${nextReview}.`, details: { governance_record_id: updated.id }, actor_name: args.actor_name, actor_role: args.actor_role });
4093
+ resultText = `# Review Cycle Set\n\n**System**: ${updated.system_name}\n**Frequency**: ${frequency}\n**Next Review**: ${nextReview}`;
4094
+ break;
4095
+ }
4096
+ case "set_governance_data_inventory": {
4097
+ const projectPath = resolveProjectPath(args.project_path);
4098
+ const recordId = args.record_id || "";
4099
+ const record = findGovernanceRecord(projectPath, recordId);
4100
+ if (!record) {
4101
+ resultText = `Error: Governance record "${recordId}" not found.`;
4102
+ break;
4103
+ }
4104
+ const categories = args.personal_data_categories ? String(args.personal_data_categories).split(",").map((s) => s.trim()).filter(Boolean) : [];
4105
+ const purposes = args.processing_purposes ? String(args.processing_purposes).split(",").map((s) => s.trim()).filter(Boolean) : [];
4106
+ const subjects = args.data_subjects ? String(args.data_subjects).split(",").map((s) => s.trim()).filter(Boolean) : [];
4107
+ const transfers = args.cross_border_transfers ? String(args.cross_border_transfers).split(",").map((s) => s.trim()).filter(Boolean) : [];
4108
+ const retention = args.retention_period || "";
4109
+ const updated = setGovernanceDataInventory(projectPath, record.id, { personal_data_categories: categories, processing_purposes: purposes, data_subjects: subjects, cross_border_transfers: transfers, retention_period: retention }, args.actor_name || "mcp");
4110
+ if (!updated) {
4111
+ resultText = "Error: Failed to update record.";
4112
+ break;
4113
+ }
4114
+ recordActivity(projectPath, { source: "mcp", action: "control_override", title: `Data inventory: ${updated.system_name}`, description: `Data inventory documented (${categories.length} categories).`, details: { governance_record_id: updated.id }, actor_name: args.actor_name, actor_role: args.actor_role });
4115
+ resultText = `# Data Inventory Documented\n\n**System**: ${updated.system_name}\n**Categories**: ${categories.length}\n**Retention**: ${retention || "(not set)"}`;
4116
+ break;
4117
+ }
4118
+ case "set_governance_committee": {
4119
+ const projectPath = resolveProjectPath(args.project_path);
4120
+ const recordId = args.record_id || "";
4121
+ const record = findGovernanceRecord(projectPath, recordId);
4122
+ if (!record) {
4123
+ resultText = `Error: Governance record "${recordId}" not found.`;
4124
+ break;
4125
+ }
4126
+ const committeeName = args.committee_name || "";
4127
+ const meetingDate = args.meeting_date || "";
4128
+ const meetingRef = args.meeting_reference || "";
4129
+ const attendees = args.attendees ? String(args.attendees).split(",").map((s) => s.trim()).filter(Boolean) : [];
4130
+ const summary = args.decision_summary || "";
4131
+ const updated = setGovernanceCommittee(projectPath, record.id, { committee_name: committeeName, meeting_date: meetingDate, meeting_reference: meetingRef, attendees, decision_summary: summary, evidence: [] }, args.actor_name || "mcp");
4132
+ if (!updated) {
4133
+ resultText = "Error: Failed to update record.";
4134
+ break;
4135
+ }
4136
+ recordActivity(projectPath, { source: "mcp", action: "control_override", title: `Committee: ${updated.system_name}`, description: `Committee ${committeeName} (${meetingRef}) recorded.`, details: { governance_record_id: updated.id }, actor_name: args.actor_name, actor_role: args.actor_role });
4137
+ resultText = `# Committee Approval Recorded\n\n**System**: ${updated.system_name}\n**Committee**: ${committeeName}\n**Meeting**: ${meetingDate} (${meetingRef})`;
4138
+ break;
4139
+ }
4140
+ case "set_governance_compliance_links": {
4141
+ const projectPath = resolveProjectPath(args.project_path);
4142
+ const recordId = args.record_id || "";
4143
+ const record = findGovernanceRecord(projectPath, recordId);
4144
+ if (!record) {
4145
+ resultText = `Error: Governance record "${recordId}" not found.`;
4146
+ break;
4147
+ }
4148
+ const frameworks = args.frameworks ? String(args.frameworks).split(",").map((s) => s.trim()).filter(Boolean) : [];
4149
+ const controls = args.controls_satisfied ? String(args.controls_satisfied).split(",").map((s) => s.trim()).filter(Boolean) : [];
4150
+ const packIds = args.control_pack_ids ? String(args.control_pack_ids).split(",").map((s) => s.trim()).filter(Boolean) : [];
4151
+ const updated = setGovernanceComplianceLinks(projectPath, record.id, { frameworks, controls_satisfied: controls, control_pack_ids: packIds }, args.actor_name || "mcp");
4152
+ if (!updated) {
4153
+ resultText = "Error: Failed to update record.";
4154
+ break;
4155
+ }
4156
+ recordActivity(projectPath, { source: "mcp", action: "control_override", title: `Compliance links: ${updated.system_name}`, description: `Frameworks mapped: ${frameworks.join(", ")}.`, details: { governance_record_id: updated.id }, actor_name: args.actor_name, actor_role: args.actor_role });
4157
+ resultText = `# Compliance Links Mapped\n\n**System**: ${updated.system_name}\n**Frameworks**: ${frameworks.join(", ") || "(none)"}\n**Controls**: ${controls.length}`;
4158
+ break;
4159
+ }
3484
4160
  default:
3485
4161
  return {
3486
4162
  jsonrpc: "2.0",
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.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"
6
+ "@greenarmor/ges-audit-engine": "1.4.1",
7
+ "@greenarmor/ges-cicd-generator": "1.4.1",
8
+ "@greenarmor/ges-compliance-engine": "1.4.1",
9
+ "@greenarmor/ges-core": "1.4.1",
10
+ "@greenarmor/ges-doc-generator": "1.4.1",
11
+ "@greenarmor/ges-policy-engine": "1.4.1",
12
+ "@greenarmor/ges-report-generator": "1.4.1",
13
+ "@greenarmor/ges-rules-engine": "1.4.1",
14
+ "@greenarmor/ges-scanner-integration": "1.4.1",
15
+ "@greenarmor/ges-scoring-engine": "1.4.1",
16
+ "@greenarmor/ges-web-dashboard": "1.4.1"
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.3.0",
70
+ "version": "1.4.1",
71
71
  "scripts": {
72
72
  "build": "tsc",
73
73
  "clean": "rm -rf dist bundle tsconfig.tsbuildinfo",