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>
This commit is contained in:
52
src/utils/fts-query.ts
Normal file
52
src/utils/fts-query.ts
Normal file
@@ -0,0 +1,52 @@
|
||||
/**
|
||||
* FTS5 query helpers for Israel Law MCP.
|
||||
*
|
||||
* Handles query sanitization and variant generation for SQLite FTS5.
|
||||
*/
|
||||
|
||||
/**
|
||||
* Sanitize user input for safe FTS5 queries.
|
||||
* Removes characters that have special meaning in FTS5 syntax.
|
||||
*/
|
||||
export function sanitizeFtsInput(input: string): string {
|
||||
return input
|
||||
.replace(/['"(){}[\]^~*:]/g, ' ')
|
||||
.replace(/\s+/g, ' ')
|
||||
.trim();
|
||||
}
|
||||
|
||||
/**
|
||||
* Build FTS5 query variants for a search term.
|
||||
* Returns variants in order of specificity (most specific first):
|
||||
* 1. Exact phrase match
|
||||
* 2. All terms required (AND)
|
||||
* 3. Prefix match on last term
|
||||
*/
|
||||
export function buildFtsQueryVariants(sanitized: string): string[] {
|
||||
if (!sanitized || sanitized.trim().length === 0) {
|
||||
return [];
|
||||
}
|
||||
|
||||
const terms = sanitized.split(/\s+/).filter(t => t.length > 0);
|
||||
if (terms.length === 0) return [];
|
||||
|
||||
const variants: string[] = [];
|
||||
|
||||
// Exact phrase
|
||||
if (terms.length > 1) {
|
||||
variants.push(`"${terms.join(' ')}"`);
|
||||
}
|
||||
|
||||
// AND query
|
||||
variants.push(terms.join(' AND '));
|
||||
|
||||
// Prefix match on last term (for autocomplete-like behavior)
|
||||
if (terms.length === 1 && terms[0].length >= 3) {
|
||||
variants.push(`${terms[0]}*`);
|
||||
} else if (terms.length > 1) {
|
||||
const prefix = [...terms.slice(0, -1), `${terms[terms.length - 1]}*`];
|
||||
variants.push(prefix.join(' AND '));
|
||||
}
|
||||
|
||||
return variants;
|
||||
}
|
||||
Reference in New Issue
Block a user