Skip to main content
Glama
README.md
# racemodel

racemodel is a thin local bridge to the ElectionsMCP
hosted MCP server, a modelling service for U.S.
election races. Upstream, a user can resolve places
and candidates, build a scenario, set candidate
shares and turnout assumptions, apply a swing, and
then read the race back as tallies, flipped seats,
maps, charts and precinct-level roll-ups — including
separate handling for primary races.

The bridge holds no election data and computes no
forecast of its own. It serves the upstream server's
tool list locally — exactly as captured from the live
endpoint — and forwards each tool call upstream,
returning the result untouched. Scenario and
simulation state lives upstream, so writes such as
creating a scenario or setting shares are the
service's own modelling operations, forwarded like
every other call. `billing_status` is a read; there
is no payment, donation or purchase tool in the
surface.

## Tools

Geography, candidates and contests:

- `resolve_geography` — turn a place name or shorthand
  into a canonical geo id (`query`, required).
- `resolve_candidate` — find who holds or runs for a
  seat from a person's name (`name`, required).
- `search_candidates` — browse the candidate registry
  by filters without supplying a name.
- `lookup_address` — resolve an address (`address`,
  required) to its geography.
- `list_contests` — list contests for a geography
  (`geo_id`, required).
- `election_calendar` — the election calendar. No
  required arguments.

Scenarios and simulations:

- `create_scenario` — start a new scenario (`name`,
  required).
- `list_scenarios`, `get_scenario` (`scenario_id`),
  `commit_version` (`scenario_id`), and `diff_versions`
  (`scenario_id`, `version_a`, `version_b`) — manage
  and compare saved scenario versions.
- `set_performance` — set shares for a geography in a
  scenario (`scenario_id`, `geo_id`, `shares`).
- `target_precincts` — precinct targeting for a
  scenario (`scenario_id`, required).
- `set_candidates` — set the candidate list for a
  simulation (`simulation_id`, `level`, `candidates`).
- `set_turnout` — set turnout assumptions
  (`simulation_id`, required).
- `set_race_scope` — set a simulation's race scope
  (`simulation_id`, required).
- `list_simulations` and `get_simulation`
  (`simulation_id`, required) — enumerate and read
  simulations.
- `share_simulation` — share a simulation
  (`simulation_id`, required).

Views, swings and roll-ups:

- `get_view`, `render_view` and `set_view` — read,
  render or set a simulation's view (each takes
  `simulation_id`).
- `render_chart` — render a chart (`type`, required).
- `add_map_comment` (`simulation_id`, `body`) and
  `list_map_comments` (`simulation_id`) — annotate and
  read back a simulation's map.
- `apply_uniform_swing` — apply a uniform swing in
  percentage points (`simulation_id`, `level`,
  `swing_pp`).
- `apply_swing_to_query` — apply a swing scoped by a
  query (`simulation_id`, `state`, `filter`).
- `get_tally` — the tally at a level (`simulation_id`,
  `level`).
- `list_flipped` — seats that flipped at a level
  (`simulation_id`, `level`).
- `compare_simulations` — compare two simulations at a
  level (`simulation_id_a`, `simulation_id_b`,
  `level`).
- `roll_up_race` — roll a simulation up to the race
  level (`simulation_id`, required).

Turnout, returns and reference data:

- `get_early_voting_turnout` — early-voting turnout
  for a state (`state`, required).
- `get_county_voter_turnout_and_registration`,
  `get_state_historical_turnout`,
  `get_live_early_vote_returns` and
  `get_precinct_election_returns` — turnout,
  registration, historical and returns reads. No
  required arguments.
- `get_county_officials` and
  `get_county_economic_and_wealth_profile` — county
  reference profiles. No required arguments.
- `billing_status` and `connection_status` — account
  and connection reads. No required arguments.
- `get_campaign_onboarding`, `help` and `get_catalog`
  — onboarding, help and catalog reads. No required
  arguments.

Primaries:

- `list_primary_calendar` — primary calendar for a
  party (`party`, required).
- `set_primary_shares` — set primary shares
  (`simulation_id`, `party`).
- `roll_up_primary` — roll a primary up
  (`simulation_id`, `party`).
- `primary_batch_edit` — batch-edit a primary
  (`simulation_id`, `edits`).

Meta:

- `call_tool` — invoke another tool by name through
  the upstream (`name`, required).

## Configuration

- `ELECTIONSMCP_MCP_URL` (optional) — override the
  upstream endpoint. Defaults to
  `https://www.electionsmcp.com/mcp`. (The override
  hook is this rebuild's own labelled reconstruction.)
- `ELECTIONSMCP_API_KEY` (optional) — when set, its
  value is sent on every upstream request under the
  header name the registry record pins for this
  remote, `x-electionsmcp-key`. When it is unset, no
  auth header is sent. (This forwarding is a labelled
  reconstruction; the original capture was keyless.)

### Forwarding mechanics

On the first tool call the client opens a streamable
HTTP session with the upstream: a JSON-RPC
`initialize` request (protocolVersion `2025-03-26`,
clientInfo name `racemodel`), then a
`notifications/initialized` notification, then the
`tools/call` request carrying the caller's arguments
unchanged. When the upstream answers `initialize`
with an `Mcp-Session-Id` header, that value rides on
every later request of the session, which is reused
for the life of the process. Replies may arrive as
plain JSON or as server-sent events; both are read.

Successful upstream results pass through verbatim,
including any `isError` flag the upstream sets.
Failures — a JSON-RPC error, a non-2xx status, an
unreadable response, or a request that cannot be
completed — come back as an `isError` result whose
text starts with `racemodel error:` (this rendering
is this rebuild's own labelled reconstruction).

## CLI

Each tool is also a subcommand taking one JSON
argument string (omit it for tools that take none):

```bash
npx @reneturcios/racemodel resolve_geography '{"query":"CA-03"}'
npx @reneturcios/racemodel create_scenario '{"name":"Baseline 2026"}'
npx @reneturcios/racemodel get_catalog
npx @reneturcios/racemodel --version
```

Exit codes: `0` on success, `1` on an `isError`
result, `2` on a usage error.

## MCP server

```bash
npx -y @reneturcios/racemodel mcp
```

MCP client config:

```json
{
  "mcpServers": {
    "racemodel": {
      "command": "npx",
      "args": ["-y", "@reneturcios/racemodel", "mcp"]
    }
  }
}
```

## Docker

```bash
docker run -i ghcr.io/cloudcorprecords/racemodel
docker run --rm ghcr.io/cloudcorprecords/racemodel --version
```

The container's default command is `mcp`, so the first
form is a stdio MCP server; any other arguments reach
the CLI normally.

## Development

```bash
npm install
npm run build
npm test
npm run dev
```

The test suite runs entirely against a stubbed
JSON-RPC upstream — no live network calls.

## License

MIT — see `LICENSE`.