About usaspending-mcp-server
An MCP server for querying US federal awards, recipients, agencies, and spending data from USAspending.gov. It supports tracing awards through transactions, subawards, and funding accounts, as well as spending analysis by geography, category, and time.
Key features
- 01Search federal awards
- 02Inspect award transactions and subawards
- 03Trace awards to federal accounts
- 04Search recipient and agency profiles
- 05Analyze spending by place and category
- 06Compare spending over time
Use cases
- 01Research a federal award's funding path
- 02Review recipient or agency spending
- 03Compare spending across regions or periods
MCP support
Exposes USAspending.gov award, recipient, agency, account, and analytics operations through MCP tools. The README also describes a research guide accessible as a skill resource through the Skills extension.
Configuration
Remote servers need no local install. Pick your client, then copy the config or install in one click.
https://usaspending.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-usaspending https://usaspending.caseyjhand.com/mcp{"mcpServers":{"cyanheads-usaspending":{"type":"http","url":"https://usaspending.caseyjhand.com/mcp"}}}Open in Cursor{"servers":{"cyanheads-usaspending":{"type":"http","url":"https://usaspending.caseyjhand.com/mcp"}}}Open in VS Code{"mcpServers":{"cyanheads-usaspending":{"type":"http","url":"https://usaspending.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-usaspending":{"serverUrl":"https://usaspending.caseyjhand.com/mcp"}}}Run commands in a terminal; paste JSON into the client’s MCP config and restart. Servers that need sign-in will prompt on first connect.
Tools
18| Parameter | Type | Required | Description |
|---|---|---|---|
| sort | enum | No | Sort field: agency_name (alphabetical), budget_authority_amount, obligated_amount, or outlay_amountagency_name | budget_authority_amount | obligated_amount | outlay_amount |
| order | enum | No | Sort direction: asc or descasc | desc |
Details
From the project README@cyanheads/usaspending-mcp-server
Access US federal award, recipient, agency, and spending analytics data from USAspending.gov via MCP. STDIO or Streamable HTTP.
Public Hosted Server: https://usaspending.caseyjhand.com/mcp
Overview
Where federal money goes, from USAspending.gov: awards, recipients, agencies, and spending. Search and trace awards down to transactions, subawards, and funding accounts; profile recipients and agencies; and aggregate spending by geography, category, time, and disaster appropriation.
Tools
| Tool | Description |
|---|---|
usaspending_list_agencies | List top-tier federal agencies with codes, slugs, and budget totals labelled by reporting year and quarter |
usaspending_autocomplete_filters | Look up NAICS, PSC, CFDA, agency, or recipient codes from a free-text description |
usaspending_search_awards | Search awards by keyword, recipient, agency, award type, NAICS code, assistance listing, PSC, DEF code, location, or date range |
usaspending_get_award | Fetch one award's full record: amounts, recipient, agencies, codes, parent IDV, DEF-code funding |
usaspending_get_award_transactions | List the transactions (modifications, amendments) on an award |
usaspending_get_award_subawards | List the subcontracts or subgrants under a prime award |
usaspending_get_award_federal_accounts | List the Treasury federal accounts that funded an award, with the amount from each |
usaspending_get_idv_awards | List the child orders and sub-IDVs placed under an IDV |
usaspending_search_recipients | Search recipients by name, UEI, or DUNS |
usaspending_get_recipient | Fetch a recipient's profile: address, business types, parent, and award totals |
usaspending_get_agency | Fetch an agency's mission, latest-year budget totals, sub-agencies, and DEF codes |
usaspending_spending_by_geography | Aggregate spending by state, county, or congressional district |
usaspending_spending_by_category | Aggregate spending by NAICS, PSC, agency, CFDA program, or recipient |
usaspending_spending_over_time | Aggregate spending by fiscal year, quarter, or month |
usaspending_disaster_spending | Break down disaster and emergency supplemental spending by agency, program, recipient, or geography |
usaspending_search_federal_accounts | Search federal accounts by title keyword or agency identifier |
usaspending_get_federal_account | Fetch a federal account's budget totals and its Treasury Account Symbol components |
usaspending_get_federal_account_breakdown | Break a federal account's obligations down by program activity or object class |
Research guide
The usaspending-guide covers recipient research, award-to-account funding traces, and spending comparisons across periods and emergency appropriations. Plugin installs discover it in skills/; MCP clients can read skill://usaspending-guide/SKILL.md through the Skills extension. There are no domain-data resources.
Capability reference
usaspending_list_agencies tool
- Takes
sort(agency_name,budget_authority_amount,obligated_amount,outlay_amount) andorder; returns every agency unless an optional positivelimitis supplied. Withlimit,pagedefaults to 1; without it,pageis ignored.totalalways counts the full directory andhas_nextappears only when paginating - Rows carry
toptier_codeandagency_slug, both accepted byusaspending_get_agency, plusbudget_authority_amount,obligated_amount, andoutlay_amountfor the latest reported period.fiscal_yearandfiscal_quarterpreserve the provider's reporting labels when present
usaspending_autocomplete_filters tool
type(naics,psc,cfda,awarding_agency,recipient) plussearch_text;limit1–500, default 10- Rows carry
codeandname, anduei/dunsfor recipients; no match fails asno_match. Acfdacode goes inassistance_listingsand apsccode inpsc_codesonusaspending_search_awardsand the three spending analytics tools - Agency rows carry the matched agency's own
name,abbreviation, andtier(toptierorsubtier), its parent'stoptier_nameandtoptier_code, andsubtier_codewhen supplied. Passnametoagency_nameon award search or spending analytics to match that agency; passtoptier_namefor the whole department. Agency results combine top-tier then sub-tier matches under onelimit; office matches are excluded naicsmatches official NAICS title text: "software" resolves, "cybersecurity" does not, so search with the industry term a title would use
usaspending_search_awards tool
- Filters:
keyword,agency_name,recipient_name,naics_codes,assistance_listings(Assistance Listing numbers such as93.866, fromusaspending_autocomplete_filterstype: cfda),psc_codes(1–4 characters, where a prefix such asY1covers the codes under it; contracts and IDVs only),def_codes(Disaster Emergency Fund Codes such asZ; PSC and DEF codes are sent uppercased),time_period(YYYY-MM-DDfrom 2007-10-01 on; either end may be given alone), andlocation_filter(countrydefaults toUSA, and a territory code such asPRIbecomesUSAplus its state;stateas a postal or 2-digit FIPS code,FM/MH/PW/UMand armed-forcesAA/AE/APincluded, or under another country as that country's own 2-letter subdivision code such asONwithCAN;countyas 3 digits withstateor the 5-digit FIPS code alone; echoed asapplied_location);award_type_codesdefaults to contracts (A–D) and must stay in one group: IDVsIDV_A–IDV_E, grants02–05/F001/F002, direct payments06/10/F006/F007, loans07/08/F003/F004, or other assistance09/11/-1/F005/F008/F009/F010;limitup to 100 - Rows carry
generated_internal_idforusaspending_get_awardandagency_slugforusaspending_get_agency; loan rows carryloan_value,subsidy_cost, andissued_datein place of amounts and dates. There is no total, andpage_metadata.has_nextis true on any full page. The nestedfiltersobject takes the analytics tools'filterskeys, exceptrecipient_id, and wins per field over the flat ones. Failures areassistance_listings_type_mismatch(listings with a non-assistance group),psc_codes_type_mismatch(PSC codes with an assistance group),unsupported_sort,date_range_inverted,date_before_earliest, andinvalid_location(an unknown state, a 3-digit county without a state, a county under an armed-forces code (AA/AE/AP), a county or FIPS-number state under another country, or parts naming different states) sortdepends on the group: loans takeLoan Value(default),Subsidy Cost,Issued Date,Recipient Name, orAwarding Agency; the othersAward Amount(default),Total Outlays,Start Date,End Date(not IDVs),Recipient Name, orAwarding Agency. Page numbers stop at a 50,000-result offset (pagination_limit_exceeded); past it, page with thelast_record_sort_value+last_record_unique_idcursor, returned below a 10,000-result offset
usaspending_get_award tool
award_idis agenerated_unique_award_id, thegenerated_internal_idfrom search; an unknown ID fails asaward_not_found- Returns
category,total_obligation,total_outlays,total_account_obligation,total_account_outlay,base_exercised_options,subaward_count, NAICS / PSC codes,account_obligations_by_defc,account_outlays_by_defc, and, on loans,total_loan_valueandtotal_subsidy_cost, where supplied assistance_listingslists every Assistance Listing on an assistance award, in upstream order, asnumber,title, andobligation; anumbergoes inusaspending_search_awardsassistance_listingsrecipientcarriesuei,parent_uei,business_categories(category names, not thebusiness_typescodes ofusaspending_get_recipient), and a three-lettercountry_code,county_name,congressional_code, andzip5on itslocation.place_of_performancealso carries a country code, county name, and congressional code; a location with no fields upstream is omittedrecipient.recipient_idandrecipient.parent_recipient_idchain tousaspending_get_recipientandparent_award.generated_unique_award_idto the parent IDV;category: "idv"awards list their children viausaspending_get_idv_awards
usaspending_get_award_transactions tool
award_idplussort(action_date,federal_action_obligation,modification_number);limitup to 100- Rows carry a string
id(ASST_TXorCONT_TX),action_date,modification_number,action_type, and a signedfederal_action_obligation(negative is a deobligation). Loan transactions also carryface_value_loan_guaranteeandoriginal_loan_subsidy_cost; assistance transactions carrycfda_numberwhen supplied
usaspending_get_award_subawards tool
award_idplussort(subaward_number,description,action_date,amount,recipient_name);limitup to 100- Rows carry
id,subaward_number,description,amount,action_date, andrecipient_name; the upstream listing supplies no recipient UEI or location.subaward_countonusaspending_get_awardsays whether any exist
usaspending_get_award_federal_accounts tool
award_idis agenerated_unique_award_id;limitup to 100, withpage_metadata.countas the total- Rows carry
federal_account(AGENCY-MAIN, e.g.080-0120) forusaspending_get_federal_account,total_transaction_obligated_amount, and the funding agency with itsfunding_agency_slug
usaspending_get_idv_awards tool
- Parent IDV
award_id;typeischild_awards(task and delivery orders, the default),child_idvs, orgrandchild_awards;limitup to 100 - Rows carry
generated_unique_award_idforusaspending_get_award,obligated_amount, and performance dates; there is no total, andhas_nextis true on any full page
usaspending_search_recipients tool
keywordmatches names, UEI, or DUNS, partial matches included; surrounding whitespace is trimmed, and blank text applies no keyword filter with a notice. Optionalaward_typestill scopes the totals;limitup to 100- Rows carry
id(a hash suffixed-Pparent,-Cchild, or-Rstandalone) forusaspending_get_recipient, plusuei,duns,recipient_level, andamount;page_metadata.totalis the full match count
usaspending_get_recipient tool
recipient_idfromusaspending_search_recipientsorusaspending_get_award; optionalfiscal_year(2001–2030) scopes the totals to that fiscal year, which otherwise cover the trailing 12 months; an unknown ID fails asrecipient_not_found- Returns address,
business_types,parent_name/parent_uei,alternate_names,total_transaction_amount,total_transactions, and loan face-value totals, withperiodnaming the window (FY2020ortrailing 12 months). Totals span every award type; for totals by type, useusaspending_spending_over_timewithfilters.recipient_idandaward_type_codes
usaspending_get_agency tool
- One of
toptier_code(e.g.097) oragency_slug(e.g.department-of-defense);pagewalks the sub-agency list 10 at a time. Failures aremissing_input,conflicting_input(both identifiers sent), andagency_not_found - Returns
mission, plusbudgetary_resources_amount,obligated_amount, andoutlay_amountfor the most recentfiscal_yearwith reported budget data (a newly opened year with no amounts yet is skipped),sub_agencieswith obligations and transaction and new-award counts for that same year (sub_agency_fiscal_yearnames it; without a budget year within 5 seconds the breakdown covers the current fiscal year), anddef_codes - A failed budget or sub-agency request still returns the overview: the missing fields are omitted and
unavailable_sectionsnames each section with itsreason(api_unavailable,api_timeout, orupstream_error) and whether a retry may load it
usaspending_spending_by_geography tool
scope(place_of_performance,recipient_location) andgeo_layer(state,county,district) are required;filterstakeskeywords,award_type_codes,agency_name,recipient_id,naics_codes,assistance_listings,psc_codes,def_codes(the same code filters asusaspending_search_awards, echoed asapplied_*), andtime_period_start/time_period_end(YYYY-MM-DD; either alone fills the other, as inusaspending_search_awards,applied_time_period_*echoes the range sent, a start after the end fails asdate_range_inverted, and a range starting before 2007-10-01 fails asdate_before_earliest);limit1–500, default 50- Award types may mix groups. When every one named is a contract or IDV,
assistance_listingsfails asassistance_listings_type_mismatch; when every one is assistance,psc_codesfails aspsc_codes_type_mismatch. Code-filtered amounts cover whole matching awards, and DEF-filtered totals can be negative - Rows carry
shape_code,display_name,aggregated_amount,population, andper_capita, ranked by amount;total_areas_availablecounts every match before the cap shape_codes(1–500) returns only the listed areas, in theshape_codeform of the layer: postal code forstate(NY), 5-digit state + county FIPS forcounty(36117), 4-digit state FIPS + district fordistrict(3623). Selection runs over every matched area, so it reaches areas ranked pastlimit; the codes are echoed asapplied_shape_codes, and a code that matches no area is named in thenotice- With no filters, every award type is aggregated and
applied_award_type_defaultsays so;subawards: trueswitches to subaward data
usaspending_spending_by_category tool
categoryisnaics,psc,awarding_agency,awarding_subagency,funding_agency,funding_subagency,cfda, orrecipient_duns; takes the samefiltersobject asusaspending_spending_by_geography;limitup to 100- Rows carry
id,code,name, andamount, ranked by obligation;recipient_dunsrows carry the DUNS ascodeplusrecipient_idandueiwhen upstream has them, andrecipient_idchains tousaspending_get_recipient
usaspending_spending_over_time tool
groupisfiscal_year,quarter, ormonth(fiscal month, where 1 is October); the samefiltersobject, withaward_type_codesdefaulting to contracts, soassistance_listingsneeds an assistance group named;subawards: trueswitches to subaward data- Rows carry
time_period,aggregated_amount, and per-typecontracts,grants,direct_payments,idvs,loans, andother
usaspending_disaster_spending tool
dimensionisoverview,agency,cfda,recipient, orgeography;filters.def_codesis optional foroverviewand required for every other dimension (missing_def_codeswhen absent/empty). Codes such as["L", "M", "N", "O", "P"]isolate COVID-19 funding and are echoed asapplied_def_codes.limitis up to 100 on every dimension butoverview;spending_type(award, the default, ortotal) applies to agency only, and geography takesfilters.geo_layer(state,county)- Rows carry
obligation,outlay, andaward_count, plustotal_budgetary_resourceson agency rows underspending_type: total;overviewreturns totals andfunding_by_def_code. A recipient row'sidchains tousaspending_get_recipient. Recipientfilters.querynarrows by search text; it is trimmed and echoed asapplied_query, with blanks applying no query. A recipient total of 10,000 is a reachable ceiling and a lower bound on possible matches, flaggedtruncated; narrow withfilters.queryto reach the tail. Geography areas are ranked byaggregated_amountand paged withlimitandpage, with the full area count astotal; a page with areas below it is flaggedtruncated, andtruncationCeilingis the lowest amount shown. Spending with no recorded place of performance ranks as one unattributed area - Agency, cfda, and recipient also return
totalsacross every matching row:obligation,outlay, and eithertotal_budgetary_resources(agency,total) oraward_count. Overview can still time out with codes supplied: narrowfilters.def_codes, retry later, or use the agency breakdown withspending_type: totalfor obligations, outlays, and budgetary resources - Notices preserve upstream messages, ignored filters, cap guidance, and empty-page recovery on both output surfaces.
queryis ignored outside recipient,geo_layeroutside geography, andaward_type_codeson overview. An empty page whose offset reaches a positive total directs callers to an earlier page with the same filters; an empty interior page reports the total without claiming exhaustion, and an absent total stays unknown
usaspending_search_federal_accounts tool
- Optional
keywordand 3-digitagency_identifier;sort_fieldisaccount_name,account_number,budgetary_resources(default), ormanaging_agency;limitup to 100 - Rows carry
account_number(e.g.097-8097) for the federal-account tools,managing_agency, andbudgetary_resources;page_metadata.countis the total. Optionalfiscal_yearselects the reporting year; otherwise the provider uses its latest submission year. The returnedfiscal_yearidentifies that period
usaspending_get_federal_account tool
account_codein AGENCY-MAIN format, fromaccount_numberin search results orfederal_accounton an award; an unknown code fails asaccount_not_found- Optional
fiscal_yearselects the reporting year; otherwise the provider uses the current federal fiscal year. Returnstotal_obligated_amount,total_gross_outlay_amount, andtotal_budgetary_resourcesfor the returnedfiscal_year, pluschildren: one entry per Treasury Account Symbol with its own amounts. A year without financial data retains account metadata and a notice, without substituting another year
usaspending_get_federal_account_breakdown tool
account_codeplusdimension(program_activityorobject_class);limitup to 100, withpage_metadata.totalas the row count- Rows carry
code,name, andobligations;program_activityrows addtype, eitherPAC/PANorPARK - Optional
fiscal_yearscopes financial reporting periods to October 1 through September 30, echoed intime_periodandperiod. Omission coversall available periods
All three federal-account tools accept an integer fiscal_year from 2001 through the current federal fiscal year (which rolls over October 1); blank form values mean omitted. Search and detail budget totals use different upstream calculations and need not match even for the same year.
Features
Built on @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.
USAspending-specific:
- USAspending.gov API v2, keyless: the data is public under the DATA Act
- Award search and the spending analytics tools cover action dates from 2007-10-01 on (an earlier start date fails as
date_before_earliest), and DoD contract data lags publication by 90 days usaspending_spending_by_categoryputs eight category sub-routes behind onecategoryenum, andusaspending_disaster_spendingputs five disaster endpoints behinddimension- Each request runs under one wall-clock budget (45 s by default); a slow answer gets the whole budget in one attempt, and only fast failures are retried. Failures surface as
api_timeoutorapi_unavailablewith each tool's recovery hint, and a rejected request carries USAspending's own explanation in the error message
Agent-friendly output:
- Chaining IDs as explicit fields:
generated_internal_id,agency_slug,recipient.recipient_id,federal_account, andaccount_number, so agents follow the money without parsing display strings - Honest pagination:
page_metadata.has_nexton every list, atotalorcountwhere the upstream publishes one, andtruncated/shown/caponly when rows are known to exist beyond those returned. A full page with no such evidence keepshas_nextwithout claiming truncation - Empty results are notices, not errors: account funding, account breakdown, and recipient search distinguish a known zero total from an empty page past the total, which directs callers to an earlier page with the same filters. Without a total, notices describe only the page; award search never supplies a total. The ID-keyed list tools (transactions, subawards, funding accounts, IDV children, account breakdown) return an empty list for an unknown ID rather than failing
- Typed failures with recovery hints:
award_not_found,recipient_not_found,agency_not_found,account_not_found,no_match,conflicting_input,date_before_earliest,date_range_inverted,unsupported_sort,assistance_listings_type_mismatch,psc_codes_type_mismatch,invalid_location,pagination_limit_exceeded,incomplete_cursor,missing_def_codes
Getting started
Connect to the hosted instance, no install:
claude mcp add --transport http usaspending-mcp-server https://usaspending.caseyjhand.com/mcp
Or run it locally:
claude mcp add usaspending-mcp-server -- npx -y @cyanheads/usaspending-mcp-server
For other clients, add this to the MCP config file:
{
"mcpServers": {
"usaspending-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/usaspending-mcp-server"]
}
}
}
No API key or account is required. For Docker or a source build, see Running the server.
Configuration
No variable is required; the defaults work out of the box.
| Variable | Description | Default |
|---|---|---|
USASPENDING_BASE_URL | USAspending.gov API v2 base URL. | https://api.usaspending.gov/api/v2/ |
USASPENDING_TIMEOUT_MS | Per-attempt HTTP timeout, in ms (1000–120000). Each attempt is cut at this or at the time left in the request budget, whichever is smaller; a timed-out attempt is not retried. | 45000 |
USASPENDING_RETRY_BUDGET_MS | Wall-clock budget for one request, every attempt and backoff included, in ms (1000–300000). Fast failures such as a 503 retry inside it. | 45000, or USASPENDING_TIMEOUT_MS when higher |
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. | stateless |
MCP_AUTH_MODE | Authentication: none, jwt, or oauth. | none |
MCP_LOG_LEVEL | Log level (debug, info, warning, error, etc.). | info |
LOG_TOOL_FAILURE_PAYLOADS | Log each failed tool call's arguments and result, redacted by key name and capped at LOG_TOOL_FAILURE_PAYLOAD_MAX_BYTES (default 16384). A secret inside a free-form value is not redacted. | false |
LOGS_DIR | Directory for log files (Node.js only). | <project-root>/logs |
STORAGE_PROVIDER_TYPE | Storage backend: in-memory, filesystem, supabase, cloudflare-kv/r2/d1. | in-memory |
OTEL_ENABLED | Enable OpenTelemetry. | false |
OTEL_EXPORTER_OTLP_LOGS_ENDPOINT | Opt-in OTLP log export endpoint; the base OTEL_EXPORTER_OTLP_ENDPOINT never enables it. | — |
See .env.example for the full list of optional overrides.
Running the server
Local development
Requires Bun v1.4.0 or higher (or Node.js v24+). No API key or account is required.
git clone https://github.com/cyanheads/usaspending-mcp-server.git
cd usaspending-mcp-server && bun install
cp .env.example .env # optional overrides
-
Build and run:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
Run checks and tests:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker run --rm -p 3010:3010 ghcr.io/cyanheads/usaspending-mcp-server:latest
The image defaults to HTTP transport, stateless session mode, and logs to /var/log/usaspending-mcp-server. For stdio, use "command": "docker" with "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/usaspending-mcp-server:latest"]. Build locally with docker build -t usaspending-mcp-server .; OpenTelemetry peer dependencies are installed by default — build with --build-arg OTEL_ENABLED=false to omit them.
Project structure
| Directory | Purpose |
|---|---|
src/index.ts | createApp() entry point: registers the tools and initializes the USAspending service. |
src/config | Server-specific environment variable parsing and validation with Zod. |
src/mcp-server/tools/definitions | Tool definitions (*.tool.ts) plus shared filter, date, pagination, and formatting helpers. |
src/services/usaspending | USAspending.gov API client: request timeouts, retry budget, raw response types. |
tests/ | Unit tests for tools, the service, config, and scripts. |
Development guide
See CLAUDE.md for development guidelines and architectural rules. The short version:
- Handlers throw, framework catches — no
try/catchin tool logic - Use
ctx.logfor request-scoped logging,ctx.statefor tenant-scoped storage - Register new tools via the barrel in
src/mcp-server/tools/definitions/index.ts - Wrap external API calls: validate raw → normalize to domain type → return output schema; never fabricate missing fields
Contributing
Issues are welcome. Run checks and tests before submitting:
bun run devcheck
bun run test
License
Apache-2.0 — see LICENSE for details.
Comments
New to MCP?
From what the protocol is to installing your first server, these answers cover most first questions.
An open protocol that defines how AI applications connect to external tools and data in a uniform way. Clients talk to the model; MCP servers provide tools, resources and prompt templates. They exchange standard messages, so one server can be reused by many clients.