Skip to main content
Glama

fds-mcp — FragDenStaat.de for AI assistants

An MCP server for FragDenStaat.de, the German freedom-of-information platform built on froide.

Deutsche Fassung

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.

What is MCP, and what does it do here?

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.

Other froide instances

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

export FDS_MCP_BASE_URL=https://fragdenstaat.at

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

What it looks like

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 and Kommunalverwaltung Bonn (authority 3495), then get_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 irreversibly

FragDenStaat'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.


Related MCP server: What Do They Know

Documentation

Installation

pip install fds-mcp

Or run it without installing anything, straight from PyPI:

uvx fds-mcp serve

From a checkout, for development:

git clone https://github.com/notDIRK/fds-mcp.git
cd fds-mcp
pip install -e ".[dev]"

Requires Python 3.10 or newer.

Register it with your MCP client

The server speaks stdio. For Claude Code:

claude mcp add fds -- fds-mcp serve

For a client that reads a JSON config:

{
  "mcpServers": {
    "fds": {
      "command": "fds-mcp",
      "args": ["serve"]
    }
  }
}

Or without installing it at all:

{
  "mcpServers": {
    "fds": {
      "command": "uvx",
      "args": ["fds-mcp", "serve"]
    }
  }
}

The four green tools work immediately, with no account and no token.


OAuth setup

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).

1. Register an application

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.

2. Configure and log in

fds-mcp configure --client-id <your-client-id>
fds-mcp login

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:

fds-mcp login --manual     # uses fragdenstaat://callback, you paste the URL back

Tokens land in ~/.config/fds-mcp/tokens.json with mode 0600. Refresh happens automatically; refresh tokens are valid for 180 days.

fds-mcp status     # config, token and throttle state
fds-mcp whoami     # the authenticated account
fds-mcp logout --revoke

Scopes

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.


Tools

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 evidence

It 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.

build_submit_url is two-step for long requests

Measured on 2026-09-05: fragdenstaat.de answers GET URLs above roughly 4096 bytes with HTTP 400 (4086 bytes → 200, 4106 bytes → 400). A typical 4000-character request exceeds that once URL-encoded. Above the limit the tool returns a short URL that prefills subject and law_type, plus the body in a .body.txt file next to your draft, which you paste into the form.

What FragDenStaat's API cannot do

These are frontend-only, with no REST equivalent. The server does not pretend otherwise:

  • replying to an authorityPOST /api/v1/message/ only creates postal messages (OnlyPostalMessagesWritable), and subject/content are read-only serializer fields. E-mail replies go through /anfrage/<slug>/send/message/, a CSRF-protected Django view that ignores bearer tokens. See Sending replies;

  • choosing the legal basis — no law_type in the serializer;

  • draftsRequestDraft is not registered in the API router;

  • setting status, resolution, tags or the law after the fact; publishing a request; filing an objection or escalating to the state information commissioner.


Sending replies

An e-mail reply to an authority cannot be sent through the FragDenStaat API. Not with a different payload, not with an extra scope, not with a better token. Three measurements, taken on 2026-09-05 and kept honest by tests/test_api_contract.py:

  1. POST /api/v1/message/ with kind: "email" answers HTTP 400 and reports, under the key kind: "Nachrichten dieser Art können nicht über die API erstellt werden." That is froide's OnlyPostalMessagesWritable.

  2. The identical call with kind: "post" also answers 400 — the probe deliberately carries an unresolvable request URI, so nothing can be created either way — but it carries no kind error. That is the calibration. Without it the first measurement would prove nothing: a 400 could just as well come from the invalid URI, from the endpoint refusing every POST, or from a missing scope.

  3. POST https://fragdenstaat.de/anfrage/<slug>/send/message/ answers HTTP 302 to /account/login/ — identically with and without a bearer token, same status, same Location. The web view is session + CSRF only. OAuth is not a way around point 1.

So the honest answer is: a human sends the reply. build_reply_draft is what makes that short.

build_reply_draft

Looks the request up, validates your text, and hands back the finished message, a subject in froide's own format (AW: <title> [#<id>]) and the URL of the form. It writes nothing to the network — there is no argument that makes it send.

A follow-up is validated differently from a request, and the difference is easy to get wrong. froide frames a new request with the act's letter_start/letter_end; it does not frame a follow-up at all. The textarea arrives prefilled with

Guten Tag,

…

Mit freundlichen Grüßen
<your name>

and exactly what stands in it is what the authority receives. Hence:

  • R19 requires a salutation and a closing formula, each exactly once — the inverse of R10, which forbids both while the frame is in play;

  • R04 rejects the placeholder (U+2026) that is sitting in that form right now. It is the single most likely mistake on this path;

  • R06 keeps e-mail addresses and IBANs out of a thread that is public and CC0;

  • the subject is capped at 230 characters.

