us-gov-open-data-mcp 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (310) hide show
  1. package/README.md +211 -0
  2. package/dist/client.d.ts +54 -0
  3. package/dist/client.d.ts.map +1 -0
  4. package/dist/client.js +388 -0
  5. package/dist/client.js.map +1 -0
  6. package/dist/instructions.d.ts +8 -0
  7. package/dist/instructions.d.ts.map +1 -0
  8. package/dist/instructions.js +549 -0
  9. package/dist/instructions.js.map +1 -0
  10. package/dist/modules/bea.d.ts +28 -0
  11. package/dist/modules/bea.d.ts.map +1 -0
  12. package/dist/modules/bea.js +158 -0
  13. package/dist/modules/bea.js.map +1 -0
  14. package/dist/modules/bls.d.ts +29 -0
  15. package/dist/modules/bls.d.ts.map +1 -0
  16. package/dist/modules/bls.js +244 -0
  17. package/dist/modules/bls.js.map +1 -0
  18. package/dist/modules/bts.d.ts +26 -0
  19. package/dist/modules/bts.d.ts.map +1 -0
  20. package/dist/modules/bts.js +112 -0
  21. package/dist/modules/bts.js.map +1 -0
  22. package/dist/modules/cdc.d.ts +26 -0
  23. package/dist/modules/cdc.d.ts.map +1 -0
  24. package/dist/modules/cdc.js +310 -0
  25. package/dist/modules/cdc.js.map +1 -0
  26. package/dist/modules/census.d.ts +23 -0
  27. package/dist/modules/census.d.ts.map +1 -0
  28. package/dist/modules/census.js +141 -0
  29. package/dist/modules/census.js.map +1 -0
  30. package/dist/modules/cfpb.d.ts +20 -0
  31. package/dist/modules/cfpb.d.ts.map +1 -0
  32. package/dist/modules/cfpb.js +135 -0
  33. package/dist/modules/cfpb.js.map +1 -0
  34. package/dist/modules/clinical-trials.d.ts +22 -0
  35. package/dist/modules/clinical-trials.d.ts.map +1 -0
  36. package/dist/modules/clinical-trials.js +171 -0
  37. package/dist/modules/clinical-trials.js.map +1 -0
  38. package/dist/modules/cms.d.ts +21 -0
  39. package/dist/modules/cms.d.ts.map +1 -0
  40. package/dist/modules/cms.js +135 -0
  41. package/dist/modules/cms.js.map +1 -0
  42. package/dist/modules/college-scorecard.d.ts +39 -0
  43. package/dist/modules/college-scorecard.d.ts.map +1 -0
  44. package/dist/modules/college-scorecard.js +192 -0
  45. package/dist/modules/college-scorecard.js.map +1 -0
  46. package/dist/modules/congress.d.ts +28 -0
  47. package/dist/modules/congress.d.ts.map +1 -0
  48. package/dist/modules/congress.js +883 -0
  49. package/dist/modules/congress.js.map +1 -0
  50. package/dist/modules/dol.d.ts +27 -0
  51. package/dist/modules/dol.d.ts.map +1 -0
  52. package/dist/modules/dol.js +209 -0
  53. package/dist/modules/dol.js.map +1 -0
  54. package/dist/modules/eia.d.ts +33 -0
  55. package/dist/modules/eia.d.ts.map +1 -0
  56. package/dist/modules/eia.js +227 -0
  57. package/dist/modules/eia.js.map +1 -0
  58. package/dist/modules/epa.d.ts +21 -0
  59. package/dist/modules/epa.d.ts.map +1 -0
  60. package/dist/modules/epa.js +91 -0
  61. package/dist/modules/epa.js.map +1 -0
  62. package/dist/modules/fbi.d.ts +28 -0
  63. package/dist/modules/fbi.d.ts.map +1 -0
  64. package/dist/modules/fbi.js +143 -0
  65. package/dist/modules/fbi.js.map +1 -0
  66. package/dist/modules/fda.d.ts +35 -0
  67. package/dist/modules/fda.d.ts.map +1 -0
  68. package/dist/modules/fda.js +262 -0
  69. package/dist/modules/fda.js.map +1 -0
  70. package/dist/modules/fdic.d.ts +21 -0
  71. package/dist/modules/fdic.d.ts.map +1 -0
  72. package/dist/modules/fdic.js +186 -0
  73. package/dist/modules/fdic.js.map +1 -0
  74. package/dist/modules/fec.d.ts +29 -0
  75. package/dist/modules/fec.d.ts.map +1 -0
  76. package/dist/modules/fec.js +282 -0
  77. package/dist/modules/fec.js.map +1 -0
  78. package/dist/modules/federal-register.d.ts +24 -0
  79. package/dist/modules/federal-register.d.ts.map +1 -0
  80. package/dist/modules/federal-register.js +184 -0
  81. package/dist/modules/federal-register.js.map +1 -0
  82. package/dist/modules/fema.d.ts +20 -0
  83. package/dist/modules/fema.d.ts.map +1 -0
  84. package/dist/modules/fema.js +156 -0
  85. package/dist/modules/fema.js.map +1 -0
  86. package/dist/modules/fred.d.ts +40 -0
  87. package/dist/modules/fred.d.ts.map +1 -0
  88. package/dist/modules/fred.js +143 -0
  89. package/dist/modules/fred.js.map +1 -0
  90. package/dist/modules/govinfo.d.ts +24 -0
  91. package/dist/modules/govinfo.d.ts.map +1 -0
  92. package/dist/modules/govinfo.js +147 -0
  93. package/dist/modules/govinfo.js.map +1 -0
  94. package/dist/modules/hud.d.ts +17 -0
  95. package/dist/modules/hud.d.ts.map +1 -0
  96. package/dist/modules/hud.js +170 -0
  97. package/dist/modules/hud.js.map +1 -0
  98. package/dist/modules/naep.d.ts +27 -0
  99. package/dist/modules/naep.d.ts.map +1 -0
  100. package/dist/modules/naep.js +210 -0
  101. package/dist/modules/naep.js.map +1 -0
  102. package/dist/modules/nhtsa.d.ts +13 -0
  103. package/dist/modules/nhtsa.d.ts.map +1 -0
  104. package/dist/modules/nhtsa.js +196 -0
  105. package/dist/modules/nhtsa.js.map +1 -0
  106. package/dist/modules/noaa.d.ts +41 -0
  107. package/dist/modules/noaa.d.ts.map +1 -0
  108. package/dist/modules/noaa.js +135 -0
  109. package/dist/modules/noaa.js.map +1 -0
  110. package/dist/modules/nrel.d.ts +25 -0
  111. package/dist/modules/nrel.d.ts.map +1 -0
  112. package/dist/modules/nrel.js +87 -0
  113. package/dist/modules/nrel.js.map +1 -0
  114. package/dist/modules/regulations.d.ts +24 -0
  115. package/dist/modules/regulations.d.ts.map +1 -0
  116. package/dist/modules/regulations.js +173 -0
  117. package/dist/modules/regulations.js.map +1 -0
  118. package/dist/modules/sec.d.ts +25 -0
  119. package/dist/modules/sec.d.ts.map +1 -0
  120. package/dist/modules/sec.js +192 -0
  121. package/dist/modules/sec.js.map +1 -0
  122. package/dist/modules/senate-lobbying.d.ts +21 -0
  123. package/dist/modules/senate-lobbying.d.ts.map +1 -0
  124. package/dist/modules/senate-lobbying.js +189 -0
  125. package/dist/modules/senate-lobbying.js.map +1 -0
  126. package/dist/modules/treasury.d.ts +23 -0
  127. package/dist/modules/treasury.d.ts.map +1 -0
  128. package/dist/modules/treasury.js +234 -0
  129. package/dist/modules/treasury.js.map +1 -0
  130. package/dist/modules/usaspending.d.ts +19 -0
  131. package/dist/modules/usaspending.d.ts.map +1 -0
  132. package/dist/modules/usaspending.js +204 -0
  133. package/dist/modules/usaspending.js.map +1 -0
  134. package/dist/modules/usda-fooddata.d.ts +24 -0
  135. package/dist/modules/usda-fooddata.d.ts.map +1 -0
  136. package/dist/modules/usda-fooddata.js +118 -0
  137. package/dist/modules/usda-fooddata.js.map +1 -0
  138. package/dist/modules/usda-nass.d.ts +46 -0
  139. package/dist/modules/usda-nass.d.ts.map +1 -0
  140. package/dist/modules/usda-nass.js +151 -0
  141. package/dist/modules/usda-nass.js.map +1 -0
  142. package/dist/modules/usgs.d.ts +21 -0
  143. package/dist/modules/usgs.d.ts.map +1 -0
  144. package/dist/modules/usgs.js +203 -0
  145. package/dist/modules/usgs.js.map +1 -0
  146. package/dist/modules/uspto.d.ts +13 -0
  147. package/dist/modules/uspto.d.ts.map +1 -0
  148. package/dist/modules/uspto.js +157 -0
  149. package/dist/modules/uspto.js.map +1 -0
  150. package/dist/modules/world-bank.d.ts +21 -0
  151. package/dist/modules/world-bank.d.ts.map +1 -0
  152. package/dist/modules/world-bank.js +130 -0
  153. package/dist/modules/world-bank.js.map +1 -0
  154. package/dist/prompts.d.ts +12 -0
  155. package/dist/prompts.d.ts.map +1 -0
  156. package/dist/prompts.js +858 -0
  157. package/dist/prompts.js.map +1 -0
  158. package/dist/sdk/bea.d.ts +111 -0
  159. package/dist/sdk/bea.d.ts.map +1 -0
  160. package/dist/sdk/bea.js +242 -0
  161. package/dist/sdk/bea.js.map +1 -0
  162. package/dist/sdk/bls.d.ts +65 -0
  163. package/dist/sdk/bls.d.ts.map +1 -0
  164. package/dist/sdk/bls.js +203 -0
  165. package/dist/sdk/bls.js.map +1 -0
  166. package/dist/sdk/bts.d.ts +108 -0
  167. package/dist/sdk/bts.d.ts.map +1 -0
  168. package/dist/sdk/bts.js +121 -0
  169. package/dist/sdk/bts.js.map +1 -0
  170. package/dist/sdk/cdc.d.ts +105 -0
  171. package/dist/sdk/cdc.d.ts.map +1 -0
  172. package/dist/sdk/cdc.js +222 -0
  173. package/dist/sdk/cdc.js.map +1 -0
  174. package/dist/sdk/census.d.ts +47 -0
  175. package/dist/sdk/census.d.ts.map +1 -0
  176. package/dist/sdk/census.js +99 -0
  177. package/dist/sdk/census.js.map +1 -0
  178. package/dist/sdk/cfpb.d.ts +148 -0
  179. package/dist/sdk/cfpb.d.ts.map +1 -0
  180. package/dist/sdk/cfpb.js +153 -0
  181. package/dist/sdk/cfpb.js.map +1 -0
  182. package/dist/sdk/clinical-trials.d.ts +214 -0
  183. package/dist/sdk/clinical-trials.d.ts.map +1 -0
  184. package/dist/sdk/clinical-trials.js +134 -0
  185. package/dist/sdk/clinical-trials.js.map +1 -0
  186. package/dist/sdk/cms.d.ts +81 -0
  187. package/dist/sdk/cms.d.ts.map +1 -0
  188. package/dist/sdk/cms.js +227 -0
  189. package/dist/sdk/cms.js.map +1 -0
  190. package/dist/sdk/college-scorecard.d.ts +63 -0
  191. package/dist/sdk/college-scorecard.d.ts.map +1 -0
  192. package/dist/sdk/college-scorecard.js +131 -0
  193. package/dist/sdk/college-scorecard.js.map +1 -0
  194. package/dist/sdk/congress.d.ts +575 -0
  195. package/dist/sdk/congress.d.ts.map +1 -0
  196. package/dist/sdk/congress.js +659 -0
  197. package/dist/sdk/congress.js.map +1 -0
  198. package/dist/sdk/dol.d.ts +299 -0
  199. package/dist/sdk/dol.d.ts.map +1 -0
  200. package/dist/sdk/dol.js +252 -0
  201. package/dist/sdk/dol.js.map +1 -0
  202. package/dist/sdk/eia.d.ts +91 -0
  203. package/dist/sdk/eia.d.ts.map +1 -0
  204. package/dist/sdk/eia.js +156 -0
  205. package/dist/sdk/eia.js.map +1 -0
  206. package/dist/sdk/epa.d.ts +128 -0
  207. package/dist/sdk/epa.d.ts.map +1 -0
  208. package/dist/sdk/epa.js +120 -0
  209. package/dist/sdk/epa.js.map +1 -0
  210. package/dist/sdk/fbi.d.ts +48 -0
  211. package/dist/sdk/fbi.d.ts.map +1 -0
  212. package/dist/sdk/fbi.js +69 -0
  213. package/dist/sdk/fbi.js.map +1 -0
  214. package/dist/sdk/fda.d.ts +356 -0
  215. package/dist/sdk/fda.d.ts.map +1 -0
  216. package/dist/sdk/fda.js +162 -0
  217. package/dist/sdk/fda.js.map +1 -0
  218. package/dist/sdk/fdic.d.ts +227 -0
  219. package/dist/sdk/fdic.d.ts.map +1 -0
  220. package/dist/sdk/fdic.js +172 -0
  221. package/dist/sdk/fdic.js.map +1 -0
  222. package/dist/sdk/fec.d.ts +142 -0
  223. package/dist/sdk/fec.d.ts.map +1 -0
  224. package/dist/sdk/fec.js +92 -0
  225. package/dist/sdk/fec.js.map +1 -0
  226. package/dist/sdk/federal-register.d.ts +88 -0
  227. package/dist/sdk/federal-register.d.ts.map +1 -0
  228. package/dist/sdk/federal-register.js +100 -0
  229. package/dist/sdk/federal-register.js.map +1 -0
  230. package/dist/sdk/fema.d.ts +137 -0
  231. package/dist/sdk/fema.d.ts.map +1 -0
  232. package/dist/sdk/fema.js +197 -0
  233. package/dist/sdk/fema.js.map +1 -0
  234. package/dist/sdk/fred.d.ts +72 -0
  235. package/dist/sdk/fred.d.ts.map +1 -0
  236. package/dist/sdk/fred.js +59 -0
  237. package/dist/sdk/fred.js.map +1 -0
  238. package/dist/sdk/govinfo.d.ts +64 -0
  239. package/dist/sdk/govinfo.d.ts.map +1 -0
  240. package/dist/sdk/govinfo.js +187 -0
  241. package/dist/sdk/govinfo.js.map +1 -0
  242. package/dist/sdk/hud.d.ts +87 -0
  243. package/dist/sdk/hud.d.ts.map +1 -0
  244. package/dist/sdk/hud.js +91 -0
  245. package/dist/sdk/hud.js.map +1 -0
  246. package/dist/sdk/index.d.ts +51 -0
  247. package/dist/sdk/index.d.ts.map +1 -0
  248. package/dist/sdk/index.js +51 -0
  249. package/dist/sdk/index.js.map +1 -0
  250. package/dist/sdk/naep.d.ts +93 -0
  251. package/dist/sdk/naep.d.ts.map +1 -0
  252. package/dist/sdk/naep.js +163 -0
  253. package/dist/sdk/naep.js.map +1 -0
  254. package/dist/sdk/nhtsa.d.ts +169 -0
  255. package/dist/sdk/nhtsa.d.ts.map +1 -0
  256. package/dist/sdk/nhtsa.js +102 -0
  257. package/dist/sdk/nhtsa.js.map +1 -0
  258. package/dist/sdk/noaa.d.ts +72 -0
  259. package/dist/sdk/noaa.d.ts.map +1 -0
  260. package/dist/sdk/noaa.js +64 -0
  261. package/dist/sdk/noaa.js.map +1 -0
  262. package/dist/sdk/nrel.d.ts +145 -0
  263. package/dist/sdk/nrel.d.ts.map +1 -0
  264. package/dist/sdk/nrel.js +93 -0
  265. package/dist/sdk/nrel.js.map +1 -0
  266. package/dist/sdk/regulations.d.ts +146 -0
  267. package/dist/sdk/regulations.d.ts.map +1 -0
  268. package/dist/sdk/regulations.js +103 -0
  269. package/dist/sdk/regulations.js.map +1 -0
  270. package/dist/sdk/sec.d.ts +114 -0
  271. package/dist/sdk/sec.d.ts.map +1 -0
  272. package/dist/sdk/sec.js +151 -0
  273. package/dist/sdk/sec.js.map +1 -0
  274. package/dist/sdk/senate-lobbying.d.ts +147 -0
  275. package/dist/sdk/senate-lobbying.d.ts.map +1 -0
  276. package/dist/sdk/senate-lobbying.js +125 -0
  277. package/dist/sdk/senate-lobbying.js.map +1 -0
  278. package/dist/sdk/treasury.d.ts +59 -0
  279. package/dist/sdk/treasury.d.ts.map +1 -0
  280. package/dist/sdk/treasury.js +1397 -0
  281. package/dist/sdk/treasury.js.map +1 -0
  282. package/dist/sdk/usaspending.d.ts +126 -0
  283. package/dist/sdk/usaspending.d.ts.map +1 -0
  284. package/dist/sdk/usaspending.js +270 -0
  285. package/dist/sdk/usaspending.js.map +1 -0
  286. package/dist/sdk/usda-fooddata.d.ts +112 -0
  287. package/dist/sdk/usda-fooddata.d.ts.map +1 -0
  288. package/dist/sdk/usda-fooddata.js +80 -0
  289. package/dist/sdk/usda-fooddata.js.map +1 -0
  290. package/dist/sdk/usda-nass.d.ts +75 -0
  291. package/dist/sdk/usda-nass.d.ts.map +1 -0
  292. package/dist/sdk/usda-nass.js +83 -0
  293. package/dist/sdk/usda-nass.js.map +1 -0
  294. package/dist/sdk/usgs.d.ts +221 -0
  295. package/dist/sdk/usgs.d.ts.map +1 -0
  296. package/dist/sdk/usgs.js +182 -0
  297. package/dist/sdk/usgs.js.map +1 -0
  298. package/dist/sdk/uspto.d.ts +109 -0
  299. package/dist/sdk/uspto.d.ts.map +1 -0
  300. package/dist/sdk/uspto.js +286 -0
  301. package/dist/sdk/uspto.js.map +1 -0
  302. package/dist/sdk/world-bank.d.ts +78 -0
  303. package/dist/sdk/world-bank.d.ts.map +1 -0
  304. package/dist/sdk/world-bank.js +72 -0
  305. package/dist/sdk/world-bank.js.map +1 -0
  306. package/dist/server.d.ts +22 -0
  307. package/dist/server.d.ts.map +1 -0
  308. package/dist/server.js +196 -0
  309. package/dist/server.js.map +1 -0
  310. package/package.json +113 -0
