@ansvar/eu-regulations-mcp 0.2.3 → 0.3.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/dist/index.js CHANGED
@@ -7,6 +7,7 @@ import { fileURLToPath } from 'url';
7
7
  import { dirname, join } from 'path';
8
8
  import { searchRegulations } from './tools/search.js';
9
9
  import { getArticle } from './tools/article.js';
10
+ import { getRecital } from './tools/recital.js';
10
11
  import { listRegulations } from './tools/list.js';
11
12
  import { compareRequirements } from './tools/compare.js';
12
13
  import { mapControls } from './tools/map.js';
@@ -80,6 +81,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
80
81
  required: ['regulation', 'article'],
81
82
  },
82
83
  },
84
+ {
85
+ name: 'get_recital',
86
+ description: 'Retrieve the full text of a specific recital from a regulation. Recitals provide context and interpretation guidance for articles.',
87
+ inputSchema: {
88
+ type: 'object',
89
+ properties: {
90
+ regulation: {
91
+ type: 'string',
92
+ description: 'Regulation ID (e.g., "GDPR", "NIS2", "DORA")',
93
+ },
94
+ recital_number: {
95
+ type: 'number',
96
+ description: 'Recital number (e.g., 1, 83)',
97
+ },
98
+ },
99
+ required: ['regulation', 'recital_number'],
100
+ },
101
+ },
83
102
  {
84
103
  name: 'list_regulations',
85
104
  description: 'List available regulations and their structure. Without parameters, lists all regulations. With a regulation specified, shows chapters and articles.',
@@ -209,6 +228,19 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
209
228
  content: [{ type: 'text', text: JSON.stringify(article, null, 2) }],
210
229
  };
211
230
  }