Every result carries one more warning, unconditionally: the form has your postal address prefilled, behind a checkbox labelled "Adresse mitsenden". On a public request, ticking it publishes where you live, under CC0, permanently. Leave it unticked unless the authority has explicitly asked for your postal address.

send_reply_via_browser — optional, off by default

There is a way to automate the last step anyway: drive the form in a browser that carries your logged-in session. This server can do that, and it is not switched on. It is registered only when FDS_MCP_BROWSER_SEND=1 is set, and it needs an extra:

pip install 'fds-mcp[browser]'
python -m playwright install chromium
export FDS_MCP_BROWSER_SEND=1

It never composes text. It sends the subject and body of a reply draft file that build_reply_draft wrote and a human then approved — there is no other input it takes. Five gates:

  1. the file is a reply draft with status: approved, and send_address is false;

  2. no ERROR finding is open under the follow-up rules;

  3. confirmation_token matches, byte for byte, the token a human wrote into the file;

  4. the local ledger says another message stays inside 2/5min, 6/6h, 8/24h. froide does not enforce message_throttle on this path in a way we can rely on, so this brake is voluntary;

  5. in the form itself: "Adresse mitsenden" is off, the recipient can be read and is reported, subject and message read back byte for byte after being typed, no U+2026, and exactly one salutation and one closing formula. Anything it cannot find, it treats as a failure — a form that changed shape is a form it must not press buttons in.

Afterwards it asks the API whether a new message actually exists on the request. If none does, the outcome is reported as unconfirmed and the draft is not marked sent. Unclear is not failure and it is not success.

WARNING

What you are accepting when you switch this on

  1. Browser automation defeats the principle that a human performs the last action. Every other exit in this server ends with a person clicking send. This one does not.

  2. Next to a general-purpose file-writing tool, gates 1 and 3 are not gates. They are two values in a YAML file on your disk. No tool in this server can set either — build_reply_draft always writes status: draft and the placeholder token. But most MCP hosts also give the model a write_file tool, and a model that can write files can write status: approved and a token of its own choosing. Combine that with a prompt injection out of an authority's reply — text this server reads and labels as untrusted, but still puts in front of the model — and post to a public authority goes out with no human in the loop. It cannot be recalled.

  3. The browser carries a logged-in session of yours. A malfunction acts with your full rights on fragdenstaat.de: your requests, your account pages, your address.

  4. Countermeasures, in order of effectiveness:

    • leave the feature off. Unset FDS_MCP_BROWSER_SEND and the tool does not exist.

    • use a separate browser profile with no other logins, via FDS_MCP_BROWSER_PROFILE. The session in that profile is the blast radius.

    • put the draft directory out of reach of your other tools with FDS_MCP_DRAFT_DIR. Gates 1 and 3 are only worth something while nothing else can write that file.

    • keep dry_run=True in normal use. It fills the form and stops before the click.


Safety model

Seven rules are enforced in code, not merely documented. Each has tests in tests/test_security_gates.py that prove it bites.

  1. submit_request aborts unless the draft's status is approved — a human sets that.

  2. It aborts while any ERROR finding is open.

  3. It aborts when law.wunsch_id != law.api_default_id, because the API cannot set law_type and would file under the wrong act.

  4. It aborts unless confirmation_token matches, byte for byte, the token a human wrote into the draft file. A tool must not invent that token.

  5. Every red tool has dry_run: bool = True as its default.

  6. A local ledger checks 5/5min, 6/6h, 10/24h, 20/7d before any POST and aborts with a clear message instead of retrying. FragDenStaat's terms of use B.1.4 lock an account for a month for attempting to circumvent the limits.

  7. The HTTP client refuses every non-GET method unless allow_write=True was set explicitly. Only one function in the package ever sets it.

send_reply_via_browser has its own chain of five, listed under Sending replies, with tests in tests/test_browser_send.py. It also has a gate the others do not need: rule 0, the tool is not registered at all unless FDS_MCP_BROWSER_SEND=1.

The rule set

Offline rules R01R19 reproduce what froide's web form enforces — which is considerably more than the REST API validates. Live rules L01L06 check against the API: the authority exists and still has that name, the desired law is actually offered, the recomputed API default matches what the draft claims, no duplicate request exists, no sentence of your text is already in the law's own letter template, and the finished letter contains every element it should.

Notable ones:

  • R06 refuses e-mail addresses and IBANs in the body. Public requests are CC0 and visible to everyone — do not put other people in them.

  • R10 treats a salutation or a closing formula as an error when full_text=false: froide frames the text itself with the law's letter_start/letter_end, so writing either yourself sends a doubled greeting.

  • R12 is an error for submit_via: api and only a hint for submit_via: web_form — the web form can choose the act, the API cannot.

  • R18 and L06 check the letter the authority receives, not the body you wrote. With full_text=false the act's letter_start/letter_end supply part of the text, so an element may come from either side; L06 fetches the frame and reports what neither half supplies. Six elements: legal basis (the only ERROR), cost pre-notification, cost cap, deadline, forwarding when the body is not responsible, electronic reply.

    The cost cap is why this exists. A real request went out without one, because the LTranspG letter_end does ask to be told the expected costs but names no ceiling and no fallback to free inspection on the premises. Everything else was covered by the template, which is exactly why reading the body alone found nothing.

  • R19 is the inverse of R10 and applies only to follow-ups — see Sending replies.

