UNPKG

@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

142 lines (141 loc) • 5.64 kB
const SECTION_PATTERN = String.raw `(?<section>\d+[a-z]?(?:\s*(?:bis|-|,)\s*\d+[a-z]?){0,5})`; const PARAGRAPH_PATTERN = new RegExp(String.raw `^(?<marker>§{1,2})\s*${SECTION_PATTERN}` + String.raw `(?:\s*(?:Abs\.?|Absatz)\s*(?<paragraph>\d+[a-z]?))?` + String.raw `(?:\s*(?:S\.?|Satz)\s*(?<sentence>\d+[a-z]?))?` + String.raw `(?:\s*(?:Nr\.?|Nummer)\s*(?<number>\d+[a-z]?))?` + String.raw `(?:\s*(?:Buchst\.?|Buchstabe)\s*(?<letter>[a-z]))?` + String.raw `\s*(?<code>[A-Za-z][A-Za-z0-9_-]{1,19})$`, "i"); const ARTICLE_PATTERN = new RegExp(String.raw `^(?:Art\.?|Artikel)\s*(?<article>\d+[a-z]?)` + String.raw `(?:\s*(?:Abs\.?|Absatz)\s*(?<paragraph>\d+[a-z]?))?` + String.raw `(?:\s*(?:S\.?|Satz)\s*(?<sentence>\d+[a-z]?))?` + String.raw `(?:\s*(?:Nr\.?|Nummer)\s*(?<number>\d+[a-z]?))?` + String.raw `(?:\s*(?:Buchst\.?|Buchstabe)\s*(?<letter>[a-z]))?` + String.raw `\s*(?<code>[A-Za-z][A-Za-z0-9_-]{1,19})$`, "i"); export function parseGermanCitation(input) { const normalizedInput = normalizeWhitespace(input); const paragraphMatch = normalizedInput.match(PARAGRAPH_PATTERN); if (paragraphMatch?.groups) { const section = paragraphMatch.groups.section; const marker = paragraphMatch.groups.marker; const code = paragraphMatch.groups.code; if (!section || !marker || !code) { return null; } const paragraph = paragraphMatch.groups.paragraph; const sentence = paragraphMatch.groups.sentence; const number = paragraphMatch.groups.number; const letter = paragraphMatch.groups.letter; const normalizedCode = normalizeCode(code); const normalizedSection = normalizeWhitespace(section); const normalizedMarker = resolveSectionMarker(marker, normalizedSection); const paragraphCitationParts = { marker: normalizedMarker, section: normalizedSection, code: normalizedCode, ...(paragraph ? { paragraph } : {}), ...(sentence ? { sentence } : {}), ...(number ? { number } : {}), ...(letter ? { letter } : {}), }; return { type: "paragraph", normalized: buildParagraphCitation(paragraphCitationParts), parsed: { type: "paragraph", marker: normalizedMarker, section: normalizedSection, ...(paragraph ? { paragraph } : {}), ...(sentence ? { sentence } : {}), ...(number ? { number } : {}), ...(letter ? { letter: letter.toLowerCase() } : {}), code: normalizedCode, }, lookupCitations: [ `${normalizedMarker} ${normalizedSection} ${normalizedCode}`, ], }; } const articleMatch = normalizedInput.match(ARTICLE_PATTERN); if (articleMatch?.groups) { const article = articleMatch.groups.article; const code = articleMatch.groups.code; if (!article || !code) { return null; } const paragraph = articleMatch.groups.paragraph; const sentence = articleMatch.groups.sentence; const number = articleMatch.groups.number; const letter = articleMatch.groups.letter; const normalizedCode = normalizeCode(code); const normalizedArticle = normalizeWhitespace(article); const articleCitationParts = { article: normalizedArticle, code: normalizedCode, ...(paragraph ? { paragraph } : {}), ...(sentence ? { sentence } : {}), ...(number ? { number } : {}), ...(letter ? { letter } : {}), }; return { type: "article", normalized: buildArticleCitation(articleCitationParts), parsed: { type: "article", article: normalizedArticle, ...(paragraph ? { paragraph } : {}), ...(sentence ? { sentence } : {}), ...(number ? { number } : {}), ...(letter ? { letter: letter.toLowerCase() } : {}), code: normalizedCode, }, lookupCitations: [`Art ${normalizedArticle} ${normalizedCode}`], }; } return null; } function buildParagraphCitation(parts) { const segments = [`${parts.marker} ${parts.section}`]; if (parts.paragraph) { segments.push(`Abs. ${parts.paragraph}`); } if (parts.sentence) { segments.push(`S. ${parts.sentence}`); } if (parts.number) { segments.push(`Nr. ${parts.number}`); } if (parts.letter) { segments.push(`Buchst. ${parts.letter.toLowerCase()}`); } segments.push(parts.code); return segments.join(" "); } function buildArticleCitation(parts) { const segments = [`Art. ${parts.article}`]; if (parts.paragraph) { segments.push(`Abs. ${parts.paragraph}`); } if (parts.sentence) { segments.push(`S. ${parts.sentence}`); } if (parts.number) { segments.push(`Nr. ${parts.number}`); } if (parts.letter) { segments.push(`Buchst. ${parts.letter.toLowerCase()}`); } segments.push(parts.code); return segments.join(" "); } function normalizeWhitespace(value) { return value.trim().replace(/\s+/g, " "); } function normalizeCode(value) { return value.toUpperCase(); } function resolveSectionMarker(marker, section) { if (marker === "§§" || /(?:\bbis\b|,|-)/i.test(section)) { return "§§"; } return "§"; }