Spec Driven Development
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
제작자가 쓴 설명(영어 원문)
Freedom-of-information requests for Germany via FragDenStaat.de, the froide FOI portal.
이 세 가지는 절대 하나의 점수로 합치지 않아요. 의견, 측정, 인기는 서로 다른 것이니까요.
이 서버는 아직 쉬운 말로 된 요약을 쓰지 못했어요. 아래는 레지스트리에서 읽어 온 사실이에요.
쓰고 있는 AI 앱을 고른 다음 설정을 복사하세요.
FDS_MCP_BASE_URL, FDS_TOKEN비밀로 표시된 값은 내 계정의 열쇠예요. 절대 다른 사람과 공유하거나 채팅에 붙여 넣지 마세요.
필요한 것
어떤 앱을 쓰세요?
위에서 앱을 고르세요. 기억해 두었다가 다음부터는 버튼에 “…에 설치”라고 표시해요.
Claude Desktop → Settings → Developer → Edit Config를 열고 claude_desktop_config.json에 이 내용을 붙여 넣으세요
{
"mcpServers": {
"fds": {
"command": "uvx",
"args": [
"fds-mcp"
],
"env": {
"FDS_MCP_BASE_URL": "your-value",
"FDS_TOKEN": "your-value"
}
}
}
}터미널에서 이 명령어를 실행하세요
claude mcp add fds --env FDS_MCP_BASE_URL=your-value --env FDS_TOKEN=your-value -- uvx fds-mcp실행하기 전에 빈 값을 채워 주세요.
프로젝트의 .cursor/mcp.json 또는 ~/.cursor/mcp.json에 붙여 넣으세요
{
"mcpServers": {
"fds": {
"command": "uvx",
"args": [
"fds-mcp"
],
"env": {
"FDS_MCP_BASE_URL": "your-value",
"FDS_TOKEN": "your-value"
}
}
}
}브라우저가 앱을 열지 물어본 다음, 앱이 설정을 보여 주고 확인을 요청해요. 키 값은 비워 두니 직접 입력하세요.
프로젝트의 .vscode/mcp.json에 붙여 넣으세요
{
"servers": {
"fds": {
"type": "stdio",
"command": "uvx",
"args": [
"fds-mcp"
],
"env": {
"FDS_MCP_BASE_URL": "your-value",
"FDS_TOKEN": "your-value"
}
}
}
}브라우저가 앱을 열지 물어본 다음, 앱이 설정을 보여 주고 확인을 요청해요. 키 값은 비워 두니 직접 입력하세요.
이 서버는 내 컴퓨터에서 실행되는데, ChatGPT는 인터넷에 있는 서버에만 연결할 수 있어요. 다른 앱을 고르세요.
선택한 앱은 이 기기의 쿠키에만 저장돼요. 계정은 필요 없어요.
FDS_MCP_BASE_URL env이 설정은 제작자의 레지스트리 정보로 만들었어요. README와 다르면 README를 따르세요.
내 컴퓨터에서 실행돼요. 설치하면 AI 앱에 도구 목록이 나타나요.
누구나 볼 수 있는 정보를 점검했고, 코드는 읽지 않았어요. 해당하지 않는 항목은 개수에서 뺐어요. · 2026년 11월 7일 점검
레지스트리 이름이 GitHub 계정 notDIRK 기준으로 확인되었고, 이 계정이 저장소를 소유하고 있어요
아직 GitHub에서 이 저장소를 읽어 오지 않았어요
아직 GitHub에서 이 저장소를 읽어 오지 않았어요
내 컴퓨터에서 실행되는 서버라 인터넷 엔드포인트가 없어요
내 컴퓨터에서 실행되며, 대신 로컬 설정을 사용해요
내 컴퓨터에서 실행돼요. 도구 목록은 인터넷 엔드포인트가 있는 서버에서만 읽을 수 있어요
레지스트리에서 이 카테고리의 활성 서버 전체와 비교했어요. 스타는 게시자가 저장소를 소유한 경우에만 세요.
해당하는 기본 점검 1개 중 1개를 통과했어요. 코드는 읽지 않았어요. 키나 토큰을 넣기 전에 서버가 어떤 권한을 요구하는지 확인하고, 꼭 필요한 권한만 있는 키를 쓰세요.
입력해야 할 값: FDS_TOKEN
내 컴퓨터에서 실행돼요. AI 앱이 필요할 때 실행하고, 데이터는 제작자의 서버를 거치지 않아요(서버 자체가 온라인 서비스를 호출하는 경우는 제외).
GitHub 저장소는 마지막으로 – 변경됐고, 최신 버전(0.1.0)은 2개월 전 레지스트리에 올라왔어요.
Claude Desktop, Claude Code, Cursor, VS Code처럼 MCP를 지원하는 앱에서 쓸 수 있어요. 내 컴퓨터에 Python (uv)도 있어야 해요.
같은 카테고리에는: Spec Driven Development, Remote Desktop Commander, NexusWindows
같은 카테고리에서 다른 제작자가 만든 서버예요. 스타 순으로 정렬했어요.
카드 보는 법:★ GitHub 스타i세 기둥: 리뷰 · 점검 · 사용 정도i업데이트 = 최신 버전을 레지스트리에 공개한 날i
MCP Server that facilitates spec-driven development workflows, not just Vibe Coding.
Remote Desktop Commander: use files, terminal and processes on your own computers from any AI app.
Windows computer use in the background: desktop apps driven without your mouse or keyboard.
제작자의 저장소에서 쓰인 그대로 가져왔어요. PopNx MCP가 검토하거나 보증한 내용이 아니에요. HTML, 이미지, 스크립트는 제거했어요. 링크를 누르면 이 사이트를 벗어나요.
An [MCP](https://modelcontextprotocol.io) server for [FragDenStaat.de](https://fragdenstaat.de), the German freedom-of-information platform built on [froide](https://github.com/okfde/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.
MCP, the Model Context Protocol, is the standard that lets an AI assistant use outside tools — roughly what an extension does for a browser. fds-mcp is one such tool. Install it once and Claude, or any other MCP client, gains 15 tools that speak to FragDenStaat.de, the German freedom-of-information platform built on froide.
Four of them need no account and no token: searching authorities, reading an act, and working out who is responsible for a given place all work straight away.
froide is the Django software underneath FragDenStaat.de, and its own README names a second production portal: fragdenstaat.at. Point this server at one with
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 |
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), thenget_authority(3495)for the acts that
apply and their deadlines — and reports the API URLs it used, so you can check the answer
instead of believing it.
Submitting is possible too, but it is deliberately the hardest thing this server does.
POST /api/v1/request/ sends immediately and irreversiblyFragDenStaat's REST API has no draft mode, no preview and no undo. The moment a POST /api/v1/request/ succeeds, the e-mail is on its way to the authority, the request is public (by default) under CC0, and it cannot be recalled.
Worse, MakeRequestSerializer has no `law_type` field. The API therefore always files under publicbody.default_law, and because froide sorts by ("-meta", "-priority") that is almost always the *combined meta act* — not the specific act you meant. For a municipality in Rhineland-Palatinate, asking for the LTranspG (law 16) through the API silently files under "LTranspG, VIG" (law 18) instead.
Because of this, the recommended exit of this server is `build_submit_url`, which hands you a prefilled web form with the correct law_type and lets you press send yourself. submit_request exists, defaults to dry_run=True, and refuses unless five independent gates all agree — see Safety model.
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.
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.
The yellow and red tools need an OAuth 2.0 bearer token. FragDenStaat supports exactly two authentication schemes for its API — OAuth2 and session cookies. There is no personal API key and no Basic Auth (froide's own docs/api.rst claims otherwise; it is out of date).
Log in and open https://fragdenstaat.de/account/applications/register/. All account pages are protected by recent_auth_required, so you may be asked for your password again.
| Field | Value |
|---|---|
| Name | anything, e.g. fds-mcp |
| Client type | public (PKCE is then mandatory) |
| Authorization grant type | authorization-code |
| Redirect URI | https://localhost:8765/callback |
Only the schemes `https` and `fragdenstaat` are accepted. http://localhost/... is rejected at registration time — this is OAUTH2_PROVIDER.ALLOWED_REDIRECT_URI_SCHEMES on the server. That is why the default redirect is an HTTPS loopback listener with a self-signed certificate that fds-mcp generates for you (via openssl), and why the fallback is fragdenstaat://callback with a manual paste.
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
fds-mcp requests read:user read:request make:request by default. Override with fds-mcp configure --scopes "read:user read:request" if you never want to submit.
| Scope | Needed for |
|---|---|
read:user | identifying your own account (/api/v1/user/) |
read:request | your own, including non-public, requests |
make:request | POST /api/v1/request/ — submitting |
write:message, write:attachment | documenting postal mail (not yet implemented) |
Nothing here grants deletion. If you drop make:request, submit_request can never work, and everything else still does.
| Tool | Tier | Auth | Side effect |
|---|---|---|---|
search_authorities(query, jurisdiction=None, limit=20) | 🟢 green | none | none |
get_authority(id) | 🟢 green | none | none |
get_law(id) | 🟢 green | none | none |
check_jurisdiction(place_name) | 🟢 green | none | none |
list_my_requests(status=None, limit=50) | 🟡 yellow | token, read only | none |
get_request(id) | 🟡 yellow | token, read only | none |
get_messages(request_id) | 🟡 yellow | token, read only | none |
list_attachments(message_id) | 🟡 yellow | token, read only | none |
download_attachment(attachment_id, target_dir) | 🟡 yellow | token, read only | writes a local file |
check_deadlines() | 🟡 yellow | token, read only | none |
build_reply_draft(request_id, text, subject=None, path=None) | 🟡 yellow | token, read only | writes a local YAML file if path is given |
create_request_draft(...) | 🔴 red | none | writes a local YAML file, no network at all |
validate_draft(path) | 🔴 red | none | reads the API for L01–L05 |
build_submit_url(path) | 🔴 red | none | writes a local .body.txt sidecar |
submit_request(path, confirmation_token) | 🔴 red | token + make:request | sends the request, irreversibly |
send_reply_via_browser(draft_path, confirmation_token) | 🔴 red, opt-in | a logged-in browser profile | sends the reply, irreversibly |
All four red tools take dry_run: bool = True.
send_reply_via_browser is the sixteenth tool and is not registered unless FDS_MCP_BROWSER_SEND=1 is set. Without that variable it does not appear in the tool list at all. Read Sending replies before you switch it on.
check_jurisdiction returns its evidenceIt walks /georegion/?name= up its part_of chain and asks /publicbody/?regions= 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 requestsMeasured 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.
These are frontend-only, with no REST equivalent. The server does not pretend otherwise:
POST /api/v1/message/ only creates *postal* messages (OnlyPostalMessagesWritable), and subject/content are read-only serializer fields. E-mail replies go through /anfrage/ /send/message/, a CSRF-protected Django view that ignores bearer tokens. See Sending replies;law_type in the serializer;RequestDraft is not registered in the API router;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:
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.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 e앞부분만 보여 드려요. 나머지는 저장소에서 읽어 보세요