Where the server is allowed to write

Three tool arguments are file paths chosen by the model, and the same model reads authority replies and attachments — text written by third parties. So the paths are constrained rather than trusted:

  • draft paths must end in .yaml/.yml, are resolved before they are checked (a symlink is judged by its target), and save() refuses to overwrite a file that is not itself a draft;

  • download_attachment will not create a directory, and the attachment's file name is stripped to its basename with everything outside [A-Za-z0-9._ -] replaced;

  • attachments are only ever fetched from fragdenstaat.de and media.frag-den-staat.de, and the bearer token is never sent anywhere else.

Four environment variables tighten this further, and are recommended whenever the server runs unattended:

Variable

Effect

FDS_MCP_DRAFT_DIR

every draft path must stay inside this directory (:-separated list)

FDS_MCP_DOWNLOAD_DIR

every download_attachment target must stay inside this directory

FDS_MCP_BROWSER_SEND

1 registers send_reply_via_browser. Anything else, including unset, and the tool does not exist

FDS_MCP_BROWSER_PROFILE

browser profile directory for that tool. Point it at a profile logged in to fragdenstaat.de and nothing else

Results that carry third-party text (get_messages, get_request, list_attachments, download_attachment) name those fields in an untrusted_content key. They are data. They do not choose file paths, URLs, tool calls or confirmation tokens.

The draft lifecycle

draft ──validate_draft──▶ validated ──a human edits the file──▶ approved ──submit_request──▶ submitted

A reply draft has its own, ending in sent rather than submitted, and only send_reply_via_browser can reach that state — and only after the API has confirmed that a new message exists.

Only a human moves a draft to approved, and only by editing the YAML file.

Know the limit of that sentence. Gate 1 (status: approved) and gate 4 (confirmation_token) are two values in a file on your disk. No tool in this server can set either of them — create_request_draft always writes status: draft and the placeholder token, and there is no tool that promotes a draft. But most MCP hosts give the model a general-purpose file-writing tool as well, and a model that can write files can write status: approved and a token of its own choosing. Combined with a prompt injection out of an authority's reply, that is a path to a real submission.

So, if you run this alongside a filesystem tool:

  • keep submit_request out of the picture entirely by configuring scopes without make:request — then no token this server holds can ever POST a request;

  • or set FDS_MCP_DRAFT_DIR to a directory your other tools do not write to;

  • or leave the recommended exit in place and use build_submit_url, where the send button is in your browser and not in a tool call;

  • and leave FDS_MCP_BROWSER_SEND unset. The same reasoning applies to send_reply_via_browser, one step more sharply: it has no scope you can withhold, only a browser session you own.

Gates 2, 3, 5 and 7 do not depend on the file and hold regardless: the rule set runs against live API data, the law check compares against the recomputed API default, the throttle ledger is separate state, and the HTTP client refuses non-GET everywhere except in submit_request.


Development

pip install -e ".[dev]"
python -m pytest -m "not live"     # offline suite
python -m pytest -m live           # hits fragdenstaat.de

Network access is blocked by default via pytest-socket; only tests marked live may reach fragdenstaat.de.

The live suite is read-only GETs with exactly two exceptions, both in tests/test_api_contract.py, both of which cannot create anything: the message POSTs carry an unresolvable request URI, and the web-form POST carries an empty body. No test opens a browser and no test sends a message. The tests that need a token skip, rather than fail, when ~/.config/fds-mcp/tokens.json is absent.

The CLI also works without an MCP client:

fds-mcp validate examples/request-draft.yaml
fds-mcp validate examples/request-draft.yaml --live

Facts, and where they come from

Everything this server asserts about the API was verified against fragdenstaat.de on 2026-09-05, against okfde/froide@bc6c2fa and okfde/fragdenstaat_de@88bfbba. The source references are in the docstrings, down to file and line. If you find a claim that is wrong or has gone stale, that is a bug — please open an issue.

The API is documented at https://fragdenstaat.de/api/ with an OpenAPI 3.0.3 schema at https://fragdenstaat.de/api/v1/schema/ and a Swagger UI at https://fragdenstaat.de/api/v1/schema/swagger-ui/. (/api/v1/docs/, which froide's own docs mention, returns 404.)


Please use this responsibly

