@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
JavaScript
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 "§";
}