package/README.md ADDED
@@ -0,0 +1,211 @@
1
+ # US Government Open Data MCP
2
+
3
+ An MCP server + TypeScript SDK for **36 U.S. government and international data APIs** — 188 tools covering economic, fiscal, health, education, energy, environment, lobbying, housing, patents, safety, banking, consumer protection, workplace safety, transportation, seismic, clinical trials, and legislative data.
4
+
5
+ Works with any MCP client (VS Code Copilot, Claude Desktop, Cursor) via **stdio or HTTP Stream**. Supports **selective module loading** and standalone **SDK imports** for use without MCP.
6
+
7
+ Built with [FastMCP](https://github.com/punkpeye/fastmcp). Disk-cached, rate-limited, retries with backoff.
8
+
9
+ ## Data Sources
10
+
11
+ | API | What it covers | Auth |
12
+ |-----|---------------|------|
13
+ | **Treasury Fiscal Data** | 53 datasets, 181 endpoints: national debt, revenue, spending, interest rates, exchange rates, gold reserves | None |
14
+ | **FRED** | 800K+ economic time series: GDP, CPI, unemployment, interest rates, money supply, housing, S&P 500 | `FRED_API_KEY` |
15
+ | **BLS** | Employment by industry, wages, CPI by component, PPI, JOLTS, labor productivity | `BLS_API_KEY` (optional) |
16
+ | **BEA** | State GDP, GDP by industry, personal income by state, national accounts (NIPA) | `BEA_API_KEY` |
17
+ | **EIA** | Petroleum, electricity, natural gas prices; state energy profiles | `EIA_API_KEY` |
18
+ | **Census Bureau** | Population, demographics, income, housing from ACS and Decennial Census | `CENSUS_API_KEY` |
19
+ | **OpenFEC** | Campaign finance: candidates, committees, contributions, expenditures | `DATA_GOV_API_KEY` |
20
+ | **Congress.gov** | Bills, members, laws, amendments, House & Senate roll call votes (1990+) | `DATA_GOV_API_KEY` |
21
+ | **Federal Register** | Executive orders, presidential documents, rules, agency notices, document detail, 470+ agency directory | None |
22
+ | **USAspending** | Federal contracts, grants, loans — $6T+ annually by agency, recipient, state | None |
23
+ | **SEC EDGAR** | Company filings, XBRL financials, full-text search | `SEC_CONTACT_EMAIL` |
24
+ | **FBI Crime Data** | Crime statistics by state, national estimates, arrests | `DATA_GOV_API_KEY` |
25
+ | **GovInfo** | Full text of bills, laws, CBO reports, Federal Register | `DATA_GOV_API_KEY` |
26
+ | **NOAA Climate** | Weather observations, temperature, precipitation from U.S. stations | `NOAA_API_KEY` |
27
+ | **USDA NASS** | Crop production, livestock, farm prices, Census of Agriculture | `USDA_NASS_API_KEY` |
28
+ | **World Bank** | International indicators: GDP, population, health spending for 200+ countries | None |
29
+ | **CDC Health Data** | Leading causes of death, life expectancy, mortality rates, county/city health indicators (PLACES), drug overdose, obesity, disability, birth indicators, weekly death surveillance, COVID-19 — 13 tools across 12 datasets | None |
30
+ | **NAEP (Nation's Report Card)** | Reading, math, science test scores by state, grade, race, gender, poverty — the gold standard for U.S. education measurement | None |
31
+ | **College Scorecard** | College costs, graduation rates, post-graduation earnings, student debt, admission rates for every U.S. college | `DATA_GOV_API_KEY` |
32
+ | **NREL (Clean Energy)** | EV charging stations, alt fuel infrastructure, electricity rates, solar resource data | `DATA_GOV_API_KEY` |
33
+ | **FDA (OpenFDA)** | Drug adverse events (20M+ reports), drug recalls, FDA-approved drugs (Drugs@FDA), drug labels, food recalls, food adverse events (CAERS), medical device events, device recalls — 8 tools | None |
34
+ | **EPA** | Air quality data, facility compliance/violations, UV index forecasts | None |
35
+ | **Senate Lobbying Disclosures** | Lobbying filings, expenditures by issue, campaign contributions, individual lobbyist search — follow the money | None |
36
+ | **Regulations.gov** | Federal rulemaking: proposed rules, final rules, public comments, regulatory dockets | `DATA_GOV_API_KEY` |
37
+ | **USDA FoodData Central** | Nutritional data for 300K+ foods: calories, macros, vitamins, minerals for branded and reference foods | `DATA_GOV_API_KEY` |
38
+ | **FEMA** | Disaster declarations, housing assistance, public assistance, NFIP flood claims, hazard mitigation | None |
39
+ | **NHTSA** | Vehicle safety recalls, consumer complaints, NCAP 5-star safety ratings, VIN decoding, make/model lookup | None |
40
+ | **CMS Provider Data** | Hospital quality ratings, nursing home inspections, home health, hospice, dialysis facility data | None |
41
+ | **HUD** | Fair Market Rents by bedroom count, income limits by household size, county/metro area housing data | `HUD_USER_TOKEN` |
42
+ | **USPTO PatentsView** | U.S. patent search by keyword, assignee, inventor, CPC class; inventor and assignee lookup | None |
43
+ | **CFPB** | Consumer complaint database: 13M+ complaints against financial companies, searchable by company/product/state/issue with trend analysis | None |
44
+ | **FDIC** | Bank data: 5,000+ insured institutions, failures since 1934, quarterly financials, branch deposits, merger history | None |
45
+ | **DOL** | OSHA inspections/violations/accidents, WHD wage theft enforcement, weekly unemployment insurance claims (national + state) | `DOL_API_KEY` |
46
+ | **USGS** | Earthquake events (magnitude, location, depth, tsunami risk), water resources monitoring (real-time and daily historical streamflow, water levels) from 13,000+ stations | None |
47
+ | **ClinicalTrials.gov** | 400K+ clinical trials: search by condition, drug, sponsor, phase, status, location. Track pharma drug pipelines | None |
48
+ | **BTS** | Bureau of Transportation Statistics: monthly transport stats (airline traffic, transit, rail, safety, fuel), border crossings at U.S. ports of entry | None |
49
+
50
+ ## Quick Start
51
+
52
+ ```bash
53
+ npx us-gov-open-data-mcp
54
+ ```
55
+
56
+ ### VS Code / Copilot
57
+
58
+ Add to `.vscode/mcp.json`:
59
+
60
+ ```json
61
+ {
62
+ "servers": {
63
+ "us-gov-open-data": {
64
+ "command": "npx",
65
+ "args": ["-y", "us-gov-open-data-mcp"],
66
+ "env": {
67
+ "FRED_API_KEY": "your_key",
68
+ "DATA_GOV_API_KEY": "your_key"
69
+ }
70
+ }
71
+ }
72
+ }
73
+ ```
74
+
75
+ ### Claude Desktop
76
+
77
+ Add to `claude_desktop_config.json`:
78
+
79
+ ```json
80
+ {
81
+ "mcpServers": {
82
+ "us-gov-open-data": {
83
+ "command": "npx",
84
+ "args": ["-y", "us-gov-open-data-mcp"],
85
+ "env": {
86
+ "FRED_API_KEY": "your_key"
87
+ }
88
+ }
89
+ }
90
+ }
91
+ ```
92
+
93
+ ### HTTP Stream (for web apps, remote access)
94
+
95
+ ```bash
96
+ # Start on port 8080
97
+ node dist/server.js --transport httpStream --port 8080
98
+
99
+ # Or via npm script
100
+ npm run start:http
101
+ ```
102
+
103
+ The HTTP Stream endpoint will be at `http://localhost:8080/mcp`. Works with any MCP client that supports HTTP transport.
104
+
105
+ ### Selective Module Loading
106
+
107
+ Load only the modules you need for faster startup and smaller tool lists:
108
+
109
+ ```bash
110
+ # Via CLI flag
111
+ node dist/server.js --modules fred,treasury,congress
112
+
113
+ # Via environment variable
114
+ MODULES=fred,bls,treasury node dist/server.js
115
+
116
+ # Combine with HTTP transport
117
+ node dist/server.js --modules fred,treasury --transport httpStream --port 8080
118
+ ```
119
+
120
+ Available module names: `treasury`, `fred`, `bls`, `bea`, `eia`, `census`, `fec`, `congress`, `federal-register`, `usaspending`, `sec`, `fbi`, `govinfo`, `noaa`, `usda-nass`, `world-bank`, `cdc`, `naep`, `college-scorecard`, `nrel`, `fda`, `epa`, `senate-lobbying`, `regulations`, `usda-fooddata`, `fema`, `nhtsa`, `cms`, `hud`, `uspto`, `cfpb`, `fdic`, `dol`, `usgs`, `clinical-trials`, `bts`
121
+
122
+ ## API Keys
123
+
124
+ 18 APIs require **no key at all** (Treasury, Federal Register, USAspending, World Bank, CDC, FDA, EPA, NAEP, Senate Lobbying, FEMA, NHTSA, CMS, USPTO, CFPB, FDIC, USGS, ClinicalTrials.gov, BTS). The rest need free keys — most take under a minute to get:
125
+
126
+ | Key | Where to get it | Used by |
127
+ |-----|----------------|---------|
128
+ | `DATA_GOV_API_KEY` | [api.data.gov/signup](https://api.data.gov/signup/) | Congress, FEC, FBI, GovInfo, College Scorecard, NREL, Regulations.gov, USDA FoodData |
129
+ | `FRED_API_KEY` | [fredaccount.stlouisfed.org/apikeys](https://fredaccount.stlouisfed.org/apikeys) | FRED |
130
+ | `CENSUS_API_KEY` | [api.census.gov/data/key_signup.html](https://api.census.gov/data/key_signup.html) | Census |
131
+ | `BLS_API_KEY` | [bls.gov/developers](https://www.bls.gov/developers/) | BLS (optional — works without, higher limits with) |
132
+ | `BEA_API_KEY` | [apps.bea.gov/API/signup](https://apps.bea.gov/API/signup/) | BEA |
133
+ | `EIA_API_KEY` | [eia.gov/opendata/register.php](https://www.eia.gov/opendata/register.php) | EIA |
134
+ | `NOAA_API_KEY` | [ncei.noaa.gov/cdo-web/token](https://www.ncei.noaa.gov/cdo-web/token) | NOAA |
135
+ | `USDA_NASS_API_KEY` | [quickstats.nass.usda.gov/api](https://quickstats.nass.usda.gov/api) | USDA NASS |
136
+ | `HUD_USER_TOKEN` | [huduser.gov/hudapi/public/register](https://www.huduser.gov/hudapi/public/register) | HUD |
137
+ | `SEC_CONTACT_EMAIL` | Any valid email | SEC EDGAR |
138
+ | `DOL_API_KEY` | [data.dol.gov/registration](https://data.dol.gov/registration) | DOL (OSHA, WHD, UI Claims) |
139
+
140
+ Set keys via environment variables or a `.env` file in the project root.
141
+
142
+ ## Using as a TypeScript SDK
143
+
144
+ Every API is importable as a standalone typed client — no MCP server required:
145
+
146
+ ```typescript
147
+ // Individual module imports
148
+ import { getObservations } from "us-gov-open-data-mcp/sdk/fred";
149
+ import { getIndicator } from "us-gov-open-data-mcp/sdk/world-bank";
150
+ import { getLeadingCausesOfDeath } from "us-gov-open-data-mcp/sdk/cdc";
151
+ import { searchBills } from "us-gov-open-data-mcp/sdk/congress";
152
+
153
+ // Or barrel import everything
154
+ import * as sdk from "us-gov-open-data-mcp/sdk";
155
+ const gdp = await sdk.fred.getObservations("GDP", { sort: "desc", limit: 5 });
156
+ ```
157
+
158
+ ```typescript
159
+ // FRED
160
+ const gdp = await getObservations("GDP", { sort: "desc", limit: 5 });
161
+ console.log(gdp.observations); // [{ date: "2025-10-01", value: "31490.07" }, ...]
162
+
163
+ // World Bank — compare U.S. to Germany
164
+ const health = await getIndicator("SH.XPD.CHEX.PC.CD", { country: "US;DE", dateRange: "2015:2023" });
165
+
166
+ // CDC
167
+ const deaths = await getLeadingCausesOfDeath({ state: "California", year: 2017 });
168
+ ```
169
+
170
+ All SDK functions include disk-backed caching, retry with exponential backoff, and token-bucket rate limiting. See [docs/sdk.md](docs/sdk.md) for the full API reference.
171
+
172
+ ## Architecture
173
+
174
+ ```
175
+ src/
176
+ client.ts # createClient() factory — cache, retry, rate-limit
177
+ server.ts # FastMCP bootstrap — stdio + HTTP, selective loading
178
+ instructions.ts # Cross-referencing guide for MCP clients
179
+ sdk/
180
+ index.ts # Barrel export for all SDK modules
181
+ fred.ts, bls.ts, ... # Typed API clients (no MCP dependency)
182
+ modules/
183
+ fred.ts, bls.ts, ... # MCP tool definitions + metadata
184
+ ```
185
+
186
+ Each API is 2 files:
187
+ - **`sdk/*.ts`** — typed async functions, usable anywhere (no MCP/Zod dependency)
188
+ - **`modules/*.ts`** — MCP tool definitions wrapping the SDK, with metadata for client instructions
189
+
190
+ Adding a new API: create `sdk/new-api.ts` + `modules/new-api.ts`, add 2 lines to `server.ts`. See [docs/adding-modules.md](docs/adding-modules.md).
191
+
192
+ ## Documentation
193
+
194
+ - [SDK API Reference](docs/sdk.md) — all exported functions and types
195
+ - [Adding New Modules](docs/adding-modules.md) — how to add a new API
196
+ - [Architecture](docs/architecture.md) — how the system works
197
+
198
+ ## Disclaimer
199
+
200
+ This project was built with the assistance of AI tools. All data is sourced from official U.S. government and international APIs — the server does not generate, modify, or editorialize any data. It returns raw results from federal databases exactly as provided.
201
+
202
+ **Important:**
203
+ - Data accuracy depends on the upstream government APIs. Numbers may lag days to years behind reality depending on the source.
204
+ - Correlation does not imply causation. Cross-referencing data sources (e.g., campaign finance + legislative votes) shows documented patterns, not proven cause-and-effect.
205
+ - This tool is for research and informational purposes. It is not legal, financial, medical, or policy advice.
206
+ - API rate limits and availability vary. Some endpoints may be temporarily unavailable or return incomplete data.
207
+ - The example analyses in the `examples/` folder demonstrate the server's capabilities and should not be treated as investigative conclusions.
208
+
209
+ ## License
210
+
211
+ MIT
@@ -0,0 +1,54 @@
1
+ /**
2
+ * Lightweight API client factory with caching, retry, and rate limiting.
3
+ *
4
+ * Instead of an abstract class with 7 virtual methods, this uses a config
5
+ * object to create a client. Each module calls createClient() once.
6
+ *
7
+ * Features:
8
+ * - Disk-backed TTL cache (survives MCP server restarts)
9
+ * - Timeout (30s default)
10
+ * - Retry with exponential backoff (429, 502, 503, 504)
11
+ * - Token-bucket rate limiting
12
+ * - Auth via query param, header, or request body
13
+ */
14
+ export interface ClientConfig {
15
+ baseUrl: string;
16
+ name: string;
17
+ /** Auth configuration — how to attach the API key to requests */
18
+ auth?: {
19
+ /** Where to inject the key */
20
+ type: "query" | "header" | "body";
21
+ /** The param/header name (e.g. "api_key", "Authorization", "registrationkey") */
22
+ key: string;
23
+ /** Env var to read the key from */
24
+ envVar: string;
25
+ /** Extra static params (e.g. { file_type: "json" } for FRED) */
26
+ extraParams?: Record<string, string>;
27
+ /** For header auth: prefix like "Bearer " */
28
+ prefix?: string;
29
+ };
30
+ /** Rate limiting */
31
+ rateLimit?: {
32
+ perSecond: number;
33
+ burst: number;
34
+ };
35
+ /** Default headers on every request (e.g. User-Agent for SEC) */
36
+ defaultHeaders?: Record<string, string>;
37
+ /** Cache TTL in ms (default: 5 min). Government data often updates daily/weekly — set
38
+ * higher for infrequent data: 1 hour = 3_600_000, 1 day = 86_400_000. Set 0 to disable. */
39
+ cacheTtlMs?: number;
40
+ /** Timeout in ms (default: 30000) */
41
+ timeoutMs?: number;
42
+ /** Custom error detector — some APIs return 200 OK with errors in the body */
43
+ checkError?: (data: unknown) => string | null;
44
+ }
45
+ /** Param values: string, number, string[] (for repeated keys like facets[series][]), or undefined to skip */
46
+ export type ParamValue = string | number | string[] | undefined;
47
+ export type Params = Record<string, ParamValue>;
48
+ export interface ApiClient {
49
+ get<T = unknown>(path: string, params?: Params): Promise<T>;
50
+ post<T = unknown>(path: string, body?: Record<string, unknown>, params?: Params): Promise<T>;
51
+ clearCache(): void;
52
+ }
53
+ export declare function createClient(config: ClientConfig): ApiClient;
54
+ //# sourceMappingURL=client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"client.d.ts","sourceRoot":"","sources":["../src/client.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;GAYG;AASH,MAAM,WAAW,YAAY;IAC3B,OAAO,EAAE,MAAM,CAAC;IAChB,IAAI,EAAE,MAAM,CAAC;IAEb,iEAAiE;IACjE,IAAI,CAAC,EAAE;QACL,8BAA8B;QAC9B,IAAI,EAAE,OAAO,GAAG,QAAQ,GAAG,MAAM,CAAC;QAClC,iFAAiF;QACjF,GAAG,EAAE,MAAM,CAAC;QACZ,mCAAmC;QACnC,MAAM,EAAE,MAAM,CAAC;QACf,gEAAgE;QAChE,WAAW,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QACrC,6CAA6C;QAC7C,MAAM,CAAC,EAAE,MAAM,CAAC;KACjB,CAAC;IAEF,oBAAoB;IACpB,SAAS,CAAC,EAAE;QAAE,SAAS,EAAE,MAAM,CAAC;QAAC,KAAK,EAAE,MAAM,CAAA;KAAE,CAAC;IAEjD,iEAAiE;IACjE,cAAc,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;IAExC;gGAC4F;IAC5F,UAAU,CAAC,EAAE,MAAM,CAAC;IAEpB,qCAAqC;IACrC,SAAS,CAAC,EAAE,MAAM,CAAC;IAEnB,8EAA8E;IAC9E,UAAU,CAAC,EAAE,CAAC,IAAI,EAAE,OAAO,KAAK,MAAM,GAAG,IAAI,CAAC;CAC/C;AAED,6GAA6G;AAC7G,MAAM,MAAM,UAAU,GAAG,MAAM,GAAG,MAAM,GAAG,MAAM,EAAE,GAAG,SAAS,CAAC;AAChE,MAAM,MAAM,MAAM,GAAG,MAAM,CAAC,MAAM,EAAE,UAAU,CAAC,CAAC;AAEhD,MAAM,WAAW,SAAS;IACxB,GAAG,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC5D,IAAI,CAAC,CAAC,GAAG,OAAO,EAAE,IAAI,EAAE,MAAM,EAAE,IAAI,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EAAE,MAAM,CAAC,EAAE,MAAM,GAAG,OAAO,CAAC,CAAC,CAAC,CAAC;IAC7F,UAAU,IAAI,IAAI,CAAC;CACpB;AA8RD,wBAAgB,YAAY,CAAC,MAAM,EAAE,YAAY,GAAG,SAAS,CA0H5D"}
package/dist/client.js ADDED
@@ -0,0 +1,388 @@
1
+ /**
2
+ * Lightweight API client factory with caching, retry, and rate limiting.
3
+ *
4
+ * Instead of an abstract class with 7 virtual methods, this uses a config
5
+ * object to create a client. Each module calls createClient() once.
6
+ *
7
+ * Features:
8
+ * - Disk-backed TTL cache (survives MCP server restarts)
9
+ * - Timeout (30s default)
10
+ * - Retry with exponential backoff (429, 502, 503, 504)
11
+ * - Token-bucket rate limiting
12
+ * - Auth via query param, header, or request body
13
+ */
14
+ import { existsSync, mkdirSync, readFileSync } from "node:fs";
15
+ import { writeFile } from "node:fs/promises";
16
+ import { join } from "node:path";
17
+ import { homedir, tmpdir } from "node:os";
18
+ // ─── Token Bucket Rate Limiter ───────────────────────────────────────
19
+ class TokenBucket {
20
+ max;
21
+ rate;
22
+ tokens;
23
+ lastRefill;
24
+ constructor(max, rate) {
25
+ this.max = max;
26
+ this.rate = rate;
27
+ this.tokens = max;
28
+ this.lastRefill = Date.now();
29
+ }
30
+ async acquire() {
31
+ const now = Date.now();
32
+ this.tokens = Math.min(this.max, this.tokens + ((now - this.lastRefill) / 1000) * this.rate);
33
+ this.lastRefill = now;
34
+ if (this.tokens >= 1) {
35
+ this.tokens -= 1;
36
+ return;
37
+ }
38
+ const waitMs = Math.ceil(((1 - this.tokens) / this.rate) * 1000);
39
+ await new Promise(r => setTimeout(r, waitMs));
40
+ this.tokens = 0;
41
+ }
42
+ }
43
+ // ─── Disk-backed TTL Cache ────────────────────────────────────────────
44
+ //
45
+ // Single consolidated JSON file shared by all modules. Lazy-loaded on
46
+ // first cache miss. LRU eviction per module keeps memory bounded.
47
+ // Async writes don't block the event loop. Global write coalescing
48
+ // batches all module updates into one disk write.
49
+ function getCacheDir() {
50
+ const base = process.env.XDG_CACHE_HOME || join(homedir(), ".cache");
51
+ const dir = join(base, "us-gov-open-data-mcp");
52
+ try {
53
+ if (!existsSync(dir))
54
+ mkdirSync(dir, { recursive: true });
55
+ return dir;
56
+ }
57
+ catch {
58
+ const fallback = join(tmpdir(), "us-gov-open-data-mcp");
59
+ if (!existsSync(fallback))
60
+ mkdirSync(fallback, { recursive: true });
61
+ return fallback;
62
+ }
63
+ }
64
+ const CACHE_DIR = getCacheDir();
65
+ const CACHE_FILE = join(CACHE_DIR, "cache.json");
66
+ const MAX_ENTRIES_PER_MODULE = 200;
67
+ // ─── Global disk store (shared by all DiskCache instances) ───────────
68
+ let _globalLoaded = false;
69
+ let _globalDirty = false;
70
+ let _globalWriteTimer;
71
+ /** namespace → key → entry */
72
+ const _globalStore = new Map();
73
+ function loadGlobal() {
74
+ if (_globalLoaded)
75
+ return;
76
+ _globalLoaded = true;
77
+ try {
78
+ if (!existsSync(CACHE_FILE)) {
79
+ // Migrate: try loading legacy per-module files
80
+ migrateLegacyFiles();
81
+ return;
82
+ }
83
+ const raw = JSON.parse(readFileSync(CACHE_FILE, "utf-8"));
84
+ const now = Date.now();
85
+ let totalLoaded = 0;
86
+ for (const [ns, entries] of Object.entries(raw)) {
87
+ const map = new Map();
88
+ for (const [key, entry] of Object.entries(entries)) {
89
+ if (entry.expires > now) {
90
+ map.set(key, entry);
91
+ totalLoaded++;
92
+ }
93
+ }
94
+ if (map.size > 0)
95
+ _globalStore.set(ns, map);
96
+ }
97
+ if (totalLoaded > 0 && process.env.DEBUG_CACHE) {
98
+ console.error(`Cache: loaded ${totalLoaded} entries from disk (${_globalStore.size} modules)`);
99
+ }
100
+ }
101
+ catch {
102
+ // Corrupted — start fresh
103
+ }
104
+ }
105
+ /** One-time migration from the old per-module *.json files to the consolidated cache.json */
106
+ function migrateLegacyFiles() {
107
+ try {
108
+ const { readdirSync, unlinkSync } = require("node:fs");
109
+ const files = readdirSync(CACHE_DIR).filter((f) => f.endsWith(".json") && f !== "cache.json");
110
+ if (files.length === 0)
111
+ return;
112
+ const now = Date.now();
113
+ let migrated = 0;
114
+ for (const file of files) {
115
+ try {
116
+ const ns = file.replace(/\.json$/, "");
117
+ const raw = JSON.parse(readFileSync(join(CACHE_DIR, file), "utf-8"));
118
+ const map = new Map();
119
+ for (const [key, entry] of Object.entries(raw)) {
120
+ if (entry.expires > now) {
121
+ // Add lastAccess if missing (legacy entries don't have it)
122
+ if (!entry.lastAccess)
123
+ entry.lastAccess = now;
124
+ map.set(key, entry);
125
+ migrated++;
126
+ }
127
+ }
128
+ if (map.size > 0)
129
+ _globalStore.set(ns, map);
130
+ unlinkSync(join(CACHE_DIR, file)); // Remove legacy file
131
+ }
132
+ catch {
133
+ // Skip corrupt file
134
+ }
135
+ }
136
+ if (migrated > 0) {
137
+ _globalDirty = true;
138
+ scheduleGlobalWrite();
139
+ if (process.env.DEBUG_CACHE) {
140
+ console.error(`Cache: migrated ${migrated} entries from ${files.length} legacy files`);
141
+ }
142
+ }
143
+ }
144
+ catch {
145
+ // Migration is best-effort
146
+ }
147
+ }
148
+ function scheduleGlobalWrite() {
149
+ if (_globalWriteTimer)
150
+ return;
151
+ _globalWriteTimer = setTimeout(() => {
152
+ _globalWriteTimer = undefined;
153
+ if (!_globalDirty)
154
+ return;
155
+ _globalDirty = false;
156
+ const now = Date.now();
157
+ const obj = {};
158
+ for (const [ns, map] of _globalStore) {
159
+ const entries = {};
160
+ for (const [key, entry] of map) {
161
+ if (entry.expires > now)
162
+ entries[key] = entry;
163
+ }
164
+ if (Object.keys(entries).length > 0)
165
+ obj[ns] = entries;
166
+ }
167
+ // Async write — non-blocking
168
+ writeFile(CACHE_FILE, JSON.stringify(obj), "utf-8").catch(() => { });
169
+ }, 2000);
170
+ if (typeof _globalWriteTimer === "object" && "unref" in _globalWriteTimer) {
171
+ _globalWriteTimer.unref();
172
+ }
173
+ }
174
+ // ─── Per-module cache interface ──────────────────────────────────────
175
+ class DiskCache {
176
+ ns;
177
+ ttlMs;
178
+ constructor(ttlMs, name) {
179
+ this.ttlMs = ttlMs;
180
+ this.ns = name;
181
+ }
182
+ getMap() {
183
+ loadGlobal(); // Lazy — only reads disk on first access
184
+ let map = _globalStore.get(this.ns);
185
+ if (!map) {
186
+ map = new Map();
187
+ _globalStore.set(this.ns, map);
188
+ }
189
+ return map;
190
+ }
191
+ get(key) {
192
+ const map = this.getMap();
193
+ const entry = map.get(key);
194
+ if (!entry)
195
+ return undefined;
196
+ if (Date.now() > entry.expires) {
197
+ map.delete(key);
198
+ _globalDirty = true;
199
+ scheduleGlobalWrite();
200
+ return undefined;
201
+ }
202
+ // Update last access for LRU
203
+ entry.lastAccess = Date.now();
204
+ return entry.data;
205
+ }
206
+ set(key, data) {
207
+ if (this.ttlMs <= 0)
208
+ return;
209
+ const map = this.getMap();
210
+ // LRU eviction if at capacity
211
+ if (map.size >= MAX_ENTRIES_PER_MODULE && !map.has(key)) {
212
+ let oldestKey;
213
+ let oldestAccess = Infinity;
214
+ for (const [k, e] of map) {
215
+ const access = e.lastAccess ?? e.expires - this.ttlMs;
216
+ if (access < oldestAccess) {
217
+ oldestAccess = access;
218
+ oldestKey = k;
219
+ }
220
+ }
221
+ if (oldestKey)
222
+ map.delete(oldestKey);
223
+ }
224
+ const now = Date.now();
225
+ map.set(key, { data, expires: now + this.ttlMs, lastAccess: now });
226
+ _globalDirty = true;
227
+ scheduleGlobalWrite();
228
+ }
229
+ clear() {
230
+ _globalStore.delete(this.ns);
231
+ _globalDirty = true;
232
+ scheduleGlobalWrite();
233
+ }
234
+ get size() {
235
+ const map = _globalStore.get(this.ns);
236
+ if (!map)
237
+ return 0;
238
+ const now = Date.now();
239
+ let count = 0;
240
+ for (const entry of map.values()) {
241
+ if (now <= entry.expires)
242
+ count++;
243
+ }
244
+ return count;
245
+ }
246
+ }
247
+ // ─── Fetch with timeout ──────────────────────────────────────────────
248
+ async function fetchTimeout(url, init, timeoutMs) {
249
+ const controller = new AbortController();
250
+ const timer = setTimeout(() => controller.abort(), timeoutMs);
251
+ try {
252
+ return await fetch(url, { ...init, signal: controller.signal });
253
+ }
254
+ finally {
255
+ clearTimeout(timer);
256
+ }
257
+ }
258
+ // ─── Retry logic ─────────────────────────────────────────────────────
259
+ const RETRYABLE = [429, 502, 503, 504];
260
+ async function fetchRetry(url, init, timeoutMs, limiter, name, maxRetries = 2) {
261
+ let lastErr = null;
262
+ for (let attempt = 0; attempt <= maxRetries; attempt++) {
263
+ await limiter.acquire();
264
+ try {
265
+ const res = await fetchTimeout(url, init, timeoutMs);
266
+ if (RETRYABLE.includes(res.status) && attempt < maxRetries) {
267
+ const retryAfter = res.headers.get("Retry-After");
268
+ const delay = retryAfter ? (+retryAfter || 1) * 1000 : 1000 * 2 ** attempt;
269
+ console.error(`${name}: HTTP ${res.status}, retry in ${delay}ms (${attempt + 1}/${maxRetries})`);
270
+ await new Promise(r => setTimeout(r, delay));
271
+ continue;
272
+ }
273
+ return res;
274
+ }
275
+ catch (e) {
276
+ lastErr = e instanceof Error ? e : new Error(String(e));
277
+ if (attempt < maxRetries) {
278
+ const delay = 1000 * 2 ** attempt;
279
+ console.error(`${name}: ${lastErr.message}, retry in ${delay}ms (${attempt + 1}/${maxRetries})`);
280
+ await new Promise(r => setTimeout(r, delay));
281
+ }
282
+ }
283
+ }
284
+ throw lastErr ?? new Error("Request failed");
285
+ }
286
+ // ─── Client Factory ──────────────────────────────────────────────────
287
+ export function createClient(config) {
288
+ const { baseUrl, name, auth, defaultHeaders = {}, cacheTtlMs = 5 * 60 * 1000, timeoutMs = 30_000, checkError, } = config;
289
+ const rl = config.rateLimit ?? { perSecond: 5, burst: 10 };
290
+ const limiter = new TokenBucket(rl.burst, rl.perSecond);
291
+ const cache = new DiskCache(cacheTtlMs, name);
292
+ function getApiKey() {
293
+ return auth ? process.env[auth.envVar] : undefined;
294
+ }
295
+ function buildUrl(path, params) {
296
+ const parts = [];
297
+ // Auth via query param
298
+ if (auth?.type === "query") {
299
+ const key = getApiKey();
300
+ if (key)
301
+ parts.push(`${auth.key}=${encodeURIComponent(key)}`);
302
+ if (auth.extraParams) {
303
+ for (const [k, v] of Object.entries(auth.extraParams))
304
+ parts.push(`${k}=${encodeURIComponent(v)}`);
305
+ }
306
+ }
307
+ // User params — supports string, number, and string[] (repeated keys)
308
+ // Keys are NOT encoded — preserves bracket syntax like page[number], facets[series][]
309
+ if (params) {
310
+ for (const [k, v] of Object.entries(params)) {
311
+ if (v === undefined || v === "")
312
+ continue;
313
+ if (Array.isArray(v)) {
314
+ for (const item of v)
315
+ parts.push(`${k}=${encodeURIComponent(String(item))}`);
316
+ }
317
+ else {
318
+ parts.push(`${k}=${encodeURIComponent(String(v))}`);
319
+ }
320
+ }
321
+ }
322
+ const p = path.startsWith("/") ? path : `/${path}`;
323
+ return parts.length ? `${baseUrl}${p}?${parts.join("&")}` : `${baseUrl}${p}`;
324
+ }
325
+ function buildHeaders(extra) {
326
+ const h = { ...defaultHeaders, ...extra };
327
+ if (auth?.type === "header") {
328
+ const key = getApiKey();
329
+ if (key)
330
+ h[auth.key] = (auth.prefix ?? "") + key;
331
+ }
332
+ return h;
333
+ }
334
+ async function request(url, init) {
335
+ // Check cache
336
+ const cacheKey = init?.body ? `${url}|${init.body}` : url;
337
+ const cached = cache.get(cacheKey);
338
+ if (cached !== undefined)
339
+ return cached;
340
+ const res = await fetchRetry(url, init, timeoutMs, limiter, name);
341
+ if (!res.ok) {
342
+ const body = await res.text();
343
+ // Friendly error for auth failures when API key is missing
344
+ if ((res.status === 401 || res.status === 403) && auth && !getApiKey()) {
345
+ throw new Error(`${name}: API key required (HTTP ${res.status}). ` +
346
+ `Set the ${auth.envVar} environment variable in your .env file or MCP config.`);
347
+ }
348
+ throw new Error(`${name}: HTTP ${res.status} — ${body || res.statusText}`);
349
+ }
350
+ const data = await res.json();
351
+ // Check for API-level errors in body
352
+ if (checkError) {
353
+ const err = checkError(data);
354
+ if (err)
355
+ throw new Error(`${name}: ${err}`);
356
+ }
357
+ cache.set(cacheKey, data);
358
+ return data;
359
+ }
360
+ return {
361
+ async get(path, params) {
362
+ const url = buildUrl(path, params);
363
+ const headers = buildHeaders();
364
+ return request(url, Object.keys(headers).length ? { headers } : undefined);
365
+ },
366
+ async post(path, body, params) {
367
+ const url = buildUrl(path, params);
368
+ const headers = buildHeaders({ "Content-Type": "application/json" });
369
+ // Auth via body (e.g. BLS)
370
+ const finalBody = { ...body };
371
+ if (auth?.type === "body") {
372
+ const key = getApiKey();
373
+ if (key) {
374
+ finalBody[auth.key] = key;
375
+ if (auth.extraParams)
376
+ Object.assign(finalBody, auth.extraParams);
377
+ }
378
+ }
379
+ return request(url, {
380
+ method: "POST",
381
+ headers,
382
+ body: JSON.stringify(finalBody),
383
+ });
384
+ },
385
+ clearCache() { cache.clear(); },
386
+ };
387
+ }
388
+ //# sourceMappingURL=client.js.map