Skip to content

cstillick/statefinance-mcp

Repository files navigation

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.

About

State-level campaign finance as an MCP server, through one common schema. Donors, expenditures, committees, candidates — Oklahoma first, more states via adapters.

Resources

License

Stars

Watchers

Forks

Packages

 
 
 

Contributors

Languages