statefinance-mcp
Enables querying state-level campaign finance data, including donors, expenditures, committees, and candidates, from multiple state disclosure systems via a unified MCP interface.
README
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.
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 |
|---|---|
search_contributions |
Filter contributions by donor, recipient, candidate, amount/date range, state, cycle. Returns matches + full-match count and total. |
search_expenditures |
Filter expenditures by committee, payee, purpose, date, state, cycle. |
get_committee |
A committee plus its contribution and expenditure totals. |
get_candidate |
A candidate (by id or name), their committees, and money raised/spent. |
top_donors |
Top donors to a committee, grouped by a light-normalized donor key. |
donor_history |
Every contribution by a donor, with a per-recipient breakdown. |
summary |
Aggregate totals for a committee, candidate, or cycle. |
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 . # lint
If 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.
Recommended Servers
playwright-mcp
A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.
Magic Component Platform (MCP)
An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.
Audiense Insights MCP Server
Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.
VeyraX MCP
Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.
graphlit-mcp-server
The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.
Kagi MCP Server
An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.
E2B
Using MCP to run code via e2b.
Neon Database
MCP server for interacting with Neon Management API and databases
Exa Search
A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.
Qdrant Server
This repository is an example of how to create a MCP server for Qdrant, a vector search engine.