@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,124 @@
1
+ import { execFileSync } from 'child_process';
2
+ import * as fs from 'fs';
3
+ import * as os from 'os';
4
+ import * as path from 'path';
5
+ function decodeHtmlEntities(input) {
6
+ return input
7
+ .replace(/ /g, ' ')
8
+ .replace(/&/g, '&')
9
+ .replace(/&lt;/g, '<')
10
+ .replace(/&gt;/g, '>')
11
+ .replace(/&quot;/g, '"')
12
+ .replace(/&#39;/g, "'")
13
+ .replace(/&apos;/g, "'")
14
+ .replace(/&#x([0-9a-fA-F]+);/g, (_, hex) => String.fromCodePoint(parseInt(hex, 16)))
15
+ .replace(/&#(\d+);/g, (_, dec) => String.fromCodePoint(parseInt(dec, 10)));
16
+ }
17
+ function normalizeWhitespace(input) {
18
+ return input
19
+ .replace(/\u00a0/g, ' ')
20
+ .replace(/[ \t]+\n/g, '\n')
21
+ .replace(/\n{3,}/g, '\n\n')
22
+ .replace(/[ \t]{2,}/g, ' ')
23
+ .trim();
24
+ }
25
+ function safeRun(command, args) {
26
+ execFileSync(command, args, {
27
+ stdio: ['ignore', 'ignore', 'pipe'],
28
+ env: process.env,
29
+ });
30
+ }
31
+ function extractPlainText(pdfPath, outputPath) {
32
+ safeRun('pdftotext', ['-enc', 'UTF-8', pdfPath, outputPath]);
33
+ return fs.readFileSync(outputPath, 'utf-8');
34
+ }
35
+ function extractCenterColumnFromBbox(pdfPath, outputPath) {
36
+ safeRun('pdftotext', ['-bbox-layout', '-enc', 'UTF-8', pdfPath, outputPath]);
37
+ const xml = fs.readFileSync(outputPath, 'utf-8');
38
+ const pageRegex = /<page\s+width="([0-9.]+)"\s+height="([0-9.]+)">([\s\S]*?)<\/page>/g;
39
+ const linesOut = [];
40
+ let pageMatch;
41
+ while ((pageMatch = pageRegex.exec(xml)) !== null) {
42
+ const pageWidth = Number.parseFloat(pageMatch[1]);
43
+ const pageBody = pageMatch[3];
44
+ const xMin = pageWidth * 0.36;
45
+ const xMax = pageWidth * 0.62;
46
+ const lineRegex = /<line\b[^>]*>([\s\S]*?)<\/line>/g;
47
+ let lineMatch;
48
+ while ((lineMatch = lineRegex.exec(pageBody)) !== null) {
49
+ const wordRegex = /<word\s+[^>]*xMin="([0-9.]+)"[^>]*>([\s\S]*?)<\/word>/g;
50
+ const words = [];
51
+ let wordMatch;
52
+ while ((wordMatch = wordRegex.exec(lineMatch[1])) !== null) {
53
+ const x = Number.parseFloat(wordMatch[1]);
54
+ if (x < xMin || x > xMax)
55
+ continue;
56
+ words.push(decodeHtmlEntities(wordMatch[2]));
57
+ }
58
+ if (words.length === 0)
59
+ continue;
60
+ const line = words.join(' ').replace(/\s+/g, ' ').trim();
61
+ if (!line)
62
+ continue;
63
+ if (/^Official Gazette\b/i.test(line))
64
+ continue;
65
+ if (/^\d+$/.test(line))
66
+ continue;
67
+ linesOut.push(line);
68
+ }
69
+ linesOut.push('');
70
+ }
71
+ return normalizeWhitespace(linesOut.join('\n'));
72
+ }
73
+ function countArticleHeadings(text) {
74
+ const matches = text.match(/^Article\s+[A-Za-z0-9]+/gim);
75
+ return matches ? matches.length : 0;
76
+ }
77
+ function isLikelyTrilingual(text) {
78
+ return /Ingingo\b|ISHAKIRO|TABLE DES MATIERES|Sommaire|Loi\b/i.test(text);
79
+ }
80
+ export function extractTextFromPdf(pdfPath) {
81
+ const tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), 'rw-law-pdf-'));
82
+ const plainPath = path.join(tmpDir, 'plain.txt');
83
+ const bboxPath = path.join(tmpDir, 'bbox.html');
84
+ const warnings = [];
85
+ try {
86
+ const plainRaw = extractPlainText(pdfPath, plainPath);
87
+ const plainText = normalizeWhitespace(plainRaw);
88
+ let chosenText = plainText;
89
+ let method = 'plain';
90
+ if (isLikelyTrilingual(plainText)) {
91
+ try {
92
+ const centerText = extractCenterColumnFromBbox(pdfPath, bboxPath);
93
+ const plainArticleCount = countArticleHeadings(plainText);
94
+ const centerArticleCount = countArticleHeadings(centerText);
95
+ if (centerText.length > 0 &&
96
+ (centerArticleCount >= Math.max(3, Math.floor(plainArticleCount * 0.3)) ||
97
+ centerText.length > plainText.length * 0.35)) {
98
+ chosenText = centerText;
99
+ method = 'bbox_center';
100
+ }
101
+ }
102
+ catch (error) {
103
+ warnings.push(`bbox extraction failed: ${error instanceof Error ? error.message : String(error)}`);
104
+ }
105
+ }
106
+ if (chosenText.length < 1200) {
107
+ warnings.push('very low extracted text volume; PDF may be image-only or heavily degraded');
108
+ }
109
+ return {
110
+ text: chosenText,
111
+ method,
112
+ warnings,
113
+ };
114
+ }
115
+ finally {
116
+ try {
117
+ fs.rmSync(tmpDir, { recursive: true, force: true });
118
+ }
119
+ catch {
120
+ // ignore temporary cleanup failures
121
+ }
122
+ }
123
+ }
124
+ //# sourceMappingURL=pdf.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"pdf.js","sourceRoot":"","sources":["../../../scripts/lib/pdf.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,16 @@
1
+ /**
2
+ * Runtime capability detection for Rwandan Law MCP.
3
+ * Detects which database tables are available to enable/disable features.
4
+ */
5
+ import type Database from '@ansvar/mcp-sqlite';
6
+ export type Capability = 'core_legislation' | 'eu_references' | 'case_law' | 'preparatory_works';
7
+ export declare function detectCapabilities(db: InstanceType<typeof Database>): Set<Capability>;
8
+ export interface DbMetadata {
9
+ tier: string;
10
+ schema_version: string;
11
+ built_at?: string;
12
+ builder?: string;
13
+ }
14
+ export declare function readDbMetadata(db: InstanceType<typeof Database>): DbMetadata;
15
+ export declare function upgradeMessage(feature: string): string;
16
+ //# sourceMappingURL=capabilities.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.d.ts","sourceRoot":"","sources":["../../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,KAAK,QAAQ,MAAM,oBAAoB,CAAC;AAE/C,MAAM,MAAM,UAAU,GAClB,kBAAkB,GAClB,eAAe,GACf,UAAU,GACV,mBAAmB,CAAC;AASxB,wBAAgB,kBAAkB,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,GAAG,GAAG,CAAC,UAAU,CAAC,CAcrF;AAED,MAAM,WAAW,UAAU;IACzB,IAAI,EAAE,MAAM,CAAC;IACb,cAAc,EAAE,MAAM,CAAC;IACvB,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,OAAO,CAAC,EAAE,MAAM,CAAC;CAClB;AAED,wBAAgB,cAAc,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,GAAG,UAAU,CAgB5E;AAED,wBAAgB,cAAc,CAAC,OAAO,EAAE,MAAM,GAAG,MAAM,CAEtD"}
@@ -0,0 +1,43 @@
1
+ /**
2
+ * Runtime capability detection for Rwandan Law MCP.
3
+ * Detects which database tables are available to enable/disable features.
4
+ */
5
+ const TABLE_MAP = {
6
+ core_legislation: ['legal_documents', 'legal_provisions', 'provisions_fts'],
7
+ eu_references: ['eu_documents', 'eu_references'],
8
+ case_law: ['case_law'],
9
+ preparatory_works: ['preparatory_works'],
10
+ };
11
+ export function detectCapabilities(db) {
12
+ const caps = new Set();
13
+ const tables = new Set(db.prepare("SELECT name FROM sqlite_master WHERE type='table'").all()
14
+ .map(r => r.name));
15
+ for (const [cap, required] of Object.entries(TABLE_MAP)) {
16
+ if (required.every(t => tables.has(t))) {
17
+ caps.add(cap);
18
+ }
19
+ }
20
+ return caps;
21
+ }
22
+ export function readDbMetadata(db) {
23
+ const meta = {};
24
+ try {
25
+ const rows = db.prepare('SELECT key, value FROM db_metadata').all();
26
+ for (const row of rows) {
27
+ meta[row.key] = row.value;
28
+ }
29
+ }
30
+ catch {
31
+ // db_metadata table may not exist
32
+ }
33
+ return {
34
+ tier: meta.tier ?? 'free',
35
+ schema_version: meta.schema_version ?? '1.0',
36
+ built_at: meta.built_at,
37
+ builder: meta.builder,
38
+ };
39
+ }
40
+ export function upgradeMessage(feature) {
41
+ return `The "${feature}" feature requires a professional-tier database. Contact hello@ansvar.ai for access.`;
42
+ }
43
+ //# sourceMappingURL=capabilities.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"capabilities.js","sourceRoot":"","sources":["../../src/capabilities.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,MAAM,SAAS,GAAiC;IAC9C,gBAAgB,EAAE,CAAC,iBAAiB,EAAE,kBAAkB,EAAE,gBAAgB,CAAC;IAC3E,aAAa,EAAE,CAAC,cAAc,EAAE,eAAe,CAAC;IAChD,QAAQ,EAAE,CAAC,UAAU,CAAC;IACtB,iBAAiB,EAAE,CAAC,mBAAmB,CAAC;CACzC,CAAC;AAEF,MAAM,UAAU,kBAAkB,CAAC,EAAiC;IAClE,MAAM,IAAI,GAAG,IAAI,GAAG,EAAc,CAAC;IACnC,MAAM,MAAM,GAAG,IAAI,GAAG,CACnB,EAAE,CAAC,OAAO,CAAC,mDAAmD,CAAC,CAAC,GAAG,EAAyB;SAC1F,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC,CAAC,IAAI,CAAC,CACpB,CAAC;IAEF,KAAK,MAAM,CAAC,GAAG,EAAE,QAAQ,CAAC,IAAI,MAAM,CAAC,OAAO,CAAC,SAAS,CAAC,EAAE,CAAC;QACxD,IAAI,QAAQ,CAAC,KAAK,CAAC,CAAC,CAAC,EAAE,CAAC,MAAM,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC;YACvC,IAAI,CAAC,GAAG,CAAC,GAAiB,CAAC,CAAC;QAC9B,CAAC;IACH,CAAC;IAED,OAAO,IAAI,CAAC;AACd,CAAC;AASD,MAAM,UAAU,cAAc,CAAC,EAAiC;IAC9D,MAAM,IAAI,GAA2B,EAAE,CAAC;IACxC,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,oCAAoC,CAAC,CAAC,GAAG,EAAsC,CAAC;QACxG,KAAK,MAAM,GAAG,IAAI,IAAI,EAAE,CAAC;YACvB,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,GAAG,GAAG,CAAC,KAAK,CAAC;QAC5B,CAAC;IACH,CAAC;IAAC,MAAM,CAAC;QACP,kCAAkC;IACpC,CAAC;IACD,OAAO;QACL,IAAI,EAAE,IAAI,CAAC,IAAI,IAAI,MAAM;QACzB,cAAc,EAAE,IAAI,CAAC,cAAc,IAAI,KAAK;QAC5C,QAAQ,EAAE,IAAI,CAAC,QAAQ;QACvB,OAAO,EAAE,IAAI,CAAC,OAAO;KACtB,CAAC;AACJ,CAAC;AAED,MAAM,UAAU,cAAc,CAAC,OAAe;IAC5C,OAAO,QAAQ,OAAO,sFAAsF,CAAC;AAC/G,CAAC"}
@@ -0,0 +1,7 @@
1
+ export declare const SERVER_NAME = "rwandan-law-mcp";
2
+ export declare const SERVER_VERSION = "1.0.0";
3
+ export declare const SERVER_LABEL = "Rwandan Law MCP";
4
+ export declare const PACKAGE_NAME = "@ansvar/rwandan-law-mcp";
5
+ export declare const REPOSITORY_URL = "https://github.com/Ansvar-Systems/Rwandan-law-mcp";
6
+ export declare const DB_ENV_VAR = "RWANDAN_LAW_DB_PATH";
7
+ //# sourceMappingURL=constants.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.d.ts","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,eAAO,MAAM,WAAW,oBAAoB,CAAC;AAC7C,eAAO,MAAM,cAAc,UAAU,CAAC;AACtC,eAAO,MAAM,YAAY,oBAAoB,CAAC;AAC9C,eAAO,MAAM,YAAY,4BAA4B,CAAC;AACtD,eAAO,MAAM,cAAc,sDAAsD,CAAC;AAClF,eAAO,MAAM,UAAU,wBAAwB,CAAC"}
@@ -0,0 +1,7 @@
1
+ export const SERVER_NAME = 'rwandan-law-mcp';
2
+ export const SERVER_VERSION = '1.0.0';
3
+ export const SERVER_LABEL = 'Rwandan Law MCP';
4
+ export const PACKAGE_NAME = '@ansvar/rwandan-law-mcp';
5
+ export const REPOSITORY_URL = 'https://github.com/Ansvar-Systems/Rwandan-law-mcp';
6
+ export const DB_ENV_VAR = 'RWANDAN_LAW_DB_PATH';
7
+ //# sourceMappingURL=constants.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"constants.js","sourceRoot":"","sources":["../../src/constants.ts"],"names":[],"mappings":"AAAA,MAAM,CAAC,MAAM,WAAW,GAAG,iBAAiB,CAAC;AAC7C,MAAM,CAAC,MAAM,cAAc,GAAG,OAAO,CAAC;AACtC,MAAM,CAAC,MAAM,YAAY,GAAG,iBAAiB,CAAC;AAC9C,MAAM,CAAC,MAAM,YAAY,GAAG,yBAAyB,CAAC;AACtD,MAAM,CAAC,MAAM,cAAc,GAAG,mDAAmD,CAAC;AAClF,MAAM,CAAC,MAAM,UAAU,GAAG,qBAAqB,CAAC"}
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Rwandan Law MCP Server — stdio entry point.
4
+ *
5
+ * Provides Rwandan legislation search via Model Context Protocol.
6
+ */
7
+ export {};
8
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":";AAEA;;;;GAIG"}
@@ -0,0 +1,80 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * Rwandan Law MCP Server — stdio entry point.
4
+ *
5
+ * Provides Rwandan legislation search via Model Context Protocol.
6
+ */
7
+ import { Server } from '@modelcontextprotocol/sdk/server/index.js';
8
+ import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
9
+ import Database from '@ansvar/mcp-sqlite';
10
+ import { join, dirname } from 'path';
11
+ import { fileURLToPath } from 'url';
12
+ import { createHash } from 'crypto';
13
+ import { readFileSync } from 'fs';
14
+ import { registerTools } from './tools/registry.js';
15
+ import { detectCapabilities, readDbMetadata } from './capabilities.js';
16
+ import { DB_ENV_VAR, SERVER_NAME, SERVER_VERSION, } from './constants.js';
17
+ const __dirname = dirname(fileURLToPath(import.meta.url));
18
+ function resolveDbPath() {
19
+ if (process.env[DB_ENV_VAR]) {
20
+ return process.env[DB_ENV_VAR];
21
+ }
22
+ return join(__dirname, '..', 'data', 'database.db');
23
+ }
24
+ let db = null;
25
+ function getDb() {
26
+ if (!db) {
27
+ const dbPath = resolveDbPath();
28
+ db = new Database(dbPath, { readonly: true });
29
+ db.pragma('foreign_keys = ON');
30
+ const caps = detectCapabilities(db);
31
+ const meta = readDbMetadata(db);
32
+ console.error(`[${SERVER_NAME}] DB opened: tier=${meta.tier}, caps=[${[...caps].join(',')}]`);
33
+ }
34
+ return db;
35
+ }
36
+ function computeAboutContext() {
37
+ const dbPath = resolveDbPath();
38
+ let fingerprint = 'unknown';
39
+ let dbBuilt = 'unknown';
40
+ try {
41
+ const buf = readFileSync(dbPath);
42
+ fingerprint = createHash('sha256').update(buf).digest('hex').slice(0, 12);
43
+ }
44
+ catch {
45
+ // DB might not exist in dev
46
+ }
47
+ try {
48
+ const database = getDb();
49
+ const row = database.prepare("SELECT value FROM db_metadata WHERE key = 'built_at'").get();
50
+ if (row)
51
+ dbBuilt = row.value;
52
+ }
53
+ catch {
54
+ // Ignore
55
+ }
56
+ return { version: SERVER_VERSION, fingerprint, dbBuilt };
57
+ }
58
+ async function main() {
59
+ const database = getDb();
60
+ const aboutContext = computeAboutContext();
61
+ const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
62
+ registerTools(server, database, aboutContext);
63
+ const transport = new StdioServerTransport();
64
+ await server.connect(transport);
65
+ console.error(`[${SERVER_NAME}] Server running on stdio`);
66
+ const cleanup = () => {
67
+ if (db) {
68
+ db.close();
69
+ db = null;
70
+ }
71
+ process.exit(0);
72
+ };
73
+ process.on('SIGINT', cleanup);
74
+ process.on('SIGTERM', cleanup);
75
+ }
76
+ main().catch((err) => {
77
+ console.error(`[${SERVER_NAME}] Fatal error:`, err);
78
+ process.exit(1);
79
+ });
80
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,45 @@
1
+ /**
2
+ * about — Server metadata, dataset statistics, and provenance.
3
+ */
4
+ import type Database from '@ansvar/mcp-sqlite';
5
+ export interface AboutContext {
6
+ version: string;
7
+ fingerprint: string;
8
+ dbBuilt: string;
9
+ }
10
+ export declare function getAbout(db: InstanceType<typeof Database>, context: AboutContext): {
11
+ server: string;
12
+ version: string;
13
+ repository: string;
14
+ database: {
15
+ fingerprint: string;
16
+ built_at: string;
17
+ tier: string;
18
+ schema_version: string;
19
+ capabilities: import("../capabilities.js").Capability[];
20
+ };
21
+ statistics: {
22
+ documents: number;
23
+ provisions: number;
24
+ definitions: number;
25
+ eu_documents: number;
26
+ eu_references: number;
27
+ };
28
+ disclaimer: string;
29
+ network: {
30
+ name: string;
31
+ open_law: string;
32
+ directory: string;
33
+ total_servers: number;
34
+ law_jurisdictions: number;
35
+ };
36
+ data_source: {
37
+ name: string;
38
+ authority: string;
39
+ url: string;
40
+ license: string;
41
+ jurisdiction: string;
42
+ languages: string[];
43
+ };
44
+ };
45
+ //# sourceMappingURL=about.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"about.d.ts","sourceRoot":"","sources":["../../../src/tools/about.ts"],"names":[],"mappings":"AAAA;;GAEG;AAEH,OAAO,KAAK,QAAQ,MAAM,oBAAoB,CAAC;AAI/C,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,WAAW,EAAE,MAAM,CAAC;IACpB,OAAO,EAAE,MAAM,CAAC;CACjB;AAWD,wBAAgB,QAAQ,CAAC,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,EAAE,OAAO,EAAE,YAAY;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;EAuChF"}
@@ -0,0 +1,54 @@
1
+ /**
2
+ * about — Server metadata, dataset statistics, and provenance.
3
+ */
4
+ import { detectCapabilities, readDbMetadata } from '../capabilities.js';
5
+ import { SERVER_NAME, REPOSITORY_URL } from '../constants.js';
6
+ function safeCount(db, sql) {
7
+ try {
8
+ const row = db.prepare(sql).get();
9
+ return row ? Number(row.count) : 0;
10
+ }
11
+ catch {
12
+ return 0;
13
+ }
14
+ }
15
+ export function getAbout(db, context) {
16
+ const caps = detectCapabilities(db);
17
+ const meta = readDbMetadata(db);
18
+ return {
19
+ server: SERVER_NAME,
20
+ version: context.version,
21
+ repository: REPOSITORY_URL,
22
+ database: {
23
+ fingerprint: context.fingerprint,
24
+ built_at: context.dbBuilt,
25
+ tier: meta.tier,
26
+ schema_version: meta.schema_version,
27
+ capabilities: [...caps],
28
+ },
29
+ statistics: {
30
+ documents: safeCount(db, 'SELECT COUNT(*) as count FROM legal_documents'),
31
+ provisions: safeCount(db, 'SELECT COUNT(*) as count FROM legal_provisions'),
32
+ definitions: safeCount(db, 'SELECT COUNT(*) as count FROM definitions'),
33
+ eu_documents: safeCount(db, 'SELECT COUNT(*) as count FROM eu_documents'),
34
+ eu_references: safeCount(db, 'SELECT COUNT(*) as count FROM eu_references'),
35
+ },
36
+ disclaimer: 'This is a research tool, not legal advice. Verify critical citations against official sources.',
37
+ network: {
38
+ name: 'Ansvar MCP Network',
39
+ open_law: 'https://ansvar.eu/open-law',
40
+ directory: 'https://ansvar.ai/mcp',
41
+ total_servers: 83,
42
+ law_jurisdictions: 70,
43
+ },
44
+ data_source: {
45
+ name: 'RwandaLII Legislation Portal',
46
+ authority: 'RwandaLII / Laws.Africa',
47
+ url: 'https://rwandalii.org/legislation/',
48
+ license: 'RwandaLII Terms of Use',
49
+ jurisdiction: 'RW',
50
+ languages: ['en', 'rw', 'fr'],
51
+ },
52
+ };
53
+ }
54
+ //# sourceMappingURL=about.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"about.js","sourceRoot":"","sources":["../../../src/tools/about.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,kBAAkB,EAAE,cAAc,EAAE,MAAM,oBAAoB,CAAC;AACxE,OAAO,EAAE,WAAW,EAAkB,cAAc,EAAE,MAAM,iBAAiB,CAAC;AAQ9E,SAAS,SAAS,CAAC,EAAiC,EAAE,GAAW;IAC/D,IAAI,CAAC;QACH,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,EAAmC,CAAC;QACnE,OAAO,GAAG,CAAC,CAAC,CAAC,MAAM,CAAC,GAAG,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;IACrC,CAAC;IAAC,MAAM,CAAC;QACP,OAAO,CAAC,CAAC;IACX,CAAC;AACH,CAAC;AAED,MAAM,UAAU,QAAQ,CAAC,EAAiC,EAAE,OAAqB;IAC/E,MAAM,IAAI,GAAG,kBAAkB,CAAC,EAAE,CAAC,CAAC;IACpC,MAAM,IAAI,GAAG,cAAc,CAAC,EAAE,CAAC,CAAC;IAEhC,OAAO;QACL,MAAM,EAAE,WAAW;QACnB,OAAO,EAAE,OAAO,CAAC,OAAO;QACxB,UAAU,EAAE,cAAc;QAC1B,QAAQ,EAAE;YACR,WAAW,EAAE,OAAO,CAAC,WAAW;YAChC,QAAQ,EAAE,OAAO,CAAC,OAAO;YACzB,IAAI,EAAE,IAAI,CAAC,IAAI;YACf,cAAc,EAAE,IAAI,CAAC,cAAc;YACnC,YAAY,EAAE,CAAC,GAAG,IAAI,CAAC;SACxB;QACD,UAAU,EAAE;YACV,SAAS,EAAE,SAAS,CAAC,EAAE,EAAE,+CAA+C,CAAC;YACzE,UAAU,EAAE,SAAS,CAAC,EAAE,EAAE,gDAAgD,CAAC;YAC3E,WAAW,EAAE,SAAS,CAAC,EAAE,EAAE,2CAA2C,CAAC;YACvE,YAAY,EAAE,SAAS,CAAC,EAAE,EAAE,4CAA4C,CAAC;YACzE,aAAa,EAAE,SAAS,CAAC,EAAE,EAAE,6CAA6C,CAAC;SAC5E;QACD,UAAU,EAAE,gGAAgG;QAC5G,OAAO,EAAE;YACP,IAAI,EAAE,oBAAoB;YAC1B,QAAQ,EAAE,4BAA4B;YACtC,SAAS,EAAE,uBAAuB;YAClC,aAAa,EAAE,EAAE;YACjB,iBAAiB,EAAE,EAAE;SACtB;QACD,WAAW,EAAE;YACX,IAAI,EAAE,8BAA8B;YACpC,SAAS,EAAE,yBAAyB;YACpC,GAAG,EAAE,oCAAoC;YACzC,OAAO,EAAE,wBAAwB;YACjC,YAAY,EAAE,IAAI;YAClB,SAAS,EAAE,CAAC,IAAI,EAAE,IAAI,EAAE,IAAI,CAAC;SAC9B;KACF,CAAC;AACJ,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * build_legal_stance — Build a comprehensive set of citations for a legal question.
3
+ */
4
+ import type Database from '@ansvar/mcp-sqlite';
5
+ import { type ToolResponse } from '../utils/metadata.js';
6
+ export interface BuildLegalStanceInput {
7
+ query: string;
8
+ document_id?: string;
9
+ limit?: number;
10
+ }
11
+ export interface LegalStanceResult {
12
+ document_id: string;
13
+ document_title: string;
14
+ provision_ref: string;
15
+ section: string;
16
+ title: string | null;
17
+ snippet: string;
18
+ relevance: number;
19
+ }
20
+ export declare function buildLegalStance(db: InstanceType<typeof Database>, input: BuildLegalStanceInput): Promise<ToolResponse<LegalStanceResult[]>>;
21
+ //# sourceMappingURL=build-legal-stance.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-legal-stance.d.ts","sourceRoot":"","sources":["../../../src/tools/build-legal-stance.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,qBAAqB;IACpC,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,iBAAiB;IAChC,WAAW,EAAE,MAAM,CAAC;IACpB,cAAc,EAAE,MAAM,CAAC;IACvB,aAAa,EAAE,MAAM,CAAC;IACtB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAED,wBAAsB,gBAAgB,CACpC,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,EACjC,KAAK,EAAE,qBAAqB,GAC3B,OAAO,CAAC,YAAY,CAAC,iBAAiB,EAAE,CAAC,CAAC,CA4C5C"}
@@ -0,0 +1,46 @@
1
+ /**
2
+ * build_legal_stance — Build a comprehensive set of citations for a legal question.
3
+ */
4
+ import { buildFtsQueryVariants, sanitizeFtsInput } from '../utils/fts-query.js';
5
+ import { generateResponseMetadata } from '../utils/metadata.js';
6
+ export async function buildLegalStance(db, input) {
7
+ if (!input.query || input.query.trim().length === 0) {
8
+ return { results: [], _metadata: generateResponseMetadata(db) };
9
+ }
10
+ const limit = Math.min(Math.max(input.limit ?? 5, 1), 20);
11
+ const queryVariants = buildFtsQueryVariants(sanitizeFtsInput(input.query));
12
+ for (const ftsQuery of queryVariants) {
13
+ let sql = `
14
+ SELECT
15
+ lp.document_id,
16
+ ld.title as document_title,
17
+ lp.provision_ref,
18
+ lp.section,
19
+ lp.title,
20
+ snippet(provisions_fts, 0, '>>>', '<<<', '...', 48) as snippet,
21
+ bm25(provisions_fts) as relevance
22
+ FROM provisions_fts
23
+ JOIN legal_provisions lp ON lp.id = provisions_fts.rowid
24
+ JOIN legal_documents ld ON ld.id = lp.document_id
25
+ WHERE provisions_fts MATCH ?
26
+ `;
27
+ const params = [ftsQuery];
28
+ if (input.document_id) {
29
+ sql += ' AND lp.document_id = ?';
30
+ params.push(input.document_id);
31
+ }
32
+ sql += ' ORDER BY relevance LIMIT ?';
33
+ params.push(limit);
34
+ try {
35
+ const rows = db.prepare(sql).all(...params);
36
+ if (rows.length > 0) {
37
+ return { results: rows, _metadata: generateResponseMetadata(db) };
38
+ }
39
+ }
40
+ catch {
41
+ continue;
42
+ }
43
+ }
44
+ return { results: [], _metadata: generateResponseMetadata(db) };
45
+ }
46
+ //# sourceMappingURL=build-legal-stance.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"build-legal-stance.js","sourceRoot":"","sources":["../../../src/tools/build-legal-stance.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,qBAAqB,EAAE,gBAAgB,EAAE,MAAM,uBAAuB,CAAC;AAChF,OAAO,EAAE,wBAAwB,EAAqB,MAAM,sBAAsB,CAAC;AAkBnF,MAAM,CAAC,KAAK,UAAU,gBAAgB,CACpC,EAAiC,EACjC,KAA4B;IAE5B,IAAI,CAAC,KAAK,CAAC,KAAK,IAAI,KAAK,CAAC,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACpD,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,wBAAwB,CAAC,EAAE,CAAC,EAAE,CAAC;IAClE,CAAC;IAED,MAAM,KAAK,GAAG,IAAI,CAAC,GAAG,CAAC,IAAI,CAAC,GAAG,CAAC,KAAK,CAAC,KAAK,IAAI,CAAC,EAAE,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC;IAC1D,MAAM,aAAa,GAAG,qBAAqB,CAAC,gBAAgB,CAAC,KAAK,CAAC,KAAK,CAAC,CAAC,CAAC;IAE3E,KAAK,MAAM,QAAQ,IAAI,aAAa,EAAE,CAAC;QACrC,IAAI,GAAG,GAAG;;;;;;;;;;;;;KAaT,CAAC;QACF,MAAM,MAAM,GAAwB,CAAC,QAAQ,CAAC,CAAC;QAE/C,IAAI,KAAK,CAAC,WAAW,EAAE,CAAC;YACtB,GAAG,IAAI,yBAAyB,CAAC;YACjC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,WAAW,CAAC,CAAC;QACjC,CAAC;QAED,GAAG,IAAI,6BAA6B,CAAC;QACrC,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;QAEnB,IAAI,CAAC;YACH,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,GAAG,MAAM,CAAwB,CAAC;YACnE,IAAI,IAAI,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;gBACpB,OAAO,EAAE,OAAO,EAAE,IAAI,EAAE,SAAS,EAAE,wBAAwB,CAAC,EAAE,CAAC,EAAE,CAAC;YACpE,CAAC;QACH,CAAC;QAAC,MAAM,CAAC;YACP,SAAS;QACX,CAAC;IACH,CAAC;IAED,OAAO,EAAE,OAAO,EAAE,EAAE,EAAE,SAAS,EAAE,wBAAwB,CAAC,EAAE,CAAC,EAAE,CAAC;AAClE,CAAC"}
@@ -0,0 +1,20 @@
1
+ /**
2
+ * check_currency — Check whether an Rwandan statute is currently in force.
3
+ */
4
+ import type Database from '@ansvar/mcp-sqlite';
5
+ import { type ToolResponse } from '../utils/metadata.js';
6
+ export interface CheckCurrencyInput {
7
+ document_id: string;
8
+ provision_ref?: string;
9
+ as_of_date?: string;
10
+ }
11
+ export interface CheckCurrencyResult {
12
+ document_id: string;
13
+ title: string;
14
+ status: string;
15
+ issued_date: string | null;
16
+ in_force_date: string | null;
17
+ warnings: string[];
18
+ }
19
+ export declare function checkCurrency(db: InstanceType<typeof Database>, input: CheckCurrencyInput): Promise<ToolResponse<CheckCurrencyResult>>;
20
+ //# sourceMappingURL=check-currency.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-currency.d.ts","sourceRoot":"","sources":["../../../src/tools/check-currency.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,kBAAkB;IACjC,WAAW,EAAE,MAAM,CAAC;IACpB,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,UAAU,CAAC,EAAE,MAAM,CAAC;CACrB;AAED,MAAM,WAAW,mBAAmB;IAClC,WAAW,EAAE,MAAM,CAAC;IACpB,KAAK,EAAE,MAAM,CAAC;IACd,MAAM,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,QAAQ,EAAE,MAAM,EAAE,CAAC;CACpB;AAED,wBAAsB,aAAa,CACjC,EAAE,EAAE,YAAY,CAAC,OAAO,QAAQ,CAAC,EACjC,KAAK,EAAE,kBAAkB,GACxB,OAAO,CAAC,YAAY,CAAC,mBAAmB,CAAC,CAAC,CA4C5C"}
@@ -0,0 +1,41 @@
1
+ /**
2
+ * check_currency — Check whether an Rwandan statute is currently in force.
3
+ */
4
+ import { resolveDocumentId } from '../utils/statute-id.js';
5
+ import { generateResponseMetadata } from '../utils/metadata.js';
6
+ export async function checkCurrency(db, input) {
7
+ const resolvedId = resolveDocumentId(db, input.document_id);
8
+ if (!resolvedId) {
9
+ return {
10
+ results: {
11
+ document_id: input.document_id,
12
+ title: 'Unknown',
13
+ status: 'not_found',
14
+ issued_date: null,
15
+ in_force_date: null,
16
+ warnings: [`Document not found: "${input.document_id}"`],
17
+ },
18
+ _metadata: generateResponseMetadata(db),
19
+ };
20
+ }
21
+ const doc = db.prepare('SELECT id, title, status, issued_date, in_force_date FROM legal_documents WHERE id = ?').get(resolvedId);
22
+ const warnings = [];
23
+ if (doc.status === 'repealed') {
24
+ warnings.push('This statute has been repealed and is no longer in force.');
25
+ }
26
+ else if (doc.status === 'not_yet_in_force') {
27
+ warnings.push('This statute has not yet entered into force.');
28
+ }
29
+ return {
30
+ results: {
31
+ document_id: doc.id,
32
+ title: doc.title,
33
+ status: doc.status,
34
+ issued_date: doc.issued_date,
35
+ in_force_date: doc.in_force_date,
36
+ warnings,
37
+ },
38
+ _metadata: generateResponseMetadata(db),
39
+ };
40
+ }
41
+ //# sourceMappingURL=check-currency.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"check-currency.js","sourceRoot":"","sources":["../../../src/tools/check-currency.ts"],"names":[],"mappings":"AAAA;;GAEG;AAGH,OAAO,EAAE,iBAAiB,EAAE,MAAM,wBAAwB,CAAC;AAC3D,OAAO,EAAE,wBAAwB,EAAqB,MAAM,sBAAsB,CAAC;AAiBnF,MAAM,CAAC,KAAK,UAAU,aAAa,CACjC,EAAiC,EACjC,KAAyB;IAEzB,MAAM,UAAU,GAAG,iBAAiB,CAAC,EAAE,EAAE,KAAK,CAAC,WAAW,CAAC,CAAC;IAC5D,IAAI,CAAC,UAAU,EAAE,CAAC;QAChB,OAAO;YACL,OAAO,EAAE;gBACP,WAAW,EAAE,KAAK,CAAC,WAAW;gBAC9B,KAAK,EAAE,SAAS;gBAChB,MAAM,EAAE,WAAW;gBACnB,WAAW,EAAE,IAAI;gBACjB,aAAa,EAAE,IAAI;gBACnB,QAAQ,EAAE,CAAC,wBAAwB,KAAK,CAAC,WAAW,GAAG,CAAC;aACzD;YACD,SAAS,EAAE,wBAAwB,CAAC,EAAE,CAAC;SACxC,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CACpB,wFAAwF,CACzF,CAAC,GAAG,CAAC,UAAU,CAMf,CAAC;IAEF,MAAM,QAAQ,GAAa,EAAE,CAAC;IAC9B,IAAI,GAAG,CAAC,MAAM,KAAK,UAAU,EAAE,CAAC;QAC9B,QAAQ,CAAC,IAAI,CAAC,2DAA2D,CAAC,CAAC;IAC7E,CAAC;SAAM,IAAI,GAAG,CAAC,MAAM,KAAK,kBAAkB,EAAE,CAAC;QAC7C,QAAQ,CAAC,IAAI,CAAC,8CAA8C,CAAC,CAAC;IAChE,CAAC;IAED,OAAO;QACL,OAAO,EAAE;YACP,WAAW,EAAE,GAAG,CAAC,EAAE;YACnB,KAAK,EAAE,GAAG,CAAC,KAAK;YAChB,MAAM,EAAE,GAAG,CAAC,MAAM;YAClB,WAAW,EAAE,GAAG,CAAC,WAAW;YAC5B,aAAa,EAAE,GAAG,CAAC,aAAa;YAChC,QAAQ;SACT;QACD,SAAS,EAAE,wBAAwB,CAAC,EAAE,CAAC;KACxC,CAAC;AACJ,CAAC"}
@@ -0,0 +1,14 @@
1
+ /**
2
+ * format_citation — Format an Rwandan legal citation per standard conventions.
3
+ */
4
+ export interface FormatCitationInput {
5
+ citation: string;
6
+ format?: 'full' | 'short' | 'pinpoint';
7
+ }
8
+ export interface FormatCitationResult {
9
+ original: string;
10
+ formatted: string;
11
+ format: string;
12
+ }
13
+ export declare function formatCitationTool(input: FormatCitationInput): Promise<FormatCitationResult>;
14
+ //# sourceMappingURL=format-citation.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"format-citation.d.ts","sourceRoot":"","sources":["../../../src/tools/format-citation.ts"],"names":[],"mappings":"AAAA;;GAEG;AAKH,MAAM,WAAW,mBAAmB;IAClC,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,GAAG,UAAU,CAAC;CACxC;AAED,MAAM,WAAW,oBAAoB;IACnC,QAAQ,EAAE,MAAM,CAAC;IACjB,SAAS,EAAE,MAAM,CAAC;IAClB,MAAM,EAAE,MAAM,CAAC;CAChB;AAED,wBAAsB,kBAAkB,CACtC,KAAK,EAAE,mBAAmB,GACzB,OAAO,CAAC,oBAAoB,CAAC,CA2B/B"}
@@ -0,0 +1,28 @@
1
+ /**
2
+ * format_citation — Format an Rwandan legal citation per standard conventions.
3
+ */
4
+ export async function formatCitationTool(input) {
5
+ const format = input.format ?? 'full';
6
+ const trimmed = input.citation.trim();
7
+ // Parse "Section N <Act>" or "Section N, <Act>"
8
+ const sectionFirst = trimmed.match(/^Section\s+(\d+[A-Za-z]*(?:\(\d+\))?)\s*[,;]?\s+(.+)$/i);
9
+ // Parse "<Act> s N" or "<Act>, s N" or "<Act> Section N"
10
+ const sectionLast = trimmed.match(/^(.+?)\s*[,;]?\s+(?:s\.?\s+|Section\s+)(\d+[A-Za-z]*(?:\(\d+\))?)$/i);
11
+ const section = sectionFirst?.[1] ?? sectionLast?.[2];
12
+ const act = sectionFirst?.[2] ?? sectionLast?.[1] ?? trimmed;
13
+ let formatted;
14
+ switch (format) {
15
+ case 'short':
16
+ formatted = section ? `${act.split('(')[0].trim()} s ${section}` : act;
17
+ break;
18
+ case 'pinpoint':
19
+ formatted = section ? `s ${section}` : act;
20
+ break;
21
+ case 'full':
22
+ default:
23
+ formatted = section ? `Section ${section}, ${act}` : act;
24
+ break;
25
+ }
26
+ return { original: input.citation, formatted, format };
27
+ }
28
+ //# sourceMappingURL=format-citation.js.map