@ansvar/eu-regulations-mcp 1.0.0 โ†’ 1.1.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 (58) hide show
  1. package/README.md +60 -22
  2. package/data/regulations.db +0 -0
  3. package/dist/database/sqlite-adapter.d.ts +2 -2
  4. package/dist/database/sqlite-adapter.d.ts.map +1 -1
  5. package/dist/database/sqlite-adapter.js.map +1 -1
  6. package/dist/http-server.js +27 -5
  7. package/dist/http-server.js.map +1 -1
  8. package/dist/index.js +27 -4
  9. package/dist/index.js.map +1 -1
  10. package/dist/tools/about.d.ts +40 -0
  11. package/dist/tools/about.d.ts.map +1 -0
  12. package/dist/tools/about.js +61 -0
  13. package/dist/tools/about.js.map +1 -0
  14. package/dist/tools/list.d.ts +7 -0
  15. package/dist/tools/list.d.ts.map +1 -1
  16. package/dist/tools/list.js +73 -8
  17. package/dist/tools/list.js.map +1 -1
  18. package/dist/tools/registry.d.ts +11 -1
  19. package/dist/tools/registry.d.ts.map +1 -1
  20. package/dist/tools/registry.js +56 -4
  21. package/dist/tools/registry.js.map +1 -1
  22. package/dist/worker.d.ts.map +1 -1
  23. package/dist/worker.js +17 -5
  24. package/dist/worker.js.map +1 -1
  25. package/package.json +6 -5
  26. package/scripts/add-cross-references.sql +0 -200
  27. package/scripts/analyze-survey-responses.ts +0 -285
  28. package/scripts/build-db.ts +0 -421
  29. package/scripts/bulk-reingest-all.ts +0 -331
  30. package/scripts/check-updates.ts +0 -294
  31. package/scripts/extract-eprivacy-recitals.ts +0 -98
  32. package/scripts/ingest-eurlex-browser.ts +0 -113
  33. package/scripts/ingest-eurlex.ts +0 -349
  34. package/scripts/ingest-unece.ts +0 -382
  35. package/scripts/migrate-postgres.ts +0 -445
  36. package/scripts/migrate-to-postgres.ts +0 -353
  37. package/scripts/reingest-all-with-recitals.sh +0 -81
  38. package/scripts/sync-versions.ts +0 -206
  39. package/scripts/test-cross-refs.js +0 -26
  40. package/scripts/test-postgres-adapter.ts +0 -146
  41. package/scripts/update-dora-rts-metadata.ts +0 -112
  42. package/src/database/postgres-adapter.ts +0 -84
  43. package/src/database/sqlite-adapter.ts +0 -44
  44. package/src/database/types.ts +0 -10
  45. package/src/http-server.ts +0 -149
  46. package/src/index.ts +0 -61
  47. package/src/middleware/rate-limit.ts +0 -104
  48. package/src/tools/applicability.ts +0 -167
  49. package/src/tools/article.ts +0 -81
  50. package/src/tools/compare.ts +0 -217
  51. package/src/tools/definitions.ts +0 -49
  52. package/src/tools/evidence.ts +0 -84
  53. package/src/tools/list.ts +0 -124
  54. package/src/tools/map.ts +0 -86
  55. package/src/tools/recital.ts +0 -60
  56. package/src/tools/registry.ts +0 -311
  57. package/src/tools/search.ts +0 -297
  58. package/src/worker.ts +0 -708
