This release is a pre-release and may not be stable for production use.
MuckRock & DocumentCloud Model Context Protocol (MCP) Server
A stateless Model Context Protocol (MCP) server providing structured access to MuckRock Requests and DocumentCloud APIs — eight read-only tools, six resource templates, and dual stdio/HTTP transports (see Features).
Overview: LLMs & Public Records (FOIA/PRA)
MuckRock is an open-government platform that helps journalists, researchers, and anyone seeking to hold public institutions accountable file, track, and share public records requests. DocumentCloud is an open repository and analysis tool for primary source documents released by government agencies.
This Model Context Protocol (MCP) server bridges AI agents (such as Goose, OpenCode, Hermes Agent, Claude Code, Codex, or custom local LLM pipelines) with MuckRock and DocumentCloud data. Rather than requiring models to parse arbitrary web pages, this server provides structured, machine-readable access to:
- Investigative Discovery: Searching dockets, tracking communication logs between requesters and agencies, and inspecting responsive records.
- Document Analysis: Searching released document corpuses, fetching clean page-level extracted plain text, and extracting identified entities (people, organizations, locations).
- Automated Workflows: Resolving agency identifiers, monitoring docket status changes, and auditing statutory response deadlines.
All interactions adhere to the standardized Model Context Protocol, allowing AI hosts to dynamically discover tools, resolve URI resources directly into model context, and autocomplete request parameters.
Architecture Overview
┌──────────────────────────────┐
│ LLM Client / MCP Host │
│ (OpenCode, Goose, Codex etc) │
└──────────────┬───────────────┘
│
┌──────────────┴───────────────┐
│ Transport Layer │
│ StdioServerTransport (CLI) │
│ StreamableHTTPTransport │
└──────────────┬───────────────┘
│
┌──────────────┴───────────────┐
│ JSONRPCDispatcher │
│ (MCP 2026-07-28, Discover) │
└──────────────┬───────────────┘
│
┌──────────────────────────────┼──────────────────────────────┐
│ │ │
┌────────┴────────┐ ┌────────┴────────┐ ┌────────┴────────┐
│ CatalogManager │ │ Tools & Registry│ │Resources/Resolve│
│ • Deterministic │ │ • 8 FOIA Tools │ │ • 6 Templates │
│ Sorted Lists │ │ • Structured Out│ │ • RFC 6570 Match│
│ • Cache Direct. │ │ • ResourceLinks │ │ • Autocomplete │
└─────────────────┘ └────────┬────────┘ └─────────────────┘
│
┌────────┴────────┐
│ Upstream Client │
│ MuckRockClient │
│ DocumentCloud │
└────────┬────────┘
│
┌────────┴────────┐
│ Auth & Rate │
│ TokenStore(JWT) │
│ TokenBucket 15/5│
│ Jitter Backoff │
└─────────────────┘
Features
- Ingress Transports:
- Standard I/O (
stdio): Fast, newline-delimited JSON-RPC 2.0 streaming over standard input/output with diagnostic logging strictly isolated to standard error. - Streamable HTTP (ASGI 3.0): Stateless
POST /mcpcommand ingress (per-request JSON responses) with a strict 4MB framing safeguard.GET/DELETEon the endpoint return405— no server-side sessions.
- Standard I/O (
- Squarelet OAuth / JWT Authentication: Proactive token refresh when access token TTL drops below 60 seconds (
POST https://accounts.muckrock.com/api/token/refresh/). Falls back gracefully to unauthenticated mode for public FOIA browsing when no credentials are configured; explicitly configured credentials that the upstream rejects fail loudly with401instead of silently serving public-only results. - Dual-Tier Sliding-Window Token Bucket:
- General Tier: 15 req/min (0.25 tokens/s) with burst capacity up to 100 tokens.
- Sensitive Tier: 5 req/min (~0.0833 tokens/s) with zero burst capacity (capacity 1) for agency, organization, and user endpoints.
- Jittered Exponential Backoff: Automatically parses
Retry-After(seconds or RFC 7231 / RFC 2822 HTTP-date) upon HTTP 429 and applies full jitter backoff.
- Deterministic Prompt Cache Optimization: All catalog listings (
tools/list,resources/list,resources/templates/list) are lexicographically sorted to maximize LLM prompt cache hit rates, returningttlMs: 300000(5 min) andcacheScope: "public". - Eight Core Tools (catalog below): Comprehensive FOIA search, request dockets, chronological correspondence threads, agency lookups, DocumentCloud search, full-text extracted plain-text retrieval, and categorized entity extraction. Tool responses embed typed
ResourceLinkobjects connecting returned data to matching RFC 6570 URI templates. - Six RFC 6570 Resource Templates (resolvers below): Concrete URI resolvers for requests, threads, agencies, documents, pages, and entities.
- Argument Autocompletion (details below): Fast MCP autocompletion (
completion/complete) for request statuses, jurisdictions, and dynamic agency search. - Opt-in Ingress Authentication: Set
MCP_API_TOKENto requireAuthorization: Bearer <token>on HTTP ingress (unset means open); unauthenticatedGET /healthzreports liveness. CORS is loopback-only by default — no wildcard.
Protocol Support Statement (Dual-Era Contract)
Requests without the MCP-Protocol-Version header are served as protocol version 2025-03-26 (legacy headerless clients are tolerated as 2025-03-26 era, spec-permitted, no elevated privilege). server/discover works with zero prior initialize — no handshake or session is required. subscriptions/listen and live list_changed notifications are not supported: the catalog is static with listChanged: false, so poll server/discover instead.
Era-by-Transport Matrix
| Era / transport row | Status | Pinning test |
|---|---|---|
| HTTP modern headered (match served) | Supported | tests/integration/test_http_server.py::TestProtocolVersionHeader::test_match_served_normally |
HTTP header/body mismatch → -32020 |
Supported | tests/integration/test_http_server.py::TestProtocolVersionHeader::test_mismatch_yields_32020 |
HTTP unsupported version → -32022 + supported payload |
Supported | tests/integration/test_http_server.py::TestProtocolVersionHeader::test_unsupported_yields_32022 |
HTTP legacy headerless initialize tolerated as 2025-03-26 |
Supported | tests/integration/test_http_server.py::TestProtocolVersionHeader::test_legacy_headerless_served |
HTTP legacy headerless server/discover tolerated as 2025-03-26 |
Supported | tests/integration/test_http_server.py::TestProtocolVersionHeader::test_legacy_headerless_discover_served |
HTTP legacy headerless tools/list tolerated as 2025-03-26 |
Supported | tests/integration/test_http_server.py::TestProtocolVersionHeader::test_legacy_headerless_tools_list_served |
HTTP stateless posture (no sessions, 405 on GET/DELETE, forged IDs ignored) |
Supported | tests/integration/test_http_server.py::TestHTTPIntegration::test_full_http_stateless_workflow |
Zero-handshake server/discover (no prior initialize) |
Supported | tests/unit/test_protocol.py::TestJSONRPCDispatcher::test_no_handshake_calls_succeed |
listChanged: false static catalog |
Supported | tests/unit/test_protocol.py::TestJSONRPCDispatcher::test_no_handshake_calls_succeed |
subscriptions/listen → -32601 Method not found |
Not supported (by design) | tests/unit/test_protocol.py::TestJSONRPCDispatcher::test_subscriptions_listen_method_not_found |
stdio legacy conversation (initialize → tools/call → resources/read → completion) |
Supported | tests/integration/test_stdio_server.py::TestStdioIntegration::test_full_stdio_conversation |
| Goose harness | Unverified as of 2026-09-08 | — |
| OpenCode harness | Unverified as of 2026-09-08 | — |
| Hermes Agent harness | Unverified as of 2026-09-08 | — |
| Claude Code harness | Unverified as of 2026-09-08 | — |
| Codex harness | Unverified as of 2026-09-08 | — |
Installation
For users
From PyPI
Run the server without touching source code, in an isolated environment:
# From PyPI, using uv (recommended)
uv tool install muckrock-mcp
# From PyPi, using pip
pip install muckrock-mcp
# With pipx
pipx install git+https://github.com/pmocek/muckrock-mcp
From a wheel
A wheel
(.whl) is Python's installable package format — handy for air-gapped
machines or pinning an exact build. Build one from a checkout, copy the
single .whl file anywhere, and install it:
# In the checkout
uv build # writes dist/muckrock_mcp-*.whl
# On the target machine (uv or pipx)
uv tool install muckrock_mcp-0.1.0-py3-none-any.whl
# or
pipx install muckrock_mcp-0.1.0-py3-none-any.whl
For contributors
Install with dev tools to run the test suite (uv run pytest tests/)
or contribute code (formatting via black). The dev extras are
pytest, pytest-asyncio, pytest-httpx, and black.
# Standard checkout install
uv sync
# With test and development dependencies
uv sync --extra dev
Without uv:
pip install -e .
pip install -e ".[dev]"
Configuration & Environment Variables
Set environment variables MUCKROCK_USERNAME / MUCKROCK_PASSWORD with
valid credentials; without them the server cannot authenticate to MuckRock,
and provides very limited utility (mainly to verify operation).
For HTTP mode, also set MCP_API_TOKEN to require a bearer token on ingress.
| Variable | Description | Default |
|---|---|---|
MUCKROCK_USERNAME |
MuckRock / Squarelet account username | None (Anonymous mode) |
MUCKROCK_PASSWORD |
MuckRock / Squarelet account password | None (Anonymous mode) |
MCP_TRANSPORT |
Server transport mechanism (stdio or http) |
stdio |
MCP_HOST |
Host interface to bind HTTP transport | 127.0.0.1 |
MCP_PORT / PORT |
Port number to bind HTTP transport | 8000 |
MCP_API_TOKEN |
Bearer token required on HTTP ingress (Authorization: Bearer <token>); unset means open |
None (open) |
MCP_DEBUG |
Enable verbose diagnostic logging (1, true, on) |
false |
MCP_CACHE_TTL_MS |
Prompt cache TTL hint in milliseconds | 300000 (5 minutes) |
MCP_CACHE_SCOPE |
Prompt cache scope hint (public/private) |
public |
MCP_MAX_MESSAGE_SIZE |
Max ingress JSON-RPC bytes | 4194304 (4MB) |
MCP_CORS_ORIGINS |
Comma-separated CORS allowlist; default 127.0.0.1-only (no *) |
`` (loopback only) |
Running the Server
Command-Line Interface (muckrock-mcp)
The server provides a unified CLI with full argument parsing:
# Run over stdio (default)
muckrock-mcp
# Run over Streamable HTTP on custom port
muckrock-mcp --transport http --host 0.0.0.0 --port 8080
# Authenticate via env vars
MUCKROCK_USERNAME="my_user" MUCKROCK_PASSWORD="my_secret" muckrock-mcp
# Enable verbose debug logging
muckrock-mcp --debug
# Show version
muckrock-mcp --version
Health Check
When running over HTTP, liveness (upstream reachability + rate-limiter state) is exposed without authentication:
# GET /healthz -> {"status": "ok", ...}
curl http://127.0.0.1:8000/healthz
Direct Python Invocation
python -m muckrock_mcp.cli --transport stdio
Client Integration
OpenCode
Add to your OpenCode configuration file (opencode.json):
{
"$schema": "https://opencode.ai/config.json",
"mcp": {
"muckrock": {
"type": "local",
"command": ["muckrock-mcp", "--transport", "stdio"],
"environment": {
"MUCKROCK_USERNAME": "your_muckrock_username",
"MUCKROCK_PASSWORD": "your_muckrock_password"
}
}
}
}
Goose CLI
goose configure
# Select command-line extension and enter:
muckrock-mcp --transport stdio
Or add a stdio command extension directly to ~/.config/goose/config.yaml:
# ~/.config/goose/config.yaml
extensions:
muckrock:
enabled: true
type: stdio
# stdio command extension: launches muckrock-mcp with stdio transport
cmd: muckrock-mcp
args:
- --transport
- stdio
envs:
MUCKROCK_USERNAME: your_muckrock_username
MUCKROCK_PASSWORD: your_muckrock_password
Hermes Agent
Add to your Hermes Agent configuration file (~/.hermes/config.yaml):
# ~/.hermes/config.yaml
mcp_servers:
muckrock:
command: muckrock-mcp
args:
- --transport
- stdio
env:
MUCKROCK_USERNAME: your_muckrock_username
MUCKROCK_PASSWORD: your_muckrock_password
Claude Code
Add via the Claude Code CLI:
claude mcp add muckrock \
--env MUCKROCK_USERNAME=your_muckrock_username \
--env MUCKROCK_PASSWORD=your_muckrock_password \
-- muckrock-mcp --transport stdio
Or add to your project's .mcp.json:
{
"mcpServers": {
"muckrock": {
"command": "muckrock-mcp",
"args": ["--transport", "stdio"],
"env": {
"MUCKROCK_USERNAME": "your_muckrock_username",
"MUCKROCK_PASSWORD": "your_muckrock_password"
}
}
}
}
Claude Desktop
Add to your Claude Desktop configuration file (claude_desktop_config.json):
{
"mcpServers": {
"muckrock": {
"command": "muckrock-mcp",
"args": ["--transport", "stdio"],
"env": {
"MUCKROCK_USERNAME": "your_muckrock_username",
"MUCKROCK_PASSWORD": "your_muckrock_password"
}
}
}
}
Codex
Add to your Codex configuration file (~/.codex/config.toml):
[mcp_servers.muckrock]
command = "muckrock-mcp"
args = ["--transport", "stdio"]
[mcp_servers.muckrock.env]
MUCKROCK_USERNAME = "your_muckrock_username"
MUCKROCK_PASSWORD = "your_muckrock_password"
Tool Catalog
The server exposes eight idempotent, read-only tools:
| Tool Name | Parameters | Description |
|---|---|---|
search_requests |
query (str, optional), agency (int/str, optional), jurisdiction (int/str, optional), status (str, optional), has_files (bool, optional), page (int, default 1) |
Search public records requests across MuckRock by keyword, agency, jurisdiction, or status. |
get_request_docket |
request_id (int, required) |
Retrieve top-level docket data, filing dates, jurisdiction, and status for a FOIA request. |
get_request_thread |
request_id (int, required) |
Retrieve chronological correspondence, communications, sender directions, and attached files. |
search_agencies |
query (str, optional), jurisdiction (int/str, optional), page (int, default 1) |
Locate government agencies, unique identifiers, and jurisdictional mappings. |
get_agency_profile |
agency_id (int, required) |
Retrieve agency details, contact email, portal existence, and historical disposition statistics. |
search_documents |
query (str, required), page (int, default 1), per_page (int, default 10) |
Query public documents on DocumentCloud matching keywords or queries. |
get_document_text |
document_id (str/int, required), page_number (int, optional), max_chars (int, default 20000) |
Retrieve full or page-specific extracted plain text from DocumentCloud, capped with truncation hints. |
get_document_entities |
document_id (str/int, required) |
Retrieve categorized entities (people, organizations, locations) identified in DocumentCloud. |
Resource Templates
The server provides six RFC 6570 Resource Templates with URI resolvers:
| URI Template | MIME Type | Resolver Description |
|---|---|---|
muckrock://requests/{request_id} |
application/json |
Request metadata, dates, status, and agency info. |
muckrock://requests/{request_id}/thread |
application/json |
Complete communication thread and correspondence history. |
muckrock://agencies/{agency_id} |
application/json |
Agency details, contacts, and statistical disposition metrics. |
doccloud://documents/{document_id} |
application/json |
DocumentCloud document metadata, title, pages, and source. |
doccloud://documents/{document_id}/pages/{page_number}/text |
text/plain |
Extracted plain text of an individual document page. |
doccloud://documents/{document_id}/entities |
application/json |
Extracted entities (people, organizations, locations). |
Autocompletion (completion/complete)
The server implements dynamic argument autocompletion for:
status: Matches against valid MuckRock lifecycle states (submitted,processing,done,rejected,appealed,no_docs, etc.).jurisdiction: Matches against standard Federal, State, and Territorial jurisdictions.agency: Queries MuckRock dynamically and formats results as"{name} (ID: {id})".
Testing & Verification
The test harness provides comprehensive, 100% offline verification across unit, integration, end-to-end tiers, and challenger suites:
# Run the complete test suite
uv run pytest tests/
# Run specific tiers
uv run pytest tests/unit/ # Unit tests (protocol, auth, ratelimit, tools, etc.)
uv run pytest tests/integration/ # Multi-turn transport integration tests
uv run pytest tests/e2e/ # End-to-end multi-tier test suites
uv run pytest tests/e2e/test_tier5_adversarial.py # Tier 5 Adversarial hardening tests
Test Suite Structure
| Tier | Focus |
|---|---|
| Unit | Serialization, models, transports, auth TTL, sliding buckets, tools, lifecycle, auth edges |
| Integration | Stdio & Streamable HTTP multi-turn protocol conversations |
| Tier 1 (Features) | Happy-path nominal coverage (>=5 tests per feature F01–F22) |
| Tier 2 (Boundaries) | Edge cases, limits, and boundary error conditions |
| Tier 3 (Combinations) | Pairwise cross-subsystem interactions |
| Tier 4 (Scenarios) | 6 Real-world investigative user journeys |
| Tier 5 (Adversarial) | Malformed payloads, 4MB overflow, session forgery, path escape, egress caps, burst recovery |
| Challengers | Stress, tools/resources cross-checks |
Metadata
Release files for muckrock-mcp 0.4.1b0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| muckrock_mcp-0.4.1b0.tar.gz | 621.6 kB | With provenance | Details for muckrock_mcp-0.4.1b0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| muckrock_mcp-0.4.1b0-py3-none-any.whl | Python 3 | none | any | Details for muckrock_mcp-0.4.1b0-py3-none-any.whl |
Total release size: 713.2 kB
Release files / muckrock_mcp-0.4.1b0.tar.gz
| Download URL | muckrock_mcp-0.4.1b0.tar.gz |
|---|---|
| Size | 621.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
8bb57a8451ec0aa3b6f08842f4a6301990b9d83a638c2f259cc0f031847d400c
|
|
BLAKE2b-256 checksum How to use checksums |
53bee538188e962fdaca963556f17fe29e0b84554e5ddf95bc9f0addf624584b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logRelease files / muckrock_mcp-0.4.1b0-py3-none-any.whl
| Download URL | muckrock_mcp-0.4.1b0-py3-none-any.whl |
|---|---|
| Size | 91.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bb8e5f0ae6548cdec3647b141bfe24eeb685bd4d8045381293605d446c50b687
|
|
BLAKE2b-256 checksum How to use checksums |
79f0385e7afbf306ab09017e0db29a5f3e51ddde89b6a461077a0116f2a0181b
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logSecurity for muckrock-mcp 0.4.1b0
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
Full provenance
All 2 files in this release have been uploaded with provenance metadata.
Attestation for muckrock_mcp-0.4.1b0.tar.gz
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 9, 2026.
Transparency logA note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.