@ansvar/rwandan-law-mcp 1.0.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 (124) hide show
  1. package/LICENSE +110 -0
  2. package/README.md +83 -0
  3. package/dist/__tests__/contract/golden.test.d.ts +9 -0
  4. package/dist/__tests__/contract/golden.test.d.ts.map +1 -0
  5. package/dist/__tests__/contract/golden.test.js +76 -0
  6. package/dist/__tests__/contract/golden.test.js.map +1 -0
  7. package/dist/api/health.d.ts +3 -0
  8. package/dist/api/health.d.ts.map +1 -0
  9. package/dist/api/health.js +101 -0
  10. package/dist/api/health.js.map +1 -0
  11. package/dist/api/mcp.d.ts +3 -0
  12. package/dist/api/mcp.d.ts.map +1 -0
  13. package/dist/api/mcp.js +119 -0
  14. package/dist/api/mcp.js.map +1 -0
  15. package/dist/scripts/build-db.d.ts +11 -0
  16. package/dist/scripts/build-db.d.ts.map +1 -0
  17. package/dist/scripts/build-db.js +375 -0
  18. package/dist/scripts/build-db.js.map +1 -0
  19. package/dist/scripts/drift-detect.d.ts +9 -0
  20. package/dist/scripts/drift-detect.d.ts.map +1 -0
  21. package/dist/scripts/drift-detect.js +62 -0
  22. package/dist/scripts/drift-detect.js.map +1 -0
  23. package/dist/scripts/ingest.d.ts +10 -0
  24. package/dist/scripts/ingest.d.ts.map +1 -0
  25. package/dist/scripts/ingest.js +333 -0
  26. package/dist/scripts/ingest.js.map +1 -0
  27. package/dist/scripts/lib/fetcher.d.ts +26 -0
  28. package/dist/scripts/lib/fetcher.d.ts.map +1 -0
  29. package/dist/scripts/lib/fetcher.js +102 -0
  30. package/dist/scripts/lib/fetcher.js.map +1 -0
  31. package/dist/scripts/lib/parser.d.ts +60 -0
  32. package/dist/scripts/lib/parser.d.ts.map +1 -0
  33. package/dist/scripts/lib/parser.js +560 -0
  34. package/dist/scripts/lib/parser.js.map +1 -0
  35. package/dist/scripts/lib/pdf.d.ts +7 -0
  36. package/dist/scripts/lib/pdf.d.ts.map +1 -0
  37. package/dist/scripts/lib/pdf.js +124 -0
  38. package/dist/scripts/lib/pdf.js.map +1 -0
  39. package/dist/src/capabilities.d.ts +16 -0
  40. package/dist/src/capabilities.d.ts.map +1 -0
  41. package/dist/src/capabilities.js +43 -0
  42. package/dist/src/capabilities.js.map +1 -0
  43. package/dist/src/constants.d.ts +7 -0
  44. package/dist/src/constants.d.ts.map +1 -0
  45. package/dist/src/constants.js +7 -0
  46. package/dist/src/constants.js.map +1 -0
  47. package/dist/src/index.d.ts +8 -0
  48. package/dist/src/index.d.ts.map +1 -0
  49. package/dist/src/index.js +80 -0
  50. package/dist/src/index.js.map +1 -0
  51. package/dist/src/tools/about.d.ts +45 -0
  52. package/dist/src/tools/about.d.ts.map +1 -0
  53. package/dist/src/tools/about.js +54 -0
  54. package/dist/src/tools/about.js.map +1 -0
  55. package/dist/src/tools/build-legal-stance.d.ts +21 -0
  56. package/dist/src/tools/build-legal-stance.d.ts.map +1 -0
  57. package/dist/src/tools/build-legal-stance.js +46 -0
  58. package/dist/src/tools/build-legal-stance.js.map +1 -0
  59. package/dist/src/tools/check-currency.d.ts +20 -0
  60. package/dist/src/tools/check-currency.d.ts.map +1 -0
  61. package/dist/src/tools/check-currency.js +41 -0
  62. package/dist/src/tools/check-currency.js.map +1 -0
  63. package/dist/src/tools/format-citation.d.ts +14 -0
  64. package/dist/src/tools/format-citation.d.ts.map +1 -0
  65. package/dist/src/tools/format-citation.js +28 -0
  66. package/dist/src/tools/format-citation.js.map +1 -0
  67. package/dist/src/tools/get-eu-basis.d.ts +21 -0
  68. package/dist/src/tools/get-eu-basis.d.ts.map +1 -0
  69. package/dist/src/tools/get-eu-basis.js +52 -0
  70. package/dist/src/tools/get-eu-basis.js.map +1 -0
  71. package/dist/src/tools/get-provision-eu-basis.d.ts +20 -0
  72. package/dist/src/tools/get-provision-eu-basis.d.ts.map +1 -0
  73. package/dist/src/tools/get-provision-eu-basis.js +45 -0
  74. package/dist/src/tools/get-provision-eu-basis.js.map +1 -0
  75. package/dist/src/tools/get-provision.d.ts +24 -0
  76. package/dist/src/tools/get-provision.d.ts.map +1 -0
  77. package/dist/src/tools/get-provision.js +80 -0
  78. package/dist/src/tools/get-provision.js.map +1 -0
  79. package/dist/src/tools/get-rwandan-implementations.d.ts +21 -0
  80. package/dist/src/tools/get-rwandan-implementations.d.ts.map +1 -0
  81. package/dist/src/tools/get-rwandan-implementations.js +42 -0
  82. package/dist/src/tools/get-rwandan-implementations.js.map +1 -0
  83. package/dist/src/tools/list-sources.d.ts +25 -0
  84. package/dist/src/tools/list-sources.d.ts.map +1 -0
  85. package/dist/src/tools/list-sources.js +41 -0
  86. package/dist/src/tools/list-sources.js.map +1 -0
  87. package/dist/src/tools/registry.d.ts +13 -0
  88. package/dist/src/tools/registry.d.ts.map +1 -0
  89. package/dist/src/tools/registry.js +365 -0
  90. package/dist/src/tools/registry.js.map +1 -0
  91. package/dist/src/tools/search-eu-implementations.d.ts +24 -0
  92. package/dist/src/tools/search-eu-implementations.d.ts.map +1 -0
  93. package/dist/src/tools/search-eu-implementations.js +58 -0
  94. package/dist/src/tools/search-eu-implementations.js.map +1 -0
  95. package/dist/src/tools/search-legislation.d.ts +24 -0
  96. package/dist/src/tools/search-legislation.d.ts.map +1 -0
  97. package/dist/src/tools/search-legislation.js +54 -0
  98. package/dist/src/tools/search-legislation.js.map +1 -0
  99. package/dist/src/tools/validate-citation.d.ts +20 -0
  100. package/dist/src/tools/validate-citation.d.ts.map +1 -0
  101. package/dist/src/tools/validate-citation.js +107 -0
  102. package/dist/src/tools/validate-citation.js.map +1 -0
  103. package/dist/src/tools/validate-eu-compliance.d.ts +20 -0
  104. package/dist/src/tools/validate-eu-compliance.d.ts.map +1 -0
  105. package/dist/src/tools/validate-eu-compliance.js +98 -0
  106. package/dist/src/tools/validate-eu-compliance.js.map +1 -0
  107. package/dist/src/utils/as-of-date.d.ts +9 -0
  108. package/dist/src/utils/as-of-date.d.ts.map +1 -0
  109. package/dist/src/utils/as-of-date.js +25 -0
  110. package/dist/src/utils/as-of-date.js.map +1 -0
  111. package/dist/src/utils/fts-query.d.ts +19 -0
  112. package/dist/src/utils/fts-query.d.ts.map +1 -0
  113. package/dist/src/utils/fts-query.js +47 -0
  114. package/dist/src/utils/fts-query.js.map +1 -0
  115. package/dist/src/utils/metadata.d.ts +16 -0
  116. package/dist/src/utils/metadata.d.ts.map +1 -0
  117. package/dist/src/utils/metadata.js +22 -0
  118. package/dist/src/utils/metadata.js.map +1 -0
  119. package/dist/src/utils/statute-id.d.ts +18 -0
  120. package/dist/src/utils/statute-id.d.ts.map +1 -0
  121. package/dist/src/utils/statute-id.js +34 -0
  122. package/dist/src/utils/statute-id.js.map +1 -0
  123. package/package.json +74 -0
  124. package/server.json +30 -0
