normkompass-mcp
This MCP server provides read-only tools for searching German federal law and asking citation-backed questions through a configured normkompass API.
search_law: search the corpus by question (1–1000 chars) with an optional limit (1–20, default 5), returning ranked passages with source law, citation label, title, URL, Stand, and text.ask_law: ask a question and get an answer with citations (source, path, URL, quote) and Stand, or a normalrefused: trueresult when the corpus does not support an answer; requires an answer model configured in normkompass.Covers five German federal laws: UStG, UStDV, AO, BDSG, and DDG; not legal advice.
Acts as a thin MCP client: no retrieval, model, or storage of its own; it depends on the normkompass API and can start/list tools even when that API is down.
Supports stdio by default and optional streamable HTTP; HTTP uses a bearer token, required on non-loopback hosts.
Tools are annotated read-only, non-destructive, and idempotent;
ask_lawmay still send questions/passages to the API’s configured answer model.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@normkompass-mcp Wann gilt ein Landfahrzeug als neu? Bitte mit Gesetzeszitat."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
normkompass-mcp
A small MCP server in Python that lets an AI agent search German federal law and ask cited questions through normkompass, a TypeScript RAG service that answers only with verifiable citations and refuses when the corpus does not support an answer.
Status: reference implementation, version 0.1.0. It is a thin client: it adds no retrieval, no model and no storage. Not legal advice.
Tools
Tool | Input | Result |
|
| Ranked passages in the API's order: source law, citation label (for example "§ 1b Abs. 3 UStG"), title, URL, Stand, text. |
|
|
|
Refusal is a normal result, not an error. When normkompass finds no answer that its citation check supports, the agent gets
refused: true. The quote checking (every quote a verbatim part of a retrieved passage) happens in normkompass; this server passes the citations through exactly as returned and discards an answer that comes without one.Errors are short tool errors: invalid input (nothing is sent to the API), API not reachable (names the configured address), timeout (30 s for search, 120 s for ask, no silent retry), a response in an unexpected shape, and an API without an answer model (
ask_lawfails,search_lawstill works).The server starts and lists its tools even when the API is down; the failure shows when a tool is called.
Related MCP server: Legal MCP
Requirements
A running normkompass API (see its README; default address
http://localhost:3000).ask_lawadditionally needs an answer model configured there.Python 3.12 or newer and uv.
Set NORMKOMPASS_API_URL if the API is elsewhere (see .env.example). No secrets are read or stored. The server
sends requests only to that address; what normkompass does with a question (for example sending it to a hosted
answer model) is described in its README.
Connect it to an MCP client
The server speaks MCP over stdio. The command is the same for every client:
uv run --directory /path/to/normkompass-mcp normkompass-mcpClaude Code:
claude mcp add normkompass -e NORMKOMPASS_API_URL=http://localhost:3000 -- \
uv run --directory /path/to/normkompass-mcp normkompass-mcpA client configured through JSON (for example Claude Desktop) takes the same command in its mcpServers entry:
{
"mcpServers": {
"normkompass": {
"command": "uv",
"args": ["run", "--directory", "/path/to/normkompass-mcp", "normkompass-mcp"],
"env": { "NORMKOMPASS_API_URL": "http://localhost:3000" }
}
}
}What has been run
On 2026-11-05 the server was started with that command (uv run normkompass-mcp) by the MCP Python SDK's
client over stdio against a live normkompass API with the default search and the answer model
gemini-3.5-flash-lite (free hosted tier, a third party). The client listed the two tools; search_law returned
§ 18 Abs. 10 UStG and § 1b Abs. 3 UStG for "Wann gilt ein Landfahrzeug als neu?"; ask_law answered that
question with the citation § 1b Abs. 3 and refused "Wie hoch ist der gesetzliche Mindestlohn pro Stunde?".
On 2026-11-07 the Docker image was run against a live normkompass API (answer model gemini-3.5-flash-lite, free
hosted tier). Over stdio (docker run -i) the tools were listed with their annotations and search_law
returned § 18 Abs. 10 UStG and § 1b Abs. 3 UStG. Over HTTP with a token, a request without a token and one
with a wrong token got 401, the right token got 200, and ask_law answered "Wann gilt ein Landfahrzeug als
neu?" with the citation § 1b Abs. 3. The container log contained the rejections but not the token.
Not run: the two client snippets above were not run in Claude Code or Claude Desktop themselves. They use the documented configuration shape with the command that was run. If a client rejects them, that is a bug in this page; please open an issue.
Annotations
Both tools are annotated as read-only, not destructive and idempotent, and as contacting one fixed service (the
configured normkompass API). These are hints for clients, as the MCP specification says, not a security control.
"Read-only" describes the tool's effect on the law corpus: neither tool changes anything in normkompass. ask_law
is not free of side effects outside it: the normkompass API sends the question and the retrieved passages to the
answer model it is configured with, which may be a hosted service, and the same question can get a different answer.
HTTP transport
stdio is the default and is unchanged. For a process that cannot start the server itself, an optional streamable HTTP transport is available:
NORMKOMPASS_MCP_TRANSPORT=http uv run normkompass-mcp # http://127.0.0.1:8000/mcpVariable | Meaning |
|
|
| address to listen on, default |
| port, default |
| bearer token, at least 16 characters; required on a non-loopback host |
On a loopback address the token is optional. On any other address the server refuses to start without one, and
every request must carry Authorization: Bearer <token>; others get 401 before any tool runs. The token is
compared in constant time and is never logged or returned, and a rejected request is logged without either the
expected or the received value.
Limits you should know. The server does not terminate TLS, so a bearer token over plain HTTP can be read by anyone on the network path: put a reverse proxy with TLS in front, or stay on loopback. A public deployment is not supported. The normkompass API behind this server has no authentication and no rate limits, and it may forward questions to a hosted answer model, so exposing the server would hand that capability, and its cost, to whoever can reach it. There is one token for all users, no per-user control and no rate limiting here either.
Container
docker build -t normkompass-mcp .
# stdio (the client starts the container; -i keeps stdin open)
docker run -i --rm -e NORMKOMPASS_API_URL=http://host.docker.internal:3000 normkompass-mcp
# HTTP with a token (the token is read from your environment, not baked into the image)
docker run --rm -p 127.0.0.1:8000:8000 \
-e NORMKOMPASS_MCP_TRANSPORT=http -e NORMKOMPASS_MCP_HOST=0.0.0.0 \
-e NORMKOMPASS_MCP_TOKEN="$TOKEN" -e NORMKOMPASS_API_URL=http://host.docker.internal:3000 \
normkompass-mcpThe image runs as a non-root user and contains no secret; the API address and the token are given at run time.
host.docker.internal reaches an API on the host on Docker Desktop; elsewhere use an address the container can
reach. Binding to 0.0.0.0 inside the container is safe only because the token is mandatory and the port is
published to 127.0.0.1.
The image is built in CI (docker build -t normkompass-mcp:ci . in .github/workflows/ci.yml) to check that the
Dockerfile works. It is not pushed to any registry and no image is published; build it yourself with the command
above. The bearer token is a single shared secret that is optional on a loopback address, and the transport has no
TLS of its own.
Development
uv sync
uv run ruff format --check . && uv run ruff check . && uv run mypy && uv run pytestTests run against a stub of the normkompass API (71 tests, no corpus, database or model needed), including a test
that starts the server as a subprocess over stdio. CI runs the same checks plus openspec validate --strict.
Work follows OpenSpec: the specification in openspec/ was merged as a
separate pull request before any code.
Limits
Answer quality is that of normkompass and its model; nothing about quality is measured here. See the normkompass README ("Limitations") for what is known, in particular that Claude has not been measured there.
No TLS, no per-user tokens, no rate limiting, no caching, and no public hosting (see "HTTP transport").
One corpus (five German federal laws, a snapshot with the Stand shown per result). No promise that the law shown is current or complete.
License
MIT, see LICENSE.
Available Tools
2 toolsask_lawARead-onlyIdempotent
Ask a question and get an answer with citations, or an explicit refusal when the corpus does not support an answer. normkompass checked that every quote in the citations is a verbatim part of a retrieved passage; this server adds nothing to it. Needs an answer model configured in normkompass. German federal law (UStG, UStDV, AO, BDSG, DDG). Not legal advice. Every result carries the Stand (version date) of the law it comes from.
Inputs: question (required). Returns: stand, answer, message, refused, +1 more
search_lawARead-onlyIdempotent
Search the normkompass corpus and return the best matching passages, in the API's order, each with its citation label (for example '§ 14 Abs. 1 UStG'), title, source URL, Stand and text. Use it to find the exact passage before citing. German federal law (UStG, UStDV, AO, BDSG, DDG). Not legal advice. Every result carries the Stand (version date) of the law it comes from.
Inputs: question (required), limit. Returns: passages
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 2 tools
The two tools are close but distinguishable: search_law returns raw ranked passages, while ask_law returns a synthesized answer with verbatim-checked citations or an explicit refusal. The descriptions make clear that search is for finding exact passage text and ask is for answering a question, though an agent could plausibly reach for either on the same query.
Both tools follow the identical verb_noun pattern (search_law, ask_law) over the same domain noun, giving a fully predictable and readable convention.
Two tools is on the thin side; the server is essentially one retrieval endpoint and one RAG endpoint. Both earn their place for the stated corpus-QA purpose, but there is no third operation (e.g. fetching a provision by citation) to round out the surface.
The core legal-research loop is covered: retrieve passages, then get a grounded answer with citations and refusal behavior. Minor gaps remain — no way to fetch a provision directly by its citation label and no tool to enumerate the corpus or covered statutes — but agents can work around these via search_law.
Maintenance
Related MCP Connectors
Verified, citable German & EU law for any LLM. Daily updates from official sources, hosted in DE.
Verified, citable German & EU law for any LLM. Daily updates from official sources, hosted in DE.
Search German and EU law from official sources with your AI assistant. PRIMAMCP provides citable legal texts via MCP, with daily updates and hosting in Germany.
Cited answers from your organisation's documents. GDPR-compliant, hosted in Germany.
Related MCP Servers
- AlicenseAqualityCmaintenanceProvides access to the official German Federal Legal Information Portal (rechtsinformationen.bund.de) enabling AI agents to search German federal laws, court decisions, and legal documentation with authoritative citations from official sources.524MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI assistants to search and analyze German legal texts using vector embeddings and semantic search.91MIT
- AlicenseNot gradedqualityCmaintenanceEnables querying 6,870 German federal statutes, case law, and legislative preparatory works directly from AI assistants and MCP-compatible clients.356 npm4Apache 2.0
- AlicenseAqualityCmaintenanceEnables AI agents to query German federal and state statutes, court decisions, and citation graphs, grounding citations, retrieving provision texts and versions, and understanding coverage limits through read-only tools.91AGPL 3.0