statefinance-mcp
Click on "Deploy 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., "@statefinance-mcp top donors for Tallchief for Oklahoma"
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.
statefinance-mcp
State-level campaign finance, as an MCP server. Donors, expenditures, committees, and candidates from state disclosure systems — the data the FEC doesn't cover and that no MCP server currently exposes — through one common schema. Oklahoma first; more states via adapters.
Why this exists
Federal campaign finance (FEC) is wrapped to death. State campaign finance — where most money-in-politics questions actually live — is fragmented across ~46 portals with no unified API, and the one historical normalizer (FollowTheMoney) is winding down. The hard, valuable part isn't the MCP plumbing; it's the normalization layer that maps a messy state portal into a clean, sourced schema. That's the product.
Related MCP server: @cyanheads/openfec-mcp-server
Quick start (offline, no keys)
The v1 demo runs on a committed synthetic Oklahoma extract (see the data note below), so a fresh checkout works with no network and no keys.
uv sync
uv run statefinance ingest ok # load the OK 2024 sample into the store
uv run statefinance top-donors "Tallchief for Oklahoma"
# Redbud Ranch LLC: $10,000
# Acme Energy, LLC: $5,000 (spelling variants combined)
# Patterson, John Q.: $1,500
uv run statefinance summary cycle 2024
# raised $30,650, spent $76,200, top recipient: Tallchief for Oklahoma
uv run statefinance donor-history "Patterson, John Q." # all of a donor's giving
uv run statefinance serve # run the MCP server (stdio)Use it as an MCP server
{
"mcpServers": {
"statefinance": {
"command": "uv",
"args": ["run", "statefinance", "serve"],
"cwd": "/path/to/statefinance-mcp"
}
}
}Tools
Tool | What it does |
| Filter contributions by donor, recipient, candidate, amount/date range, state, cycle. Returns matches + full-match count and total. |
| Filter expenditures by committee, payee, purpose, date, state, cycle. |
| A committee plus its contribution and expenditure totals. |
| A candidate (by id or name), their committees, and money raised/spent. |
| Top donors to a committee, grouped by a light-normalized donor key. |
| Every contribution by a donor, with a per-recipient breakdown. |
| Aggregate totals for a |
Every result carries each record's source_url + as_of — this is accountability
data, so a wrong number is worse than no number.
Design: one schema, many adapters
state portal ─▶ StateAdapter.fetch() ─▶ raw snapshot (data/raw/<state>/)
StateAdapter.normalize() ─▶ common schema ─▶ DuckDB store
│
MCP server (reads the store only)Ingestion and serving are decoupled. Ingest populates a local normalized store; the MCP server only reads it, so tool calls are fast and portals stay un-hammered.
Adding a state is implement
fetch+normalize, ship a fixture, register — core, store, and tools untouched. See docs/adding-a-state.md (enforced by a test that adds a second state through the adapter contract alone).Donor normalization is light and honest (trim/case/whitespace, org suffixes). Fuzzy entity resolution is explicitly deferred — see docs/donor-normalization.md.
⚠️ Data note
The committed Oklahoma sample is synthetic (fictional committees, candidates,
donors — clearly labeled), so the offline demo never asserts fabricated facts
about real people. Acquisition of real Oklahoma data and the ToS posture are
documented in docs/sources/oklahoma.md; live
ingestion is intentionally gated — enable ingest ok --live only after
confirming the portal's current export method and terms of service.
Development
uv run pytest # offline suite (committed synthetic extract)
uv run ruff check . # lintIf uv run statefinance ever reports No module named 'statefinance' (a known
editable-install quirk on some setups, e.g. paths with spaces), run with
PYTHONPATH=src uv run statefinance ....
See SPEC.md and BUILD_PLAN.md for the design.
License
MIT.
Available Tools
9 toolsdonor_historyC
Every contribution by a donor, with totals and a per-recipient breakdown.
Inputs: name (required), cycle, state.
get_candidateC
A candidate (by id or name), their committees, and money raised/spent.
Inputs: candidate_id (required).
get_committeeC
A committee plus its contribution and expenditure totals.
Inputs: committee_id (required).
pingA
Health check. Returns 'pong' if the server is alive.
Inputs: no named parameters. Returns: result
search_contributionsA
Search campaign contributions by donor, recipient committee, candidate, amount range, date range, state, or cycle. Returns matching contributions (each with source_url + as_of) plus the full-match count and total.
Inputs: donor, recipient, candidate, min_amount, +6 more.
search_expendituresA
Search expenditures by committee, payee, purpose, date range, state, or cycle. Returns matching expenditures (with provenance) plus count and total.
Inputs: committee, payee, purpose, date_from, +4 more.
store_statsB
Row counts for the normalized store (committees, candidates, ...).
Inputs: no named parameters.
summaryC
Aggregate totals for a committee, candidate, or cycle.
Inputs: scope_type (required), scope_id (required).
top_donorsB
The top donors to a recipient committee, ranked by total given (spelling variants combined via light normalization).
Inputs: recipient (required), cycle, n.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 9 tools
Each tool serves a distinct purpose: donor history, candidate info, committee info, health check, contribution search, expenditure search, store stats, summary aggregates, and top donors. There is no ambiguity between them.
Tool names mix verb-noun patterns (get_candidate, search_contributions) with noun phrases (donor_history, summary, ping) and inconsistent styles. This lack of a consistent pattern reduces predictability.
With 9 tools, the set is well-scoped for a state finance data server. It covers essential queries without being overwhelming or too sparse.
The tools cover core workflows: lookup candidates, committees, donors, contributions, expenditures, and summaries. Minor gaps exist (e.g., no listing of all candidates or committees), but the surface is largely complete for a read-oriented server.
Maintenance
Related MCP Connectors
Access FEC campaign finance data. Query data about candidates, money trails, and election filings.
OpenFEC MCP — Federal Election Commission campaign finance data
Access U.S. congressional data (bills, votes, members, committees) via MCP.
Search US lobbying disclosures and foreign-agent registrations via MCP.
Related MCP Servers
- AlicenseAqualityCmaintenanceQuery FEC campaign finance data — search candidates, track donations, analyze spending, and monitor Super PAC activity via the OpenFEC API.85 npm4MIT
- AlicenseNot gradedqualityBmaintenanceAccess FEC campaign finance data through MCP. Query data about candidates, money trails, and election filings. STDIO & Streamable HTTP.203 npm2Apache 2.0
- AlicenseBqualityFmaintenanceMCP server for Oklahoma campaign finance data, enabling queries on combined pre-primary and continuing contributions, candidate flags, and more via natural language.13MIT
- AlicenseNot gradedqualityCmaintenanceAccess Federal Election Commission campaign finance data through MCP tools. Enables querying OpenFEC data using natural language via ask_pipeworx or direct tool calls.295 npmMIT