@@ -0,0 +1,107 @@
1
+ /**
2
+ * validate_citation — Validate an Rwandan legal citation against the database.
3
+ */
4
+ import { resolveDocumentId } from '../utils/statute-id.js';
5
+ import { generateResponseMetadata } from '../utils/metadata.js';
6
+ /**
7
+ * Parse an Rwandan legal citation.
8
+ * Supports:
9
+ * - "Section 13 Privacy Act 1988" / "Section 13, Privacy Act 1988"
10
+ * - "Privacy Act 1988 s 13" / "Privacy Act 1988, s 13"
11
+ * - "[Act Title Year] s N"
12
+ * - "s 13" (section only, no document)
13
+ * - Plain document reference (e.g., "Privacy Act 1988")
14
+ */
15
+ function parseCitation(citation) {
16
+ const trimmed = citation.trim();
17
+ // "Section N <Act>" or "Section N, <Act>"
18
+ const sectionFirst = trimmed.match(/^Section\s+(\d+[A-Za-z]*(?:\(\d+\))?)\s*[,;]?\s+(.+)$/i);
19
+ if (sectionFirst) {
20
+ return { documentRef: sectionFirst[2].trim(), sectionRef: sectionFirst[1] };
21
+ }
22
+ // "<Act> s N" or "<Act>, s N" or "<Act> s. N"
23
+ const sectionLast = trimmed.match(/^(.+?)\s*[,;]?\s+s\.?\s+(\d+[A-Za-z]*(?:\(\d+\))?)$/i);
24
+ if (sectionLast) {
25
+ return { documentRef: sectionLast[1].trim(), sectionRef: sectionLast[2] };
26
+ }
27
+ // "<Act> Section N" or "<Act>, Section N"
28
+ const sectionWordLast = trimmed.match(/^(.+?)\s*[,;]?\s+Section\s+(\d+[A-Za-z]*(?:\(\d+\))?)$/i);
29
+ if (sectionWordLast) {
30
+ return { documentRef: sectionWordLast[1].trim(), sectionRef: sectionWordLast[2] };
31
+ }
32
+ // Just a document reference (no section)
33
+ return { documentRef: trimmed };
34
+ }
35
+ export async function validateCitationTool(db, input) {
36
+ const warnings = [];
37
+ const parsed = parseCitation(input.citation);
38
+ if (!parsed) {
39
+ return {
40
+ results: {
41
+ valid: false,
42
+ citation: input.citation,
43
+ warnings: ['Could not parse citation format'],
44
+ },
45
+ _metadata: generateResponseMetadata(db),
46
+ };
47
+ }
48
+ const docId = resolveDocumentId(db, parsed.documentRef);
49
+ if (!docId) {
50
+ return {
51
+ results: {
52
+ valid: false,
53
+ citation: input.citation,
54
+ warnings: [`Document not found: "${parsed.documentRef}"`],
55
+ },
56
+ _metadata: generateResponseMetadata(db),
57
+ };
58
+ }
59
+ const doc = db.prepare('SELECT id, title, status FROM legal_documents WHERE id = ?').get(docId);
60
+ if (doc.status === 'repealed') {
61
+ warnings.push(`WARNING: This statute has been repealed.`);
62
+ }
63
+ else if (doc.status === 'amended') {
64
+ warnings.push(`Note: This statute has been amended. Verify you are referencing the current version.`);
65
+ }
66
+ if (parsed.sectionRef) {
67
+ const provision = db.prepare("SELECT provision_ref FROM legal_provisions WHERE document_id = ? AND (provision_ref = ? OR provision_ref = ? OR section = ?)").get(docId, parsed.sectionRef, `s${parsed.sectionRef}`, parsed.sectionRef);
68
+ if (!provision) {
69
+ return {
70
+ results: {
71
+ valid: false,
72
+ citation: input.citation,
73
+ document_id: docId,
74
+ document_title: doc.title,
75
+ warnings: [...warnings, `Provision "Section ${parsed.sectionRef}" not found in ${doc.title}`],
76
+ },
77
+ _metadata: generateResponseMetadata(db),
78
+ };
79
+ }
80
+ return {
81
+ results: {
82
+ valid: true,
83
+ citation: input.citation,
84
+ normalized: `Section ${parsed.sectionRef}, ${doc.title}`,
85
+ document_id: docId,
86
+ document_title: doc.title,
87
+ provision_ref: provision.provision_ref,
88
+ status: doc.status,
89
+ warnings,
90
+ },
91
+ _metadata: generateResponseMetadata(db),
92
+ };
93
+ }
94
+ return {
95
+ results: {
96
+ valid: true,
97
+ citation: input.citation,
98
+ normalized: doc.title,
99
+ document_id: docId,
100
+ document_title: doc.title,
101
+ status: doc.status,
102
+ warnings,
103
+ },
104
+ _metadata: generateResponseMetadata(db),
105
+ };
106
+ }
107
+ //# sourceMappingURL=validate-citation.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-citation.js","sourceRoot":"","sources":["../../../src/tools/validate-citation.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * validate_eu_compliance — Check EU alignment status for an Rwandan statute.
3
+ */
4
+ import type Database from '@ansvar/mcp-sqlite';
5
+ import { type ToolResponse } from '../utils/metadata.js';
6
+ export interface ValidateEUComplianceInput {
7
+ document_id: string;
8
+ provision_ref?: string;
9
+ eu_document_id?: string;
10
+ }
11
+ export interface EUComplianceResult {
12
+ document_id: string;
13
+ document_title: string;
14
+ compliance_status: 'compliant' | 'partial' | 'unclear' | 'not_applicable';
15
+ eu_references_found: number;
16
+ warnings: string[];
17
+ recommendations: string[];
18
+ }
19
+ export declare function validateEUCompliance(db: InstanceType<typeof Database>, input: ValidateEUComplianceInput): Promise<ToolResponse<EUComplianceResult>>;
20
+ //# sourceMappingURL=validate-eu-compliance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-eu-compliance.d.ts","sourceRoot":"","sources":["../../../src/tools/validate-eu-compliance.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,oBAAoB,CAAC;AAE/C,OAAO,EAA4B,KAAK,YAAY,EAAE,MAAM,sBAAsB,CAAC;AAEnF,MAAM,WAAW,yBAAyB;IACxC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED,MAAM,WAAW,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,iBAAiB,EAAE,WAAW,GAAG,SAAS,GAAG,SAAS,GAAG,gBAAgB,CAAC;IAC1E,mBAAmB,EAAE,MAAM,CAAC;IAC5B,QAAQ,EAAE,MAAM,EAAE,CAAC;IACnB,eAAe,EAAE,MAAM,EAAE,CAAC;CAC3B;AAED,wBAAsB,oBAAoB,CACxC,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,EACjC,KAAK,EAAE,yBAAyB,GAC/B,OAAO,CAAC,YAAY,CAAC,kBAAkB,CAAC,CAAC,CAuG3C"}
@@ -0,0 +1,98 @@
1
+ /**
2
+ * validate_eu_compliance — Check EU alignment status for an Rwandan statute.
3
+ */
4
+ import { resolveDocumentId } from '../utils/statute-id.js';
5
+ import { generateResponseMetadata } from '../utils/metadata.js';
6
+ export async function validateEUCompliance(db, input) {
7
+ const resolvedId = resolveDocumentId(db, input.document_id);
8
+ if (!resolvedId) {
9
+ return {
10
+ results: {
11
+ document_id: input.document_id,
12
+ document_title: 'Unknown',
13
+ compliance_status: 'not_applicable',
14
+ eu_references_found: 0,
15
+ warnings: [`Document not found: "${input.document_id}"`],
16
+ recommendations: [],
17
+ },
18
+ _metadata: generateResponseMetadata(db),
19
+ };
20
+ }
21
+ const doc = db.prepare('SELECT id, title, status FROM legal_documents WHERE id = ?').get(resolvedId);
22
+ const warnings = [];
23
+ const recommendations = [];
24
+ // Check if EU reference tables exist
25
+ let euRefCount = 0;
26
+ try {
27
+ let sql = 'SELECT COUNT(*) as count FROM eu_references WHERE document_id = ?';
28
+ const params = [resolvedId];
29
+ if (input.eu_document_id) {
30
+ sql += ' AND eu_document_id = ?';
31
+ params.push(input.eu_document_id);
32
+ }
33
+ const row = db.prepare(sql).get(...params);
34
+ euRefCount = row.count;
35
+ }
36
+ catch {
37
+ return {
38
+ results: {
39
+ document_id: resolvedId,
40
+ document_title: doc.title,
41
+ compliance_status: 'not_applicable',
42
+ eu_references_found: 0,
43
+ warnings: ['EU references not available in this database tier'],
44
+ recommendations: [],
45
+ },
46
+ _metadata: generateResponseMetadata(db),
47
+ };
48
+ }
49
+ if (euRefCount === 0) {
50
+ return {
51
+ results: {
52
+ document_id: resolvedId,
53
+ document_title: doc.title,
54
+ compliance_status: 'not_applicable',
55
+ eu_references_found: 0,
56
+ warnings: [],
57
+ recommendations: ['No EU cross-references found for this Rwandan statute. EU alignment checks are only applicable when the statute text explicitly references EU instruments.'],
58
+ },
59
+ _metadata: generateResponseMetadata(db),
60
+ };
61
+ }
62
+ if (doc.status === 'repealed') {
63
+ warnings.push('This statute has been repealed.');
64
+ recommendations.push('Check for replacement legislation.');
65
+ }
66
+ // Check implementation status
67
+ const statuses = db.prepare('SELECT implementation_status, COUNT(*) as count FROM eu_references WHERE document_id = ? GROUP BY implementation_status').all(resolvedId);
68
+ const statusMap = new Map(statuses.map(s => [s.implementation_status, s.count]));
69
+ const completeCount = statusMap.get('complete') ?? 0;
70
+ const partialCount = statusMap.get('partial') ?? 0;
71
+ const unknownCount = statusMap.get('unknown') ?? 0;
72
+ let compliance_status;
73
+ if (completeCount > 0 && partialCount === 0 && unknownCount === 0) {
74
+ compliance_status = 'compliant';
75
+ }
76
+ else if (partialCount > 0) {
77
+ compliance_status = 'partial';
78
+ warnings.push(`${partialCount} EU reference(s) have partial alignment status.`);
79
+ }
80
+ else {
81
+ compliance_status = 'unclear';
82
+ if (unknownCount > 0) {
83
+ recommendations.push(`${unknownCount} EU reference(s) have unknown alignment status. Manual review recommended.`);
84
+ }
85
+ }
86
+ return {
87
+ results: {
88
+ document_id: resolvedId,
89
+ document_title: doc.title,
90
+ compliance_status,
91
+ eu_references_found: euRefCount,
92
+ warnings,
93
+ recommendations,
94
+ },
95
+ _metadata: generateResponseMetadata(db),
96
+ };
97
+ }
98
+ //# sourceMappingURL=validate-eu-compliance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"validate-eu-compliance.js","sourceRoot":"","sources":["../../../src/tools/validate-eu-compliance.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,9 @@
1
+ /**
2
+ * Date normalization for temporal queries.
3
+ */
4
+ /**
5
+ * Normalize an as-of date string to ISO 8601 format.
6
+ * Returns null if the input is not a valid date.
7
+ */
8
+ export declare function normalizeAsOfDate(input?: string): string | null;
9
+ //# sourceMappingURL=as-of-date.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"as-of-date.d.ts","sourceRoot":"","sources":["../../../src/utils/as-of-date.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,wBAAgB,iBAAiB,CAAC,KAAK,CAAC,EAAE,MAAM,GAAG,MAAM,GAAG,IAAI,CAkB/D"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * Date normalization for temporal queries.
3
+ */
4
+ /**
5
+ * Normalize an as-of date string to ISO 8601 format.
6
+ * Returns null if the input is not a valid date.
7
+ */
8
+ export function normalizeAsOfDate(input) {
9
+ if (!input || input.trim().length === 0)
10
+ return null;
11
+ const trimmed = input.trim();
12
+ // Already ISO 8601
13
+ if (/^\d{4}-\d{2}-\d{2}$/.test(trimmed)) {
14
+ const date = new Date(trimmed);
15
+ if (!isNaN(date.getTime()))
16
+ return trimmed;
17
+ }
18
+ // Try parsing as a general date
19
+ const date = new Date(trimmed);
20
+ if (!isNaN(date.getTime())) {
21
+ return date.toISOString().slice(0, 10);
22
+ }
23
+ return null;
24
+ }
25
+ //# sourceMappingURL=as-of-date.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"as-of-date.js","sourceRoot":"","sources":["../../../src/utils/as-of-date.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH;;;GAGG;AACH,MAAM,UAAU,iBAAiB,CAAC,KAAc;IAC9C,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,IAAI,CAAC;IAErD,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAE7B,mBAAmB;IACnB,IAAI,qBAAqB,CAAC,IAAI,CAAC,OAAO,CAAC,EAAE,CAAC;QACxC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;QAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC;YAAE,OAAO,OAAO,CAAC;IAC7C,CAAC;IAED,gCAAgC;IAChC,MAAM,IAAI,GAAG,IAAI,IAAI,CAAC,OAAO,CAAC,CAAC;IAC/B,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,EAAE,CAAC,EAAE,CAAC;QAC3B,OAAO,IAAI,CAAC,WAAW,EAAE,CAAC,KAAK,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IACzC,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC"}
@@ -0,0 +1,19 @@
1
+ /**
2
+ * FTS5 query helpers for Rwandan Law MCP.
3
+ *
4
+ * Handles query sanitization and variant generation for SQLite FTS5.
5
+ */
6
+ /**
7
+ * Sanitize user input for safe FTS5 queries.
8
+ * Removes characters that have special meaning in FTS5 syntax.
9
+ */
10
+ export declare function sanitizeFtsInput(input: string): string;
11
+ /**
12
+ * Build FTS5 query variants for a search term.
13
+ * Returns variants in order of specificity (most specific first):
14
+ * 1. Exact phrase match
15
+ * 2. All terms required (AND)
16
+ * 3. Prefix match on last term
17
+ */
18
+ export declare function buildFtsQueryVariants(sanitized: string): string[];
19
+ //# sourceMappingURL=fts-query.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts-query.d.ts","sourceRoot":"","sources":["../../../src/utils/fts-query.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,wBAAgB,gBAAgB,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAKtD;AAED;;;;;;GAMG;AACH,wBAAgB,qBAAqB,CAAC,SAAS,EAAE,MAAM,GAAG,MAAM,EAAE,CA2BjE"}
@@ -0,0 +1,47 @@
1
+ /**
2
+ * FTS5 query helpers for Rwandan Law MCP.
3
+ *
4
+ * Handles query sanitization and variant generation for SQLite FTS5.
5
+ */
6
+ /**
7
+ * Sanitize user input for safe FTS5 queries.
8
+ * Removes characters that have special meaning in FTS5 syntax.
9
+ */
10
+ export function sanitizeFtsInput(input) {
11
+ return input
12
+ .replace(/['"(){}[\]^~*:]/g, ' ')
13
+ .replace(/\s+/g, ' ')
14
+ .trim();
15
+ }
16
+ /**
17
+ * Build FTS5 query variants for a search term.
18
+ * Returns variants in order of specificity (most specific first):
19
+ * 1. Exact phrase match
20
+ * 2. All terms required (AND)
21
+ * 3. Prefix match on last term
22
+ */
23
+ export function buildFtsQueryVariants(sanitized) {
24
+ if (!sanitized || sanitized.trim().length === 0) {
25
+ return [];
26
+ }
27
+ const terms = sanitized.split(/\s+/).filter(t => t.length > 0);
28
+ if (terms.length === 0)
29
+ return [];
30
+ const variants = [];
31
+ // Exact phrase
32
+ if (terms.length > 1) {
33
+ variants.push(`"${terms.join(' ')}"`);
34
+ }
35
+ // AND query
36
+ variants.push(terms.join(' AND '));
37
+ // Prefix match on last term (for autocomplete-like behavior)
38
+ if (terms.length === 1 && terms[0].length >= 3) {
39
+ variants.push(`${terms[0]}*`);
40
+ }
41
+ else if (terms.length > 1) {
42
+ const prefix = [...terms.slice(0, -1), `${terms[terms.length - 1]}*`];
43
+ variants.push(prefix.join(' AND '));
44
+ }
45
+ return variants;
46
+ }
47
+ //# sourceMappingURL=fts-query.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fts-query.js","sourceRoot":"","sources":["../../../src/utils/fts-query.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH;;;GAGG;AACH,MAAM,UAAU,gBAAgB,CAAC,KAAa;IAC5C,OAAO,KAAK;SACT,OAAO,CAAC,kBAAkB,EAAE,GAAG,CAAC;SAChC,OAAO,CAAC,MAAM,EAAE,GAAG,CAAC;SACpB,IAAI,EAAE,CAAC;AACZ,CAAC;AAED;;;;;;GAMG;AACH,MAAM,UAAU,qBAAqB,CAAC,SAAiB;IACrD,IAAI,CAAC,SAAS,IAAI,SAAS,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QAChD,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,KAAK,GAAG,SAAS,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IAC/D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC;QAAE,OAAO,EAAE,CAAC;IAElC,MAAM,QAAQ,GAAa,EAAE,CAAC;IAE9B,eAAe;IACf,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QACrB,QAAQ,CAAC,IAAI,CAAC,IAAI,KAAK,CAAC,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,CAAC;IACxC,CAAC;IAED,YAAY;IACZ,QAAQ,CAAC,IAAI,CAAC,KAAK,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IAEnC,6DAA6D;IAC7D,IAAI,KAAK,CAAC,MAAM,KAAK,CAAC,IAAI,KAAK,CAAC,CAAC,CAAC,CAAC,MAAM,IAAI,CAAC,EAAE,CAAC;QAC/C,QAAQ,CAAC,IAAI,CAAC,GAAG,KAAK,CAAC,CAAC,CAAC,GAAG,CAAC,CAAC;IAChC,CAAC;SAAM,IAAI,KAAK,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC5B,MAAM,MAAM,GAAG,CAAC,GAAG,KAAK,CAAC,KAAK,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,EAAE,GAAG,KAAK,CAAC,KAAK,CAAC,MAAM,GAAG,CAAC,CAAC,GAAG,CAAC,CAAC;QACtE,QAAQ,CAAC,IAAI,CAAC,MAAM,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC,CAAC;IACtC,CAAC;IAED,OAAO,QAAQ,CAAC;AAClB,CAAC"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Response metadata utilities for Rwandan Law MCP.
3
+ */
4
+ import type Database from '@ansvar/mcp-sqlite';
5
+ export interface ResponseMetadata {
6
+ data_source: string;
7
+ jurisdiction: string;
8
+ disclaimer: string;
9
+ freshness?: string;
10
+ }
11
+ export interface ToolResponse<T> {
12
+ results: T;
13
+ _metadata: ResponseMetadata;
14
+ }
15
+ export declare function generateResponseMetadata(db: InstanceType<typeof Database>): ResponseMetadata;
16
+ //# sourceMappingURL=metadata.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.d.ts","sourceRoot":"","sources":["../../../src/utils/metadata.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,oBAAoB,CAAC;AAE/C,MAAM,WAAW,gBAAgB;IAC/B,WAAW,EAAE,MAAM,CAAC;IACpB,YAAY,EAAE,MAAM,CAAC;IACrB,UAAU,EAAE,MAAM,CAAC;IACnB,SAAS,CAAC,EAAE,MAAM,CAAC;CACpB;AAED,MAAM,WAAW,YAAY,CAAC,CAAC;IAC7B,OAAO,EAAE,CAAC,CAAC;IACX,SAAS,EAAE,gBAAgB,CAAC;CAC7B;AAED,wBAAgB,wBAAwB,CACtC,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,GAChC,gBAAgB,CAmBlB"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * Response metadata utilities for Rwandan Law MCP.
3
+ */
4
+ export function generateResponseMetadata(db) {
5
+ let freshness;
6
+ try {
7
+ const row = db.prepare("SELECT value FROM db_metadata WHERE key = 'built_at'").get();
8
+ if (row)
9
+ freshness = row.value;
10
+ }
11
+ catch {
12
+ // Ignore
13
+ }
14
+ return {
15
+ data_source: 'RwandaLII law catalog (AKN + PDF-backed laws) with Official Gazette publication links',
16
+ jurisdiction: 'RW',
17
+ disclaimer: 'This database is built from publicly available RwandaLII law pages, including PDF-backed laws, and links to Official Gazette publications. ' +
18
+ 'For legal certainty, verify against the Official Gazette publication.',
19
+ freshness,
20
+ };
21
+ }
22
+ //# sourceMappingURL=metadata.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"metadata.js","sourceRoot":"","sources":["../../../src/utils/metadata.ts"],"names":[],"mappings":"AAAA;;GAEG;AAgBH,MAAM,UAAU,wBAAwB,CACtC,EAAiC;IAEjC,IAAI,SAA6B,CAAC;IAClC,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CACpB,sDAAsD,CACvD,CAAC,GAAG,EAAmC,CAAC;QACzC,IAAI,GAAG;YAAE,SAAS,GAAG,GAAG,CAAC,KAAK,CAAC;IACjC,CAAC;IAAC,MAAM,CAAC;QACP,SAAS;IACX,CAAC;IAED,OAAO;QACL,WAAW,EAAE,uFAAuF;QACpG,YAAY,EAAE,IAAI;QAClB,UAAU,EACR,6IAA6I;YAC7I,uEAAuE;QACzE,SAAS;KACV,CAAC;AACJ,CAAC"}
@@ -0,0 +1,18 @@
1
+ /**
2
+ * Statute ID resolution for Rwandan Law MCP.
3
+ *
4
+ * Resolves fuzzy document references (titles, IDs) to database document IDs.
5
+ * Rwandan legislation identifier resolution
6
+ * (e.g., "Privacy Act 1988", "Corporations Act 2001").
7
+ */
8
+ import type Database from '@ansvar/mcp-sqlite';
9
+ /**
10
+ * Resolve a document identifier to a database document ID.
11
+ * Supports:
12
+ * - Direct ID match (e.g., "privacy-act-1988")
13
+ * - Title match (e.g., "Privacy Act 1988", "Privacy Act")
14
+ * - Short name/abbreviation match (e.g., "SOCI Act")
15
+ * - Fuzzy title substring match
16
+ */
17
+ export declare function resolveDocumentId(db: InstanceType<typeof Database>, input: string): string | null;
18
+ //# sourceMappingURL=statute-id.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statute-id.d.ts","sourceRoot":"","sources":["../../../src/utils/statute-id.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH,OAAO,KAAK,QAAQ,MAAM,oBAAoB,CAAC;AAE/C;;;;;;;GAOG;AACH,wBAAgB,iBAAiB,CAC/B,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,EACjC,KAAK,EAAE,MAAM,GACZ,MAAM,GAAG,IAAI,CAuBf"}
@@ -0,0 +1,34 @@
1
+ /**
2
+ * Statute ID resolution for Rwandan Law MCP.
3
+ *
4
+ * Resolves fuzzy document references (titles, IDs) to database document IDs.
5
+ * Rwandan legislation identifier resolution
6
+ * (e.g., "Privacy Act 1988", "Corporations Act 2001").
7
+ */
8
+ /**
9
+ * Resolve a document identifier to a database document ID.
10
+ * Supports:
11
+ * - Direct ID match (e.g., "privacy-act-1988")
12
+ * - Title match (e.g., "Privacy Act 1988", "Privacy Act")
13
+ * - Short name/abbreviation match (e.g., "SOCI Act")
14
+ * - Fuzzy title substring match
15
+ */
16
+ export function resolveDocumentId(db, input) {
17
+ const trimmed = input.trim();
18
+ if (!trimmed)
19
+ return null;
20
+ // Direct ID match
21
+ const directMatch = db.prepare('SELECT id FROM legal_documents WHERE id = ?').get(trimmed);
22
+ if (directMatch)
23
+ return directMatch.id;
24
+ // Title/short_name exact match
25
+ const titleResult = db.prepare("SELECT id FROM legal_documents WHERE title LIKE ? OR short_name LIKE ? OR title_en LIKE ? LIMIT 1").get(`%${trimmed}%`, `%${trimmed}%`, `%${trimmed}%`);
26
+ if (titleResult)
27
+ return titleResult.id;
28
+ // Case-insensitive fallback
29
+ const lowerResult = db.prepare("SELECT id FROM legal_documents WHERE LOWER(title) LIKE LOWER(?) OR LOWER(short_name) LIKE LOWER(?) OR LOWER(title_en) LIKE LOWER(?) LIMIT 1").get(`%${trimmed}%`, `%${trimmed}%`, `%${trimmed}%`);
30
+ if (lowerResult)
31
+ return lowerResult.id;
32
+ return null;
33
+ }
34
+ //# sourceMappingURL=statute-id.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"statute-id.js","sourceRoot":"","sources":["../../../src/utils/statute-id.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAIH;;;;;;;GAOG;AACH,MAAM,UAAU,iBAAiB,CAC/B,EAAiC,EACjC,KAAa;IAEb,MAAM,OAAO,GAAG,KAAK,CAAC,IAAI,EAAE,CAAC;IAC7B,IAAI,CAAC,OAAO;QAAE,OAAO,IAAI,CAAC;IAE1B,kBAAkB;IAClB,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAC5B,6CAA6C,CAC9C,CAAC,GAAG,CAAC,OAAO,CAA+B,CAAC;IAC7C,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC,EAAE,CAAC;IAEvC,+BAA+B;IAC/B,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAC5B,mGAAmG,CACpG,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,EAAE,IAAI,OAAO,GAAG,EAAE,IAAI,OAAO,GAAG,CAA+B,CAAC;IACpF,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC,EAAE,CAAC;IAEvC,4BAA4B;IAC5B,MAAM,WAAW,GAAG,EAAE,CAAC,OAAO,CAC5B,6IAA6I,CAC9I,CAAC,GAAG,CAAC,IAAI,OAAO,GAAG,EAAE,IAAI,OAAO,GAAG,EAAE,IAAI,OAAO,GAAG,CAA+B,CAAC;IACpF,IAAI,WAAW;QAAE,OAAO,WAAW,CAAC,EAAE,CAAC;IAEvC,OAAO,IAAI,CAAC;AACd,CAAC"}
package/package.json ADDED
@@ -0,0 +1,74 @@
1
+ {
2
+ "name": "@ansvar/rwandan-law-mcp",
3
+ "version": "1.0.0",
4
+ "mcpName": "eu.ansvar/rwandan-law-mcp",
5
+ "description": "Rwandan law database covering data protection, cybersecurity, e-commerce, and criminal law provisions via Model Context Protocol",
6
+ "author": "Ansvar Systems <hello@ansvar.eu>",
7
+ "license": "Apache-2.0",
8
+ "repository": {
9
+ "type": "git",
10
+ "url": "https://github.com/Ansvar-Systems/Rwandan-law-mcp.git"
11
+ },
12
+ "homepage": "https://ansvar.eu",
13
+ "keywords": [
14
+ "mcp",
15
+ "model-context-protocol",
16
+ "rwandan-law",
17
+ "data-protection",
18
+ "cybersecurity",
19
+ "legislation",
20
+ "legal",
21
+ "compliance",
22
+ "ansvar"
23
+ ],
24
+ "type": "module",
25
+ "main": "dist/index.js",
26
+ "bin": {
27
+ "rwandan-law-mcp": "dist/index.js"
28
+ },
29
+ "files": [
30
+ "dist",
31
+ "data/database.db",
32
+ "server.json"
33
+ ],
34
+ "scripts": {
35
+ "build": "tsc",
36
+ "build:db": "node --import tsx scripts/build-db.ts",
37
+ "dev": "node --import tsx src/index.ts",
38
+ "start": "node dist/index.js",
39
+ "test": "vitest run",
40
+ "test:watch": "vitest",
41
+ "test:coverage": "vitest run --coverage",
42
+ "ingest": "node --import tsx scripts/ingest.ts",
43
+ "test:contract": "vitest run __tests__/contract/",
44
+ "drift:detect": "node --import tsx scripts/drift-detect.ts",
45
+ "validate": "npm run lint && npm test && npm run test:contract",
46
+ "lint": "tsc --noEmit",
47
+ "prepublishOnly": "npm run build",
48
+ "postinstall": "test -d dist || npm run build || true"
49
+ },
50
+ "dependencies": {
51
+ "@ansvar/mcp-sqlite": "^1.0.3",
52
+ "@modelcontextprotocol/sdk": "^1.25.3"
53
+ },
54
+ "devDependencies": {
55
+ "@types/better-sqlite3": "^7.6.13",
56
+ "@types/node": "^22.15.29",
57
+ "@vercel/node": "^5.6.4",
58
+ "better-sqlite3": "^12.6.2",
59
+ "tsx": "^4.21.0",
60
+ "typescript": "^5.9.3",
61
+ "vitest": "^3.0.0"
62
+ },
63
+ "engines": {
64
+ "node": ">=18"
65
+ },
66
+ "publishConfig": {
67
+ "access": "public"
68
+ },
69
+ "overrides": {
70
+ "path-to-regexp": "^8.0.0",
71
+ "undici": "^7.0.0",
72
+ "ajv": "^8.18.0"
73
+ }
74
+ }
package/server.json ADDED
@@ -0,0 +1,30 @@
1
+ {
2
+ "$schema": "https://static.modelcontextprotocol.io/schemas/2025-12-11/server.schema.json",
3
+ "name": "eu.ansvar/rwandan-law-mcp",
4
+ "description": "Rwandan legislation via MCP — full-text search across statutes and provisions",
5
+ "repository": {
6
+ "url": "https://github.com/Ansvar-Systems/Rwandan-law-mcp",
7
+ "source": "github"
8
+ },
9
+ "homepage": "https://ansvar.eu",
10
+ "version": "1.0.0",
11
+ "license": "Apache-2.0",
12
+ "packages": [
13
+ {
14
+ "registryType": "npm",
15
+ "identifier": "@ansvar/rwandan-law-mcp",
16
+ "version": "1.0.0",
17
+ "transport": {
18
+ "type": "stdio"
19
+ }
20
+ }
21
+ ],
22
+ "remoteEndpoints": [
23
+ {
24
+ "url": "https://rwandan-law-mcp.vercel.app/mcp",
25
+ "transport": {
26
+ "type": "streamable-http"
27
+ }
28
+ }
29
+ ]
30
+ }