froide-mcp
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@froide-mcplist my open FOI requests"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
froide-mcp
MCP server for the Froide FOI platform.
Runs as a standalone Cloud Run Service alongside a Froide installation. Authenticates users via Google OAuth2 SSO (same as Froide admin) and exposes Froide's REST API as MCP tools consumable by Claude and other MCP clients.
All requests to /mcp/* require a valid session token obtained via /auth/login. No anonymous tool access.
Operational invariants
A few pieces must stay aligned across code, Terraform, and docs:
MCP_BASE_URLis required at runtime for the Google OAuth callback URI and must be managed by Terraform so later applies do not silently remove it.Smoke tests are intentionally conservative. They check deploy health and auth error handling without pinning FastMCP wire-transport details unless that protocol is explicitly documented and version-locked.
End-user session tokens expire after 8 hours, so they are fine for interactive use and short post-deploy checks but not for long-lived unattended monitoring.
Related MCP server: claudecode-mcp
Architecture
Claude / MCP client
│ Streamable HTTP + X-Froide-Session header
▼
froide-mcp (Cloud Run Service, FastMCP 3.x)
└─ RequireSessionMiddleware ← enforces Google SSO on every /mcp/* request
│ OAuth2 Bearer token
▼
Froide REST API (Cloud Run Service)
│
▼
PostgreSQL (Cloud SQL)This repo is applied as a patch on top of a Froide installation — it does not fork or modify Froide itself. It depends on:
A running Froide instance with the REST API enabled
A Google OAuth2 Client configured in Google Cloud Console
Froide OAuth2 application (
account.Application) withclient_credentialsgrant
Authentication flow
Visit
GET /auth/login→ redirected to GoogleGoogle redirects to
GET /auth/callback?code=...Server verifies the ID token, optionally checks
hd(hosted domain)Exchanges for a Froide OAuth2 bearer token via
client_credentialsReturns a signed session token (HMAC-SHA256, 8 h TTL)
Include the token as
X-Froide-Session: <token>in all MCP requests
Tool naming rules
Tool names intentionally communicate both shape and side effects:
list_*returns a collection, optionally filtered with query parameters such asq,status, orjurisdiction.get_*returns one resource by ID.draft_*returns structured drafting context and never mutates Froide data.preflight_*validates a prospective action and never mutates Froide data.send_*,make_*, andset_*mutate Froide data and should only be called after explicit user or agent confirmation.
This contract reduces ambiguity for MCP clients about which tools are safe to call speculatively and which have irreversible or externally visible effects.
Tool contract
FOI Requests
Tool | Mutates data | Input | Returns | Typical use |
| No | optional | Froide paginated request collection | Browse or search requests by status or keyword |
| No |
| One request with thread metadata | Inspect one request before manual action |
| Yes |
| Created Froide request object | Submit a new FOI request |
| Yes |
| Created follow-up message object | Send a reviewed follow-up to an authority |
| Yes |
| Updated request object | Mark request outcome after review |
Public Bodies, Jurisdictions, Campaigns, Laws, Attachments, Profile
Tool | Mutates data | Input | Returns | Typical use |
| No | optional | Froide paginated public body collection | Find target authority by name or jurisdiction |
| No |
| One public body | Verify authority identity before drafting |
| No | optional | Froide paginated jurisdiction collection | Discover jurisdiction IDs for filtering |
| No | optional | Froide paginated campaign collection | Browse campaigns for request association |
| No |
| One campaign | Inspect campaign details |
| No | optional | Froide paginated law collection | Find applicable FOI laws |
| No |
| One law | Inspect law before request creation |
| No |
| Froide paginated attachment collection | Review request documents |
| No | none | Authenticated user profile | Confirm current user identity |
Orchestration helpers
These tools compose existing API calls into higher-level workflows. They do not modify any Froide data unless they explicitly redirect the caller to a mutating tool such as send_followup or make_request.
Tool | Mutates data | Input | Returns | Typical use |
| No | optional | Ranked work queue with status, priority, rationale, next step | Build an operator worklist |
| No | optional | Urgent subset of the triage queue | Identify requests needing a human decision now |
| No |
| Compact briefing: counts, priority, next step | Orient before opening a request in the Froide UI |
| No |
|
| Language-aware follow-up drafting context |
| No |
|
| Language-aware request drafting context before review |
| No |
| Validation issues, warnings, preview | Validate a request before calling |
| No | optional | Status counts, priority bands, top requests | Lightweight dashboards or summaries |
| No |
|
| Language-aware deadline follow-up drafting context |
Drafting model
Drafting tools are language-aware but language-neutral in implementation.
They do not return hard-coded prose in any specific language. Instead, they return a structured draft_context object that the MCP client, LLM, or human operator renders into the final message text in the language appropriate for the request thread and target authority.
This applies to all three drafting tools:
draft_followup_for_requestfollowup_after_deadlinedraft_request
A typical draft_context from draft_request contains fields such as request_goal, records_description, public_body_name, law_name, disclosure_preferences, and drafting_notes. The caller uses this structured context to compose the final subject line and request body in the correct language.
Drafting safety contract
draft_* tools return context, not final prose. A successful draft result means no message has been sent and no request has been created.
Recommended safe flow:
Call a
draft_*tool.Render or review the final prose in the target language.
Optionally call
preflight_request_submissionfor new requests.Only then call a mutating tool such as
send_followupormake_request.
Pagination model
Collection tools pass the page parameter through to the Froide REST API and return Froide's standard paginated envelope. MCP callers inspect count, next, and previous in the response to decide whether to request the next page.
Triage configuration
Workflow prioritisation is driven by status-based heuristics: status → priority band and status → next-step text. These rules should be treated as data, not hidden business logic.
Keep the canonical status mappings in one place (dictionary constants or a small YAML/JSON config).
Prefer dictionary lookups with a fallback over long
if/elifchains — this makes operator policy easier to review, test, and evolve without rewriting control flow.Document every workflow status token that influences triage and make it obvious which statuses are considered urgent and why.
Testing strategy
The most valuable tests protect decision semantics, not only HTTP transport. The workflow layer already contains meaningful logic, so tests should cover both layers.
Unit tests for pure decision logic
These functions are deterministic and should have fast parametrized tests with no network or authentication dependency:
_priority_for_request_derive_next_step_followup_draft_context_deadline_followup_draft_context_contains_user_action_hint
Recommended cases:
Each urgent status maps to the expected priority band.
awaiting_response,successful,refused, and unknown statuses produce stable next-step text.Deadline draft context always sets
deadline_exceeded=True.Free-text hints such as
fee,clarif,postal, andconfirmationbehave as intended, including negative phrases such asno fee.
Integration tests with a mocked FroideClient
Mock the Froide API boundary and assert the JSON shape returned by higher-level tools:
triage_my_requestspreflight_request_submissiondraft_followup_for_requestdraft_requestfollowup_after_deadlineget_request_analytics
Verify:
Required keys are always present in the response.
draft_contextshape is stable — field names and semantics do not drift silently.Mutating tools are not called by draft or triage helpers.
Pagination parameters are forwarded correctly.
Edge cases such as empty
results, missing messages, or absent optional metadata are handled gracefully.
MCP agents depend on field names and response shape; silent schema drift can break agent behaviour even when the underlying Froide API still works correctly.
Development history
The orchestration helper layer was built iteratively. The initial design and implementation draft were produced without direct repository access: a local patch-level proposal was written that added documentation and the first three orchestration tools (triage_my_requests, find_requests_needing_action, summarize_request_thread) on top of the existing API layer.
That first version was deliberately scoped to UI-level orchestration with no Django changes required — the three tools compose list_requests, get_request and list_attachments calls that already existed. The README update described the new "Orchestration helpers" section and established the principle that these tools guide operators inside the existing Froide UI rather than replicating backend logic.
The remaining five tools (draft_followup_for_request, preflight_request_submission, get_request_analytics, draft_request, followup_after_deadline) and their tests were added in subsequent PRs once repository access was available, completing the orchestration layer. All tools follow the same bearer-token authentication pattern as the core API tools and build exclusively on /api/v1/ endpoints.
Local development
cp .env.example .env
# fill in FROIDE_BASE_URL, GOOGLE_CLIENT_ID, GOOGLE_CLIENT_SECRET, ...
pip install -e .[dev]
python -m froide_mcp.serverDeployment
cd terraform
terraform init
terraform plan
terraform applySee docs/deployment.md for full setup.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
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/jaakkokorhonen/froide-mcp'
If you have feedback or need assistance with the MCP directory API, please join our Discord server