FragDenStaat
PYPI · FDS-MCP · SCANNED SEP 22
Freedom-of-information requests for Germany via FragDenStaat.de, the froide FOI portal.
Available components
How this component scores in each security and reliability category. Every signal is checked automatically from public evidence about the published package, including repeated runs of it in an isolated sandbox, and we only credit what we can confirm. How we score → Why this is hard to score →
Supply Chain Security50
- Malware scan not yet available for this package.Unverified
- No known CVEs affecting this package version or its production dependencies.Pass
- Runs hatchling.build at install time, a recognised native-build step with no shell scripting around it. View diagnostics → Pass
- 1 of 33 dependencies flagged as unhealthy. View diagnostics → Partial
Provenance & Transparency32
- Source repository is publicly reachable at the declared URL. View diagnostics → Pass
- Provenance check failed: no build-provenance attestation is published. See how to fix → View diagnostics → Fail
- License check failed: the license (MIT License) isn't a recognized OSI-approved license. See how to fix → Fail
- Actively maintained (last published 16 days ago).Pass
- Disclosure check failed: no security disclosure policy was found in the source repository. See how to fix → Fail
Schema Quality & AI Usability72
- AI-judged instruction clarity (excellent).Pass
- Context-footprint check failed: tool/resource definitions use about 2445 tokens (~163/item across 15 items; 15 tools + 0 resources), over budget; trim descriptions and params. See how to fix → Fail
- Usage-examples check failed: none of the tools include examples. See how to fix → Fail
Stability & Change Management57
- Stability observed for 17 of 30 days with no destabilising changes; credit accrues until the full window elapses.Partial
Tool Coverage67
- 100% of tools have a non-trivial description (not blank, and not just the tool's name).Pass
- 0% of tool parameters carry a description.Fail
Tool Safety100
- No prompt-injection markers were found in the server instructions, tool names or descriptions we captured.Pass
- We read all 15 captured tool definition(s), and no name or description among them implies an irreversible operation.Pass
- An AI judge read all 16 captured unit(s) of tool text and found none that tries to manipulate the model reading it.Pass
Capabilities100
- Implements a current MCP spec version (2026-07-28).Pass
How do I install the FragDenStaat MCP server?
FragDenStaat runs locally as a PyPI package, launched with uvx fds-mcp. Ready-made configuration for Claude, Cursor, VS Code, Codex and 5 more is on this page, copied from each client's own documentation.
pypi · fds-mcp
claude mcp add notdirk-fds-mcp -- uvx fds-mcp
{
"mcpServers": {
"notdirk-fds-mcp": {
"command": "uvx",
"args": [
"fds-mcp"
]
}
}
} {
"servers": {
"notdirk-fds-mcp": {
"command": "uvx",
"args": [
"fds-mcp"
]
}
}
} codex mcp add notdirk-fds-mcp -- uvx fds-mcp
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"notdirk-fds-mcp": {
"type": "local",
"command": [
"uvx",
"fds-mcp"
],
"enabled": true
}
}
} openclaw mcp add notdirk-fds-mcp --command uvx --arg fds-mcp
mcp_servers:
notdirk-fds-mcp:
command: "uvx"
args: ["fds-mcp"] {
"McpServers": {
"notdirk-fds-mcp": {
"Transport": "stdio",
"Command": "uvx",
"Arguments": [
"fds-mcp"
]
}
}
} assistant mcp add notdirk-fds-mcp -t stdio -c uvx -a fds-mcp
{
"mcpServers": {
"notdirk-fds-mcp": {
"command": "uvx",
"args": [
"fds-mcp"
]
}
}
} Every change we have recorded for this component, newest first. Security-relevant changes are always shown. ▲ marks a change for the better, ▼ a change for the worse; unmarked changes are neutral.
- 22 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 53 to 57. That category is still filling its 30-day observation window: 16 days of observed history at the previous scan, 17 at this one. The score rises as the window fills, whether or not the server changes.
- 20 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 47 to 50. That category is still filling its 30-day observation window: 14 days of observed history at the previous scan, 15 at this one. The score rises as the window fills, whether or not the server changes.
- 18 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 40 to 43. That category is still filling its 30-day observation window: 12 days of observed history at the previous scan, 13 at this one. The score rises as the window fills, whether or not the server changes.
- 16 Sept 26 +1
No change was recorded against any check on this day. Stability & Change Management went from 33 to 37. That category is still filling its 30-day observation window: 10 days of observed history at the previous scan, 11 at this one. The score rises as the window fills, whether or not the server changes.
- 13 Sept 26 +4
- Stability: unverified → 0.27 ▲ functional
- 5 Sept 26 50
First indexed and scored.
Diagnostic detail from the automated scan of this channel: what the scanner observed at each step, so you can see exactly where a check passed or failed. It is informational only and never changes the trust score.
Captured 22 Sept 2026 · Analysed pypi/fds-mcp@0.1.0
Provenance No attestation
The registry publishes no build provenance for this version, so there is nothing to verify.
| Result | No attestation |
|---|---|
| Ecosystem | pypi |
Background: How many MCP packages publish verified provenance →
Install scripts 1 script
| Hook | Tier | Command |
|---|---|---|
| build_backend | allowlisted | hatchling.build |
Background: Why install scripts are a supply-chain risk →
Dependencies 33 packages
| Packages resolved | 33 |
|---|---|
| No linked repository | 1 |
| Tree resolution | Complete |
Background: SBOMs and build attestations, explained →
The tools this component advertises to a client, with an estimated token cost for each. Expand a tool to see its parameters and schema. The per-tool counts are indicative and are not scored directly; the schema's total context footprint is one signal in Schema Quality & AI Usability. A tool's description is untrusted text the model reads on every call, which is what makes this list a security surface and not just an inventory: how tool poisoning works →
build_reply_draft ~415
Prepare a follow-up message to an authority. YELLOW: reads the API, sends nothing. The counterpart of ``build_submit_url`` for a request that already exists. It looks the request up, validates the text against the rules that apply to a follow-up, and hands back the finished text plus the URL of the form. **Pressing send stays with you**, and there is no tool argument that changes that: replying is not possible through the API at all. Measured 2026-09-05 (tests/test_api_contract.py): ``POST /api/v1/message/`` refuses ``kind: "email"``, and the web view at ``/anfrage/<slug>/send/message/`` answers 302 to the login page whether or not a bearer token is attached. A follow-up is validated differently from a request. froide does **not** frame it: the textarea arrives prefilled with a salutation, the placeholder U+2026 and a closing formula, and exactly what stands in it is what the authority receives. So R19 requires a salutation and a closing formula — the inverse of R10 — R04 rejects the placeholder that is sitting in the form right now, R06 keeps e-mail addresses and IBANs out of a public thread, and the subject is capped at 230 characters. Args: request_id: numeric id of your existing request. text: the complete message, salutation and closing formula included. subject: reply subject. Defaults to "AW: <title> [#<id>]". path: optional path to a ``.yaml`` file to write the draft to. Needed only if you intend to use ``send_reply_via_browser`` later; the file is written with ``status: draft`` and a placeholder confirmation token.
| Name | Type | Req | Description |
|---|---|---|---|
| path | – | – | – |
| request_id | integer | yes | – |
| subject | – | – | – |
| text | string | yes | – |
No output schema declared.
No examples provided.
build_submit_url ~174
Build the prefilled web form URL — the recommended way out. RED tier. This is the only route on which the legal basis can actually be chosen (``?law_type=...``); the REST API's MakeRequestSerializer has no such field. Sending stays with the human. fragdenstaat.de answers GET URLs above roughly 4096 bytes with HTTP 400 (measured 2026-09-05). Longer drafts therefore get a two-step answer: a short URL that prefills subject and law_type, plus the body in a sidecar text file. Args: path: path to the draft YAML file. dry_run: True (default) does not write the sidecar .body.txt file.
| Name | Type | Req | Description |
|---|---|---|---|
| dry_run | boolean | – | – |
| path | string | yes | – |
No output schema declared.
No examples provided.
check_deadlines ~51
Your open requests whose statutory deadline has passed. YELLOW. froide exposes no "deadline expired" flag; it is computed from ``due_date`` against the current time, exactly as the frontend does.
Input schema present but exposes no named parameters.
No output schema declared.
No examples provided.
check_jurisdiction ~219
Which authority covers a given place? GREEN — returns the evidence trail. Resolves ``/georegion/?name=<place>``, walks its ``part_of`` chain upwards, and asks ``/publicbody/?regions=<id>`` level by level, from the most specific outwards. It stops at the **first level that yields any authority**, because that is the body actually responsible; the remaining, wider levels are reported by id only. This matters in states such as Rhineland-Palatinate, where an *Ortsgemeinde* is often not listed on fragdenstaat.de at all while the *Verbandsgemeindeverwaltung* that runs its administration is. Args: place_name: name of the municipality, district or state. include_wider: also list the authorities of the wider levels. Off by default — at country level that is thousands of bodies and tells you nothing.
| Name | Type | Req | Description |
|---|---|---|---|
| include_wider | boolean | – | – |
| place_name | string | yes | – |
No output schema declared.
No examples provided.
create_request_draft ~413
Write a local YAML draft. RED tier, but performs NO network traffic at all. The file starts in status ``draft`` with a placeholder confirmation token. A human has to read the text, set ``status: approved`` and replace ``confirmation_token`` before anything can be submitted. Args: path: where to write the YAML file. subject: request subject, 8-230 characters. body: the request text, at most 5000 characters. publicbody_id: recipient authority id. law_wunsch_id: the law you want to file under. law_api_default_id: the law the REST API would apply — see get_authority(). law_wunsch_law_type: law_type of the desired law, e.g. "IFG" or "UIG". publicbody_name: authority name, for the L01 cross-check. publicbody_email: authority e-mail, for the L01 cross-check. ermittelt_ueber: how responsibility was established (a URL or a sentence). public: whether the request will be public. Public means CC0 and visible to all. full_text: True sends your text verbatim, False lets froide frame it. submit_via: "web_form" (human presses send) or "api" (immediate dispatch). dry_run: True (default) returns the draft without writing the file.
| Name | Type | Req | Description |
|---|---|---|---|
| body | string | yes | – |
| dry_run | boolean | – | – |
| ermittelt_ueber | – | – | – |
| full_text | boolean | – | – |
| law_api_default_id | – | – | – |
| law_wunsch_id | integer | yes | – |
| law_wunsch_law_type | – | – | – |
| path | string | yes | – |
| public | boolean | – | – |
| publicbody_email | – | – | – |
| publicbody_id | integer | yes | – |
| publicbody_name | – | – | – |
| subject | string | yes | – |
| submit_via | string | – | – |
No output schema declared.
No examples provided.
download_attachment ~137
Download one attachment into a local directory. YELLOW: reading only. The directory has to exist already: this tool will not create a path, because ``target_dir`` is a model-chosen argument and the file name comes from the API, and together they were enough to drop a file into e.g. ~/.config/autostart/. Set ``FDS_MCP_DOWNLOAD_DIR`` to confine downloads to one directory. Args: attachment_id: numeric attachment id (from list_attachments). target_dir: an existing local directory.
| Name | Type | Req | Description |
|---|---|---|---|
| attachment_id | integer | yes | – |
| target_dir | string | yes | – |
No output schema declared.
No examples provided.
get_authority ~116
Full record for one public body, including its laws. GREEN. The API does not expose ``default_law``, so the law the REST API would apply is recomputed here the way froide's ``get_applicable_law()`` does it (``order_by('-meta', '-priority')``). That default is almost always the *meta* law, which is why the API cannot be used to file under a specific act. Args: id: numeric public body id.
| Name | Type | Req | Description |
|---|---|---|---|
| id | integer | yes | – |
No output schema declared.
No examples provided.
get_law ~50
One freedom-of-information act with its deadline rules. GREEN. Args: id: numeric law id, e.g. from ``get_authority(...)["laws"]``.
| Name | Type | Req | Description |
|---|---|---|---|
| id | integer | yes | – |
No output schema declared.
No examples provided.
get_messages ~38
All messages of a request, oldest first. YELLOW. Args: request_id: numeric request id.
| Name | Type | Req | Description |
|---|---|---|---|
| request_id | integer | yes | – |
No output schema declared.
No examples provided.
get_request ~41
One FOI request in detail. YELLOW (public requests work without a token). Args: id: numeric request id.
| Name | Type | Req | Description |
|---|---|---|---|
| id | integer | yes | – |
No output schema declared.
No examples provided.
list_attachments ~41
Attachments belonging to one message. YELLOW. Args: message_id: numeric message id (from get_messages).
| Name | Type | Req | Description |
|---|---|---|---|
| message_id | integer | yes | – |
No output schema declared.
No examples provided.
list_my_requests ~72
Your own FOI requests. YELLOW: needs a token (scope read:request), read only. Args: status: optional filter, e.g. "awaiting_response" or "resolved". limit: maximum number of requests to return.
| Name | Type | Req | Description |
|---|---|---|---|
| limit | integer | – | – |
| status | – | – | – |
No output schema declared.
No examples provided.
search_authorities ~115
Search public bodies on fragdenstaat.de. GREEN: no auth, no side effects. Args: query: free-text search, e.g. a town or an authority name. jurisdiction: optional filter — numeric id, slug ("rheinland-pfalz") or name ("Rheinland-Pfalz"). The API itself only accepts the id. limit: maximum number of results (server page size is 50).
| Name | Type | Req | Description |
|---|---|---|---|
| jurisdiction | – | – | – |
| limit | integer | – | – |
| query | string | yes | – |
No output schema declared.
No examples provided.
submit_request ~260
Actually POST the request to fragdenstaat.de. RED tier — IRREVERSIBLE. ``POST /api/v1/request/`` sends the e-mail to the authority immediately. There is no draft, no preview and no undo. Five gates therefore have to agree, in this order: 1. the draft's status is ``approved`` (a human set it), 2. no ERROR finding is open, 3. ``law.wunsch_id == law.api_default_id`` — the API cannot set law_type, so a mismatch would file the request under the wrong act, 4. ``confirmation_token`` matches the value a human wrote into the draft file, 5. the local throttle ledger says another submission stays inside 5/5min, 6/6h, 10/24h, 20/7d. Args: path: path to the approved draft YAML file. confirmation_token: must equal the ``confirmation_token`` inside the draft file. dry_run: True (default) runs every gate and reports, but sends nothing.
| Name | Type | Req | Description |
|---|---|---|---|
| confirmation_token | string | yes | – |
| dry_run | boolean | – | – |
| path | string | yes | – |
No output schema declared.
No examples provided.
validate_draft ~113
Run the rule set against a draft. RED tier. Offline rules R01-R16 always run. The live rules L01-L04 (authority exists, law is offered, API default law, duplicate check) need the network and therefore only run with ``dry_run=False``. Args: path: path to the draft YAML file. dry_run: True (default) runs offline rules only and does not touch the file.
| Name | Type | Req | Description |
|---|---|---|---|
| dry_run | boolean | – | – |
| path | string | yes | – |
No output schema declared.
No examples provided.
What is the FragDenStaat MCP server?
FragDenStaat is an MCP server listed in the public MCP registry as io.github.notDIRK/fds-mcp. Freedom-of-information requests for Germany via FragDenStaat.de, the froide FOI portal. This page covers its PyPI package (fds-mcp).
Is the FragDenStaat MCP server safe to use?
FragDenStaat scores 58 out of 100 on VerifyMCP. We found no known CVEs affecting it as of 22 September 2026. That is a record of what we were able to check automatically, not an endorsement. The category breakdown on this page shows every signal behind the number, including the ones we could not confirm.
What tools does the FragDenStaat MCP server expose?
FragDenStaat exposes 15 tools: search_authorities, get_authority, get_law, check_jurisdiction, list_my_requests, and 10 more. Their descriptions and schemas cost roughly 2,255 tokens of context every time the server is loaded.
Is the FragDenStaat MCP server still maintained?
FragDenStaat is still listed as active in the MCP registry. We last reached this channel on 22 September 2026. Those dates come from our own scans of the registry and the channel itself, not from anything the publisher announced.