FragDenStaat is run by a non-profit and paid for by donations. Every request you file costs a public authority real working time. The rate limits are 5 requests per 5 minutes and 20 per week for a reason. This server is built to help you file better requests, not more of them.

Requests filed with public: true — the default — publish the entire e-mail correspondence, all approved attachments and all uploaded documents to the world under CC0.


Disclaimer

This project is not affiliated with, endorsed by, or connected to the Open Knowledge Foundation Deutschland e.V., FragDenStaat, or the froide project. It is an independent third-party client that talks to a public API. All trademarks belong to their owners.

This is not legal advice.

License

MIT — see LICENSE. Copyright 2026 Dirk Wolbeck.

Available Tools

15 tools
build_reply_draftA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
pathNo
textYes
subjectNo
request_idYes

TDQS

A4.9/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the full burden, and it does so thoroughly. It discloses that the tool only reads and never sends, that no argument changes this, that the API contract was verified, and that follow-up validation differs from request validation. This is exceptionally transparent about side effects and constraints.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the key purpose and safety statement, followed by behavioral context and a structured Args section. It is long but information-dense; some of the API-test evidence could be shortened, but it does earn its place by substantiating the 'sends nothing' claim.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a tool with no annotations and no output schema, the description is remarkably complete. It explains what the tool does, what it returns conceptually, validation rules, parameter semantics, the file-writing side effect, and the hard limit that sending is impossible through the API. No critical decision-relevant information appears to be missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must fully compensate for every parameter. It does: request_id is explained as the numeric id of an existing request, text must include salutation and closing formula, subject has a documented default, and path is described with its exact purpose and file behavior. This adds substantial meaning beyond the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description starts with a specific verb and resource: 'Prepare a follow-up message to an authority.' It immediately clarifies the key behavioral distinction from sibling tools: 'reads the API, sends nothing.' It also names itself as the counterpart of build_submit_url, which clearly differentiates it from related tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines5/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description explicitly frames this tool as being for an existing request, in contrast to build_submit_url. It also gives a clear exclusion: replying is not possible through the API at all, so no tool argument can turn this into a send. The path parameter is explicitly tied to the condition of later using send_reply_via_browser, which gives clear when-to-use guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

build_submit_urlA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
dry_runNo

TDQS

A4.4/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral disclosure burden and does a good job: it discloses the 4096-byte GET URL limit, the two-step sidecar fallback, and dry_run side-effect. It does not fully specify the return contract, but the core behavioral traits are clearly revealed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the summary and then gives the critical constraints and parameter docs. Every sentence carries information, including the measured URL-length limit that explains the side-car behavior.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a two-paramter tool with no annotations and no output schema, the description covers purpose, key constraints, side effects, and parameter semantics. It leaves the exact return format implicit, but the core workflow is sufficiently complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must document parameters, and it does: path is explained as a draft YAML file path, and dry_run's default and side-effect are described. This adds real meaning beyond the bare schema fields.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

States the specific verb+resource: "Build the prefilled web form URL," and positions it as the recommended route. It also distinguishes it from the REST API by noting it is the only route where legal basis can be chosen, so an agent can tell it apart from related tools.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Gives explicit guidance: it is the recommended way out, the only route that supports law_type, and sending stays with the human. It implicitly warns against using the REST API for this purpose, though it does not name a specific sibling alternative for automatic submission.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

check_deadlinesA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault

No parameters

TDQS

A3.8/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries the disclosure burden. It usefully explains that froide stores no 'deadline expired' flag and that the value is computed from due_date against the current time, matching frontend behavior. This gives an agent useful context about why the result set is time-dependent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short and front-loaded with the essential meaning. The only minor issue is the unexplained 'YELLOW' label, which adds slight ambiguity but does not bloat the text.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given that the tool takes no parameters and has no output schema, the description is sufficiently complete: it states what the tool returns and explains the deadline computation. It could mention return format or pagination, but those are not essential for a zero-argument listing tool.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema has zero parameters, so there are no parameter semantics to document. The baseline of 4 applies because the description does not need to compensate for any schema gaps.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the tool's result as the user's open requests whose statutory deadline has passed, which distinguishes it from list_my_requests and get_request. It lacks an explicit verb like 'lists' or 'returns', but the meaning is unambiguous and matches the tool name.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The usage is implied: use this when you need to see open requests with expired statutory deadlines. However, it does not explicitly compare with sibling tools such as list_my_requests or state when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

