MCP server and typed components for German, Austrian and EU legal research
German Legal MCP provides 14 well-named, read-only tools across legal research domains (German parliament, EU law, case law, arXiv). Tool names follow verb_noun conventions (search_*, get_*, list_*) and are unambiguous. Descriptions are substantive (100-300+ chars), explaining both function and constraints. All tools have JSON Schema input definitions with typed parameters and descriptions. However, output schemas are NOT documented in the visible source code, implementations exist but specifications are opaque to callers. Parameter schemas are well-structured with enums, defaults, and clear descriptions for complex APIs (e.g., dip_search's literal substring matching behavior is explicitly documented). Error handling is implicit (tools accept section/save_path params and document API limitations). No tool has destructive actions (all READ_ONLY risk). Security is sound: no secrets in params, read-only operations. Tool composition is strong: search_* tools return IDs that get_* tools accept, enabling proper chaining (e.g., arxiv_search → arxiv_get). One significant gap: return value schemas are NOT declared in the tool definitions themselves, callers must infer structure from prose descriptions. This violates the pattern:tool requirement that 'Document the output schema.' The server registers tools dynamically at runtime (verified by verify-runtime-tools.mjs), so definitions are not inferred.
Retrieve an arXiv paper by ID (e.g., "2501.02725"). Default: metadata + abstract. With `section` or `save_path`: fetches HTML full text (available for papers from ~2024+). Older papers without HTML return metadata + abstract + PDF link.
Search arXiv preprints by keywords, author, or category. Returns metadata: arXiv ID, title, authors, abstract, categories, PDF/HTML links. Use arxiv_get with the arXiv ID to retrieve full text.
Retrieve full text of a Bundestagsdrucksache by Dokumentnummer (e.g., "19/27426" for BT-Drs. 19/27426). Returns the extracted text including Gesetzesbegründung. Use `section` for partial content. `save_path` is for export only — when the user wants the document as a file to keep or process elsewhere.
Search Bundestagsdrucksachen (parliamentary documents) via DIP API. Matches title as a literal substring — NOT a keyword AND/OR search. An extra word breaks the match unless it appears verbatim, in that exact order, in the title. For example "Tiergesundheitsgesetz" alone finds dozens of documents, but "Tiergesundheitsgesetz Änderung" finds none, because real titles read "...Änderung des Tiergesundheitsgesetzes..." — reversed order and a different word ending. Prefer a single distinctive word (e.g. a law's short title) over a natural-language phrase. Returns metadata: Dokumentnummer, title, type, date, PDF URL. Use dip_get to retrieve full text (e.g., Gesetzesbegründung) of a specific Drucksache.
Output schemas are not documented in tool definitions. Descriptions mention return structure informally ('Returns metadata: ...', 'Returns full text in Markdown'), but no formal JSON Schema output specification is visible. Callers and LLMs cannot verify response structure without trial and error.
Parameter descriptions for legis_* tools are terse (legis_search has only 'Search query', legis_get only 'Document ID'). These lack context for LLM selection, what constitutes a valid 'Document ID'? Format examples or constraints would help.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 62 | <=2025-11-25 | v2 |
| 2026-03-09 | C | 63 | - | v1 |
Search Plenarprotokolle (parliamentary debate transcripts) with full text search. Returns protocols where the search term appears in the debate text.
Search legislative processes (Vorgänge) in DIP. Returns Gesetzgebungsvorgänge with status and linked Drucksachen-Nummern. Useful for tracking a law through the legislative process or finding all related documents. Matches title as a literal substring — NOT a keyword AND/OR search (see dip_search); prefer a single distinctive word over a natural-language phrase.
Retrieve EU legislation from EUR-Lex by CELEX number (e.g., "32016R0679" for GDPR, "32001L0029" for InfoSoc). Returns full text in Markdown. Use `section` for partial content: "Art. 5", "Artikel 5", or "lines:100-200". `save_path` is for export only — when the user wants the document as a file to keep or process elsewhere.
Search EU legislation (directives, regulations, treaties) via EUR-Lex SPARQL endpoint. Returns CELEX numbers, titles, and dates.
Retrieve a CJEU decision or opinion from InfoCuria by published case number, CELEX number or Logic Doc ID. Returns full text in Markdown with Randnummern as [Rn. 5]{.rn}. A published case number (e.g., "C-476/17", "T-108/25") is converted to its CELEX form internally, so all three forms work. A CELEX number (e.g., "62017CJ0476") or the "Logic Doc ID" from an icu_search result (e.g., "id_320668") resolve with one request fewer. Use `section` for partial content: "Rn 5-12", heading text, or "lines:100-200". Use `save_path` when you want the document written to a file for later use outside this conversation.
Search for decisions and opinions of the Court of Justice of the European Union (CJEU) via InfoCuria. Returns list with case numbers, ECLI, dates, and document IDs for retrieval.
Retrieve full text of German federal or state legislation by document ID. Returns full text in Markdown. Use `section` for partial content. `save_path` is for export only.
Search German federal and state legislation by query and state. Returns titles, IDs, and metadata.
List available German federal and state legislation sources with their status and backend information.
Retrieve table of contents for a German legislative document. Returns structured entries with optional filtering by depth and range.
dip_search, dip_search_vorgang, and dip_search_plenarprotokoll accept optional 'wahlperiode' (legislative period) but no enum values are provided. Descriptions say 'e.g., 20, 21' but do not constrain the range, valid periods are not machine-discoverable.
eul_search and eul_get_document accept a 'language' parameter (default: 'DE') but no enum of supported language codes is provided. Descriptions say 'Language code (default: DE)' without listing valid options. LLMs may pass unsupported codes.
legis_states tool has no input parameters documented (empty schema '{}') and a terse description ('List available ... sources'). No return type is specified. This tool is discoverable but underspecified, unclear what structure the response contains.