Congress Explorer MCP
Explore congressional hearings, witnesses, bills, members, recordings, and related documents from your AI assistant. Free and open source. No data API key required. Start with the setup guide.
A local FastMCP server for the current Congressional Document Archive and its
phase 2 entities. It reads one immutable release: every Parquet table, every
published column, and native nested lists/structs. DuckDB 2.0.0 and FastMCP
4.1.4 are pinned in this app's own uv.lock.
This is a replacement implementation. It has no pre-v6/v6 readers, separate committee catalog, legacy parser imports, review overlays, copied DuckDB database, or custom SQL language. The producer owns data assembly and SpicyRegs imports; the MCP queries the published result. It stays a separate Congress server.
Install a release (macOS or Linux)
Install uv, then:
uv tool install --python 3.12 \
'https://github.com/civictechdc/congressional-tech/releases/download/congress-mcp-v0.3.0/congressional_explorer_mcp-0.3.0-py3-none-any.whl'
congress-explorer-mcp prepare
congress-explorer-mcp check
If the command is not on your shell's PATH, run uv tool update-shell and open
a new terminal. check should report "status": "ok" and six tools. It launches
the installed server, connects over MCP and reads one document row. Python 3.12
is managed by uv. Windows is not supported yet.
Print configuration for your client:
congress-explorer-mcp config --client claude-desktop
congress-explorer-mcp config --client codex
congress-explorer-mcp config --client cursor
congress-explorer-mcp config --client claude-code
Choose one. These commands print settings with absolute paths and never edit the client's configuration. Merge the output into the appropriate settings, preserving other servers, then restart the client connection:
| Client | Configuration |
|---|---|
| Claude Desktop | Settings → Developer → Edit Config; merge into mcpServers |
| Codex | Add or replace [mcp_servers.congress] in ~/.codex/config.toml |
| Cursor | Merge into mcpServers in ~/.cursor/mcp.json |
| Claude Code | Save JSON to a file and pass --mcp-config /absolute/path/to/file.json when starting Claude Code |
The client starts serve for you. There is no port to open or terminal to keep
running. Ask: “Use Congress Explorer to find documents related to H.R. 1 in the
119th Congress.” Your assistant needs its own tools to fetch and read linked files.
Run prepare again and restart the client connection to refresh data. Software
updates are separate: install the wheel for the desired release and restart the
client. Preparation currently downloads about 65 MiB (October 30, 2026 snapshot);
the Python runtime and dependencies require additional disk space.
Queries run locally, but your AI client may send prompts and tool results to its model provider. Coverage varies by source and Congress. This is a bounded published collection, not a complete record of every congressional activity.
Run from source
From the repository root, on macOS or Linux:
uv sync --locked --project apps/explorer_mcp
uv run --locked --project apps/explorer_mcp congress-explorer-mcp prepare
uv run --locked --project apps/explorer_mcp congress-explorer-mcp serve
prepare defaults to the public archive pointer and stores metadata under
~/.cache/congress-explorer-mcp. serve uses stdio and makes no network requests.
Use --data-dir /absolute/path on each command to select another directory.
inspect prints the selected release and published table counts.
For a retained or test release:
uv run --locked --project apps/explorer_mcp congress-explorer-mcp prepare \
--publication /absolute/path/to/manifest.json \
--archive-origin https://congressional-data.spicygov.ai \
--data-dir /absolute/path/to/mcp-data
--publication accepts the current pointer or a direct release manifest, as a URL
or local path. Remote publications supply the body archive origin automatically.
For local inputs, omit --archive-origin if body URLs are unknown; the server
returns null saved URLs instead of guessing.
Configure an MCP client with absolute paths:
{
"mcpServers": {
"congress": {
"command": "/absolute/path/to/repo/apps/explorer_mcp/.venv/bin/congress-explorer-mcp",
"args": ["serve", "--data-dir", "/absolute/path/to/mcp-data"]
}
}
}
The independent environment is deliberate: producer packages still pin DuckDB below 2. The MCP imports none of those packages. Do not install it into the producer's environment.
Six tools
| Tool | Purpose |
|---|---|
get_dataset_info |
Available tables, coverage and published relationship/recipe IDs. Pass table for columns, identity and incoming/outgoing IDs; relationship for join meaning; recipe for bounded SQL examples. Also accepts table: "schema", "capture_scope", and "capture_profile". |
search_documents |
Title/filename search and exact Congress, kind, format, committee, meeting, bill/nomination, member or witness filters. |
get_document |
A document's summary, paged source links, or paged recoveries. |
search_entities |
Search the eight phase 2 entity tables with text and exact scalar filters. |
get_entity |
One entity by its published key, or its directly associated documents. |
query_sql |
One read-only DuckDB SELECT, including WITH and FROM-first SQL, with optional ? parameters. All published tables are available. |
Except for get_dataset_info, arguments are inside a query object. For example:
{"query":{"measure_key":"119-hr-1","limit":10}}
Document results include a reference containing release_id and link_key.
Pass those fields to get_document, adding section: "sources" or
section: "recoveries" when needed. Page cursors belong to the same release,
filters and page size. A running server stays on its selected release; run
prepare and restart it to adopt a newer one.
Entity keys:
| Table | Key |
|---|---|
meetings |
event_id |
witnesses |
witness_key (an appearance, not a deduplicated person) |
recordings |
video_key |
committees |
system_code |
members |
bioguide_id |
bills |
bill_id, e.g. 119-hr-1 |
nominations |
nomination_id, e.g. 119-pn-730-2 |
rosters |
Compound fields; use search_entities or SQL |
File URLs follow the recorded body hash and exact suffix. Summaries include
alternate formats; recordings include saved caption/transcript track URLs.
Recovery records retain kind and identity_note: a substitute or related file
is not necessarily the requested original. Fetch and read the files with the
agent's own tools. The MCP searches metadata and does not download, extract, OCR,
or search document bodies.
Hidden documents are excluded from ordinary searches and entity document pages. An exact document reference still resolves and exposes its review explanation. SQL exposes all rows, including hidden rows; add a filter when needed.
Query relationships directly
Start with get_dataset_info. For releases publishing native relationship
metadata, call it with {"table":"witnesses"}, then
{"relationship":"witness_submitted_documents"} or
{"recipe":"witness_transcripts"}. Use one selector at a time. The response
explains keys, result grain, parameters and missing required tables. Recipes do
not run until the caller submits their SQL to query_sql.
The producer owns this metadata in schema.json; the MCP has no separate join
registry. New definitions within the supported metadata version appear without
MCP changes. Older releases report that relationships were not published, and
unknown versions report unsupported metadata while table and SQL access remain
available. The complete producer reference is generated at
docs/release-relationships.md.
These examples use the defined phase 2 fields and require those tables in the selected release.
Documents about a bill, including their saved file identifiers:
SELECT filename, title, hex(link_key) AS link_key,
hex(saved_body) AS body, saved_suffix
FROM documents
WHERE list_contains(measure_keys, ?) AND NOT coalesce(hidden, false)
ORDER BY list_row
Hearings about a measure and their witnesses:
SELECT m.event_id, m.title, m.meeting_date, w.witness_key,
w.name, w.position, w.organization
FROM meetings m
LEFT JOIN witnesses w ON w.event_id = m.event_id
WHERE list_contains(m.subject_keys, ?)
ORDER BY m.event_id, w.panel_order, w.display_order, w.witness_key
Transcript documents and recordings for a meeting:
SELECT m.event_id, t.package_id, d.filename, hex(d.link_key) AS link_key
FROM meetings m, UNNEST(m.transcripts) AS u(t)
LEFT JOIN documents d ON d.list_row = t.list_row
WHERE m.event_id = ?
SELECT video_key, tracks
FROM recordings
WHERE list_contains(event_ids, ?)
Member terms, votes, amendments, CBO estimates, committee history and cross-system IDs remain native nested fields. SQL can unnest, filter, join or aggregate them. New Parquet tables appear automatically without an MCP release. Entity convenience tools cover the named phase 2 tables; use SQL for any additional tables.
list and detail gain a virtual, zero-based list_row. Detail shards use their
declared row offsets; sources and recoveries retain their physical list_row and
ordinal. documents joins list and detail without copying them. The raw links,
urls, URL state/variants and every other published table remain queryable.
SQL returns actual column types, bounded rows, and a truncated flag. BLOB values
use DuckDB's JSON escaping; use hex()/unhex() for portable keys. Fractional
numbers return as strings to preserve precision; integers, booleans, nulls, lists
and objects retain their JSON types. Alias duplicate columns explicitly.
Cost and lifetime
Preparation streams each changed file once, checking SHA-256 during the transfer.
It verifies Parquet footer counts without scanning all rows, then atomically
selects the completed release. An unchanged preparation reads the pointer/manifest
and checks local file metadata; it does not recopy, rehash or decode retained data.
A failed attempt keeps the prior selection and reuses completed downloads on retry.
The cache is app-owned; do not edit its immutable objects or selection.json.
Serving creates views over those exact files. There is no persistent DuckDB copy
or Python table cache. One query worker uses two DuckDB threads, a 512 MB engine
memory limit, 512 MB spill limit, and a default 10-second execution deadline.
serve --query-timeout can change the deadline up to 120 seconds. Up to four
queries can wait behind the active one. Cancellation or timeout stops and reaps
the worker and removes its spill directory; the next request starts a new worker.
The engine memory limit is not a process RSS limit.
Responses are capped at 128 KiB, browse pages at 100 rows, SQL results at 200 rows and 128 columns. A single oversized row produces an actionable error; select fewer fields or slice nested lists. Reads are limited to selected metadata files. Network access, writes, extension autoloading, persistent secrets and configuration changes are disabled in the query runtime.
Completed objects remain reusable and may still be in use by an older running server. Preparation therefore does not delete old objects. To retire a cache, stop every server using that data directory, remove that directory, then prepare again. The next preparation removes its interrupted download and selection files after acquiring the exclusive prepare lock. It keeps completed objects for live readers and rollback. If the server is killed, its worker detects parent death, interrupts DuckDB, closes the connection and removes its own spill directory. An unexpectedly killed worker produces a retryable error; the next call starts a new worker.
To roll back, prepare the previous immutable release manifest and restart the server. Existing servers remain on their selected release. If a cached object is corrupt, preparation refuses it and preserves the current selection; prepare into a fresh data directory to recover, then retire the old cache after its readers stop.
Check it
apps/explorer_mcp/.venv/bin/python scripts/dev.py test explorer-mcp -- -q
The root runner dispatches to the app's locked environment. Tests cover the canonical archive fixture, phase 2 entity shapes, raw SQL and relationships, shards, duplicates, missing versus empty data, failure/reuse, SQL restrictions, byte limits, subprocess cancellation and real stdio MCP calls. The dedicated CI workflow also builds the native phase 2 fixture and consumes its actual output over stdio in the independent MCP environment. See verification and production measurements.
Published data availability is separate from reader support. Absent tables are reported as unavailable; an empty table means zero published rows; nulls remain unknown. The server cannot supply phase 2 fields before producers publish them. The current model is defined by the data model and the phase 2 annex.
Metadata
Release files for congressional-explorer-mcp 0.3.0
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 |
|---|---|---|---|---|
| congressional_explorer_mcp-0.3.0.tar.gz | 42.3 kB | With provenance | Details for congressional_explorer_mcp-0.3.0.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| congressional_explorer_mcp-0.3.0-py3-none-any.whl | Python 3 | none | any | Details for congressional_explorer_mcp-0.3.0-py3-none-any.whl |
Total release size: 70.8 kB
Release files / congressional_explorer_mcp-0.3.0.tar.gz
| Download URL | congressional_explorer_mcp-0.3.0.tar.gz |
|---|---|
| Size | 42.3 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
0fa3d6edd789915e34740c69a644f49b36f884dddbb329fa6068b4efbe659072
|
|
BLAKE2b-256 checksum How to use checksums |
a57060408815f15847fcf67ba209f7d94b872f52bbbca3e13a72343ffa5b8482
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 30, 2026.
Transparency logRelease files / congressional_explorer_mcp-0.3.0-py3-none-any.whl
| Download URL | congressional_explorer_mcp-0.3.0-py3-none-any.whl |
|---|---|
| Size | 28.5 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
784e272488be27b3fd9c45e74a44192c2894153917809bb9e6990a110b7ccbea
|
|
BLAKE2b-256 checksum How to use checksums |
f35d6a6cd70719d494330f67f61f704148e66d88a36de3f91423fbe605f98a3c
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.16
|
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 Oct 30, 2026.
Transparency logSecurity for congressional-explorer-mcp 0.3.0
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 congressional_explorer_mcp-0.3.0.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 Oct 30, 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.