Freedom-of-information requests for Germany via FragDenStaat.de, the froide FOI portal.
Copy the AI prompt to install this server into Claude Code, Cursor, or another agent β or use 1-click editor setup below.
π‘ Paste the JSON block into your client's configuration file under mcpServers, then restart the application.
An MCP server for FragDenStaat.de, the German freedom-of-information platform built on froide.
Prepare a German freedom-of-information request by asking for it in plain language. Your assistant finds the authority that has to answer and proves why it is the right one, names the act that applies and the deadline it sets, tracks the requests you already sent, collects the replies and their attachments, and writes a new request to a local file that you read before anything leaves your machine.
MCP, the Model Context Protocol, is the standard that
lets an AI assistant use outside tools β roughly what an extension does for a browser.
fds-mcp is one such tool. Install it once and Claude, or any other MCP client, gains 15
tools that speak to FragDenStaat.de, the German
freedom-of-information platform built on froide.
Four of them need no account and no token: searching authorities, reading an act, and working out who is responsible for a given place all work straight away.
froide is the Django software underneath FragDenStaat.de, and its own README names a second production portal: fragdenstaat.at. Point this server at one with
Only https origins without a path or credentials are accepted, and the host allowlist that
keeps the bearer token from following a link to a stranger is derived from this value β so
with .at configured, fragdenstaat.de is a foreign host and is refused.
What that buys you, measured against fragdenstaat.at on 2026-09-05 and kept as live tests:
search_authorities, get_authority, get_law | work, with no account |
check_jurisdiction | cannot work there β /api/v1/georegion/ holds 0 rows on .at against 24,216 on .de. Nothing this client can fix |
| the token-bound tools | need an OAuth application registered on that instance |
| the pre-send rule set | encodes German FOI law and is not portable. Treat submitting through another instance as unverified |
You: Who has to answer a freedom-of-information request about the city of Bonn, and under which act?
The assistant calls
check_jurisdiction("Bonn"), gets the region chain andKommunalverwaltung Bonn(authority 3495), thenget_authority(3495)for the acts that apply and their deadlines β and reports the API URLs it used, so you can check the answer instead of believing it.
Submitting is possible too, but it is deliberately the hardest thing this server does.
POST /api/v1/request/ sends immediately and irreversiblyFragDenStaat's REST API has no draft mode, no preview and no undo. The moment a
POST /api/v1/request/ succeeds, the e-mail is on its way to the authority, the request
is public (by default) under CC0, and it cannot be recalled.
Worse, MakeRequestSerializer has no law_type field. The API therefore always
files under publicbody.default_law, and because froide sorts by ("-meta", "-priority")
that is almost always the combined meta act β not the specific act you meant. For a
municipality in Rhineland-Palatinate, asking for the LTranspG (law 16) through the API
silently files under "LTranspG, VIG" (law 18) instead.
Because of this, the recommended exit of this server is build_submit_url, which
hands you a prefilled web form with the correct law_type and lets you press send
yourself. submit_request exists, defaults to dry_run=True, and refuses unless five
independent gates all agree β see Safety model.
Or run it without installing anything, straight from PyPI:
From a checkout, for development:
Requires Python 3.10 or newer.
The server speaks stdio. For Claude Code:
For a client that reads a JSON config:
Or without installing it at all:
The four green tools work immediately, with no account and no token.
The yellow and red tools need an OAuth 2.0 bearer token. FragDenStaat supports exactly
two authentication schemes for its API β OAuth2 and session cookies. There is no
personal API key and no Basic Auth (froide's own docs/api.rst claims otherwise; it is
out of date).
Log in and open https://fragdenstaat.de/account/applications/register/. All account
pages are protected by recent_auth_required, so you may be asked for your password
again.
| Field | Value |
|---|---|
| Name | anything, e.g. fds-mcp |
| Client type | public (PKCE is then mandatory) |
| Authorization grant type | authorization-code |
| Redirect URI | https://localhost:8765/callback |
Only the schemes https and fragdenstaat are accepted. http://localhost/... is
rejected at registration time β this is OAUTH2_PROVIDER.ALLOWED_REDIRECT_URI_SCHEMES on
the server. That is why the default redirect is an HTTPS loopback listener with a
self-signed certificate that fds-mcp generates for you (via openssl), and why the
fallback is fragdenstaat://callback with a manual paste.
login runs Authorization Code + PKCE (S256), opens your browser, and catches the
redirect on https://localhost:8765/callback. Your browser will warn about the
self-signed certificate β that is the local listener; accept it.
Without a browser or without openssl:
Tokens land in ~/.config/fds-mcp/tokens.json with mode 0600. Refresh happens
automatically; refresh tokens are valid for 180 days.
fds-mcp requests read:user read:request make:request by default. Override with
fds-mcp configure --scopes "read:user read:request" if you never want to submit.
| Scope | Needed for |
|---|---|
read:user | identifying your own account (/api/v1/user/) |
read:request | your own, including non-public, requests |
make:request | POST /api/v1/request/ β submitting |
write:message, write:attachment | documenting postal mail (not yet implemented) |
Nothing here grants deletion. If you drop make:request, submit_request can never
work, and everything else still does.
| Tool | Tier | Auth | Side effect |
|---|---|---|---|
search_authorities(query, jurisdiction=None, limit=20) | π’ green | none | none |
get_authority(id) | π’ green | none | none |
get_law(id) | π’ green | none | none |
check_jurisdiction(place_name) | π’ green | none | none |
list_my_requests(status=None, limit=50) | π‘ yellow | token, read only | none |
get_request(id) | π‘ yellow | token, read only | none |
get_messages(request_id) | π‘ yellow | token, read only | none |
list_attachments(message_id) | π‘ yellow | token, read only | none |
download_attachment(attachment_id, target_dir) | π‘ yellow | token, read only | writes a local file |
check_deadlines() | π‘ yellow | token, read only | none |
build_reply_draft(request_id, text, subject=None, path=None) | π‘ yellow | token, read only | writes a local YAML file if path is given |
create_request_draft(...) | π΄ red | none | writes a local YAML file, no network at all |
validate_draft(path) | π΄ red | none | reads the API for L01βL05 |
build_submit_url(path) | π΄ red | none | writes a local .body.txt sidecar |
submit_request(path, confirmation_token) | π΄ red | token + make:request | sends the request, irreversibly |
send_reply_via_browser(draft_path, confirmation_token) | π΄ red, opt-in | a logged-in browser profile | sends the reply, irreversibly |
All four red tools take dry_run: bool = True.
send_reply_via_browser is the sixteenth tool and is not registered unless
FDS_MCP_BROWSER_SEND=1 is set. Without that variable it does not appear in the tool
list at all. Read Sending replies before you switch it on.
check_jurisdiction returns its evidenceIt walks /georegion/?name=<place> up its part_of chain and asks
/publicbody/?regions=<id> at every level, then returns the region chain, the matching
authorities, and the list of API URLs it used. That matters in Rhineland-Palatinate,
where an Ortsgemeinde is often not listed on FragDenStaat at all while the
Verbandsgemeindeverwaltung that administers it is.
No reviews yet β be the first to share how this listing worked for you.
Showcase your server listing on GitHub or your project documentation. Embed this dynamic SVG badge to highlight official listing status and live engagement.
[](https://allmcps.com/mcp/fragdenstaat)<a href="https://allmcps.com/mcp/fragdenstaat"><img src="https://allmcps.com/api/badge/fragdenstaat?style=directory" alt="FragDenStaat on AllMCPs" /></a>