@iflow-mcp/ansvar-systems-german-law-mcp
Version:
German legal research MCP server with statutes, case law, and preparatory works from gesetze-im-internet.de
90 lines (89 loc) • 3.39 kB
JavaScript
/**
* Runtime capability detection for the German Law MCP server.
*
* The server inspects which SQLite tables are present on startup and
* exposes a Set<Capability> so that tool handlers can gate features
* (or return an upgrade prompt) without hard-coding tier logic.
*/
// ---------------------------------------------------------------------------
// Table → Capability mapping
// ---------------------------------------------------------------------------
const CAPABILITY_TABLES = {
core_legislation: 'law_documents',
basic_case_law: 'case_law_documents',
eu_references: 'eu_references',
expanded_case_law: 'case_law_documents_full',
full_preparatory_works: 'preparatory_works_full',
agency_guidance: 'agency_guidance',
};
const PROFESSIONAL_CAPABILITIES = new Set([
'expanded_case_law',
'full_preparatory_works',
'agency_guidance',
]);
// ---------------------------------------------------------------------------
// Public API
// ---------------------------------------------------------------------------
/** Detect which capabilities the current database supports. */
export function detectCapabilities(db) {
const caps = new Set();
for (const [capability, table] of Object.entries(CAPABILITY_TABLES)) {
if (tableExists(db, table)) {
caps.add(capability);
}
}
return caps;
}
/** Read db_metadata key-value pairs. Returns sensible defaults when missing. */
export function readDbMetadata(db) {
const defaults = {
tier: 'unknown',
schema_version: '1',
built_at: 'unknown',
builder: 'unknown',
};
if (!tableExists(db, 'db_metadata')) {
return defaults;
}
try {
const rows = db
.prepare('SELECT key, value FROM db_metadata')
.all();
const map = new Map(rows.map((r) => [r.key, r.value]));
const rawTier = map.get('tier') ?? 'unknown';
const tier = rawTier === 'free' || rawTier === 'professional' ? rawTier : 'unknown';
return {
tier,
schema_version: map.get('schema_version') ?? defaults.schema_version,
built_at: map.get('built_at') ?? defaults.built_at,
builder: map.get('builder') ?? defaults.builder,
};
}
catch {
return defaults;
}
}
/** Check whether a capability belongs to the paid (professional) tier. */
export function isProfessionalCapability(cap) {
return PROFESSIONAL_CAPABILITIES.has(cap);
}
/** Human-readable upgrade prompt for gated features. */
export function upgradeMessage(feature) {
return (`${feature} is not available in this free community instance. ` +
`The full case law and preparatory works databases are too large to serve from a free hosted endpoint. ` +
`These datasets are included when Ansvar delivers consulting services, and may become available as a separate paid service in the future.`);
}
// ---------------------------------------------------------------------------
// Helpers
// ---------------------------------------------------------------------------
function tableExists(db, name) {
try {
const row = db
.prepare("SELECT 1 AS ok FROM sqlite_master WHERE type IN ('table','view') AND name = ?")
.get(name);
return Boolean(row?.ok);
}
catch {
return false;
}
}