check_jurisdictionA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
place_nameYes
include_widerNo

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden, and it delivers: it explains the resolution algorithm, the stopping rule ('first level that yields any authority'), why that rule matters, and what happens at wider levels ('reported by id only'). It also discloses the default behavior of include_wider and its rationale, giving substantial behavioral context.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loaded, moving from purpose to algorithm to example to parameters. Every section earns its place, though the initial 'GREEN —' fragment is cryptic and slightly distracting without explanation. Overall it is efficiently structured with little waste.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Given the absence of an output schema and annotations, the description explains the input semantics, the algorithm, the stopping condition, and the nature of the returned evidence trail. It does not specify the exact response structure or edge cases such as unknown places or levels with no authorities, so it is strong but not fully exhaustive.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the description fully compensates: place_name is defined as 'name of the municipality, district or state', and include_wider is explained semantically ('also list the authorities of the wider levels') plus its default and why it defaults to false. This adds real meaning beyond the raw schema titles.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with the precise question 'Which authority covers a given place?' and then explains the resolution mechanism by walking the georegion and publicbody hierarchy. This clearly identifies a specific verb+resource behavior and distinguishes it from sibling tools like search_authorities or get_authority by focusing on place-to-jurisdiction resolution rather than keyword search or direct lookup.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives a clear use case: determining the responsible authority for a municipality, district, or state, with a concrete Rhineland-Palatinate example. It also explains when include_wider is inappropriate ('at country level that is thousands of bodies'), but it does not explicitly name alternatives or state when not to use this tool versus search_authorities.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

create_request_draftA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
bodyYes
pathYes
publicNo
dry_runNo
subjectYes
full_textNo
submit_viaNoweb_form
law_wunsch_idYes
publicbody_idYes
ermittelt_ueberNo
publicbody_nameNo
publicbody_emailNo
law_api_default_idNo
law_wunsch_law_typeNo

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries full behavioral burden and does so thoroughly. It discloses that no network traffic occurs, that the file begins with status 'draft' and a placeholder confirmation token, that a human must approve/replace the token, and that dry_run returns the draft without writing the file.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with the most critical facts (local draft, no network, human approval required), followed by a compact Args list. Every sentence adds value; the length is justified by the 14 parameters it documents.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Despite having no annotations or output schema, the description gives the agent everything needed to call the tool correctly: the purpose, the safety profile, the approval workflow, parameter constraints, and dry_run behavior. Nothing essential is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, but the Args list describes all 14 parameters with useful semantics: constraints (subject 8-230 chars, body max 5000), meanings ('public' means CC0), options (submit_via choices), and cross-tool references (law_api_default_id -> get_authority()). This fully compensates for the bare schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Write a local YAML draft', which clearly distinguishes this from submission, validation, or retrieval tools. It further clarifies scope with 'performs NO network traffic at all' and explains this is a preparatory step before a human approves and submits.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives clear context for usage: this is a local, network-free drafting step that requires human approval before submission. It does not explicitly name alternatives or exclusion conditions, but the workflow context ('before anything can be submitted') makes the intended position in the process clear.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

download_attachmentA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
target_dirYes
attachment_idYes

TDQS

A4.6/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations at all, the description carries the full burden and handles it well. It labels the operation as read-only, warns that it writes a file locally, explicitly says it will not create directories, and explains the security rationale along with the FDS_MCP_DOWNLOAD_DIR confinement option. This is substantive behavioral context beyond the schema or annotations.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The one-sentence purpose is front-loaded and the Args list is terse. The second paragraph is longer, but it earns its length by explaining a real path-injection risk and the recommended confinement mechanism, so it is not padding.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple two-parameter download tool, the description covers purpose, input source, directory preconditions, and safety configuration. It omits details like return values or overwrite behavior, but those are minor for a download operation and no output schema exists to supply them.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, and the description fully compensates: attachment_id is explained as the numeric id from list_attachments, and target_dir is explained as an existing local directory with a usage constraint. Both required parameters receive meaning beyond their bare types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description states the verb, resource, and destination clearly: 'download one attachment into a local directory.' It also identifies the attachment_id source as list_attachments, so an agent can distinguish this tool from sibling listing and request tools without ambiguity.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives explicit preconditions: target_dir must already exist, and the tool will not create a path. It also points to attachment_id as coming from list_attachments, which tells the agent where to get its input. It does not name alternatives, but no sibling tool performs the same download function, so the preconditions are sufficient guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_authorityA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
idYes

TDQS

A4/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the full transparency burden and it excels: it discloses that the API does not expose default_law, that the default is recomputed using a specific ordering, that it is almost always the meta law, and that this means the API cannot be used to file under a specific act. This is valuable behavioral information beyond the schema.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The first sentence front-loads the core purpose, and the rest is a tight caveat plus parameter note. The technical detail about froide's order_by is dense but earns its place because it explains why the tool behaves as it does. No unnecessary filler.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter retrieval tool with no output schema and no annotations, this description covers the essential bases: what the tool returns, how the default law is computed, a key limitation, and the argument semantics. It could add explicit sibling routing or expected return structure, but it is largely complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must compensate. It does so by defining id as 'numeric public body id', which adds real semantic meaning beyond the schema's generic integer title 'Id'. For a single-parameter tool, this is sufficient.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly states it returns the full record for one public body, including its laws, and identifies the resource as a public body. It distinguishes itself from search_authorities by emphasizing singular retrieval, though it does not explicitly name the sibling alternative.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

