Files
israel-law-mcp/src/index.ts
Mortalus 1e28f8a6b1 feat: production MCP server with Israeli legislation (multi-source)
Complete production implementation with shell+adapter architecture,
13 MCP tools, SQLite FTS5 search, and multi-source ingestion pipeline.

Ingestion fetches from UCI mirror, UNODC SHERLOC PDFs, and Knesset
mobile PDFs (135 provisions, 33 definitions). 3 acts with full text,
7 acts metadata-only due to gov.il/nevo.co.il access restrictions.
Knesset OData API used for metadata enrichment.

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
2026-02-19 20:40:01 +01:00

103 lines
2.6 KiB
JavaScript

#!/usr/bin/env node
/**
* Israel Law MCP Server -- stdio entry point.
*
* Provides Israeli legislation search via Model Context Protocol.
*/
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import Database from '@ansvar/mcp-sqlite';
import { join, dirname } from 'path';
import { fileURLToPath } from 'url';
import { createHash } from 'crypto';
import { readFileSync } from 'fs';
import { registerTools, type AboutContext } from './tools/registry.js';
import { detectCapabilities, readDbMetadata } from './capabilities.js';
import {
DB_ENV_VAR,
SERVER_NAME,
SERVER_VERSION,
} from './constants.js';
const __dirname = dirname(fileURLToPath(import.meta.url));
function resolveDbPath(): string {
if (process.env[DB_ENV_VAR]) {
return process.env[DB_ENV_VAR];
}
return join(__dirname, '..', 'data', 'database.db');
}
let db: InstanceType<typeof Database> | null = null;
function getDb(): InstanceType<typeof Database> {
if (!db) {
const dbPath = resolveDbPath();
db = new Database(dbPath, { readonly: true });
db.pragma('foreign_keys = ON');
const caps = detectCapabilities(db);
const meta = readDbMetadata(db);
console.error(`[${SERVER_NAME}] DB opened: tier=${meta.tier}, caps=[${[...caps].join(',')}]`);
}
return db;
}
function computeAboutContext(): AboutContext {
const dbPath = resolveDbPath();
let fingerprint = 'unknown';
let dbBuilt = 'unknown';
try {
const buf = readFileSync(dbPath);
fingerprint = createHash('sha256').update(buf).digest('hex').slice(0, 12);
} catch {
// DB might not exist in dev
}
try {
const database = getDb();
const row = database.prepare("SELECT value FROM db_metadata WHERE key = 'built_at'").get() as { value: string } | undefined;
if (row) dbBuilt = row.value;
} catch {
// Ignore
}
return { version: SERVER_VERSION, fingerprint, dbBuilt };
}
async function main() {
const database = getDb();
const aboutContext = computeAboutContext();
const server = new Server(
{ name: SERVER_NAME, version: SERVER_VERSION },
{ capabilities: { tools: {} } }
);
registerTools(server, database, aboutContext);
const transport = new StdioServerTransport();
await server.connect(transport);
console.error(`[${SERVER_NAME}] Server running on stdio`);
const cleanup = () => {
if (db) {
db.close();
db = null;
}
process.exit(0);
};
process.on('SIGINT', cleanup);
process.on('SIGTERM', cleanup);
}
main().catch((err) => {
console.error(`[${SERVER_NAME}] Fatal error:`, err);
process.exit(1);
});