@cyanheads/congressgov-mcp-server

Access U.S. congressional data (bills, votes, members, committees) via MCP. STDIO or Streamable HTTP.

12 Tools • 6 Resources • 2 Prompts • 1 Skill

[![Version](https://img.shields.io/badge/Version-0.10.0-blue.svg?style=flat-square)](./CHANGELOG.md) [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![Docker](https://img.shields.io/badge/Docker-ghcr.io-2496ED?style=flat-square&logo=docker&logoColor=white)](https://github.com/users/cyanheads/packages/container/package/congressgov-mcp-server) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^2.4.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/) [![npm](https://img.shields.io/npm/v/@cyanheads/congressgov-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/congressgov-mcp-server) [![TypeScript](https://img.shields.io/badge/TypeScript-^7.0.2-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.4.4-blueviolet.svg?style=flat-square)](https://bun.sh/)
[![Install in Claude Desktop](https://img.shields.io/badge/Install_in-Claude_Desktop-D97757?style=for-the-badge&logo=anthropic&logoColor=white)](https://github.com/cyanheads/congressgov-mcp-server/releases/latest/download/congressgov-mcp-server.mcpb) [![Install in Cursor](https://cursor.com/deeplink/mcp-install-dark.svg)](https://cursor.com/en/install-mcp?name=congressgov-mcp-server&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsIkBjeWFuaGVhZHMvY29uZ3Jlc3Nnb3YtbWNwLXNlcnZlciJdLCJlbnYiOnsiQ09OR1JFU1NfQVBJX0tFWSI6InlvdXItYXBpLWtleSJ9fQ==) [![Install in VS Code](https://img.shields.io/badge/VS_Code-Install_Server-0098FF?style=for-the-badge&logo=visualstudiocode&logoColor=white)](https://vscode.dev/redirect?url=vscode:mcp/install?%7B%22name%22%3A%22congressgov-mcp-server%22%2C%22command%22%3A%22npx%22%2C%22args%22%3A%5B%22-y%22%2C%22%40cyanheads%2Fcongressgov-mcp-server%22%5D%2C%22env%22%3A%7B%22CONGRESS_API_KEY%22%3A%22your-api-key%22%7D%7D) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-67E8F9?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core)
**Public Hosted Server:** [https://congressgov.caseyjhand.com/mcp](https://congressgov.caseyjhand.com/mcp)
--- ## Overview US congressional data from Congress.gov and the Senate's official vote feed: bills, amendments, enacted laws, members, committees, roll call votes, nominations, CRS and committee reports, and the Congressional Record. Browse legislative activity, read bill and report text, and follow nominations through Senate confirmation. ### Tools | Tool | Description | |:---|:---| | `congressgov_bill_lookup` | Browse and retrieve bills: actions, sponsors, summaries, text, related bills | | `congressgov_amendment_lookup` | Browse and retrieve House and Senate amendments: sponsor, amended bill, actions, cosponsors, text | | `congressgov_enacted_laws` | Browse enacted public and private laws by congress | | `congressgov_member_lookup` | Find members by state, district, or congress, and retrieve their legislative portfolios | | `congressgov_committee_lookup` | Browse committees and their legislation, reports, and nominations | | `congressgov_roll_votes` | Retrieve House and Senate roll call votes and each member's position | | `congressgov_senate_nominations` | Browse presidential nominations and track Senate confirmation | | `congressgov_bill_summaries` | Browse recently updated CRS bill summaries | | `congressgov_crs_reports` | Browse and retrieve nonpartisan CRS policy reports | | `congressgov_committee_reports` | Browse and read committee reports accompanying legislation | | `congressgov_daily_record` | Browse and read the daily Congressional Record | | `congressgov_search_bills` | Keyword-search bill titles and CRS summaries via a local full-text mirror (opt-in) | ### Resources | Resource | Description | |:---|:---| | `congress://current` | Current congress number, session dates, chamber info | | `congress://bill-types` | Reference table of valid bill type codes | | `congress://member/{bioguideId}` | Member profile by bioguide ID | | `congress://bill/{congress}/{billType}/{billNumber}` | Bill detail by congress, type, and number | | `congress://committee/{committeeCode}` | Committee detail by committee code | | `skill://congressgov-guide/SKILL.md` | Legislative research guide: identifier sources, discovery boundaries, votes, and document evidence | Bill, member, and committee data is also reachable through `congressgov_bill_lookup`, `congressgov_member_lookup`, and `congressgov_committee_lookup` (operation `get`), since many MCP clients are tool-only and never surface resources. ### Prompts | Prompt | Description | |:---|:---| | `congressgov_bill_analysis` | Structured framework for analyzing a bill | | `congressgov_legislative_research` | Research framework for a policy area across Congress | ### Agent skill [`congressgov-guide`](./skills/congressgov-guide/SKILL.md) guides research across bills, laws, members, committees, votes, and floor debate. It covers identifier provenance, discovery limits, and version-specific document evidence. The guide ships with plugin installs, npm, MCPB, and Docker, and is served over MCP through the Skills extension and its `skill://` resource. ## Capability reference ### `congressgov_bill_lookup` tool - `list` browses one `congress`, narrowed by `billType` and a `fromDateTime`/`toDateTime` range on the record's update date, newest first unless `order: 'oldest'`; `get`, `actions`, `amendments`, `cosponsors`, `committees`, `subjects`, `summaries`, `text`, `titles`, `related`, and `content` need `congress` + `billType` + `billNumber` - `type` and `number` from a bill row are accepted as `billType` and `billNumber`; bill type codes are case-insensitive. Send one name per field, since canonical and alias keys together are rejected - `content` reads the text version at `textVersionIndex` (0 is the most recent) and returns one character window in `content.text` with `totalCharacters`, `truncated`, and `nextOffset` - `summaries` takes an optional `versionCode` (`00` introduced, `49` public law) and pages like `congressgov_bill_summaries`, using `characterOffset`/`characterLimit` - `fromDateTime`/`toDateTime` apply to `list` only; other operations are served with a notice naming them as ignored - An `actions` row that records exactly one roll call vote carries `rollVote`, the `congressgov_roll_votes` arguments for it (House votes from the 115th Congress, Senate from the 101st) --- ### `congressgov_amendment_lookup` tool - An amendment is `congress` + `amendmentType` (`hamdt`, `samdt`, or `suamdt`, in any letter case; `suamdt`, Senate unprinted amendments, exists only in the 97th and 98th Congresses) + `amendmentNumber` (a number or a digit string). Those identifiers come from `congressgov_bill_lookup` `amendments` rows, House `congressgov_roll_votes` rows (`amendmentType`/`amendmentNumber`), and `congressgov_member_lookup` `sponsored`/`cosponsored` rows, which carry the number and the type only in the row's `url` - `list` browses one `congress`, optionally one `amendmentType`, newest update first unless `order: 'oldest'`, with `fromDateTime`/`toDateTime` on the record's update date; it refuses `amendmentNumber` (`incompatible_identity`). Other operations are served with a notice naming a date bound as ignored - `get` returns the sponsor, purpose, description, the bill or amendment amended, and the latest action; `actions`, `cosponsors`, `amendments` (amendments to this amendment), and `text` page the sub-resources - An `actions` row that records exactly one roll call vote carries `rollVote`, the `congressgov_roll_votes` arguments for it - `text` lists the published text versions, each a Congressional Record page; `content` reads the HTML of the version at `textVersionIndex` as one `characterOffset`/`characterLimit` window. The page prints the amendment amid the surrounding floor proceedings, so it is often far longer than the amendment --- ### `congressgov_enacted_laws` tool - `list` browses one `congress`, optionally by `lawType` (`pub` or `priv`), the enactment filter `congressgov_bill_lookup` lacks; `get` needs `lawType` + `lawNumber`, taken from a list row's `laws[].number` (`118-90`) or its bare number (`90`) - `get` returns the origin bill record, with the law citation on its `laws[]` array --- ### `congressgov_member_lookup` tool - No name search: `list` filters by `stateCode` (plus `district`, `0` for at-large, which requires `stateCode`), `congress`, and `currentMember` - `get` returns the profile for a `bioguideId` (e.g. `P000197`); `sponsored` and `cosponsored` return that member's legislation --- ### `congressgov_committee_lookup` tool - `committeeCode` is a chamber prefix (`h`/`s`/`j`), letters, and a 2-digit suffix (e.g. `hsju00`); `get`, `bills`, `reports`, and the Senate-only `nominations` infer `chamber` from the prefix, and read it from the committee roster when the prefix's chamber doesn't hold the code (`jsat00` is a Senate committee); `chamber` is accepted in any letter case, as committee rows carry it (`Senate`) - A committee name in `committeeCode` resolves when exactly one committee matches, and otherwise returns candidate rows with a notice. With `chamber` set, only that chamber's committees are searched, so a name unique within one chamber (`judiciary` with `chamber: 'senate'`) resolves in one step, and a lone fuzzy match there comes back as a candidate row; without `chamber`, a lone fuzzy match resolves and the notice says it is approximate. The resolved committee is read in the chamber its roster row names. `list` with `filter` name-matches every committee in scope, and fuzzy hits carry `approximate: true` - `bills` is newest first by default (`order`), and its rows carry no titles, so chain `congressgov_bill_lookup` `get` per row --- ### `congressgov_roll_votes` tool - `chamber` is `house` (default, Congress.gov API) or `senate` (the Senate's LIS feed), in any letter case; every call needs `congress` + `session` (1 or 2), and `get`/`members` add `voteNumber`, which resets each session and is specific to one chamber - `members` filters by `bioguideId` (House only), `stateCode`, and `party`, all applied before paging: `pagination.count` counts the matches and `memberFilter.rosterCount` is the roster size. A Senate call with `bioguideId` fails `unsupported_filter`, whichever source answers, since the Senate feed identifies senators by its own IDs; filter senators by `stateCode` - `list` and `get` serve the call and name any of those filters as ignored - A congress's first year is session 1 and its second year session 2, but a second session can run into January 1–3 of the following year (the 116th Congress's second session voted on 2021-01-01) - `list` is newest first unless `order: 'oldest'`; `get` returns the question, result, tallies, and party breakdown, and `members` returns each member's position - House votes are covered from the 115th Congress (2017) and Senate votes from the 101st (1989), and Senate rows pair the feed's year-less `voteDate` with a derived `voteDateIso` (rows Voteview serves carry `voteDateIso` alone) - A House `get` or `members` call below the 115th Congress fails `not_found` before any request, naming that coverage and pointing at the Clerk's vote XML in a bill action's `recordedVotes` - When the Senate LIS feed can't answer a `list`, `get`, or `members` call (senate.gov refusing automated clients with HTTP 403 or its firewall page, another 4xx, a 5xx or timeout after retries, an empty body), [Voteview](https://voteview.com/)'s per-congress files answer instead, and the response carries `source` (name, url, `latestVote`, and Voteview's requested citation). Voteview's President's positions are dropped, and a vote after `source.latestVote` is `not_found` naming that vote. The feed's own not-found page, a cancelled call, a spent call budget, and congresses before the 101st never fall back. When the feed answers, the output is unchanged and Voteview is not contacted. Voteview's files are cached for 1 hour. House votes are unaffected --- ### `congressgov_senate_nominations` tool - `list` browses one `congress`, newest update first; `fromDateTime`/`toDateTime` filter on the record's update date, not its received or action date. `get`, `actions`, `committees`, `hearings`, and `nominees` need a `nominationNumber` (`1000` for PN1000; `PN730-2`, `pn730-2`, and `730-2` are equivalent), and `nominees` also needs an `ordinal` from the `nominees` array `get` returns - Multi-part nominations (e.g. PN851) keep their activity on partitioned children (`851-1`, `851-2`, …); an empty sub-resource call on the bare number returns a notice pointing there - An action whose text cites its own Senate record vote (`Record Vote Number: 225.`) carries `rollVote` — `{ chamber: 'senate', congress, session, voteNumber }`, the `congressgov_roll_votes` arguments for that vote. Every citing `actions` row gets one, cloture votes included, and so does `latestAction` on `list` rows and `get`; votes before the 101st Congress get none --- ### `congressgov_bill_summaries` tool - Optional `congress` and `billType` (which requires `congress`); `fromDateTime`/`toDateTime` filter on the summary's update time, not the bill's action date, and cover the last 7 days when both are omitted - A page returns `limit` rows, and `pagination.nextOffset` resumes; a summary over 25,000 characters arrives windowed with `textTotalCharacters`, `textTruncated`, and `textNextOffset` - For one bill's summaries, or to read a windowed summary to the end, use `congressgov_bill_lookup` `summaries` --- ### `congressgov_crs_reports` tool - `list` browses the catalog, newest update first; `fromDateTime`/`toDateTime` filter on the date Congress.gov last updated a report, not its publication date. `get` and `content` take a `reportNumber`: letters then digits (`R40097`, `RL33612`, `IF12345`) or, for older reports, two digit groups joined by a hyphen (`96-548`), in any case - `id`, `reportId`, and `report_id` are accepted as `reportNumber`; send only one identifier key - `get` returns authors, topics, summary, and download formats - `content` reads the report's HTML version as plain text in a `characterOffset`/`characterLimit` window (script, style, and comments dropped; tables as tab-separated rows). A PDF-only report fails `format_unavailable`, naming its PDF URL --- ### `congressgov_committee_reports` tool - `list` browses one `congress`, optionally by `reportType` (`hrpt` House, `srpt` Senate, `erpt` Executive). `fromDateTime`/`toDateTime` filter on the date Congress.gov last updated a report record, and `conference` selects conference reports (`true`) or the others (`false`); `get`, `text`, and `content` need `reportType` + `reportNumber` - `get` returns the citation, title, committees, and associated bill; `text` lists `{type, url}` format links, and `content` reads the report as one character window - A multi-part report is filed as one record per part: `part` (default 1) selects one on `get`, `text`, and `content`, and `parts` lists every part number when there is more than one. A part the report lacks fails `not_found`, naming the parts it has --- ### `congressgov_daily_record` tool - Navigation runs `list` (volumes) → `issues` (by `volumeNumber`) → `articles` (by `volumeNumber` + `issueNumber`) - `content` reads the article at `articleIndex` (0-based across the issue) as one character window; Record articles publish Formatted Text and PDF only, so `format: 'xml'` fails as `format_unavailable` --- ### `congressgov_search_bills` tool - `query` keywords (AND-combined) over bill titles and CRS summaries, narrowed by `congress`, `billType`, and `originChamber`; `limit` 1–100, `offset` pagination - BM25-ranked rows carry `billId` plus `congress`/`billType`/`billNumber` for `congressgov_bill_lookup`, and a `summaryPreview`; policy area and full bill text are not indexed - Every response names the searchable congresses in `mirroredCongresses`; a `congress` outside them returns an empty result with a notice pointing at `congressgov_bill_lookup` - Listed only when `CONGRESS_MIRROR_ENABLED=true`; until `bun run mirror:init` builds the index, it returns an empty result with a notice --- ### `congress://current` resource - Current congress number, session dates, and chamber info, the baseline for other queries - Cached publicly for 1 hour --- ### `congress://bill-types` resource - The 8 bill type codes (`hr`, `s`, `hjres`, `sjres`, `hconres`, `sconres`, `hres`, `sres`) with description, chamber, and an example citation - Static, with no upstream call; cached publicly for 24 hours --- ### `congress://member/{bioguideId}` resource - `bioguideId` is one uppercase letter and 6 digits (e.g. `P000197`) - Returns the member profile: name, state, party, terms, leadership, office, legislation counts --- ### `congress://bill/{congress}/{billType}/{billNumber}` resource - `congress` and `billNumber` are positive integers; `billType` is one of the 8 bill type codes - Returns bill detail: sponsor, status, policy area, committees, latest action --- ### `congress://committee/{committeeCode}` resource - `committeeCode` is `h`/`s`/`j` followed by 3–8 lowercase alphanumeric characters (e.g. `hsju00`); chamber comes from the first letter - Returns committee detail: name, chamber, subcommittees, history, legislation counts --- ### `congressgov_bill_analysis` prompt - Arguments: `congress`, `billType`, and `billNumber`, all required - Returns one user message framing a seven-part analysis, from summary to outlook, with the tools to call for each part --- ### `congressgov_legislative_research` prompt - Arguments: `topic` required; `congress` optional, defaulting to the current congress - With `CONGRESS_MIRROR_ENABLED`, the plan opens with `congressgov_search_bills`; without it, the prompt asks for a seed bill, member, committee, or CRS report ID, since no other tool takes a topic ## Features Built on [`@cyanheads/mcp-ts-core`](https://github.com/cyanheads/mcp-ts-core): stdio and Streamable HTTP transports, pluggable auth (`none` / `jwt` / `oauth`), swappable storage (`in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`), structured logging with optional OpenTelemetry tracing. Congress.gov-specific: - Type-safe client for the Congress.gov REST API v3, plus a client for the Senate's LIS XML feed that backs Senate roll votes, with Voteview's published vote files as the fallback when that feed can't be read - The Congress.gov API has no keyword search, so tools browse by congress, type, date range, chamber, state, and district; list operations page with `limit` 1–250 (default 20) and `offset` - `content` on bill text, amendment text, committee reports, Record articles, and CRS reports reads a document in a `characterOffset`/`characterLimit` window (1–100,000 characters, default 25,000), fetched only from `www.congress.gov` under a 25 MB ceiling; bill text, committee reports, and Record articles take `format` `text` (default) or `xml`, a CRS report is always read as its HTML version, and an amendment is read as the HTML of its Congressional Record page. A document longer than one window (up to 16 MB) is downloaded once and its later windows are served from a 10-minute in-memory cache - Each tool call or resource read gets 50 seconds for all of its upstream requests, retries and backoff included, whether it makes one request or chains several (a `content` call's metadata lookup and document fetch, a committee name resolved against the committee roster, one chamber's when `chamber` is set). A stalled upstream ends the call before the 60-second default timeout of MCP clients: `upstream_timeout` from Congress.gov, the retryable `upstream_error` from the Senate LIS feed and the Voteview fallback - Optional API key from [api.data.gov](https://api.data.gov/signup/): `DEMO_KEY` allows 30 requests/hour and 50/day per IP, registered keys 5,000/hour - Opt-in local SQLite FTS5 mirror (`CONGRESS_MIRROR_ENABLED`) adds keyword search over bill titles and CRS summaries Agent-friendly output: - Every list response carries `effectiveQuery` and `totalCount`, with a `notice` when nothing matched - `list` refuses an identity field (member `bioguideId`, law `lawNumber`, committee `committeeCode` with a committee name pointed at `filter`, nomination `nominationNumber`, committee report `part`, and `billNumber`, `voteNumber`, `reportNumber`, `volumeNumber`) with an `incompatible_identity` error that names the operation to use instead - Inputs an operation doesn't use are served with a notice naming them as ignored; a member's sponsored and cosponsored legislation and a committee's bills, reports, and nominations span every congress - `content` returns `offset`, `truncated`, and `nextOffset`, so walking a multi-megabyte bill never skips or repeats a character - Typed failures (`document_unavailable`, `format_unavailable`, `document_fetch_failed`, `document_too_large`, `offset_past_end`, `unsupported_filter`, `not_found`, `rate_limited`, `invalid_request`, `api_key_rejected`, `upstream_error`, `upstream_timeout`), each with a recovery hint ## Getting started Connect to the hosted instance: ```sh claude mcp add --transport http congressgov-mcp-server https://congressgov.caseyjhand.com/mcp ``` Or run it locally: ```sh claude mcp add congressgov-mcp-server -- npx -y @cyanheads/congressgov-mcp-server ``` For other clients, add this to the MCP config file: ```json { "mcpServers": { "congressgov-mcp-server": { "command": "npx", "args": ["-y", "@cyanheads/congressgov-mcp-server"] } } } ``` Optional: set `CONGRESS_API_KEY` to a free [api.data.gov key](https://api.data.gov/signup/) for 5,000 requests/hour instead of the demo key's 30/hour and 50/day per IP. For Docker or a source build, see [Running the server](#running-the-server). ## Configuration | Variable | Description | Default | |:---|:---|:---| | `CONGRESS_API_KEY` | API key from [api.data.gov](https://api.data.gov/signup/). | `DEMO_KEY` | | `CONGRESS_API_BASE_URL` | Congress.gov API base URL. | `https://api.congress.gov/v3` | | `CONGRESS_MIRROR_ENABLED` | Enable the local bill search mirror and the `congressgov_search_bills` tool. | `false` | | `CONGRESS_MIRROR_PATH` | Filesystem path to the SQLite mirror index. | `.mirror/bills.sqlite3` | | `CONGRESS_MIRROR_REFRESH_CRON` | Cron schedule for the in-process mirror refresh (HTTP transport only). Unset means running `mirror:refresh` yourself. | — | | `CONGRESS_MIRROR_CONGRESSES` | Comma-separated congress numbers to mirror (e.g. `118,119`). | current congress + 1 prior | | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http`. | `stdio` | | `MCP_HTTP_PORT` | HTTP server port. | `3010` | | `MCP_SESSION_MODE` | HTTP session mode: `stateless`, `stateful`, or `auto`; `auto` resolves to `stateful`. | `stateless` | | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth`. | `none` | | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `notice`, `warning`, `error`, etc.). | `info` | | `LOGS_DIR` | Directory for log files (Node.js only). | `/logs` | | `OTEL_ENABLED` | Enable [OpenTelemetry](https://github.com/cyanheads/mcp-ts-core/tree/main/docs/telemetry). | `false` | | `OTEL_EXPORTER_OTLP_ENDPOINT` | Base OTLP endpoint for traces and metrics. | — | | `OTEL_EXPORTER_OTLP_LOGS_ENDPOINT` | Explicit OTLP logs endpoint; the base endpoint does not enable log export. | — | | `LOG_TOOL_FAILURE_PAYLOADS` | Log failed-call input and output with key-based redaction; secrets inside free-form values are not redacted. | `false` | | `LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES` | Maximum bytes per failed-call input or output record. | `16384` | See [`.env.example`](./.env.example) for the full list of optional overrides. ## Running the server Requires [Bun v1.4.0](https://bun.sh/) or higher (or Node.js v24+). ```sh git clone https://github.com/cyanheads/congressgov-mcp-server.git cd congressgov-mcp-server && bun install cp .env.example .env # Optionally set CONGRESS_API_KEY and the mirror variables in .env. ``` ### Local development - **Build and run the production version:** ```sh bun run rebuild bun run start:http # or start:stdio ``` - **Build the bill search mirror from this checkout** (with `CONGRESS_MIRROR_ENABLED=true`): ```sh bun run mirror:init # full build bun run mirror:refresh # incremental refresh bun run mirror:verify # readiness and integrity check ``` The mirror CLI requires this checkout or the Docker image's path mapping; an npm installation cannot run these scripts. - **Run checks and tests:** ```sh bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec ``` ### Docker ```sh docker run --rm -e CONGRESS_API_KEY=your-api-key -p 3010:3010 ghcr.io/cyanheads/congressgov-mcp-server:latest ``` The image defaults to HTTP transport, stateless session mode, and logs to `/var/log/congressgov-mcp-server`. For stdio, use `docker run -i --rm -e MCP_TRANSPORT_TYPE=stdio ghcr.io/cyanheads/congressgov-mcp-server:latest`. To build locally, run `docker build -t congressgov-mcp-server .`; OpenTelemetry peer dependencies are installed by default, with `--build-arg OTEL_ENABLED=false` to omit them. The image carries the mirror scripts, so `docker exec bun run mirror:init` builds the index in place. ## Project structure | Directory | Purpose | |:---|:---| | `src/index.ts` | `createApp()` entry point: registers tools, resources, and prompts, inits services, and schedules the mirror refresh. | | `skills/` | Published legislative research guide for agents calling the server. | | `src/config/` | Server-specific environment variable parsing and validation with Zod. | | `src/mcp-server/tools/` | Tool definitions (`definitions/*.tool.ts`) plus shared input, formatting, and summary-window helpers. | | `src/mcp-server/resources/definitions/` | Resource definitions (`*.resource.ts`). | | `src/mcp-server/prompts/definitions/` | Prompt definitions (`*.prompt.ts`). | | `src/services/call-budget.ts` | The 50-second budget every tool call's upstream requests share. | | `src/services/congress-api/` | Congress.gov API client: auth, pagination, rate limiting. | | `src/services/congress-documents/` | Bounded document-text fetch: host allowlist, byte ceiling, character window, and a 10-minute cache of document bodies. | | `src/services/congress-mirror/` | Local SQLite FTS5 bill-search mirror: ingest, normalize, schema. | | `src/services/senate-lis/` | Senate LIS XML client for Senate roll call votes, with a Voteview CSV fallback. | | `src/utils/` | HTML/XML character-reference decoding and congress-number arithmetic. | | `scripts/` | Build, devcheck, and release tooling, plus the `mirror:init` / `mirror:refresh` / `mirror:verify` CLIs. | | `tests/` | Unit and integration tests, mirroring the `src/` structure. | ## Development guide See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version: - Handlers throw, framework catches — no `try/catch` in tool logic - Use `ctx.log` for request-scoped logging, `ctx.state` for tenant-scoped storage - All tools are read-only, with `readOnlyHint: true` and `idempotentHint: true` annotations - Wrap external API calls: validate raw → normalize to a domain type → return the output schema; never fabricate missing fields ## Contributing Issues are welcome. Run checks and tests before submitting: ```sh bun run devcheck bun run test ``` ## License Apache-2.0 — see [LICENSE](./LICENSE) for details.