Usage context is implied: use this when you need a complete record for a known public body by id. However, there is no explicit guidance about when to prefer this over search_authorities or get_law, and no stated exclusions beyond the filing limitation.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_lawC

One freedom-of-information act with its deadline rules. GREEN.

Args:
    id: numeric law id, e.g. from ``get_authority(...)["laws"]``.
ParametersJSON Schema
NameRequiredDescriptionDefault
idYes

TDQS

C2.8/5.0
Behavior1/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must carry the behavioral burden. It does not disclose whether the operation is read-only, what the response shape is, whether there are errors, or what 'GREEN' means. The name suggests retrieval, but the description itself offers no behavioral detail.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is short and front-loads the core resource before the args. The unexplained 'GREEN.' and odd capitalization slightly hurt clarity, but there is no fluff.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness2/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

With no output schema and no annotations, the description should explain return expectations and usage context. It only says the result is a freedom-of-information act with deadline rules and gives the id source; it does not describe response fields, errors, or how this relates to siblings like check_deadlines and get_authority. For a one-parameter geter this is below minimally complete.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema only states that id is an integer and required; the description adds that it is a numeric law id and, more valuably, that it comes from get_authority(...)['laws']. Since schema description coverage is 0%, this provenance meaningfully supplements the schema.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The phrase 'One freedom-of-information act with its deadline rules' clearly identifies the resource and the relevant content, and the example ties the id to get_authority(...)['laus']. It lacks an explicit verb like 'retrieve', and 'GREEN' is unexplained, but an agent can infer this returns a single law by id.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

There is no guidance on when to use this tool versus siblings like search_authorities, get_authority, or check_deadlines. The only usage hint is that the id comes from get_authority(...)['laws'], which explains parameter provenance rather than selection context.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_messagesB

All messages of a request, oldest first. YELLOW.

Args:
    request_id: numeric request id.
ParametersJSON Schema
NameRequiredDescriptionDefault
request_idYes

TDQS

B3.1/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description carries the behavioral burden. It does disclose useful behavior: the result covers all messages and is ordered oldest first. However, it omits pagination/limits, error behavior, permissions, and any explanation of what 'YELLOW.' means, so the behavioral profile is incomplete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The core sentence is short and front-loaded, and the Args section is appropriately compact for one parameter. The dangling 'YELLOW.' does not earn its place and creates ambiguity, which prevents a higher score.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple one-parameter getter without an output schema, the definition conveys the basic return subject and ordering. It lacks detail on the message shape, failure modes, and the meaning of 'YELLOW,' leaving some uncertainty for an agent that needs to consume the response or handle errors.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters2/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The only parameter is described as 'numeric request id,' which essentially restates the schema's integer type and 'Request Id' title. Since schema description coverage is 0%, the description needed to add operational meaning such as where to obtain the id, constraints, or an example, and it does not.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies a concrete operation and resource: it returns all messages for a request, oldest first. This is specific enough to separate it from sibling tools like get_request or list_attachments, though it does not name alternatives and the stray 'YELLOW.' marker is unexplained.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The phrase 'of a request' implies the intended context: use this when you need the message thread for a specific request_id. It gives no explicit when-not-to-use guidance and does not point to alternatives such as get_request for request metadata or list_attachments for files.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

get_requestB

One FOI request in detail. YELLOW (public requests work without a token).

Args:
    id: numeric request id.
ParametersJSON Schema
NameRequiredDescriptionDefault
idYes

TDQS

B3.4/5.0
Behavior3/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description must carry the behavioral disclosure burden. It does convey an auth-related trait: 'public requests work without a token.' However, it does not explain what happens for non-public requests, whether the operation is read-only, error behavior, or rate limits. The 'YELLOW' label is only partially explained. Some useful behavioral context is present, but notable gaps remain.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is extremely compact: a one-line purpose, a brief auth note, and an argument definition. Every sentence carries useful information and the core purpose is front-loaded. There is no redundancy with the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a simple single-parameter getter, the description covers the essential call: what it returns and what parameter is needed. However, with no output schema and no annotations, it leaves out details about what 'in detail' includes, how non-public requests are authenticated, and possible failure modes. It is adequate for a basic call but not fully self-sufficient.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

With schema description coverage at 0%, the description adds the crucial semantic that 'id' is a numeric request id, going beyond the schema's bare integer type. It tells the agent which identifier to pass. It is minimal but sufficient for a single parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description clearly identifies the tool as returning a single FOI request's details ('One FOI request in detail'). This distinguishes it from list-oriented siblings like list_my_requests, though it does not explicitly name an alternative. The verb and resource are clear enough for an agent to understand what the tool does.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines2/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives no guidance about when to use this tool versus siblings such as list_my_requests or search_authorities. It implies the agent should already have a request id, but it does not state that a prior lookup is needed or mention any exclusions or alternatives. This leaves usage context entirely to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_attachmentsB

