@ansvar/singapore-law-mcp
Version:
Complete Singapore law database — 523 Acts, 28K+ provisions from Singapore Statutes Online (sso.agc.gov.sg) with full-text search, definitions, and citation support
119 lines • 4.5 kB
JavaScript
import { Server } from '@modelcontextprotocol/sdk/server/index.js';
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js';
import Database from '@ansvar/mcp-sqlite';
import { join } from 'path';
import { copyFileSync, existsSync, readFileSync, rmSync, statSync, writeFileSync } from 'fs';
import { createHash } from 'crypto';
import { registerTools } from '../src/tools/registry.js';
import { DB_ENV_VAR, SERVER_NAME, SERVER_VERSION, } from '../src/constants.js';
const SOURCE_DB = process.env[DB_ENV_VAR]
|| join(process.cwd(), 'data', 'database.db');
const TMP_DB = '/tmp/database.db';
const TMP_DB_LOCK = '/tmp/database.db.lock';
const TMP_DB_SHM = '/tmp/database.db-shm';
const TMP_DB_WAL = '/tmp/database.db-wal';
const TMP_DB_META = '/tmp/database.db.meta.json';
let db = null;
function computeSourceSignature() {
const stats = statSync(SOURCE_DB);
return `${stats.size}:${Math.trunc(stats.mtimeMs)}`;
}
function readTmpMeta() {
if (!existsSync(TMP_DB_META))
return null;
try {
const parsed = JSON.parse(readFileSync(TMP_DB_META, 'utf-8'));
if (parsed.source_db && parsed.source_signature) {
return { source_db: parsed.source_db, source_signature: parsed.source_signature };
}
}
catch {
// Ignore corrupted metadata
}
return null;
}
function clearTmpDbArtifacts() {
rmSync(TMP_DB_LOCK, { recursive: true, force: true });
rmSync(TMP_DB_SHM, { force: true });
rmSync(TMP_DB_WAL, { force: true });
rmSync(TMP_DB, { force: true });
rmSync(TMP_DB_META, { force: true });
}
function ensureTempDbIsFresh() {
const sourceSignature = computeSourceSignature();
const meta = readTmpMeta();
const shouldRefresh = !existsSync(TMP_DB) || !meta || meta.source_db !== SOURCE_DB || meta.source_signature !== sourceSignature;
if (shouldRefresh) {
clearTmpDbArtifacts();
copyFileSync(SOURCE_DB, TMP_DB);
writeFileSync(TMP_DB_META, JSON.stringify({ source_db: SOURCE_DB, source_signature: sourceSignature }), 'utf-8');
return;
}
rmSync(TMP_DB_LOCK, { recursive: true, force: true });
}
function computeAboutContext() {
let fingerprint = 'unknown';
let dbBuilt = 'unknown';
try {
const buf = readFileSync(SOURCE_DB);
fingerprint = createHash('sha256').update(buf).digest('hex').slice(0, 12);
}
catch { /* ignore */ }
try {
const database = getDatabase();
const row = database.prepare("SELECT value FROM db_metadata WHERE key = 'built_at'").get();
if (row?.value)
dbBuilt = row.value;
}
catch { /* ignore */ }
return { version: SERVER_VERSION, fingerprint, dbBuilt };
}
function getDatabase() {
if (!db) {
ensureTempDbIsFresh();
db = new Database(TMP_DB, { readonly: true });
db.pragma('foreign_keys = ON');
}
return db;
}
export default async function handler(req, res) {
res.setHeader('Access-Control-Allow-Origin', '*');
res.setHeader('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS');
res.setHeader('Access-Control-Allow-Headers', 'Content-Type, mcp-session-id');
res.setHeader('Access-Control-Expose-Headers', 'mcp-session-id');
if (req.method === 'OPTIONS') {
res.status(204).end();
return;
}
if (req.method === 'GET') {
res.status(200).json({
name: SERVER_NAME,
version: SERVER_VERSION,
protocol: 'mcp-streamable-http',
});
return;
}
try {
if (!existsSync(SOURCE_DB)) {
res.status(500).json({ error: `Database not found at ${SOURCE_DB}` });
return;
}
const database = getDatabase();
const server = new Server({ name: SERVER_NAME, version: SERVER_VERSION }, { capabilities: { tools: {} } });
registerTools(server, database, computeAboutContext());
const transport = new StreamableHTTPServerTransport({
sessionIdGenerator: undefined,
enableJsonResponse: true,
});
await server.connect(transport);
await transport.handleRequest(req, res, req.body);
}
catch (err) {
const message = err instanceof Error ? err.message : String(err);
console.error('MCP handler error:', message);
if (!res.headersSent) {
res.status(500).json({ error: message });
}
}
}
//# sourceMappingURL=mcp.js.map