231
+ case 'get_recital': {
232
+ const input = args;
233
+ const recital = await getRecital(database, input);
234
+ if (!recital) {
235
+ return {
236
+ content: [{ type: 'text', text: `Recital ${input.recital_number} not found in ${input.regulation}` }],
237
+ isError: true,
238
+ };
239
+ }
240
+ return {
241
+ content: [{ type: 'text', text: JSON.stringify(recital, null, 2) }],
242
+ };
243
+ }
212
244
  case 'list_regulations': {
213
245
  const input = (args ?? {});
214
246
  const result = await listRegulations(database, input);
package/dist/index.js.map CHANGED
@@ -1 +1 @@
1
- {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,13 @@
1
+ import type { Database } from 'better-sqlite3';
2
+ export interface GetRecitalInput {
3
+ regulation: string;
4
+ recital_number: number;
5
+ }
6
+ export interface Recital {
7
+ regulation: string;
8
+ recital_number: number;
9
+ text: string;
10
+ related_articles: string[] | null;
11
+ }
12
+ export declare function getRecital(db: Database, input: GetRecitalInput): Promise<Recital | null>;
13
+ //# sourceMappingURL=recital.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recital.d.ts","sourceRoot":"","sources":["../../src/tools/recital.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,eAAe;IAC9B,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;CACxB;AAED,MAAM,WAAW,OAAO;IACtB,UAAU,EAAE,MAAM,CAAC;IACnB,cAAc,EAAE,MAAM,CAAC;IACvB,IAAI,EAAE,MAAM,CAAC;IACb,gBAAgB,EAAE,MAAM,EAAE,GAAG,IAAI,CAAC;CACnC;AAED,wBAAsB,UAAU,CAC9B,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,eAAe,GACrB,OAAO,CAAC,OAAO,GAAG,IAAI,CAAC,CA8BzB"}
@@ -0,0 +1,23 @@
1
+ export async function getRecital(db, input) {
2
+ const { regulation, recital_number } = input;
3
+ const sql = `
4
+ SELECT
5
+ regulation,
6
+ recital_number,
7
+ text,
8
+ related_articles
9
+ FROM recitals
10
+ WHERE regulation = ? AND recital_number = ?
11
+ `;
12
+ const row = db.prepare(sql).get(regulation, recital_number);
13
+ if (!row) {
14
+ return null;
15
+ }
16
+ return {
17
+ regulation: row.regulation,
18
+ recital_number: row.recital_number,
19
+ text: row.text,
20
+ related_articles: row.related_articles ? JSON.parse(row.related_articles) : null,
21
+ };
22
+ }
23
+ //# sourceMappingURL=recital.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"recital.js","sourceRoot":"","sources":["../../src/tools/recital.ts"],"names":[],"mappings":"AAcA,MAAM,CAAC,KAAK,UAAU,UAAU,CAC9B,EAAY,EACZ,KAAsB;IAEtB,MAAM,EAAE,UAAU,EAAE,cAAc,EAAE,GAAG,KAAK,CAAC;IAE7C,MAAM,GAAG,GAAG;;;;;;;;GAQX,CAAC;IAEF,MAAM,GAAG,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC,GAAG,CAAC,UAAU,EAAE,cAAc,CAK7C,CAAC;IAEd,IAAI,CAAC,GAAG,EAAE,CAAC;QACT,OAAO,IAAI,CAAC;IACd,CAAC;IAED,OAAO;QACL,UAAU,EAAE,GAAG,CAAC,UAAU;QAC1B,cAAc,EAAE,GAAG,CAAC,cAAc;QAClC,IAAI,EAAE,GAAG,CAAC,IAAI;QACd,gBAAgB,EAAE,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,IAAI,CAAC,KAAK,CAAC,GAAG,CAAC,gBAAgB,CAAC,CAAC,CAAC,CAAC,IAAI;KACjF,CAAC;AACJ,CAAC"}
@@ -10,6 +10,7 @@ export interface SearchResult {
10
10
  title: string;
11
11
  snippet: string;
12
12
  relevance: number;
13
+ type?: 'article' | 'recital';
13
14
  }
14
15
  export declare function searchRegulations(db: Database, input: SearchInput): Promise<SearchResult[]>;
15
16
  //# sourceMappingURL=search.d.ts.map
@@ -1 +1 @@
1
- {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;CACnB;AAiBD,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CA4DzB"}
1
+ {"version":3,"file":"search.d.ts","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAAA,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,gBAAgB,CAAC;AAE/C,MAAM,WAAW,WAAW;IAC1B,KAAK,EAAE,MAAM,CAAC;IACd,WAAW,CAAC,EAAE,MAAM,EAAE,CAAC;IACvB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED,MAAM,WAAW,YAAY;IAC3B,UAAU,EAAE,MAAM,CAAC;IACnB,OAAO,EAAE,MAAM,CAAC;IAChB,KAAK,EAAE,MAAM,CAAC;IACd,OAAO,EAAE,MAAM,CAAC;IAChB,SAAS,EAAE,MAAM,CAAC;IAClB,IAAI,CAAC,EAAE,SAAS,GAAG,SAAS,CAAC;CAC9B;AAiBD,wBAAsB,iBAAiB,CACrC,EAAE,EAAE,QAAQ,EACZ,KAAK,EAAE,WAAW,GACjB,OAAO,CAAC,YAAY,EAAE,CAAC,CA2GzB"}
@@ -21,35 +21,72 @@ export async function searchRegulations(db, input) {
21
21
  if (!escapedQuery) {
22
22
  return [];
23
23
  }
24
- // Build the SQL query with optional regulation filter
25
- let sql = `
24
+ const params = [escapedQuery];
25
+ // Build optional regulation filter
26
+ let regulationFilter = '';
27
+ if (regulations && regulations.length > 0) {
28
+ const placeholders = regulations.map(() => '?').join(', ');
29
+ regulationFilter = ` AND regulation IN (${placeholders})`;
30
+ params.push(...regulations);
31
+ }
32
+ // Search in articles
33
+ const articlesQuery = `
26
34
  SELECT
27
35
  articles_fts.regulation,
28
36
  articles_fts.article_number as article,
29
37
  articles_fts.title,
30
38
  snippet(articles_fts, 3, '>>>', '<<<', '...', 32) as snippet,
31
- bm25(articles_fts) as relevance
39
+ bm25(articles_fts) as relevance,
40
+ 'article' as type
32
41
  FROM articles_fts
33
42
  WHERE articles_fts MATCH ?
43
+ ${regulationFilter}
44
+ ORDER BY bm25(articles_fts)
45
+ LIMIT ?
46
+ `;
47
+ // Search in recitals
48
+ const recitalsQuery = `
49
+ SELECT
50
+ recitals_fts.regulation,
51
+ CAST(recitals_fts.recital_number AS TEXT) as article,
52
+ 'Recital ' || recitals_fts.recital_number as title,
53
+ snippet(recitals_fts, 2, '>>>', '<<<', '...', 32) as snippet,
54
+ bm25(recitals_fts) as relevance,
55
+ 'recital' as type
56
+ FROM recitals_fts
57
+ WHERE recitals_fts MATCH ?
58
+ ${regulationFilter}
59
+ ORDER BY bm25(recitals_fts)
60
+ LIMIT ?
34
61
  `;
35
- const params = [escapedQuery];
36
- if (regulations && regulations.length > 0) {
37
- const placeholders = regulations.map(() => '?').join(', ');
38
- sql += ` AND articles_fts.regulation IN (${placeholders})`;
39
- params.push(...regulations);
40
- }
41
- // Order by relevance (bm25 returns negative scores, more negative = more relevant)
42
- sql += ` ORDER BY bm25(articles_fts)`;
43
- sql += ` LIMIT ?`;
44
- params.push(limit);
45
62
  try {
46
- const stmt = db.prepare(sql);
47
- const rows = stmt.all(...params);
48
- // Convert bm25 scores to positive values (higher = more relevant)
49
- return rows.map(row => ({
63
+ // Execute both queries
64
+ const articlesParams = [...params, limit];
65
+ const recitalsParams = [...params, limit];
66
+ const articleStmt = db.prepare(articlesQuery);
67
+ const recitalStmt = db.prepare(recitalsQuery);
68
+ const articleRows = articleStmt.all(...articlesParams);
69
+ const recitalRows = recitalStmt.all(...recitalsParams);
70
+ // Combine and sort by relevance, prioritizing articles
71
+ const combined = [...articleRows, ...recitalRows]
72
+ .map(row => ({
50
73
  ...row,
51
74
  relevance: Math.abs(row.relevance),
52
- }));
75
+ }))
76
+ .sort((a, b) => {
77
+ // First sort by relevance
78
+ if (Math.abs(a.relevance - b.relevance) > 0.01) {
79
+ return b.relevance - a.relevance;
80
+ }
81
+ // If relevance is similar, prioritize articles over recitals
82
+ if (a.type === 'article' && b.type === 'recital')
83
+ return -1;
84
+ if (a.type === 'recital' && b.type === 'article')
85
+ return 1;
86
+ return 0;
87
+ })
88
+ .slice(0, limit);
89
+ return combined;
53
90
  }
54
91
  catch (error) {
55
92
  // If FTS5 query fails (e.g., syntax error), return empty results
@@ -1 +1 @@
1
- {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"AAgBA;;;GAGG;AACH,SAAS,eAAe,CAAC,KAAa;IACpC,sDAAsD;IACtD,yDAAyD;IACzD,OAAO,KAAK;SACT,OAAO,CAAC,OAAO,EAAE,EAAE,CAAC,CAAC,gBAAgB;SACrC,KAAK,CAAC,KAAK,CAAC;SACZ,MAAM,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,CAAC,MAAM,GAAG,CAAC,CAAC;SAC/B,GAAG,CAAC,IAAI,CAAC,EAAE,CAAC,IAAI,IAAI,GAAG,CAAC;SACxB,IAAI,CAAC,GAAG,CAAC,CAAC;AACf,CAAC;AAED,MAAM,CAAC,KAAK,UAAU,iBAAiB,CACrC,EAAY,EACZ,KAAkB;IAElB,MAAM,EAAE,KAAK,EAAE,WAAW,EAAE,KAAK,GAAG,EAAE,EAAE,GAAG,KAAK,CAAC;IAEjD,IAAI,CAAC,KAAK,IAAI,KAAK,CAAC,IAAI,EAAE,CAAC,MAAM,KAAK,CAAC,EAAE,CAAC;QACxC,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,MAAM,YAAY,GAAG,eAAe,CAAC,KAAK,CAAC,CAAC;IAE5C,IAAI,CAAC,YAAY,EAAE,CAAC;QAClB,OAAO,EAAE,CAAC;IACZ,CAAC;IAED,sDAAsD;IACtD,IAAI,GAAG,GAAG;;;;;;;;;GAST,CAAC;IAEF,MAAM,MAAM,GAAwB,CAAC,YAAY,CAAC,CAAC;IAEnD,IAAI,WAAW,IAAI,WAAW,CAAC,MAAM,GAAG,CAAC,EAAE,CAAC;QAC1C,MAAM,YAAY,GAAG,WAAW,CAAC,GAAG,CAAC,GAAG,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;QAC3D,GAAG,IAAI,oCAAoC,YAAY,GAAG,CAAC;QAC3D,MAAM,CAAC,IAAI,CAAC,GAAG,WAAW,CAAC,CAAC;IAC9B,CAAC;IAED,mFAAmF;IACnF,GAAG,IAAI,8BAA8B,CAAC;IACtC,GAAG,IAAI,UAAU,CAAC;IAClB,MAAM,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IAEnB,IAAI,CAAC;QACH,MAAM,IAAI,GAAG,EAAE,CAAC,OAAO,CAAC,GAAG,CAAC,CAAC;QAC7B,MAAM,IAAI,GAAG,IAAI,CAAC,GAAG,CAAC,GAAG,MAAM,CAM7B,CAAC;QAEH,kEAAkE;QAClE,OAAO,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,EAAE,CAAC,CAAC;YACtB,GAAG,GAAG;YACN,SAAS,EAAE,IAAI,CAAC,GAAG,CAAC,GAAG,CAAC,SAAS,CAAC;SACnC,CAAC,CAAC,CAAC;IACN,CAAC;IAAC,OAAO,KAAK,EAAE,CAAC;QACf,iEAAiE;QACjE,IAAI,KAAK,YAAY,KAAK,IAAI,KAAK,CAAC,OAAO,CAAC,QAAQ,CAAC,MAAM,CAAC,EAAE,CAAC;YAC7D,OAAO,EAAE,CAAC;QACZ,CAAC;QACD,MAAM,KAAK,CAAC;IACd,CAAC;AACH,CAAC"}
1
+ {"version":3,"file":"search.js","sourceRoot":"","sources":["../../src/tools/search.ts"],"names":[],"mappings":"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"}
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@ansvar/eu-regulations-mcp",
3
- "version": "0.2.3",
3
+ "version": "0.3.0",
4
4
  "description": "The first open-source MCP server for European cybersecurity regulations. Query DORA, NIS2, GDPR, EU AI Act, and more directly from Claude.",
5
5
  "main": "dist/index.js",
6
6
  "type": "module",
@@ -114,6 +114,43 @@ CREATE TABLE IF NOT EXISTS source_registry (
114
114
  quality_status TEXT CHECK(quality_status IN ('complete', 'review', 'incomplete')),
115
115
  notes TEXT
116
116
  );
117
+
118
+ -- Recitals table
119
+ CREATE TABLE IF NOT EXISTS recitals (
120
+ id INTEGER PRIMARY KEY,
121
+ regulation TEXT NOT NULL REFERENCES regulations(id),
122
+ recital_number INTEGER NOT NULL,
123
+ text TEXT NOT NULL,
124
+ related_articles TEXT,
125
+ UNIQUE(regulation, recital_number)
126
+ );
127
+
128
+ -- FTS5 virtual table for recital search
129
+ CREATE VIRTUAL TABLE IF NOT EXISTS recitals_fts USING fts5(
130
+ regulation,
131
+ recital_number,
132
+ text,
133
+ content='recitals',
134
+ content_rowid='id'
135
+ );
136
+
137
+ -- FTS5 triggers for recitals
138
+ CREATE TRIGGER IF NOT EXISTS recitals_ai AFTER INSERT ON recitals BEGIN
139
+ INSERT INTO recitals_fts(rowid, regulation, recital_number, text)
140
+ VALUES (new.id, new.regulation, new.recital_number, new.text);
141
+ END;
142
+
143
+ CREATE TRIGGER IF NOT EXISTS recitals_ad AFTER DELETE ON recitals BEGIN
144
+ INSERT INTO recitals_fts(recitals_fts, rowid, regulation, recital_number, text)
145
+ VALUES('delete', old.id, old.regulation, old.recital_number, old.text);
146
+ END;
147
+
148
+ CREATE TRIGGER IF NOT EXISTS recitals_au AFTER UPDATE ON recitals BEGIN
149
+ INSERT INTO recitals_fts(recitals_fts, rowid, regulation, recital_number, text)
150
+ VALUES('delete', old.id, old.regulation, old.recital_number, old.text);
151
+ INSERT INTO recitals_fts(rowid, regulation, recital_number, text)
152
+ VALUES (new.id, new.regulation, new.recital_number, new.text);
153
+ END;
117
154
  `;
118
155
 
119
156
  interface RegulationSeed {
@@ -135,6 +172,11 @@ interface RegulationSeed {
135
172
  definition: string;
136
173
  article: string;
137
174
  }>;
175
+ recitals?: Array<{
176
+ recital_number: number;
177
+ text: string;
178
+ related_articles?: string;
179
+ }>;
138
180
  }
139
181
 
140
182
  function buildDatabase() {
@@ -212,6 +254,23 @@ function buildDatabase() {
212
254
  }
213
255
  }
214
256
 
257
+ // Insert recitals
258
+ if (regulation.recitals) {
259
+ const insertRecital = db.prepare(`
260
+ INSERT OR IGNORE INTO recitals (regulation, recital_number, text, related_articles)
261
+ VALUES (?, ?, ?, ?)
262
+ `);
263
+
264
+ for (const recital of regulation.recitals) {
265
+ insertRecital.run(
266
+ regulation.id,
267
+ recital.recital_number,
268
+ recital.text,
269
+ recital.related_articles ? JSON.stringify(recital.related_articles) : null
270
+ );
271
+ }
272
+ }
273
+
215
274
  // Update source registry with timestamps
216
275
  const now = new Date().toISOString();
217
276
  const eurLexVersion = regulation.effective_date || now.split('T')[0];
@@ -221,6 +280,9 @@ function buildDatabase() {
221
280
  `).run(regulation.id, regulation.celex_id, eurLexVersion, now, regulation.articles.length, regulation.articles.length);
222
281
 
223
282
  console.log(` Loaded ${regulation.articles.length} articles, ${regulation.definitions?.length || 0} definitions`);
283
+ if (regulation.recitals && regulation.recitals.length > 0) {
284
+ console.log(` Loaded ${regulation.recitals.length} recitals`);
285
+ }
224
286
  }
225
287
 
226
288
  // Load mappings
@@ -23,6 +23,12 @@ interface Definition {
23
23
  article: string;
24
24
  }
25
25
 
26
+ interface Recital {
27
+ recital_number: number;
28
+ text: string;
29
+ related_articles?: string;
30
+ }
31
+
26
32
  interface RegulationData {
27
33
  id: string;
28
34
  full_name: string;
@@ -31,6 +37,7 @@ interface RegulationData {
31
37
  eur_lex_url: string;
32
38
  articles: Article[];
33
39
  definitions: Definition[];
40
+ recitals?: Recital[];
34
41
  }
35
42
 
36
43
  const REGULATION_METADATA: Record<string, { id: string; full_name: string; effective_date?: string }> = {
@@ -68,6 +75,80 @@ async function fetchEurLexHtml(celexId: string): Promise<string> {
68
75
  return response.text();
69
76
  }
70
77
 
78
+ function parseRecitals(html: string): Recital[] {
79
+ const dom = new JSDOM(html);
80
+ const doc = dom.window.document;
81
+
82
+ const recitals: Recital[] = [];
83
+ const allText = doc.body?.textContent || '';
84
+ const lines = allText.split('\n').map(l => l.trim()).filter(l => l);
85
+
86
+ let inRecitalsSection = false;
87
+ let currentRecital: { number: number; lines: string[] } | null = null;
88
+
89
+ for (const line of lines) {
90
+ // Detect start of recitals section
91
+ if (line.match(/^Having regard to/i) || line.match(/^Whereas:/i)) {
92
+ inRecitalsSection = true;
93
+ continue;
94
+ }
95
+
96
+ // Detect end of recitals (usually "HAVE ADOPTED" or "Article 1")
97
+ if (line.match(/^HAVE ADOPTED/i) || line.match(/^Article\s+1$/i)) {
98
+ inRecitalsSection = false;
99
+ if (currentRecital && currentRecital.lines.length > 0) {
100
+ recitals.push({
101
+ recital_number: currentRecital.number,
102
+ text: currentRecital.lines.join('\n\n'),
103
+ });
104
+ }
105
+ break;
106
+ }
107
+
108
+ if (!inRecitalsSection) continue;
109
+
110
+ // Match recital number: "(1)", "(123)", etc.
111
+ const recitalMatch = line.match(/^\((\d+)\)/);
112
+ if (recitalMatch) {
113
+ // Save previous recital
114
+ if (currentRecital && currentRecital.lines.length > 0) {
115
+ recitals.push({
116
+ recital_number: currentRecital.number,
117
+ text: currentRecital.lines.join('\n\n'),
118
+ });
119
+ }
120
+
121
+ // Start new recital
122
+ currentRecital = {
123
+ number: parseInt(recitalMatch[1]),
124
+ lines: [],
125
+ };
126
+
127
+ // Add remaining text after number
128
+ const textAfterNumber = line.substring(recitalMatch[0].length).trim();
129
+ if (textAfterNumber) {
130
+ currentRecital.lines.push(textAfterNumber);
131
+ }
132
+ continue;
133
+ }
134
+
135
+ // Add line to current recital
136
+ if (currentRecital && line.length > 0) {
137
+ currentRecital.lines.push(line);
138
+ }
139
+ }
140
+
141
+ // Don't forget the last recital
142
+ if (currentRecital && currentRecital.lines.length > 0) {
143
+ recitals.push({
144
+ recital_number: currentRecital.number,
145
+ text: currentRecital.lines.join('\n\n'),
146
+ });
147
+ }
148
+
149
+ return recitals;
150
+ }
151
+
71
152
  function parseArticles(html: string, celexId: string): { articles: Article[]; definitions: Definition[] } {
72
153
  const dom = new JSDOM(html);
73
154
  const doc = dom.window.document;
@@ -182,6 +263,10 @@ async function ingestRegulation(celexId: string, outputPath: string): Promise<vo
182
263
  const html = await fetchEurLexHtml(celexId);
183
264
  console.log(`Fetched ${html.length} bytes`);
184
265
 
266
+ // Parse recitals BEFORE articles
267
+ const recitals = parseRecitals(html);
268
+ console.log(`Parsed ${recitals.length} recitals`);
269
+
185
270
  const { articles, definitions } = parseArticles(html, celexId);
186
271
  console.log(`Parsed ${articles.length} articles, ${definitions.length} definitions`);
187
272
 
@@ -200,12 +285,14 @@ async function ingestRegulation(celexId: string, outputPath: string): Promise<vo
200
285
  eur_lex_url: `https://eur-lex.europa.eu/legal-content/EN/TXT/?uri=CELEX:${celexId}`,
201
286
  articles,
202
287
  definitions,
288
+ recitals,
203
289
  };
204
290
 
205
291
  writeFileSync(outputPath, JSON.stringify(regulation, null, 2));
206
292
  console.log(`\nSaved to: ${outputPath}`);
207
293
  console.log(`Articles: ${articles.length}`);
208
294
  console.log(`Definitions: ${definitions.length}`);
295
+ console.log(`Recitals: ${recitals.length}`);
209
296
  }
210
297
 
211
298
  // Main
@@ -0,0 +1,81 @@
1
+ #!/bin/bash
2
+
3
+ # Re-ingest all 37 regulations to add recitals
4
+ # Run this script from the project root
5
+
6
+ echo "Re-ingesting all EU regulations with recitals..."
7
+ echo "This will take 5-10 minutes..."
8
+ echo ""
9
+
10
+ REGULATIONS=(
11
+ "32016R0679:gdpr"
12
+ "32022L2555:nis2"
13
+ "32022R2554:dora"
14
+ "32024R1689:ai-act"
15
+ "32024R2847:cra"
16
+ "32019R0881:cybersecurity-act"
17
+ "32025R0038:cyber_solidarity"
18
+ "02002L0058-20091219:eprivacy"
19
+ "32016L0680:led"
20
+ "32024R0482:eucc"
21
+ "02014R0910-20241018:eidas2"
22
+ "32023R2854:data-act"
23
+ "32022R2065:dsa"
24
+ "32022R1925:dma"
25
+ "32022R0868:dga"
26
+ "32018L1972:eecc"
27
+ "32025R0327:ehds"
28
+ "32017R0745:mdr"
29
+ "32017R0746:ivdr"
30
+ "32023R1114:mica"
31
+ "32015L2366:psd2"
32
+ "32014L0065:mifid2"
33
+ "32014R0600:mifir"
34
+ "32011L0061:aifmd"
35
+ "32019R2088:sfdr"
36
+ "32020R0852:eu_taxonomy"
37
+ "32023R0988:gpsr"
38
+ "32023R1230:machinery"
39
+ "32024L2853:pld"
40
+ "32014L0053:red"
41
+ "32022L2464:csrd"
42
+ "32024L1760:csddd"
43
+ "32023R0956:cbam"
44
+ "32023R1115:eudr"
45
+ "32022L2557:cer"
46
+ "42021X0387:un-r155"
47
+ "42021X0388:un-r156"
48
+ )
49
+
50
+ TOTAL=${#REGULATIONS[@]}
51
+ CURRENT=0
52
+
53
+ for reg in "${REGULATIONS[@]}"; do
54
+ CURRENT=$((CURRENT + 1))
55
+ IFS=':' read -r celex filename <<< "$reg"
56
+
57
+ echo "[$CURRENT/$TOTAL] Re-ingesting $filename (CELEX: $celex)"
58
+
59
+ if [[ "$celex" == 42021X* ]]; then
60
+ # UN/ECE regulations use different ingestion script
61
+ npx tsx scripts/ingest-unece.ts "$celex" "data/seed/${filename}.json"
62
+ else
63
+ # EU regulations use EUR-Lex ingestion
64
+ npx tsx scripts/ingest-eurlex.ts "$celex" "data/seed/${filename}.json"
65
+ fi
66
+
67
+ if [ $? -ne 0 ]; then
68
+ echo " ⚠️ Warning: Failed to ingest $filename"
69
+ fi
70
+ echo ""
71
+ done
72
+
73
+ echo "Re-building database..."
74
+ npm run build:db
75
+
76
+ echo ""
77
+ echo "✅ Done! Checking recital counts..."
78
+ sqlite3 data/regulations.db "SELECT regulation, COUNT(*) as recital_count FROM recitals GROUP BY regulation ORDER BY recital_count DESC LIMIT 10;"
79
+
80
+ echo ""
81
+ echo "Run 'npm test' to verify everything works."
package/src/index.ts CHANGED
@@ -12,6 +12,7 @@ import { dirname, join } from 'path';
12
12
 
13
13
  import { searchRegulations, type SearchInput } from './tools/search.js';
14
14
  import { getArticle, type GetArticleInput } from './tools/article.js';
15
+ import { getRecital, type GetRecitalInput } from './tools/recital.js';
15
16
  import { listRegulations, type ListInput } from './tools/list.js';
16
17
  import { compareRequirements, type CompareInput } from './tools/compare.js';
17
18
  import { mapControls, type MapControlsInput } from './tools/map.js';
@@ -93,6 +94,24 @@ server.setRequestHandler(ListToolsRequestSchema, async () => ({
93
94
  required: ['regulation', 'article'],
94
95
  },
95
96
  },
97
+ {
98
+ name: 'get_recital',
99
+ description: 'Retrieve the full text of a specific recital from a regulation. Recitals provide context and interpretation guidance for articles.',
100
+ inputSchema: {
101
+ type: 'object',
102
+ properties: {
103
+ regulation: {
104
+ type: 'string',
105
+ description: 'Regulation ID (e.g., "GDPR", "NIS2", "DORA")',
106
+ },
107
+ recital_number: {
108
+ type: 'number',
109
+ description: 'Recital number (e.g., 1, 83)',
110
+ },
111
+ },
112
+ required: ['regulation', 'recital_number'],
113
+ },
114
+ },
96
115
  {
97
116
  name: 'list_regulations',
98
117
  description: 'List available regulations and their structure. Without parameters, lists all regulations. With a regulation specified, shows chapters and articles.',
@@ -227,6 +246,20 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
227
246
  };
228
247
  }
229
248
 
249
+ case 'get_recital': {
250
+ const input = args as unknown as GetRecitalInput;
251
+ const recital = await getRecital(database, input);
252
+ if (!recital) {
253
+ return {
254
+ content: [{ type: 'text', text: `Recital ${input.recital_number} not found in ${input.regulation}` }],
255
+ isError: true,
256
+ };
257
+ }
258
+ return {
259
+ content: [{ type: 'text', text: JSON.stringify(recital, null, 2) }],
260
+ };
261
+ }
262
+
230
263
  case 'list_regulations': {
231
264
  const input = (args ?? {}) as unknown as ListInput;
232
265
  const result = await listRegulations(database, input);
@@ -0,0 +1,48 @@
1
+ import type { Database } from 'better-sqlite3';
2
+
3
+ export interface GetRecitalInput {
4
+ regulation: string;
5
+ recital_number: number;
6
+ }
7
+
8
+ export interface Recital {
9
+ regulation: string;
10
+ recital_number: number;
11
+ text: string;
12
+ related_articles: string[] | null;
13
+ }
14
+
15
+ export async function getRecital(
16
+ db: Database,
17
+ input: GetRecitalInput
18
+ ): Promise<Recital | null> {
19
+ const { regulation, recital_number } = input;
20
+
21
+ const sql = `
22
+ SELECT
23
+ regulation,
24
+ recital_number,
25
+ text,
26
+ related_articles
27
+ FROM recitals
28
+ WHERE regulation = ? AND recital_number = ?
29
+ `;
30
+
31
+ const row = db.prepare(sql).get(regulation, recital_number) as {
32
+ regulation: string;
33
+ recital_number: number;
34
+ text: string;
35
+ related_articles: string | null;
36
+ } | undefined;
37
+
38
+ if (!row) {
39
+ return null;
40
+ }
41
+
42
+ return {
43
+ regulation: row.regulation,
44
+ recital_number: row.recital_number,
45
+ text: row.text,
46
+ related_articles: row.related_articles ? JSON.parse(row.related_articles) : null,
47
+ };
48
+ }