Attachments belonging to one message. YELLOW.

Args:
    message_id: numeric message id (from get_messages).
ParametersJSON Schema
NameRequiredDescriptionDefault
message_idYes

TDQS

B3.2/5.0
Behavior2/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description is the only source of behavioral info. It only states the resource; it does not disclose whether the operation is read-only, what is returned, or any side effects. The unexplained 'YELLOW.' label adds ambiguity rather than transparency.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness3/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The definition is appropriately tiny and front-loaded. However 'YELLOW.' is an unexplained fragment that does not earn its place; without known tool conventions, it confuses more than it clarifies.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a one-parameter list operation, the essential call data is present and the prerequisite is stated. Yet without an annotations safety profile or output schema, the description should at least note that this lists metadata and that download_attachment is the sibling for retrieving content; that distinction is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters4/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The schema only provides title/type, and description coverage is 0%. The description compensates by explaining that message_id is the numeric id from get_messages, which gives provenance and reduces ambiguity enough for the sole parameter.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the resource ('attachments') and scope ('belonging to one message'), and the tool name supplies the 'list' verb. However, the sentence is a noun phrase and does not explicitly differentiate from download_attachment, so it is clear but not fully explicit.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The parameter line tells the agent to obtain message_id from get_messages, establishing a prerequisite and a minimal usage context. But it does not say when to prefer this over download_attachment or any other sibling, nor when not to use it.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

list_my_requestsA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
statusNo

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description discloses that the operation is read-only, requires a token with scope read:request, and is limited to the caller's own requests. That is meaningful behavioral context. It does not mention response format or pagination beyond the limit parameter, but for a simple list operation the core behavior is clear.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is compact and front-loads the resource and safety profile, followed by two parameter lines. No filler or repetition of schema defaults.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a low-complexity list tool with no output schema, it covers purpose, auth, read-only behavior, and all parameters. It could additionaly state the shape of the returned request objects, but that is not essential for basic invocation.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description cover age is 0%, yet the description compensates by explaining both parameters: status is an optional filter with concrete example values, and limit is the maximum number of results. This adds meaning the schema's titles and defaults alone do not provide.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description identifies the resource as the user's own FOI requests, which distinguishes it from siblings like get_request or search_authorities. It lacks an explicit verb, though the tool name 'list' carries the action. Overall clear and specific.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It states the scope ('your own') and an authorization prerequisite, which gives context for when to call it. It does not explicitly name alternatives or say when not to use it, so routing to siblings is left to inference.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

search_authoritiesA

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).
ParametersJSON Schema
NameRequiredDescriptionDefault
limitNo
queryYes
jurisdictionNo

TDQS

A4.3/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations provided, the description carries the full burden and explicitly discloses 'no auth, no side effects,' which is critical behavioral information. It also reveals the API-only-id caveat for jurisdiction. It could go further by describing the response shape, but the safety and side-effect profile is transparent.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

Every sentence is purposeful: the opening states the action, the GREEN tag summarizes safety in one token, and each parameter gets a compact line with no redundancy. The most important operational detail is front-loaded.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness4/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a three-parameter read-only search, the description covers the service, auth, side effects, and each parameter, including the jurisdiction id conversion caveat. It does not explicitly state the return shape, which would be more important here because no output schema exists, but the gap is minor given the tool's simplicity.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

The input schema provides only names, types, and defaults, so the description must add meaning. It fully compensates: query gets a free-text explanation with examples, jurisdiction gets accepted formats and the API-only-id constraint, and limit gets its maximum-result meaning plus the server page-size hint. This gives an agent everything needed to construct arguments correctly.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific verb and resource: 'Search public bodies on fragdenstaat.de.' This clearly identifies it as a discovery/list-search tool and distinguishes it from siblings like get_authority, which implies direct retrieval. The scope is unambiguous.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines3/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description implies the tool should be used when an agent needs to find public bodies by free-text, but it does not explicitly state when not to use it or name alternatives such as get_authority for known IDs. The GREEN safety note adds operational context but not routing guidance.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

submit_requestA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
dry_runNo
confirmation_tokenYes

TDQS

A4.8/5.0
Behavior5/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

No annotations are provided, so the description carries full responsibility for behavioral disclosure. It clearly states that the action is IRREVERSIBLE, sends immediately, has no draft/preview/undo, and enumerates the exact gate conditions that control whether the request is actually submitted. This is unusually complete.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness5/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is long but every sentence carries necessary safety-critical information. The irreversibility warning is front-loaded, the gates are listed in a clear numbered order, and the parameter explanations are tightly integrated rather than repeated from the schema.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness5/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

