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
@@ -0,0 +1,659 @@
1
+ /**
2
+ * Congress.gov SDK — typed API client for the Congress.gov API (v3).
3
+ *
4
+ * Standalone — no MCP server required. Usage:
5
+ *
6
+ * import { searchBills, getBillDetails } from "us-gov-open-data/sdk/congress";
7
+ *
8
+ * Requires DATA_GOV_API_KEY env var. Get one at https://api.data.gov/signup/
9
+ */
10
+ import { createClient } from "../client.js";
11
+ // ─── Client ──────────────────────────────────────────────────────────
12
+ const api = createClient({
13
+ baseUrl: "https://api.congress.gov/v3",
14
+ name: "congress",
15
+ auth: {
16
+ type: "query",
17
+ key: "api_key",
18
+ envVar: "DATA_GOV_API_KEY",
19
+ extraParams: { format: "json" },
20
+ },
21
+ rateLimit: { perSecond: 5, burst: 10 },
22
+ cacheTtlMs: 30 * 60 * 1000, // 30 min
23
+ });
24
+ // ─── Helpers ─────────────────────────────────────────────────────────
25
+ /** Current congress number based on date. */
26
+ export function currentCongress() {
27
+ const year = new Date().getFullYear();
28
+ return Math.floor((year - 1789) / 2) + 1;
29
+ }
30
+ /** Map bill type codes to Congress.gov URL segments. */
31
+ const BILL_URL_SEGMENTS = {
32
+ hr: "house-bill",
33
+ s: "senate-bill",
34
+ hjres: "house-joint-resolution",
35
+ sjres: "senate-joint-resolution",
36
+ hconres: "house-concurrent-resolution",
37
+ sconres: "senate-concurrent-resolution",
38
+ hres: "house-resolution",
39
+ sres: "senate-resolution",
40
+ };
41
+ export function billTypeToUrlSegment(billType) {
42
+ return BILL_URL_SEGMENTS[billType.toLowerCase()] || "house-bill";
43
+ }
44
+ // ─── Reference data ──────────────────────────────────────────────────
45
+ export const billTypes = {
46
+ hr: { name: "House Bill", urlSegment: "house-bill" },
47
+ s: { name: "Senate Bill", urlSegment: "senate-bill" },
48
+ hjres: { name: "House Joint Resolution", urlSegment: "house-joint-resolution" },
49
+ sjres: { name: "Senate Joint Resolution", urlSegment: "senate-joint-resolution" },
50
+ hconres: { name: "House Concurrent Resolution", urlSegment: "house-concurrent-resolution" },
51
+ sconres: { name: "Senate Concurrent Resolution", urlSegment: "senate-concurrent-resolution" },
52
+ hres: { name: "House Simple Resolution", urlSegment: "house-resolution" },
53
+ sres: { name: "Senate Simple Resolution", urlSegment: "senate-resolution" },
54
+ };
55
+ export const congressNumbers = {
56
+ 119: "2025-2026", 118: "2023-2024", 117: "2021-2022",
57
+ 116: "2019-2020", 115: "2017-2018", 114: "2015-2016",
58
+ 113: "2013-2014", 112: "2011-2012", 111: "2009-2010",
59
+ };
60
+ // ─── Public API ──────────────────────────────────────────────────────
61
+ /**
62
+ * Search/list bills by congress number and/or bill type.
63
+ *
64
+ * NOTE: The Congress.gov API v3 does NOT support text/keyword search on the /bill endpoint.
65
+ * The `query` parameter is accepted but used for client-side title filtering only.
66
+ * To find specific bills, use `getBillDetails()` with known bill numbers, or browse
67
+ * by congress/bill_type and filter results.
68
+ */
69
+ export async function searchBills(opts = {}) {
70
+ const congressNum = opts.congress ?? currentCongress();
71
+ let path = `/bill/${congressNum}`;
72
+ if (opts.bill_type)
73
+ path += `/${opts.bill_type.toLowerCase()}`;
74
+ // Fetch more if we need to filter client-side
75
+ const fetchLimit = opts.query ? Math.min((opts.limit ?? 20) * 5, 250) : (opts.limit ?? 20);
76
+ const res = await api.get(path, {
77
+ limit: fetchLimit,
78
+ offset: opts.offset ?? 0,
79
+ sort: "updateDate+desc",
80
+ });
81
+ let bills = res.bills ?? [];
82
+ // Client-side keyword filtering since the API doesn't support text search
83
+ if (opts.query) {
84
+ const terms = opts.query.toLowerCase().split(/\s+/).filter(Boolean);
85
+ bills = bills.filter(b => {
86
+ const title = (b.title ?? "").toLowerCase();
87
+ return terms.some(t => title.includes(t));
88
+ });
89
+ bills = bills.slice(0, opts.limit ?? 20);
90
+ }
91
+ return { bills };
92
+ }
93
+ /** Get detailed information about a specific bill, including cosponsors with party breakdown. */
94
+ export async function getBillDetails(congress, billType, billNumber) {
95
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}`);
96
+ const bill = res.bill ?? res;
97
+ // Secondary call for cosponsors
98
+ let cosponsors = [];
99
+ const cosponsorPartyBreakdown = {};
100
+ try {
101
+ const cRes = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/cosponsors`, { limit: 250 });
102
+ cosponsors = cRes.cosponsors ?? [];
103
+ for (const c of cosponsors) {
104
+ const party = (c.party ?? "?");
105
+ cosponsorPartyBreakdown[party] = (cosponsorPartyBreakdown[party] ?? 0) + 1;
106
+ }
107
+ }
108
+ catch {
109
+ // Non-critical — some bills may not have cosponsors
110
+ }
111
+ return { bill, cosponsors, cosponsorPartyBreakdown };
112
+ }
113
+ /** Search members of Congress by state, congress, and/or district. */
114
+ export async function searchMembers(opts = {}) {
115
+ let path;
116
+ if (opts.state && opts.district !== undefined) {
117
+ const congressNum = opts.congress ?? currentCongress();
118
+ path = `/member/congress/${congressNum}/${opts.state.toUpperCase()}/${opts.district}`;
119
+ }
120
+ else if (opts.state) {
121
+ path = `/member/${opts.state.toUpperCase()}`;
122
+ }
123
+ else if (opts.congress) {
124
+ path = `/member/congress/${opts.congress}`;
125
+ }
126
+ else {
127
+ path = `/member`;
128
+ }
129
+ const res = await api.get(path, { limit: opts.limit ?? 50 });
130
+ return { members: res.members ?? [] };
131
+ }
132
+ /** Convert a calendar year to Congress number and session. */
133
+ function yearToCongress(year) {
134
+ const congress = Math.floor((year - 1789) / 2) + 1;
135
+ const session = (year % 2 === 1 ? 1 : 2);
136
+ return { congress, session };
137
+ }
138
+ /**
139
+ * Get House roll call votes.
140
+ *
141
+ * Primary source: Congress.gov API (currently 118th–119th Congress, beta).
142
+ * Fallback: clerk.house.gov XML (coverage: 1990 to present) — fills gaps
143
+ * where the API returns no data (e.g. older congresses).
144
+ *
145
+ * Data sources:
146
+ * - https://api.congress.gov/v3/house-vote
147
+ * - https://clerk.house.gov/Votes
148
+ */
149
+ export async function getHouseVotes(opts = {}) {
150
+ // Resolve congress+session from year if needed (API uses congress/session)
151
+ const resolvedOpts = { ...opts };
152
+ if (opts.year && !opts.congress) {
153
+ const { congress, session } = yearToCongress(opts.year);
154
+ resolvedOpts.congress = congress;
155
+ resolvedOpts.session = opts.session ?? session;
156
+ }
157
+ // Try Congress.gov API first
158
+ try {
159
+ const apiResult = await getHouseVotesFromApi(resolvedOpts);
160
+ const hasData = opts.vote_number
161
+ ? (apiResult.members?.length ?? 0) > 0 || apiResult.vote != null
162
+ : (apiResult.votes?.length ?? 0) > 0;
163
+ if (hasData)
164
+ return apiResult;
165
+ }
166
+ catch {
167
+ // API error — continue to clerk fallback
168
+ }
169
+ // Fall back to clerk.house.gov XML (1990–present)
170
+ return getHouseVotesFromClerk(opts);
171
+ }
172
+ /** Fallback: Fetch House votes from clerk.house.gov XML (1990–present). */
173
+ async function getHouseVotesFromClerk(opts) {
174
+ // Resolve year (either explicit or derived from congress+session)
175
+ let year;
176
+ if (opts.year) {
177
+ year = opts.year;
178
+ }
179
+ else {
180
+ const congressNum = opts.congress ?? currentCongress();
181
+ const sessionNum = opts.session ?? currentSession();
182
+ year = congressSessionToYear(congressNum, sessionNum);
183
+ }
184
+ if (opts.vote_number) {
185
+ // Fetch individual vote XML from clerk.house.gov
186
+ const num = String(opts.vote_number).padStart(3, "0");
187
+ const url = `${HOUSE_CLERK_BASE}/${year}/roll${num}.xml`;
188
+ const resp = await fetch(url, {
189
+ headers: { "User-Agent": "us-gov-open-data-mcp/2.0 (gov-accountability-tool)" },
190
+ signal: AbortSignal.timeout(30_000),
191
+ });
192
+ if (!resp.ok)
193
+ throw new Error(`House vote fetch failed: ${resp.status} ${resp.statusText} (${url})`);
194
+ const xml = await resp.text();
195
+ const parsed = parseXml(xml);
196
+ const rc = (parsed["rollcall-vote"] ?? {});
197
+ const meta = (rc["vote-metadata"] ?? {});
198
+ const voteDataNode = (rc["vote-data"] ?? {});
199
+ // Build vote summary
200
+ const vote = {
201
+ rollCallNumber: Number(meta["rollcall-num"]) || opts.vote_number,
202
+ date: String(meta["action-date"] ?? ""),
203
+ question: String(meta["vote-question"] ?? ""),
204
+ voteQuestion: String(meta["vote-question"] ?? ""),
205
+ result: String(meta["vote-result"] ?? ""),
206
+ description: String(meta["vote-desc"] ?? ""),
207
+ legislationNumber: String(meta["legis-num"] ?? ""),
208
+ voteType: String(meta["vote-type"] ?? ""),
209
+ };
210
+ // Parse members from recorded-vote entries
211
+ const recordedVotes = (voteDataNode["recorded-vote"] ?? []);
212
+ const members = recordedVotes.map((rv) => {
213
+ const leg = (rv.legislator ?? {});
214
+ return {
215
+ bioguideID: String(leg["@_name-id"] ?? ""),
216
+ lastName: String(leg["@_sort-field"] ?? leg["@_unaccented-name"] ?? ""),
217
+ firstName: "",
218
+ voteParty: String(leg["@_party"] ?? ""),
219
+ party: String(leg["@_party"] ?? ""),
220
+ voteState: String(leg["@_state"] ?? ""),
221
+ voteCast: String(rv.vote ?? ""),
222
+ votePosition: String(rv.vote ?? ""),
223
+ };
224
+ });
225
+ // Build party tally
226
+ const partyTally = {};
227
+ for (const m of members) {
228
+ const p = m.party ?? "?";
229
+ const v = m.votePosition ?? "?";
230
+ if (!partyTally[p])
231
+ partyTally[p] = {};
232
+ partyTally[p][v] = (partyTally[p][v] ?? 0) + 1;
233
+ }
234
+ return { vote, members, partyTally, source: "clerk.house.gov" };
235
+ }
236
+ // List votes: parse HTML index page from clerk.house.gov
237
+ const url = `${HOUSE_CLERK_BASE}/${year}/index.asp`;
238
+ const resp = await fetch(url, {
239
+ headers: { "User-Agent": "us-gov-open-data-mcp/2.0 (gov-accountability-tool)" },
240
+ signal: AbortSignal.timeout(30_000),
241
+ });
242
+ if (!resp.ok)
243
+ throw new Error(`House vote index fetch failed: ${resp.status} (${url})`);
244
+ const html = await resp.text();
245
+ const votes = parseHouseVoteIndex(html, opts.limit ?? 20);
246
+ return { votes, source: "clerk.house.gov" };
247
+ }
248
+ /** Primary: Fetch House votes from Congress.gov API (118th–119th Congress, beta). */
249
+ async function getHouseVotesFromApi(opts) {
250
+ const congressNum = opts.congress ?? currentCongress();
251
+ if (opts.vote_number && opts.session) {
252
+ // Try member-level breakdown first
253
+ try {
254
+ const res = await api.get(`/house-vote/${congressNum}/${opts.session}/${opts.vote_number}/members`);
255
+ const raw = res.houseRollCallVoteMemberVotes?.results ?? [];
256
+ const members = raw.map((m) => ({
257
+ ...m,
258
+ party: m.voteParty,
259
+ votePosition: m.voteCast,
260
+ }));
261
+ const partyTally = {};
262
+ for (const m of members) {
263
+ const party = (m.party ?? "?");
264
+ const pos = (m.votePosition ?? "?");
265
+ if (!partyTally[party])
266
+ partyTally[party] = {};
267
+ partyTally[party][pos] = (partyTally[party][pos] ?? 0) + 1;
268
+ }
269
+ return { members, partyTally, source: "api.congress.gov" };
270
+ }
271
+ catch {
272
+ // Fall back to vote summary
273
+ const vRes = await api.get(`/house-vote/${congressNum}/${opts.session}/${opts.vote_number}`);
274
+ const vote = vRes.houseRollCallVote ?? vRes;
275
+ if (vote.votePartyTotal) {
276
+ const partyTally = {};
277
+ for (const pt of vote.votePartyTotal) {
278
+ partyTally[pt.voteParty] = {
279
+ Yea: pt.yeaTotal,
280
+ Nay: pt.nayTotal,
281
+ "Not Voting": pt.notVotingTotal,
282
+ Present: pt.presentTotal,
283
+ };
284
+ }
285
+ return { vote, partyTally, source: "api.congress.gov" };
286
+ }
287
+ return { vote, source: "api.congress.gov" };
288
+ }
289
+ }
290
+ // List recent votes via API
291
+ let path = `/house-vote/${congressNum}`;
292
+ if (opts.session)
293
+ path += `/${opts.session}`;
294
+ const res = await api.get(path, { limit: opts.limit ?? 20 });
295
+ return { votes: res.houseRollCallVotes ?? [], source: "api.congress.gov" };
296
+ }
297
+ /** Parse the clerk.house.gov HTML index page to extract a vote list. */
298
+ function parseHouseVoteIndex(html, limit) {
299
+ const votes = [];
300
+ // Match each table row that contains vote data
301
+ const rowPattern = /<TR>\s*<TD>[\s\S]*?<\/TR>/gi;
302
+ let rowMatch;
303
+ while ((rowMatch = rowPattern.exec(html)) !== null && votes.length < limit) {
304
+ const row = rowMatch[0];
305
+ // Extract TD cells — content may contain nested tags
306
+ const cellPattern = /<TD[^>]*>([\s\S]*?)<\/TD>/gi;
307
+ const cells = [];
308
+ let cellMatch;
309
+ while ((cellMatch = cellPattern.exec(row)) !== null) {
310
+ // Strip all HTML tags, collapse whitespace
311
+ cells.push(cellMatch[1].replace(/<[^>]+>/g, "").replace(/\s+/g, " ").trim());
312
+ }
313
+ if (cells.length < 6)
314
+ continue;
315
+ const rollNum = Number(cells[0]);
316
+ if (!rollNum)
317
+ continue;
318
+ const resultCode = cells[4].trim();
319
+ const result = resultCode === "P" ? "Passed" : resultCode === "F" ? "Failed" : resultCode === "A" ? "Agreed to" : resultCode;
320
+ votes.push({
321
+ rollCallNumber: rollNum,
322
+ date: cells[1],
323
+ question: cells[3],
324
+ result,
325
+ description: cells[5],
326
+ legislationNumber: cells[2],
327
+ });
328
+ }
329
+ return votes;
330
+ }
331
+ /** Get recently enacted laws. */
332
+ export async function getRecentLaws(opts = {}) {
333
+ const congressNum = opts.congress ?? currentCongress();
334
+ const res = await api.get(`/law/${congressNum}`, { limit: opts.limit ?? 20 });
335
+ return { laws: res.bills ?? res.laws ?? [] };
336
+ }
337
+ /** Get bills sponsored or cosponsored by a specific member. */
338
+ export async function getMemberBills(bioguideId, type = "sponsored", limit = 20) {
339
+ const legType = type === "cosponsored" ? "cosponsored-legislation" : "sponsored-legislation";
340
+ const res = await api.get(`/member/${bioguideId}/${legType}`, { limit });
341
+ return { bills: res.sponsoredLegislation ?? res.cosponsoredLegislation ?? [] };
342
+ }
343
+ // ─── Bill Sub-resource Methods ───────────────────────────────────────
344
+ /** Get the action history / timeline for a bill. */
345
+ export async function getBillActions(congress, billType, billNumber, limit = 100) {
346
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/actions`, { limit });
347
+ return { actions: res.actions ?? [] };
348
+ }
349
+ /** Get amendments filed on a bill. */
350
+ export async function getBillAmendments(congress, billType, billNumber, limit = 50) {
351
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/amendments`, { limit });
352
+ return { amendments: res.amendments ?? [] };
353
+ }
354
+ /** Get committees a bill was referred to. */
355
+ export async function getBillCommittees(congress, billType, billNumber) {
356
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/committees`);
357
+ return { committees: res.committees ?? [] };
358
+ }
359
+ /** Get related/companion bills. */
360
+ export async function getBillRelatedBills(congress, billType, billNumber, limit = 50) {
361
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/relatedbills`, { limit });
362
+ return { relatedBills: res.relatedBills ?? [] };
363
+ }
364
+ /** Get legislative subjects tagged on a bill. */
365
+ export async function getBillSubjects(congress, billType, billNumber, limit = 100) {
366
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/subjects`, { limit });
367
+ return {
368
+ subjects: res.subjects?.legislativeSubjects ?? res.legislativeSubjects ?? [],
369
+ policyArea: res.subjects?.policyArea?.name ?? res.policyArea?.name,
370
+ };
371
+ }
372
+ /** Get CRS summaries of a bill. */
373
+ export async function getBillSummaries(congress, billType, billNumber) {
374
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/summaries`);
375
+ return { summaries: res.summaries ?? [] };
376
+ }
377
+ /** Get text versions available for a bill. */
378
+ export async function getBillTextVersions(congress, billType, billNumber) {
379
+ const res = await api.get(`/bill/${congress}/${billType.toLowerCase()}/${billNumber}/text`);
380
+ return { textVersions: res.textVersions ?? [] };
381
+ }
382
+ // ─── Member Details ──────────────────────────────────────────────────
383
+ /** Get detailed information about a specific member by bioguide ID. */
384
+ export async function getMemberDetails(bioguideId) {
385
+ const res = await api.get(`/member/${bioguideId}`);
386
+ return { member: res.member ?? res };
387
+ }
388
+ // ─── Amendments ──────────────────────────────────────────────────────
389
+ /** Search/list amendments. */
390
+ export async function searchAmendments(opts = {}) {
391
+ let path = "/amendment";
392
+ if (opts.congress) {
393
+ path += `/${opts.congress}`;
394
+ if (opts.amendmentType)
395
+ path += `/${opts.amendmentType.toLowerCase()}`;
396
+ }
397
+ const res = await api.get(path, {
398
+ limit: opts.limit ?? 20,
399
+ });
400
+ return { amendments: res.amendments ?? [] };
401
+ }
402
+ /** Get details about a specific amendment (includes actions). */
403
+ export async function getAmendmentDetails(congress, amendmentType, amendmentNumber) {
404
+ const amtType = amendmentType.toLowerCase();
405
+ const res = await api.get(`/amendment/${congress}/${amtType}/${amendmentNumber}`);
406
+ let actions = [];
407
+ try {
408
+ const aRes = await api.get(`/amendment/${congress}/${amtType}/${amendmentNumber}/actions`);
409
+ actions = aRes.actions ?? [];
410
+ }
411
+ catch { /* non-critical */ }
412
+ return {
413
+ amendment: res.amendment ?? res,
414
+ actions,
415
+ };
416
+ }
417
+ // ─── Committees ──────────────────────────────────────────────────────
418
+ /** List congressional committees. */
419
+ export async function listCommittees(opts = {}) {
420
+ let path = "/committee";
421
+ if (opts.congress) {
422
+ path += `/${opts.congress}`;
423
+ }
424
+ if (opts.chamber) {
425
+ path += `/${opts.chamber.toLowerCase()}`;
426
+ }
427
+ const res = await api.get(path, {
428
+ limit: opts.limit ?? 50,
429
+ });
430
+ return { committees: res.committees ?? [] };
431
+ }
432
+ /** Get detailed committee info. */
433
+ export async function getCommitteeDetails(chamber, committeeCode) {
434
+ const res = await api.get(`/committee/${chamber.toLowerCase()}/${committeeCode}`);
435
+ return { committee: res.committee ?? res };
436
+ }
437
+ /** Get bills assigned to a committee. */
438
+ export async function getCommitteeBills(chamber, committeeCode, limit = 20) {
439
+ const res = await api.get(`/committee/${chamber.toLowerCase()}/${committeeCode}/bills`, { limit });
440
+ return { bills: res["committee-bills"]?.bills ?? res.bills ?? [] };
441
+ }
442
+ // ─── Nominations ─────────────────────────────────────────────────────
443
+ /** List presidential nominations for a congress. */
444
+ export async function listNominations(opts = {}) {
445
+ const congressNum = opts.congress ?? currentCongress();
446
+ const res = await api.get(`/nomination/${congressNum}`, { limit: opts.limit ?? 20 });
447
+ return { nominations: res.nominations ?? [] };
448
+ }
449
+ /** Get details about a specific nomination (includes actions). */
450
+ export async function getNominationDetails(congress, nominationNumber) {
451
+ const res = await api.get(`/nomination/${congress}/${nominationNumber}`);
452
+ let actions = [];
453
+ try {
454
+ const aRes = await api.get(`/nomination/${congress}/${nominationNumber}/actions`);
455
+ actions = aRes.actions ?? [];
456
+ }
457
+ catch { /* non-critical */ }
458
+ return {
459
+ nomination: res.nomination ?? res,
460
+ actions,
461
+ };
462
+ }
463
+ // ─── Treaties ────────────────────────────────────────────────────────
464
+ /** List treaties. */
465
+ export async function listTreaties(opts = {}) {
466
+ let path = "/treaty";
467
+ if (opts.congress)
468
+ path += `/${opts.congress}`;
469
+ const res = await api.get(path, {
470
+ limit: opts.limit ?? 20,
471
+ });
472
+ return { treaties: res.treaties ?? [] };
473
+ }
474
+ /** Get details about a specific treaty (includes actions). */
475
+ export async function getTreatyDetails(congress, treatyNumber) {
476
+ const res = await api.get(`/treaty/${congress}/${treatyNumber}`);
477
+ let actions = [];
478
+ try {
479
+ const aRes = await api.get(`/treaty/${congress}/${treatyNumber}/actions`);
480
+ actions = aRes.actions ?? [];
481
+ }
482
+ catch { /* non-critical */ }
483
+ return {
484
+ treaty: res.treaty ?? res,
485
+ actions,
486
+ };
487
+ }
488
+ // ─── CRS Reports ─────────────────────────────────────────────────────
489
+ /** Search Congressional Research Service reports. */
490
+ export async function searchCrsReports(opts = {}) {
491
+ const res = await api.get("/crsreport", { limit: opts.limit ?? 20 });
492
+ return { reports: res.CRSReports ?? res.reports ?? [] };
493
+ }
494
+ /** Get a specific CRS report by report number. */
495
+ export async function getCrsReportDetails(reportNumber) {
496
+ const res = await api.get(`/crsreport/${reportNumber}`);
497
+ return { report: res.CRSReport ?? res.report ?? res };
498
+ }
499
+ // ─── Congressional Record ────────────────────────────────────────────
500
+ /** Get Congressional Record issues. */
501
+ export async function getCongressionalRecord(opts = {}) {
502
+ const params = { limit: opts.limit ?? 20 };
503
+ if (opts.year)
504
+ params.y = opts.year;
505
+ if (opts.month)
506
+ params.m = opts.month;
507
+ if (opts.day)
508
+ params.d = opts.day;
509
+ const res = await api.get("/congressional-record", params);
510
+ return { issues: res.Results?.Issues ?? res.congressionalRecord ?? res.issues ?? [] };
511
+ }
512
+ // ─── Roll Call Votes (from clerk.house.gov & senate.gov XML) ─────────
513
+ import { XMLParser } from "fast-xml-parser";
514
+ const xmlParser = new XMLParser({
515
+ ignoreAttributes: false,
516
+ trimValues: true,
517
+ // Ensure arrays for repeating elements in House and Senate vote XML
518
+ isArray: (name) => name === "member" || name === "vote" ||
519
+ name === "recorded-vote" || name === "totals-by-party",
520
+ // Parse numeric-looking values as numbers
521
+ parseTagValue: true,
522
+ });
523
+ /** Parse XML string into a JS object using fast-xml-parser. */
524
+ function parseXml(xml) {
525
+ return xmlParser.parse(xml);
526
+ }
527
+ const HOUSE_CLERK_BASE = "https://clerk.house.gov/evs";
528
+ const SENATE_BASE = "https://www.senate.gov/legislative/LIS";
529
+ function padVoteNumber(n) {
530
+ return String(n).padStart(5, "0");
531
+ }
532
+ /** Convert congress number + session to calendar year. */
533
+ function congressSessionToYear(congress, session) {
534
+ return (congress - 1) * 2 + 1788 + session;
535
+ }
536
+ /**
537
+ * Get Senate roll call votes from senate.gov XML data.
538
+ *
539
+ * Coverage: 101st Congress (1989) to present — far deeper than the Congress.gov API
540
+ * which only has House votes for 118th-119th Congress.
541
+ *
542
+ * Data source: https://www.senate.gov/general/XML.htm
543
+ */
544
+ export async function getSenateVotes(opts = {}) {
545
+ const congressNum = opts.congress ?? currentCongress();
546
+ const sessionNum = opts.session ?? currentSession();
547
+ if (opts.vote_number) {
548
+ // Fetch individual vote XML
549
+ const url = `${SENATE_BASE}/roll_call_votes/vote${congressNum}${sessionNum}` +
550
+ `/vote_${congressNum}_${sessionNum}_${padVoteNumber(opts.vote_number)}.xml`;
551
+ const resp = await fetch(url, {
552
+ headers: { "User-Agent": "us-gov-open-data-mcp/2.0 (gov-accountability-tool)" },
553
+ signal: AbortSignal.timeout(30_000),
554
+ });
555
+ if (!resp.ok)
556
+ throw new Error(`Senate vote fetch failed: ${resp.status} ${resp.statusText} (${url})`);
557
+ const xml = await resp.text();
558
+ const parsed = parseXml(xml);
559
+ const rc = parsed.roll_call_vote ?? {};
560
+ // Parse vote metadata
561
+ const doc = rc.document;
562
+ const cnt = (rc.count ?? {});
563
+ const tb = (rc.tie_breaker ?? {});
564
+ const vote = {
565
+ congress: Number(rc.congress) || congressNum,
566
+ session: Number(rc.session) || sessionNum,
567
+ voteNumber: Number(rc.vote_number) || opts.vote_number,
568
+ date: String(rc.vote_date ?? ""),
569
+ question: String(rc.question ?? ""),
570
+ result: String(rc.vote_result ?? ""),
571
+ title: String(rc.vote_title ?? ""),
572
+ description: String(rc.vote_document_text ?? ""),
573
+ majorityRequired: String(rc.majority_requirement ?? ""),
574
+ count: {
575
+ yeas: Number(cnt.yeas) || 0,
576
+ nays: Number(cnt.nays) || 0,
577
+ present: Number(cnt.present) || 0,
578
+ absent: Number(cnt.absent) || 0,
579
+ },
580
+ };
581
+ if (doc) {
582
+ vote.document = {
583
+ type: String(doc.document_type ?? ""),
584
+ number: String(doc.document_number ?? ""),
585
+ name: String(doc.document_name ?? ""),
586
+ title: String(doc.document_title ?? ""),
587
+ };
588
+ }
589
+ const tbWho = String(tb.by_whom ?? "");
590
+ if (tbWho) {
591
+ vote.tieBreaker = { byWhom: tbWho, vote: String(tb.tie_breaker_vote ?? "") };
592
+ }
593
+ // Parse members
594
+ const membersContainer = (rc.members ?? {});
595
+ const rawMembers = (membersContainer.member ?? []);
596
+ const members = rawMembers.map((m) => ({
597
+ fullName: String(m.member_full ?? ""),
598
+ firstName: String(m.first_name ?? ""),
599
+ lastName: String(m.last_name ?? ""),
600
+ party: String(m.party ?? ""),
601
+ state: String(m.state ?? ""),
602
+ voteCast: String(m.vote_cast ?? ""),
603
+ }));
604
+ // Build party tally
605
+ const partyTally = {};
606
+ for (const m of members) {
607
+ const p = m.party || "?";
608
+ const v = m.voteCast || "?";
609
+ if (!partyTally[p])
610
+ partyTally[p] = {};
611
+ partyTally[p][v] = (partyTally[p][v] ?? 0) + 1;
612
+ }
613
+ return { vote, members, partyTally };
614
+ }
615
+ // List recent votes — fetch list XML
616
+ const listUrl = `${SENATE_BASE}/roll_call_lists/vote_menu_${congressNum}_${sessionNum}.xml`;
617
+ const resp = await fetch(listUrl, {
618
+ headers: { "User-Agent": "us-gov-open-data-mcp/2.0 (gov-accountability-tool)" },
619
+ signal: AbortSignal.timeout(30_000),
620
+ });
621
+ if (!resp.ok)
622
+ throw new Error(`Senate vote list fetch failed: ${resp.status} ${resp.statusText} (${listUrl})`);
623
+ const xml = await resp.text();
624
+ const parsed = parseXml(xml);
625
+ const summary = parsed.vote_summary ?? {};
626
+ const rawVotes = (summary.votes ?? {}).vote ?? [];
627
+ const voteArr = (Array.isArray(rawVotes) ? rawVotes : [rawVotes]);
628
+ const maxResults = opts.limit ?? 20;
629
+ const votes = voteArr.slice(0, maxResults).map((v) => {
630
+ const tally = (v.vote_tally ?? {});
631
+ return {
632
+ congress: congressNum,
633
+ session: sessionNum,
634
+ voteNumber: Number(v.vote_number) || 0,
635
+ date: String(v.vote_date ?? ""),
636
+ question: String(v.question ?? ""),
637
+ result: String(v.result ?? ""),
638
+ title: String(v.title ?? ""),
639
+ description: String(v.issue ?? ""),
640
+ majorityRequired: "",
641
+ count: {
642
+ yeas: Number(tally.yeas) || 0,
643
+ nays: Number(tally.nays) || 0,
644
+ present: 0,
645
+ absent: 0,
646
+ },
647
+ };
648
+ });
649
+ return { votes };
650
+ }
651
+ /** Helper: current session (1 for odd years, 2 for even years). */
652
+ function currentSession() {
653
+ return new Date().getFullYear() % 2 === 1 ? 1 : 2;
654
+ }
655
+ /** Clear cached responses. */
656
+ export function clearCache() {
657
+ api.clearCache();
658
+ }
659
+ //# sourceMappingURL=congress.js.map