@@ -1,146 +0,0 @@
1
- #!/usr/bin/env npx tsx
2
-
3
- /**
4
- * Test PostgreSQL adapter with migrated data
5
- *
6
- * Usage:
7
- * DATABASE_URL=postgresql://... npx tsx scripts/test-postgres-adapter.ts
8
- */
9
-
10
- import { PostgreSQLAdapter } from '../packages/core/src/adapters/postgresql-adapter.js';
11
-
12
- async function main() {
13
- console.log('๐Ÿงช Testing PostgreSQL Adapter\n');
14
-
15
- const connectionString = process.env.DATABASE_URL;
16
- if (!connectionString) {
17
- console.error('โŒ DATABASE_URL environment variable is required');
18
- process.exit(1);
19
- }
20
-
21
- console.log(`๐Ÿ˜ Connecting to: ${connectionString.replace(/:[^:@]+@/, ':***@')}\n`);
22
-
23
- const adapter = new PostgreSQLAdapter(connectionString);
24
-
25
- try {
26
- // Test 1: Connection
27
- console.log('Test 1: Connection');
28
- const connected = await adapter.testConnection();
29
- console.log(connected ? 'โœ… Connected\n' : 'โŒ Connection failed\n');
30
- if (!connected) process.exit(1);
31
-
32
- // Test 2: Get all regulations
33
- console.log('Test 2: Get all regulations');
34
- const regulations = await adapter.getAllRegulations();
35
- console.log(`โœ… Found ${regulations.length} regulations`);
36
- console.log(` First 3: ${regulations.slice(0, 3).map(r => r.id).join(', ')}\n`);
37
-
38
- // Test 3: Get specific regulation
39
- console.log('Test 3: Get specific regulation (GDPR)');
40
- const gdpr = await adapter.getRegulationById('GDPR');
41
- if (gdpr) {
42
- console.log(`โœ… ${gdpr.full_name}`);
43
- console.log(` CELEX: ${gdpr.celex_id}\n`);
44
- } else {
45
- console.log('โŒ GDPR not found\n');
46
- process.exit(1);
47
- }
48
-
49
- // Test 4: Search articles
50
- console.log('Test 4: Search articles for "incident reporting"');
51
- const searchResults = await adapter.searchArticles('incident reporting', undefined, 5);
52
- console.log(`โœ… Found ${searchResults.length} results`);
53
- searchResults.forEach((result, i) => {
54
- console.log(` ${i + 1}. ${result.item.regulation} Article ${result.item.article_number}: ${result.item.title}`);
55
- console.log(` Rank: ${result.rank.toFixed(4)}`);
56
- });
57
- console.log();
58
-
59
- // Test 5: Get specific article
60
- console.log('Test 5: Get GDPR Article 17');
61
- const article = await adapter.getArticle('GDPR', '17');
62
- if (article) {
63
- console.log(`โœ… ${article.title}`);
64
- console.log(` Text length: ${article.text.length} characters`);
65
- if (article.recitals) {
66
- console.log(` Related recitals: ${article.recitals}\n`);
67
- }
68
- } else {
69
- console.log('โŒ Article not found\n');
70
- process.exit(1);
71
- }
72
-
73
- // Test 6: Get recital
74
- console.log('Test 6: Get GDPR Recital 1');
75
- const recital = await adapter.getRecital('GDPR', 1);
76
- if (recital) {
77
- console.log(`โœ… Text: ${recital.text.substring(0, 100)}...`);
78
- console.log(` Full length: ${recital.text.length} characters\n`);
79
- } else {
80
- console.log('โŒ Recital not found\n');
81
- process.exit(1);
82
- }
83
-
84
- // Test 7: Search recitals
85
- console.log('Test 7: Search recitals for "data protection"');
86
- const recitalResults = await adapter.searchRecitals('data protection', 3);
87
- console.log(`โœ… Found ${recitalResults.length} recital results`);
88
- recitalResults.forEach((result, i) => {
89
- console.log(` ${i + 1}. ${result.item.regulation} Recital ${result.item.recital_number}`);
90
- console.log(` Rank: ${result.rank.toFixed(4)}`);
91
- });
92
- console.log();
93
-
94
- // Test 8: Get definitions
95
- console.log('Test 8: Search definitions for "personal data"');
96
- const definitions = await adapter.getDefinitions('personal data');
97
- console.log(`โœ… Found ${definitions.length} definitions`);
98
- definitions.slice(0, 3).forEach((def, i) => {
99
- console.log(` ${i + 1}. ${def.regulation}: ${def.term}`);
100
- console.log(` ${def.definition.substring(0, 80)}...`);
101
- });
102
- console.log();
103
-
104
- // Test 9: Get control mappings
105
- console.log('Test 9: Get ISO 27001 control mappings');
106
- const mappings = await adapter.getControlMappings('ISO27001', undefined, undefined);
107
- console.log(`โœ… Found ${mappings.length} ISO 27001 mappings`);
108
- const sample = mappings.slice(0, 2);
109
- sample.forEach((map, i) => {
110
- console.log(` ${i + 1}. ${map.control_id}: ${map.control_name}`);
111
- console.log(` โ†’ ${map.regulation} (${map.coverage})`);
112
- });
113
- console.log();
114
-
115
- // Test 10: Get applicability rules
116
- console.log('Test 10: Get applicability rules for financial sector');
117
- const rules = await adapter.getApplicabilityRules('financial');
118
- console.log(`โœ… Found ${rules.length} applicability rules`);
119
- rules.slice(0, 3).forEach((rule, i) => {
120
- console.log(` ${i + 1}. ${rule.regulation}: ${rule.applies ? 'Applies' : 'Does not apply'}`);
121
- console.log(` Confidence: ${rule.confidence}`);
122
- });
123
- console.log();
124
-
125
- // Test 11: Statistics
126
- console.log('Test 11: Get database statistics');
127
- const stats = await adapter.getStatistics();
128
- console.log('โœ… Statistics:');
129
- console.log(` Regulations: ${stats.regulations}`);
130
- console.log(` Articles: ${stats.articles}`);
131
- console.log(` Recitals: ${stats.recitals}`);
132
- console.log(` Definitions: ${stats.definitions}`);
133
- console.log(` Control Mappings: ${stats.control_mappings}\n`);
134
-
135
- console.log('='.repeat(60));
136
- console.log('โœ… All tests passed!');
137
- console.log('='.repeat(60));
138
- } catch (error) {
139
- console.error('\nโŒ Test failed:', error);
140
- process.exit(1);
141
- } finally {
142
- await adapter.close();
143
- }
144
- }
145
-
146
- main();
@@ -1,112 +0,0 @@
1
- #!/usr/bin/env node
2
- /**
3
- * Update metadata for DORA RTS/ITS JSON files
4
- */
5
- import * as fs from 'fs';
6
- import * as path from 'path';
7
-
8
- interface RegulationMetadata {
9
- id: string;
10
- full_name: string;
11
- effective_date: string;
12
- }
13
-
14
- const DORA_RTS_ITS_METADATA: Record<string, RegulationMetadata> = {
15
- '32024R1774': {
16
- id: 'DORA_RTS_ICT_RISK',
17
- full_name: 'Commission Delegated Regulation (EU) 2024/1774 - ICT Risk Management Tools, Methods, Processes and Simplified Framework',
18
- effective_date: '2025-01-17',
19
- },
20
- '32024R1772': {
21
- id: 'DORA_RTS_INCIDENT_CLASS',
22
- full_name: 'Commission Delegated Regulation (EU) 2024/1772 - Classification of ICT-Related Incidents and Cyber Threats',
23
- effective_date: '2025-01-17',
24
- },
25
- '32024R1773': {
26
- id: 'DORA_RTS_ICT_SERVICES',
27
- full_name: 'Commission Delegated Regulation (EU) 2024/1773 - Policy on ICT Services Supporting Critical or Important Functions',
28
- effective_date: '2025-01-17',
29
- },
30
- '32024R2956': {
31
- id: 'DORA_ITS_REGISTER',
32
- full_name: 'Commission Implementing Regulation (EU) 2024/2956 - Standard Templates for Register of Information',
33
- effective_date: '2025-01-17',
34
- },
35
- '32024R1502': {
36
- id: 'DORA_RTS_CRITICAL_PROVIDER',
37
- full_name: 'Commission Delegated Regulation (EU) 2024/1502 - Criteria for Designation of Critical ICT Third-Party Service Providers',
38
- effective_date: '2025-01-17',
39
- },
40
- '32024R1505': {
41
- id: 'DORA_RTS_OVERSIGHT_FEES',
42
- full_name: 'Commission Delegated Regulation (EU) 2024/1505 - Oversight Fees for Critical ICT Third-Party Service Providers',
43
- effective_date: '2025-01-17',
44
- },
45
- '32025R0295': {
46
- id: 'DORA_RTS_OVERSIGHT',
47
- full_name: 'Commission Delegated Regulation (EU) 2025/295 - Harmonization of Oversight Activities Conditions',
48
- effective_date: '2025-01-17',
49
- },
50
- '32025R0301': {
51
- id: 'DORA_RTS_INCIDENT_REPORTING',
52
- full_name: 'Commission Delegated Regulation (EU) 2025/301 - Content and Time Limits for Incident Reporting',
53
- effective_date: '2025-01-17',
54
- },
55
- '32025R0302': {
56
- id: 'DORA_ITS_INCIDENT_FORMS',
57
- full_name: 'Commission Implementing Regulation (EU) 2025/302 - Standard Forms and Templates for Incident Reporting',
58
- effective_date: '2025-01-17',
59
- },
60
- '32025R1190': {
61
- id: 'DORA_RTS_TLPT',
62
- full_name: 'Commission Delegated Regulation (EU) 2025/1190 - Threat-Led Penetration Testing (TLPT)',
63
- effective_date: '2025-01-17',
64
- },
65
- };
66
-
67
- const FILE_MAPPING: Record<string, string> = {
68
- '32024R1774': 'data/seed/dora-rts-ict-risk.json',
69
- '32024R1772': 'data/seed/dora-rts-incident-classification.json',
70
- '32024R1773': 'data/seed/dora-rts-ict-services-policy.json',
71
- '32024R2956': 'data/seed/dora-its-register-templates.json',
72
- '32024R1502': 'data/seed/dora-rts-critical-provider-designation.json',
73
- '32024R1505': 'data/seed/dora-rts-oversight-fees.json',
74
- '32025R0295': 'data/seed/dora-rts-oversight-harmonization.json',
75
- '32025R0301': 'data/seed/dora-rts-incident-reporting.json',
76
- '32025R0302': 'data/seed/dora-its-incident-forms.json',
77
- '32025R1190': 'data/seed/dora-rts-tlpt.json',
78
- };
79
-
80
- function updateRegulationMetadata(celex: string, filePath: string): void {
81
- const metadata = DORA_RTS_ITS_METADATA[celex];
82
- if (!metadata) {
83
- console.error(`No metadata found for CELEX ${celex}`);
84
- return;
85
- }
86
-
87
- const fullPath = path.resolve(process.cwd(), filePath);
88
- if (!fs.existsSync(fullPath)) {
89
- console.error(`File not found: ${fullPath}`);
90
- return;
91
- }
92
-
93
- const data = JSON.parse(fs.readFileSync(fullPath, 'utf-8'));
94
-
95
- // Update metadata
96
- data.id = metadata.id;
97
- data.full_name = metadata.full_name;
98
- data.effective_date = metadata.effective_date;
99
-
100
- // Write back
101
- fs.writeFileSync(fullPath, JSON.stringify(data, null, 2) + '\n', 'utf-8');
102
- console.log(`โœ… Updated ${celex} โ†’ ${metadata.id} (${filePath})`);
103
- }
104
-
105
- // Main
106
- console.log('Updating DORA RTS/ITS metadata...\n');
107
-
108
- for (const [celex, filePath] of Object.entries(FILE_MAPPING)) {
109
- updateRegulationMetadata(celex, filePath);
110
- }
111
-
112
- console.log('\nโœ… All metadata updated successfully!');
@@ -1,84 +0,0 @@
1
- import pg from 'pg';
2
- import type { DatabaseAdapter, QueryResult } from './types.js';
3
-
4
- export async function createPostgresAdapter(
5
- connectionString: string
6
- ): Promise<DatabaseAdapter> {
7
- const pool = new pg.Pool({
8
- connectionString,
9
- max: 10,
10
- idleTimeoutMillis: 30000,
11
- connectionTimeoutMillis: 10000,
12
- });
13
-
14
- // Test connection
15
- try {
16
- await pool.query('SELECT 1');
17
- } catch (error) {
18
- throw new Error(`Failed to connect to PostgreSQL: ${error}`);
19
- }
20
-
21
- return {
22
- type: 'postgres' as const,
23
-
24
- async query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>> {
25
- const QUERY_TIMEOUT_MS = 10000; // 10 seconds
26
-
27
- try {
28
- // Create timeout promise
29
- const timeoutPromise = new Promise<never>((_, reject) => {
30
- setTimeout(() => {
31
- reject(new Error('Database query timeout: Query exceeded 10 seconds'));
32
- }, QUERY_TIMEOUT_MS);
33
- });
34
-
35
- // Race between query and timeout
36
- const result = await Promise.race([
37
- pool.query(sql, params),
38
- timeoutPromise,
39
- ]);
40
-
41
- return {
42
- rows: result.rows as T[],
43
- rowCount: result.rowCount || 0,
44
- };
45
- } catch (error) {
46
- const pgError = error as {
47
- code?: string;
48
- message: string;
49
- detail?: string;
50
- };
51
- console.error('PostgreSQL query failed:', {
52
- sql: sql.substring(0, 100), // Truncate for logging
53
- params,
54
- code: pgError.code,
55
- message: pgError.message,
56
- detail: pgError.detail,
57
- });
58
-
59
- // Check for timeout error
60
- if (pgError.message?.includes('timeout')) {
61
- throw new Error(
62
- 'Database query timeout: Query exceeded 10 seconds'
63
- );
64
- }
65
-
66
- // Provide helpful error messages based on error code
67
- if (pgError.code?.startsWith('08')) {
68
- throw new Error(`Database connection error: ${pgError.message}`);
69
- }
70
- if (pgError.code === '42P01') {
71
- throw new Error(`Table not found: ${pgError.message}`);
72
- }
73
-
74
- throw new Error(
75
- `Query failed: ${pgError.message}${pgError.code ? ` (${pgError.code})` : ''}`
76
- );
77
- }
78
- },
79
-
80
- async close(): Promise<void> {
81
- await pool.end();
82
- },
83
- };
84
- }
@@ -1,44 +0,0 @@
1
- import type Database from 'better-sqlite3';
2
- import type { DatabaseAdapter, QueryResult } from './types.js';
3
-
4
- /**
5
- * Adapter that wraps better-sqlite3 Database to match DatabaseAdapter interface.
6
- * Allows existing SQLite-based entry points (stdio) to work with the new adapter interface.
7
- */
8
- export function createSqliteAdapter(db: Database.Database): DatabaseAdapter {
9
- return {
10
- type: 'sqlite' as const,
11
-
12
- async query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>> {
13
- try {
14
- // Convert PostgreSQL syntax to SQLite syntax
15
- let sqliteSql = sql
16
- // Convert $1, $2 placeholders to ?
17
- .replace(/\$\d+/g, '?')
18
- // Convert ILIKE to LIKE (SQLite is case-insensitive by default with LIKE)
19
- .replace(/\sILIKE\s/gi, ' LIKE ')
20
- // Convert ::TEXT type casting to CAST(... AS TEXT)
21
- .replace(/(\w+)::TEXT/g, 'CAST($1 AS TEXT)')
22
- .replace(/(\w+)::INTEGER/g, 'CAST($1 AS INTEGER)')
23
- // Convert DISTINCT ON (col) to GROUP BY col
24
- // PostgreSQL: SELECT DISTINCT ON (col1) col1, col2 FROM ... ORDER BY col1, col2
25
- // SQLite: SELECT col1, col2 FROM ... GROUP BY col1 HAVING MIN(col2) = col2
26
- .replace(/SELECT\s+DISTINCT\s+ON\s*\([^)]+\)/gi, 'SELECT');
27
-
28
- const stmt = db.prepare(sqliteSql);
29
- const rows = params ? stmt.all(...params) : stmt.all();
30
- return {
31
- rows: rows as T[],
32
- rowCount: rows.length,
33
- };
34
- } catch (error) {
35
- console.error('SQLite query failed:', sql, params, error);
36
- throw error;
37
- }
38
- },
39
-
40
- async close(): Promise<void> {
41
- db.close();
42
- },
43
- };
44
- }
@@ -1,10 +0,0 @@
1
- export interface QueryResult<T = any> {
2
- rows: T[];
3
- rowCount: number;
4
- }
5
-
6
- export interface DatabaseAdapter {
7
- query<T = any>(sql: string, params?: any[]): Promise<QueryResult<T>>;
8
- close(): Promise<void>;
9
- type: 'sqlite' | 'postgres';
10
- }
@@ -1,149 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- /**
4
- * HTTP Server Entry Point for Smithery Hosted Deployment
5
- *
6
- * This provides Streamable HTTP transport for remote MCP clients.
7
- * Use src/index.ts for local stdio-based usage.
8
- */
9
-
10
- import { createServer } from 'node:http';
11
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
12
- import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
13
- import {
14
- CallToolRequestSchema,
15
- ListToolsRequestSchema,
16
- } from '@modelcontextprotocol/sdk/types.js';
17
- import Database from 'better-sqlite3';
18
- import { fileURLToPath } from 'url';
19
- import { dirname, join } from 'path';
20
- import { randomUUID } from 'crypto';
21
-
22
- import { registerTools } from './tools/registry.js';
23
- import { createSqliteAdapter } from './database/sqlite-adapter.js';
24
- import type { DatabaseAdapter } from './database/types.js';
25
-
26
- const __filename = fileURLToPath(import.meta.url);
27
- const __dirname = dirname(__filename);
28
-
29
- // Database path - look for regulations.db in data folder
30
- const DB_PATH = process.env.EU_COMPLIANCE_DB_PATH || join(__dirname, '..', 'data', 'regulations.db');
31
-
32
- // HTTP server port
33
- const PORT = parseInt(process.env.PORT || '3000', 10);
34
-
35
- let db: DatabaseAdapter;
36
-
37
- function getDatabase(): DatabaseAdapter {
38
- if (!db) {
39
- try {
40
- const sqliteDb = new Database(DB_PATH, { readonly: true });
41
- db = createSqliteAdapter(sqliteDb);
42
- } catch (error) {
43
- throw new Error(`Failed to open database at ${DB_PATH}: ${error}`);
44
- }
45
- }
46
- return db;
47
- }
48
-
49
- // Create MCP server instance
50
- function createMcpServer(): Server {
51
- const db = getDatabase();
52
- const server = new Server(
53
- {
54
- name: 'eu-regulations-mcp',
55
- version: '0.1.0',
56
- },
57
- {
58
- capabilities: {
59
- tools: {},
60
- },
61
- }
62
- );
63
-
64
- // Register all tools using shared registry
65
- registerTools(server, db);
66
-
67
- return server;
68
- }
69
-
70
- // Start HTTP server with Streamable HTTP transport
71
- async function main() {
72
- const mcpServer = createMcpServer();
73
-
74
- // Map to store transports by session ID
75
- const transports = new Map<string, StreamableHTTPServerTransport>();
76
-
77
- const httpServer = createServer(async (req, res) => {
78
- const url = new URL(req.url || '/', `http://localhost:${PORT}`);
79
-
80
- // Health check endpoint
81
- if (url.pathname === '/health') {
82
- res.writeHead(200, { 'Content-Type': 'application/json' });
83
- res.end(JSON.stringify({ status: 'ok', server: 'eu-regulations-mcp' }));
84
- return;
85
- }
86
-
87
- // MCP endpoint
88
- if (url.pathname === '/mcp') {
89
- // Get or create session
90
- const sessionId = req.headers['mcp-session-id'] as string | undefined;
91
-
92
- let transport: StreamableHTTPServerTransport;
93
-
94
- if (sessionId && transports.has(sessionId)) {
95
- // Reuse existing transport for this session
96
- transport = transports.get(sessionId)!;
97
- } else {
98
- // Create new transport with session ID generator
99
- transport = new StreamableHTTPServerTransport({
100
- sessionIdGenerator: () => randomUUID(),
101
- });
102
-
103
- // Connect MCP server to transport
104
- await mcpServer.connect(transport);
105
-
106
- // Store transport by session ID once it's assigned
107
- transport.onclose = () => {
108
- if (transport.sessionId) {
109
- transports.delete(transport.sessionId);
110
- }
111
- };
112
- }
113
-
114
- // Handle the request
115
- await transport.handleRequest(req, res);
116
-
117
- // Store transport if new session was created
118
- if (transport.sessionId && !transports.has(transport.sessionId)) {
119
- transports.set(transport.sessionId, transport);
120
- }
121
-
122
- return;
123
- }
124
-
125
- // 404 for other paths
126
- res.writeHead(404, { 'Content-Type': 'application/json' });
127
- res.end(JSON.stringify({ error: 'Not found' }));
128
- });
129
-
130
- httpServer.listen(PORT, () => {
131
- console.error(`EU Regulations MCP server (HTTP) listening on port ${PORT}`);
132
- console.error(`MCP endpoint: http://localhost:${PORT}/mcp`);
133
- console.error(`Health check: http://localhost:${PORT}/health`);
134
- });
135
-
136
- // Graceful shutdown
137
- process.on('SIGTERM', () => {
138
- console.error('Received SIGTERM, shutting down...');
139
- httpServer.close(() => {
140
- if (db) db.close();
141
- process.exit(0);
142
- });
143
- });
144
- }
145
-
146
- main().catch((error) => {
147
- console.error('Fatal error:', error);
148
- process.exit(1);
149
- });
package/src/index.ts DELETED
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env node
2
-
3
- import { Server } from '@modelcontextprotocol/sdk/server/index.js';
4
- import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
5
- import {
6
- CallToolRequestSchema,
7
- ListToolsRequestSchema,
8
- } from '@modelcontextprotocol/sdk/types.js';
9
- import Database from 'better-sqlite3';
10
- import { fileURLToPath } from 'url';
11
- import { dirname, join } from 'path';
12
-
13
- import { registerTools } from './tools/registry.js';
14
- import { createSqliteAdapter } from './database/sqlite-adapter.js';
15
- import type { DatabaseAdapter } from './database/types.js';
16
-
17
- const __filename = fileURLToPath(import.meta.url);
18
- const __dirname = dirname(__filename);
19
-
20
- // Database path - look for regulations.db in data folder
21
- const DB_PATH = process.env.EU_COMPLIANCE_DB_PATH || join(__dirname, '..', 'data', 'regulations.db');
22
-
23
- let db: DatabaseAdapter;
24
-
25
- function getDatabase(): DatabaseAdapter {
26
- if (!db) {
27
- try {
28
- const sqliteDb = new Database(DB_PATH, { readonly: true });
29
- db = createSqliteAdapter(sqliteDb);
30
- } catch (error) {
31
- throw new Error(`Failed to open database at ${DB_PATH}: ${error}`);
32
- }
33
- }
34
- return db;
35
- }
36
- const server = new Server(
37
- {
38
- name: 'eu-regulations-mcp',
39
- version: '0.1.0',
40
- },
41
- {
42
- capabilities: {
43
- tools: {},
44
- },
45
- }
46
- );
47
-
48
- // Register all tools using shared registry
49
- registerTools(server, getDatabase());
50
-
51
- // Start the server
52
- async function main() {
53
- const transport = new StdioServerTransport();
54
- await server.connect(transport);
55
- console.error('EU Regulations MCP server started');
56
- }
57
-
58
- main().catch((error) => {
59
- console.error('Fatal error:', error);
60
- process.exit(1);
61
- });