For a high-risk, irreversible submission tool with no annotations and no output schema, the description covers the essential context: preconditions, gate order, parameter constraints, safety default behavior, and the fact that no undo is possible. Nothing needed to decide whether and how to invoke it is missing.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema description coverage is 0%, so the description must explain the parameters itself. It does: path is the approved draft YAML file, confirmation_token must match the value inside that draft, and dry_run defaults to true and prevents any sending. Every parameter is given meaningful semantics beyond its bare schema definition.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose5/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description names a specific action ('POST the request'), a target resource ('fragdenstaat.de'), and a clear scope ('sends the e-mail to the authority immediately'). It also distinguishes itself from sibling draft/build/validate tools by emphasizing this is the actual irreversible submission step, not a draft or preview.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

The description gives strong contextual guidance: the five gates must agree before submission, and dry_run is presented as a safe way to test every gate without sending. It does not explicitly name sibling alternatives such as validate_draft or build_submit_url, so the when-not-to-use guidance is implied rather than fully explicit.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

validate_draftA

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.
ParametersJSON Schema
NameRequiredDescriptionDefault
pathYes
dry_runNo

TDQS

A4.1/5.0
Behavior4/5

Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?

With no annotations, the description must carry the transparency burden, and it does: offline rules R01-R16 always run, live rules L01-L04 require network and only run with dry_run=False, and dry_run=True does not touch the file. The main unknown is what happens if dry_run=False, but the key side-effect and network dependencies are disclosed.

Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.

Conciseness4/5

Is the description appropriately sized, front-loaded, and free of redundancy?

The description is front-loaded with purpose, then behavior, then arguments, with no fluff. There is slight redundancy between the live-rules paragraph and the dry_run argument explanation, but it does not hurt readability.

Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.

Completeness3/5

Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?

Invocation is well specified, but there is no output schema and the description never says what validate_draft returns or how the agent should interpret the result. Because no annotations or output schema exist, this is a meaningful gap.

Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.

Parameters5/5

Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?

Schema coverage is 0%, and the description fully compensates: path is explained as a YAML file path, and dry_run is described as controlling offline-only vs live execution, including the default and side-effect. This adds real meaning beyond the schema's bare names and types.

Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.

Purpose4/5

Does the description clearly state what the tool does and how it differs from similar tools?

The description opens with a specific action and object: 'Run the rule set against a draft,' which makes the tool's validation role clear and distinguishes it from sibling build/create/submit tools. It does not explicitly name or contrast a sibling tool, so it stops short of 5.

Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.

Usage Guidelines4/5

Does the description explain when to use this tool, when not to, or what alternatives exist?

It gives concrete conditions for when offline-only vs live validation runs, directly explaining how dry_run changes execution. It omits an explicit 'use before submitting' statement and does not name alternatives, but the context is clear enough.

Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.

Tool Schema Changelog

Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.

  1. 15 tool updatesv0.1.0
    • First observedbuild_reply_draft
    • First observedbuild_submit_url
    • First observedcheck_deadlines
    • First observedcheck_jurisdiction
    • First observedcreate_request_draft
    • First observeddownload_attachment
    • First observedget_authority
    • First observedget_law
    • First observedget_messages
    • First observedget_request
    • First observedlist_attachments
    • First observedlist_my_requests
    • First observedsearch_authorities
    • First observedsubmit_request
    • First observedvalidate_draft

TDQS

A3.9/5.0
Disambiguation5/5

Every tool maps to a distinct resource or action: authority lookup, jurisdiction resolution, request retrieval, message/attachment access, deadline checks, and the draft/submit pipeline. Even the several draft-related tools are clearly separated by request vs reply and validate/build/submit roles.

Naming Consistency4/5

Tool names mostly follow a clear verb_noun pattern like list_my_requests, get_request, download_attachment, and submit_request. Minor inconsistencies exist, such as create_request_draft vs build_reply_draft and get_messages vs list_attachments, but the overall convention remains predictable.

Tool Count5/5

With 15 tools, the server is at the upper edge of a well-scoped set but each tool earns its place. The count reflects a complete workflow: discovery, retrieval, attachments, deadlines, draft validation, URL generation, and submission.

Completeness4/5

The domain is well covered: authority and law lookup, request/message/attachment retrieval, deadline checking, request drafting/validation/submission, and reply drafting all exist. The main gap is that reply submission is not provided by any tool, though the description explains this is an API limitation and the workflow hands off to a human.

Related MCP Connectors

Related MCP Servers

  • F
    license
    A
    quality
    B
    maintenance
    Provides 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.
    5
    23
    -
  • A
    license
    B
    quality
    C
    maintenance
    MCP server for UK Freedom of Information research. Connects AI assistants to WhatDoTheyKnow — the UK's largest FOI request platform to search requests, read responses, look up public authorities, and draft new requests.
    8
    MIT

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/notDIRK/fds-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server