@cliwant/mcp-sam-gov 1.3.0 → 1.5.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.
- package/README.ja.md +20 -12
- package/README.ko.md +20 -12
- package/README.md +62 -14
- package/dist/bea.d.ts +1 -1
- package/dist/bea.js +1 -1
- package/dist/cbp-border.d.ts +51 -0
- package/dist/cbp-border.d.ts.map +1 -0
- package/dist/cbp-border.js +123 -0
- package/dist/cbp-border.js.map +1 -0
- package/dist/census-economic.d.ts +1 -1
- package/dist/census-economic.d.ts.map +1 -1
- package/dist/census-economic.js +12 -6
- package/dist/census-economic.js.map +1 -1
- package/dist/cms-facility.d.ts +112 -0
- package/dist/cms-facility.d.ts.map +1 -0
- package/dist/cms-facility.js +311 -0
- package/dist/cms-facility.js.map +1 -0
- package/dist/cms-hospital.d.ts +105 -0
- package/dist/cms-hospital.d.ts.map +1 -0
- package/dist/cms-hospital.js +290 -0
- package/dist/cms-hospital.js.map +1 -0
- package/dist/cms-supplier.d.ts +133 -0
- package/dist/cms-supplier.d.ts.map +1 -0
- package/dist/cms-supplier.js +414 -0
- package/dist/cms-supplier.js.map +1 -0
- package/dist/cms-utilization.d.ts +113 -0
- package/dist/cms-utilization.d.ts.map +1 -0
- package/dist/cms-utilization.js +328 -0
- package/dist/cms-utilization.js.map +1 -0
- package/dist/courtlistener.d.ts +115 -0
- package/dist/courtlistener.d.ts.map +1 -0
- package/dist/courtlistener.js +398 -0
- package/dist/courtlistener.js.map +1 -0
- package/dist/cpsc.d.ts +81 -0
- package/dist/cpsc.d.ts.map +1 -0
- package/dist/cpsc.js +283 -0
- package/dist/cpsc.js.map +1 -0
- package/dist/datagov-catalog.d.ts.map +1 -1
- package/dist/datagov-catalog.js +16 -2
- package/dist/datagov-catalog.js.map +1 -1
- package/dist/dol.d.ts +2 -2
- package/dist/dol.js +5 -5
- package/dist/dol.js.map +1 -1
- package/dist/ecfr.d.ts +2 -2
- package/dist/ecfr.d.ts.map +1 -1
- package/dist/ecfr.js +24 -10
- package/dist/ecfr.js.map +1 -1
- package/dist/edgar.d.ts.map +1 -1
- package/dist/edgar.js +26 -6
- package/dist/edgar.js.map +1 -1
- package/dist/epa-envirofacts.d.ts +97 -0
- package/dist/epa-envirofacts.d.ts.map +1 -0
- package/dist/epa-envirofacts.js +305 -0
- package/dist/epa-envirofacts.js.map +1 -0
- package/dist/errors.d.ts.map +1 -1
- package/dist/errors.js +11 -0
- package/dist/errors.js.map +1 -1
- package/dist/far.d.ts.map +1 -1
- package/dist/far.js +3 -1
- package/dist/far.js.map +1 -1
- package/dist/federal-register.d.ts +2 -2
- package/dist/federal-register.d.ts.map +1 -1
- package/dist/federal-register.js +26 -10
- package/dist/federal-register.js.map +1 -1
- package/dist/fema.d.ts +36 -0
- package/dist/fema.d.ts.map +1 -1
- package/dist/fema.js +124 -0
- package/dist/fema.js.map +1 -1
- package/dist/fred.d.ts +1 -1
- package/dist/fred.js +1 -1
- package/dist/gov-domains.d.ts +66 -0
- package/dist/gov-domains.d.ts.map +1 -0
- package/dist/gov-domains.js +211 -0
- package/dist/gov-domains.js.map +1 -0
- package/dist/keys.d.ts +6 -5
- package/dist/keys.d.ts.map +1 -1
- package/dist/keys.js +25 -6
- package/dist/keys.js.map +1 -1
- package/dist/nhtsa.d.ts +91 -0
- package/dist/nhtsa.d.ts.map +1 -0
- package/dist/nhtsa.js +263 -0
- package/dist/nhtsa.js.map +1 -0
- package/dist/nist-controls.d.ts +48 -0
- package/dist/nist-controls.d.ts.map +1 -0
- package/dist/nist-controls.js +174 -0
- package/dist/nist-controls.js.map +1 -0
- package/dist/nonprofit.d.ts +116 -0
- package/dist/nonprofit.d.ts.map +1 -0
- package/dist/nonprofit.js +342 -0
- package/dist/nonprofit.js.map +1 -0
- package/dist/nws-weather.d.ts +57 -0
- package/dist/nws-weather.d.ts.map +1 -0
- package/dist/nws-weather.js +131 -0
- package/dist/nws-weather.js.map +1 -0
- package/dist/openfda-device.d.ts +85 -0
- package/dist/openfda-device.d.ts.map +1 -0
- package/dist/openfda-device.js +277 -0
- package/dist/openfda-device.js.map +1 -0
- package/dist/openfda-drugsfda.d.ts +72 -0
- package/dist/openfda-drugsfda.d.ts.map +1 -0
- package/dist/openfda-drugsfda.js +230 -0
- package/dist/openfda-drugsfda.js.map +1 -0
- package/dist/openfda.d.ts +133 -0
- package/dist/openfda.d.ts.map +1 -0
- package/dist/openfda.js +425 -0
- package/dist/openfda.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +996 -16
- package/dist/server.js.map +1 -1
- package/dist/treasury.d.ts +2 -0
- package/dist/treasury.d.ts.map +1 -1
- package/dist/treasury.js +7 -0
- package/dist/treasury.js.map +1 -1
- package/dist/usaspending.d.ts +32 -1
- package/dist/usaspending.d.ts.map +1 -1
- package/dist/usaspending.js +143 -16
- package/dist/usaspending.js.map +1 -1
- package/package.json +3 -2
- package/src/bea.ts +1 -1
- package/src/cbp-border.ts +177 -0
- package/src/census-economic.ts +12 -6
- package/src/cms-facility.ts +379 -0
- package/src/cms-hospital.ts +344 -0
- package/src/cms-supplier.ts +527 -0
- package/src/cms-utilization.ts +389 -0
- package/src/courtlistener.ts +465 -0
- package/src/cpsc.ts +333 -0
- package/src/datagov-catalog.ts +18 -2
- package/src/dol.ts +5 -5
- package/src/ecfr.ts +27 -10
- package/src/edgar.ts +39 -7
- package/src/epa-envirofacts.ts +358 -0
- package/src/errors.ts +11 -0
- package/src/far.ts +3 -1
- package/src/federal-register.ts +29 -10
- package/src/fema.ts +139 -0
- package/src/fred.ts +1 -1
- package/src/gov-domains.ts +237 -0
- package/src/keys.ts +27 -6
- package/src/nhtsa.ts +352 -0
- package/src/nist-controls.ts +219 -0
- package/src/nonprofit.ts +460 -0
- package/src/nws-weather.ts +167 -0
- package/src/openfda-device.ts +356 -0
- package/src/openfda-drugsfda.ts +313 -0
- package/src/openfda.ts +518 -0
- package/src/server.ts +1127 -27
- package/src/treasury.ts +7 -0
- package/src/usaspending.ts +189 -17
package/src/server.ts
CHANGED
|
@@ -55,20 +55,36 @@ import * as nsf from "./nsf.js";
|
|
|
55
55
|
import * as clinicaltrials from "./clinicaltrials.js";
|
|
56
56
|
import * as census from "./census.js";
|
|
57
57
|
import * as censusEconomic from "./census-economic.js";
|
|
58
|
+
import * as epaEnvirofacts from "./epa-envirofacts.js";
|
|
59
|
+
import * as cmsUtilization from "./cms-utilization.js";
|
|
60
|
+
import * as cmsHospital from "./cms-hospital.js";
|
|
61
|
+
import * as cmsFacility from "./cms-facility.js";
|
|
62
|
+
import * as cmsSupplier from "./cms-supplier.js";
|
|
58
63
|
import * as fred from "./fred.js";
|
|
59
64
|
import * as bea from "./bea.js";
|
|
60
65
|
import * as gsaPerdiem from "./gsa-perdiem.js";
|
|
61
66
|
import * as dol from "./dol.js";
|
|
62
67
|
import * as lda from "./lda.js";
|
|
68
|
+
import * as courtlistener from "./courtlistener.js";
|
|
69
|
+
import * as nonprofit from "./nonprofit.js";
|
|
63
70
|
import * as fema from "./fema.js";
|
|
71
|
+
import * as nws from "./nws-weather.js";
|
|
72
|
+
import * as govDomains from "./gov-domains.js";
|
|
64
73
|
import * as fdic from "./fdic.js";
|
|
65
74
|
import * as bls from "./bls.js";
|
|
66
75
|
import * as ofac from "./ofac.js";
|
|
67
76
|
import * as nvd from "./nvd.js";
|
|
77
|
+
import * as nistControls from "./nist-controls.js";
|
|
68
78
|
import * as nppes from "./nppes.js";
|
|
69
79
|
import * as cms from "./cms.js";
|
|
70
80
|
import * as fac from "./fac.js";
|
|
71
81
|
import * as usitc from "./usitc.js";
|
|
82
|
+
import * as openfda from "./openfda.js";
|
|
83
|
+
import * as openfdaDevice from "./openfda-device.js";
|
|
84
|
+
import * as openfdaDrugsfda from "./openfda-drugsfda.js";
|
|
85
|
+
import * as nhtsa from "./nhtsa.js";
|
|
86
|
+
import * as cpsc from "./cpsc.js";
|
|
87
|
+
import * as cbpBorder from "./cbp-border.js";
|
|
72
88
|
import { fetchAttachmentText } from "./attachments.js";
|
|
73
89
|
import * as keys from "./keys.js";
|
|
74
90
|
import { toToolError, ToolErrorCarrier, errorFromResponse } from "./errors.js";
|
|
@@ -84,7 +100,7 @@ import { realpathSync } from "node:fs";
|
|
|
84
100
|
const SERVER_NAME = "mcp-sam-gov";
|
|
85
101
|
// Kept in lockstep with package.json / manifest.json / server.json.
|
|
86
102
|
// Keep in sync with package.json "version" (asserted at release; see CHANGELOG).
|
|
87
|
-
const SERVER_VERSION = "1.
|
|
103
|
+
const SERVER_VERSION = "1.5.0";
|
|
88
104
|
|
|
89
105
|
// ─── Tool input schemas (Zod) ────────────────────────────────────
|
|
90
106
|
|
|
@@ -105,6 +121,7 @@ const SamSearchInput = z.object({
|
|
|
105
121
|
"Set-aside codes: SBA, 8A, HZS, SDVOSBC, WOSB, EDWOSB, VSA, VSS",
|
|
106
122
|
),
|
|
107
123
|
limit: z.number().min(1).max(50).optional(),
|
|
124
|
+
offset: z.number().min(0).optional().describe("Page offset into the result set (default 0)."),
|
|
108
125
|
});
|
|
109
126
|
|
|
110
127
|
// Pre-solicitation shaping radar (doc 06 §3.1). Surfaces Sources Sought /
|
|
@@ -218,7 +235,11 @@ const UsasIndividualAwardsInput = UsasFiltersBase.extend({
|
|
|
218
235
|
});
|
|
219
236
|
|
|
220
237
|
const UsasSubAgencyInput = z.object({
|
|
221
|
-
agency: z
|
|
238
|
+
agency: z
|
|
239
|
+
.string()
|
|
240
|
+
.describe(
|
|
241
|
+
"Canonical agency NAME (e.g. 'Department of Veterans Affairs'), NOT a toptier code — this filter matches by name; a numeric code silently matches nothing. Resolve via usas_lookup_agency / usas_list_toptier_agencies.",
|
|
242
|
+
),
|
|
222
243
|
fiscalYear: z.number().int().min(2007).optional(),
|
|
223
244
|
});
|
|
224
245
|
|
|
@@ -235,7 +256,14 @@ const UsasRecipientAwardsInput = z.object({
|
|
|
235
256
|
});
|
|
236
257
|
|
|
237
258
|
const UsasSubawardsInput = z.object({
|
|
238
|
-
|
|
259
|
+
// DRIFT/SEMANTICS FIX (dogfooding 2026-07-16): this filters the SUBAWARDEE name,
|
|
260
|
+
// NOT the prime. On spending_by_award{subawards:true} the only keyless recipient
|
|
261
|
+
// filter is `recipient_search_text`, which USAspending matches against the
|
|
262
|
+
// SUB-recipient (live-verified: recipient_search_text:["Leidos"] returns rows
|
|
263
|
+
// whose Sub-Awardee Name IS Leidos, under OTHER primes). The old name
|
|
264
|
+
// `primeRecipientName` promised the opposite. Renamed to `subRecipientName`; the
|
|
265
|
+
// #182 unknown-key guard makes the old name fail loud with the valid-key list.
|
|
266
|
+
subRecipientName: z.string().optional(),
|
|
239
267
|
agency: z.string().optional(),
|
|
240
268
|
naics: z.string().optional(),
|
|
241
269
|
fiscalYear: z.number().int().min(2007).optional(),
|
|
@@ -358,14 +386,24 @@ const UsasSpendingOverTimeInput = z.object({
|
|
|
358
386
|
});
|
|
359
387
|
|
|
360
388
|
const UsasCategorySpendingInput = z.object({
|
|
361
|
-
agency: z
|
|
389
|
+
agency: z
|
|
390
|
+
.string()
|
|
391
|
+
.optional()
|
|
392
|
+
.describe(
|
|
393
|
+
"Canonical agency NAME (e.g. 'Department of Veterans Affairs'), NOT a toptier code — this filter matches by name; a numeric code silently matches nothing. Resolve via usas_lookup_agency.",
|
|
394
|
+
),
|
|
362
395
|
naics: z.string().optional(),
|
|
363
396
|
fiscalYear: z.number().int().min(2007).optional(),
|
|
364
397
|
limit: z.number().min(1).max(50).optional(),
|
|
365
398
|
});
|
|
366
399
|
|
|
367
400
|
const UsasCfdaInput = z.object({
|
|
368
|
-
agency: z
|
|
401
|
+
agency: z
|
|
402
|
+
.string()
|
|
403
|
+
.optional()
|
|
404
|
+
.describe(
|
|
405
|
+
"Canonical agency NAME (e.g. 'Department of Veterans Affairs'), NOT a toptier code — this filter matches by name; a numeric code silently matches nothing. Resolve via usas_lookup_agency.",
|
|
406
|
+
),
|
|
369
407
|
fiscalYear: z.number().int().min(2007).optional(),
|
|
370
408
|
limit: z.number().min(1).max(50).optional(),
|
|
371
409
|
});
|
|
@@ -430,6 +468,25 @@ const UsasListAgenciesInput = z.object({
|
|
|
430
468
|
limit: z.number().min(1).max(150).optional(),
|
|
431
469
|
});
|
|
432
470
|
|
|
471
|
+
const UsasListDisasterCodesInput = z.object({});
|
|
472
|
+
|
|
473
|
+
const UsasDisasterSpendingInput = z.object({
|
|
474
|
+
defCodes: z
|
|
475
|
+
.array(z.string().min(1))
|
|
476
|
+
.min(1)
|
|
477
|
+
.describe(
|
|
478
|
+
"Disaster Emergency Fund Codes (DEFC) to include — REQUIRED. e.g. ['L','M'] (COVID-19 relief) or ['1'] (IIJA / infrastructure). Discover the full code set via usas_list_disaster_codes.",
|
|
479
|
+
),
|
|
480
|
+
spendingType: z
|
|
481
|
+
.enum(["obligation", "outlay"])
|
|
482
|
+
.optional()
|
|
483
|
+
.describe("obligation (default) or outlay. Some DEFCs report $0 obligations but real outlays — try both."),
|
|
484
|
+
geoLayer: z
|
|
485
|
+
.enum(["state", "county", "district"])
|
|
486
|
+
.optional()
|
|
487
|
+
.describe("Geographic breakout: state (default), county, or congressional district."),
|
|
488
|
+
});
|
|
489
|
+
|
|
433
490
|
// Federal Register
|
|
434
491
|
const FedRegSearchInput = z.object({
|
|
435
492
|
query: z.string().optional(),
|
|
@@ -984,6 +1041,41 @@ const CisaKevLookupInput = z.object({
|
|
|
984
1041
|
offset: z.number().min(0).optional().describe("Zero-based page offset (default 0)."),
|
|
985
1042
|
});
|
|
986
1043
|
|
|
1044
|
+
const NistControlsInput = z.object({
|
|
1045
|
+
controlId: z
|
|
1046
|
+
.string()
|
|
1047
|
+
.min(1)
|
|
1048
|
+
.optional()
|
|
1049
|
+
.describe("Exact control identifier, e.g. 'AC-2', 'SC-7', 'AC-2(1)' (case-insensitive; zero-padding is normalized)."),
|
|
1050
|
+
family: z
|
|
1051
|
+
.string()
|
|
1052
|
+
.min(1)
|
|
1053
|
+
.optional()
|
|
1054
|
+
.describe("Control family — the 2-letter code ('AC', 'SC', 'IA') OR a substring of the family name ('Access Control', 'Audit'). Case-insensitive."),
|
|
1055
|
+
keyword: z
|
|
1056
|
+
.string()
|
|
1057
|
+
.min(1)
|
|
1058
|
+
.optional()
|
|
1059
|
+
.describe("Case-insensitive substring searched over the control title + requirement statement."),
|
|
1060
|
+
limit: z.number().int().min(1).max(200).optional().describe("Max controls returned (default 25, max 200)."),
|
|
1061
|
+
offset: z.number().int().min(0).optional().describe("Zero-based page offset (default 0)."),
|
|
1062
|
+
});
|
|
1063
|
+
|
|
1064
|
+
const CbpBorderWaitInput = z.object({
|
|
1065
|
+
border: z
|
|
1066
|
+
.string()
|
|
1067
|
+
.min(1)
|
|
1068
|
+
.optional()
|
|
1069
|
+
.describe("Filter by border — case-insensitive substring, e.g. 'Canadian' or 'Mexican' (the feed labels ports 'Canadian Border' / 'Mexican Border')."),
|
|
1070
|
+
portName: z
|
|
1071
|
+
.string()
|
|
1072
|
+
.min(1)
|
|
1073
|
+
.optional()
|
|
1074
|
+
.describe("Filter by port name — case-insensitive substring, e.g. 'Laredo', 'Detroit'."),
|
|
1075
|
+
limit: z.number().int().min(1).max(200).optional().describe("Max ports returned (default 100, max 200)."),
|
|
1076
|
+
offset: z.number().int().min(0).optional().describe("Zero-based page offset (default 0)."),
|
|
1077
|
+
});
|
|
1078
|
+
|
|
987
1079
|
// ━━━ NPPES NPI Registry — the healthcare-provider identity/credentialing lane (1) ━━━ ADR-0036
|
|
988
1080
|
// nppes_lookup_provider: exact NPI detail OR search over CMS/HHS's keyless public
|
|
989
1081
|
// registry of every US healthcare provider (npiregistry.cms.hhs.gov/api, version=2.1
|
|
@@ -1315,9 +1407,11 @@ const TreasuryDatasetEnum = z
|
|
|
1315
1407
|
"mts_table_1",
|
|
1316
1408
|
"rates_of_exchange",
|
|
1317
1409
|
"debt_outstanding",
|
|
1410
|
+
"interest_expense",
|
|
1411
|
+
"tror",
|
|
1318
1412
|
])
|
|
1319
1413
|
.describe(
|
|
1320
|
-
"Which confirmed Treasury Fiscal Data dataset to query: debt_to_penny (daily total public debt), avg_interest_rates (avg rate by security type), mts_table_1 (Monthly Treasury Statement receipts/outlays/deficit), rates_of_exchange (quarterly FX by currency), debt_outstanding (historical fiscal-year-end debt).",
|
|
1414
|
+
"Which confirmed Treasury Fiscal Data dataset to query: debt_to_penny (daily total public debt), avg_interest_rates (avg rate by security type), mts_table_1 (Monthly Treasury Statement receipts/outlays/deficit), rates_of_exchange (quarterly FX by currency), debt_outstanding (historical fiscal-year-end debt), interest_expense (ACTUAL interest PAID / debt-service cost by security type — distinct from the rate), tror (Treasury Report on Receivables: federal receivables + delinquent-debt collections BY AGENCY).",
|
|
1321
1415
|
);
|
|
1322
1416
|
|
|
1323
1417
|
const TreasuryQueryDatasetInput = z.object({
|
|
@@ -2459,6 +2553,127 @@ const FemaDisasterDeclarationsInput = z.object({
|
|
|
2459
2553
|
.describe("0-based row offset ($skip) for pagination, default 0."),
|
|
2460
2554
|
});
|
|
2461
2555
|
|
|
2556
|
+
const FemaSearchHazardMitigationInput = z.object({
|
|
2557
|
+
state: z
|
|
2558
|
+
.string()
|
|
2559
|
+
.min(1)
|
|
2560
|
+
.optional()
|
|
2561
|
+
.describe("Filter by state (→ state eq '...'). Accepts EITHER a 2-letter code ('AL', like the other FEMA tools) OR the full name ('Alabama'); the module maps a 2-letter code to the full name this dataset requires."),
|
|
2562
|
+
programArea: z
|
|
2563
|
+
.string()
|
|
2564
|
+
.min(1)
|
|
2565
|
+
.optional()
|
|
2566
|
+
.describe("Filter by mitigation program (→ programArea eq '...'): HMGP (Hazard Mitigation Grant Program), FMA (Flood Mitigation Assistance), PDM (Pre-Disaster Mitigation), BRIC (Building Resilient Infrastructure and Communities), LPDM, FMA-SL."),
|
|
2567
|
+
disasterNumber: z
|
|
2568
|
+
.number()
|
|
2569
|
+
.int()
|
|
2570
|
+
.positive()
|
|
2571
|
+
.optional()
|
|
2572
|
+
.describe("Filter by FEMA disaster number (→ disasterNumber eq N)."),
|
|
2573
|
+
status: z
|
|
2574
|
+
.string()
|
|
2575
|
+
.min(1)
|
|
2576
|
+
.optional()
|
|
2577
|
+
.describe("Filter by project status (→ status eq '...'). e.g. 'Closed', 'Open'."),
|
|
2578
|
+
programFy: z
|
|
2579
|
+
.number()
|
|
2580
|
+
.int()
|
|
2581
|
+
.optional()
|
|
2582
|
+
.describe("Filter by program fiscal year (→ programFy eq N). e.g. 2005."),
|
|
2583
|
+
region: z
|
|
2584
|
+
.number()
|
|
2585
|
+
.int()
|
|
2586
|
+
.min(1)
|
|
2587
|
+
.max(10)
|
|
2588
|
+
.optional()
|
|
2589
|
+
.describe("Filter by FEMA region number 1–10 (→ region eq N)."),
|
|
2590
|
+
minProjectAmount: z
|
|
2591
|
+
.number()
|
|
2592
|
+
.optional()
|
|
2593
|
+
.describe("Minimum project amount (→ projectAmount ge N)."),
|
|
2594
|
+
maxProjectAmount: z
|
|
2595
|
+
.number()
|
|
2596
|
+
.optional()
|
|
2597
|
+
.describe("Maximum project amount (→ projectAmount le N)."),
|
|
2598
|
+
limit: z
|
|
2599
|
+
.number()
|
|
2600
|
+
.int()
|
|
2601
|
+
.min(1)
|
|
2602
|
+
.max(1000)
|
|
2603
|
+
.default(100)
|
|
2604
|
+
.describe("Rows per page ($top), 1..1000, default 100."),
|
|
2605
|
+
offset: z
|
|
2606
|
+
.number()
|
|
2607
|
+
.int()
|
|
2608
|
+
.min(0)
|
|
2609
|
+
.default(0)
|
|
2610
|
+
.describe("0-based row offset ($skip) for pagination, default 0."),
|
|
2611
|
+
});
|
|
2612
|
+
|
|
2613
|
+
const NwsActiveAlertsInput = z.object({
|
|
2614
|
+
state: z
|
|
2615
|
+
.string()
|
|
2616
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
2617
|
+
.optional()
|
|
2618
|
+
.describe("2-letter US state/territory code to scope alerts (→ NWS ?area=), e.g. 'CA'. Omit for all active US alerts."),
|
|
2619
|
+
event: z
|
|
2620
|
+
.string()
|
|
2621
|
+
.min(1)
|
|
2622
|
+
.optional()
|
|
2623
|
+
.describe("Filter by event type — case-insensitive substring, e.g. 'Flood', 'Wind', 'Winter Storm'."),
|
|
2624
|
+
severity: z
|
|
2625
|
+
.enum(["Extreme", "Severe", "Moderate", "Minor", "Unknown"])
|
|
2626
|
+
.optional()
|
|
2627
|
+
.describe("Filter by severity (exact): Extreme | Severe | Moderate | Minor | Unknown."),
|
|
2628
|
+
limit: z.number().int().min(1).max(500).optional().describe("Max alerts returned (default 50, max 500)."),
|
|
2629
|
+
offset: z.number().int().min(0).optional().describe("Zero-based page offset (default 0)."),
|
|
2630
|
+
});
|
|
2631
|
+
|
|
2632
|
+
const SearchGovDomainsInput = z.object({
|
|
2633
|
+
scope: z
|
|
2634
|
+
.enum(["all", "federal"])
|
|
2635
|
+
.optional()
|
|
2636
|
+
.describe("'all' (federal + SLED: state/county/city/school-district/special-district/tribal, ~16k rows, DEFAULT) or 'federal' (federal-only, ~1.3k rows)."),
|
|
2637
|
+
organization: z
|
|
2638
|
+
.string()
|
|
2639
|
+
.min(1)
|
|
2640
|
+
.optional()
|
|
2641
|
+
.describe("Organization name — case-insensitive SUBSTRING match (e.g. 'veterans', 'cybersecurity')."),
|
|
2642
|
+
domain: z
|
|
2643
|
+
.string()
|
|
2644
|
+
.min(1)
|
|
2645
|
+
.optional()
|
|
2646
|
+
.describe("Domain name — case-insensitive SUBSTRING match (e.g. 'cdc.gov', 'irs')."),
|
|
2647
|
+
domainType: z
|
|
2648
|
+
.string()
|
|
2649
|
+
.min(1)
|
|
2650
|
+
.optional()
|
|
2651
|
+
.describe("Domain type — case-insensitive match (e.g. 'Federal - Executive', 'County', 'Tribal', 'State or territory', 'School district')."),
|
|
2652
|
+
state: z
|
|
2653
|
+
.string()
|
|
2654
|
+
.min(1)
|
|
2655
|
+
.optional()
|
|
2656
|
+
.describe("2-letter state/territory code — case-insensitive exact match (e.g. 'CA')."),
|
|
2657
|
+
city: z
|
|
2658
|
+
.string()
|
|
2659
|
+
.min(1)
|
|
2660
|
+
.optional()
|
|
2661
|
+
.describe("City — case-insensitive SUBSTRING match."),
|
|
2662
|
+
limit: z
|
|
2663
|
+
.number()
|
|
2664
|
+
.int()
|
|
2665
|
+
.min(1)
|
|
2666
|
+
.max(500)
|
|
2667
|
+
.optional()
|
|
2668
|
+
.describe("Rows per page, 1..500, default 50."),
|
|
2669
|
+
offset: z
|
|
2670
|
+
.number()
|
|
2671
|
+
.int()
|
|
2672
|
+
.min(0)
|
|
2673
|
+
.optional()
|
|
2674
|
+
.describe("0-based row offset for pagination, default 0."),
|
|
2675
|
+
});
|
|
2676
|
+
|
|
2462
2677
|
// ─── EPA ECHO REST (keyless facility compliance/enforcement) — input schemas ──
|
|
2463
2678
|
// ADR-0009. KEYLESS, single fixed host (echodata.epa.gov) + three fixed service
|
|
2464
2679
|
// paths (the SSRF core — no free host/path). `state` is a curated US state/
|
|
@@ -2719,7 +2934,7 @@ const DatagovSearchDatasetsInput = z.object({
|
|
|
2719
2934
|
.min(1)
|
|
2720
2935
|
.max(500)
|
|
2721
2936
|
.optional()
|
|
2722
|
-
.describe("Free-text search over the dataset catalog (→
|
|
2937
|
+
.describe("Free-text search over the dataset catalog (→ q), e.g. 'wildfire'. LIVE-CONFIRMED to narrow (2026-07-16: the v4 API param is `q`; the old `_q` is silently ignored)."),
|
|
2723
2938
|
organization: z
|
|
2724
2939
|
.string()
|
|
2725
2940
|
.min(1)
|
|
@@ -3427,24 +3642,200 @@ const CensusBusinessPatternsInput = z.object({
|
|
|
3427
3642
|
),
|
|
3428
3643
|
});
|
|
3429
3644
|
|
|
3430
|
-
// ───
|
|
3431
|
-
//
|
|
3432
|
-
//
|
|
3433
|
-
//
|
|
3434
|
-
const
|
|
3435
|
-
|
|
3645
|
+
// ─── EPA Envirofacts TRI facilities (ADR-0059) — keyless, PATH-segment SSRF ──
|
|
3646
|
+
// data.epa.gov /efservice/tri_facility. Two requests: a count sub-query for the
|
|
3647
|
+
// EXACT total (P1) + the data slice. All user values ride as PATH SEGMENTS, so each
|
|
3648
|
+
// is charclass-validated + encodeURIComponent-encoded (the load-bearing SSRF guard).
|
|
3649
|
+
const EpaTriFacilitiesInput = z
|
|
3650
|
+
.object({
|
|
3651
|
+
state: z
|
|
3652
|
+
.string()
|
|
3653
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3654
|
+
.optional()
|
|
3655
|
+
.describe(
|
|
3656
|
+
"A 2-letter US state/territory code, e.g. 'VA', 'CA', 'PR' (→ state_abbr; case-insensitive). Provide at least this OR `facilityName`. Validated ^[A-Za-z]{2}$ (it rides in the request path).",
|
|
3657
|
+
),
|
|
3658
|
+
facilityName: z
|
|
3659
|
+
.string()
|
|
3660
|
+
.min(1)
|
|
3661
|
+
.max(100)
|
|
3662
|
+
.regex(/^[A-Za-z0-9 &.\-]+$/)
|
|
3663
|
+
.optional()
|
|
3664
|
+
.describe(
|
|
3665
|
+
"A partial facility-name match (→ facility_name/CONTAINING/…; case-insensitive), e.g. 'chemical', 'boeing'. Provide at least this OR `state`. Allowed: letters/digits/space/& - . (≤100 chars); '/' and '..' rejected (path-injection guard).",
|
|
3666
|
+
),
|
|
3667
|
+
county: z
|
|
3668
|
+
.string()
|
|
3669
|
+
.min(1)
|
|
3670
|
+
.max(100)
|
|
3671
|
+
.regex(/^[A-Za-z0-9 &.\-]+$/)
|
|
3672
|
+
.optional()
|
|
3673
|
+
.describe(
|
|
3674
|
+
"A partial county-name match (→ county_name/CONTAINING/…), e.g. 'FAIRFAX'. Optional additional filter; same charclass as facilityName.",
|
|
3675
|
+
),
|
|
3676
|
+
limit: z
|
|
3677
|
+
.number()
|
|
3678
|
+
.int()
|
|
3679
|
+
.min(1)
|
|
3680
|
+
.max(100)
|
|
3681
|
+
.optional()
|
|
3682
|
+
.describe("Max facilities to return (1–100, default 25). Offset-paginated."),
|
|
3683
|
+
offset: z
|
|
3684
|
+
.number()
|
|
3685
|
+
.int()
|
|
3686
|
+
.min(0)
|
|
3687
|
+
.optional()
|
|
3688
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3689
|
+
})
|
|
3690
|
+
.refine((v) => v.state !== undefined || v.facilityName !== undefined, {
|
|
3691
|
+
message: "Provide at least `state` or `facilityName` (an all-empty query would scan the whole national TRI table and is refused).",
|
|
3692
|
+
path: ["state"],
|
|
3693
|
+
});
|
|
3694
|
+
|
|
3695
|
+
// ─── CMS Medicare provider-service utilization (ADR-0061) — keyless, two-request ──
|
|
3696
|
+
// data.cms.gov /data-api/v1/dataset/{uuid}. Two requests: a stats count sub-query
|
|
3697
|
+
// for the EXACT total (P1 — found_rows) + the data slice (a bare JSON array). Filter
|
|
3698
|
+
// VALUES ride via URLSearchParams (bracket key + value encoded). REQUIRE npi OR
|
|
3699
|
+
// state (the 9.78M-row table is never scanned unscoped).
|
|
3700
|
+
const CmsMedicareProviderServicesInput = z
|
|
3701
|
+
.object({
|
|
3702
|
+
npi: z
|
|
3703
|
+
.string()
|
|
3704
|
+
.regex(/^\d{10}$/)
|
|
3705
|
+
.optional()
|
|
3706
|
+
.describe(
|
|
3707
|
+
"A 10-digit National Provider Identifier (→ Rndrng_NPI), e.g. '1003000126'. Provide at least this OR `state`. Validated ^\\d{10}$.",
|
|
3708
|
+
),
|
|
3709
|
+
state: z
|
|
3710
|
+
.string()
|
|
3711
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3712
|
+
.optional()
|
|
3713
|
+
.describe(
|
|
3714
|
+
"A 2-letter US state/territory code (→ Rndrng_Prvdr_State_Abrvtn), e.g. 'VA', 'CA'. Provide at least this OR `npi`. Validated ^[A-Za-z]{2}$.",
|
|
3715
|
+
),
|
|
3716
|
+
providerType: z
|
|
3717
|
+
.string()
|
|
3718
|
+
.min(1)
|
|
3719
|
+
.max(100)
|
|
3720
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
3721
|
+
.optional()
|
|
3722
|
+
.describe(
|
|
3723
|
+
"An optional specialty filter matching the CMS provider type EXACTLY (→ Rndrng_Prvdr_Type), e.g. 'Family Practice', 'Physical Therapist in Private Practice'. Allowed: letters/digits/space/& . , ( ) / ' - (≤100 chars).",
|
|
3724
|
+
),
|
|
3725
|
+
hcpcsCode: z
|
|
3726
|
+
.string()
|
|
3727
|
+
.regex(/^[A-Za-z0-9]{1,10}$/)
|
|
3728
|
+
.optional()
|
|
3729
|
+
.describe(
|
|
3730
|
+
"An optional HCPCS/CPT service code filter (→ HCPCS_Cd), e.g. '97110', 'G0463'. Validated ^[A-Za-z0-9]{1,10}$.",
|
|
3731
|
+
),
|
|
3732
|
+
size: z
|
|
3733
|
+
.number()
|
|
3734
|
+
.int()
|
|
3735
|
+
.min(1)
|
|
3736
|
+
.max(100)
|
|
3737
|
+
.optional()
|
|
3738
|
+
.describe("Max provider-service rows to return (1–100, default 25). Offset-paginated."),
|
|
3739
|
+
offset: z
|
|
3740
|
+
.number()
|
|
3741
|
+
.int()
|
|
3742
|
+
.min(0)
|
|
3743
|
+
.optional()
|
|
3744
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3745
|
+
})
|
|
3746
|
+
.refine((v) => v.npi !== undefined || v.state !== undefined, {
|
|
3747
|
+
message: "Provide at least `npi` or `state` (an all-empty query would scan the entire 9.78M-row Medicare utilization table and is refused; providerType/hcpcsCode alone are not enough to scope).",
|
|
3748
|
+
path: ["npi"],
|
|
3749
|
+
});
|
|
3750
|
+
|
|
3751
|
+
// ─── CMS Hospital Compare "Hospital General Information" (ADR-0062) — keyless ──
|
|
3752
|
+
// data.cms.gov /provider-data/api/1/datastore/query/{datasetId}/0. A SINGLE request:
|
|
3753
|
+
// the response's top-level `count` is the EXACT per-filter total (P1). Filters ride
|
|
3754
|
+
// as DKAN conditions[] triples via URLSearchParams (bracket key + value encoded).
|
|
3755
|
+
// REQUIRE state OR facilityName (the ~5,432-hospital table is never scanned unscoped).
|
|
3756
|
+
const CmsHospitalCompareInput = z
|
|
3757
|
+
.object({
|
|
3758
|
+
state: z
|
|
3759
|
+
.string()
|
|
3760
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3761
|
+
.optional()
|
|
3762
|
+
.describe(
|
|
3763
|
+
"A 2-letter US state/territory code (→ state, EXACT match), e.g. 'VA', 'CA'. Provide at least this OR `facilityName`. Validated ^[A-Za-z]{2}$.",
|
|
3764
|
+
),
|
|
3765
|
+
facilityName: z
|
|
3766
|
+
.string()
|
|
3767
|
+
.min(1)
|
|
3768
|
+
.max(100)
|
|
3769
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
3770
|
+
.optional()
|
|
3771
|
+
.describe(
|
|
3772
|
+
"A hospital-name fragment (→ facility_name, case-insensitive SUBSTRING/contains match), e.g. 'children'. Provide at least this OR `state`. Allowed: letters/digits/space/& . , ( ) / ' - (≤100 chars).",
|
|
3773
|
+
),
|
|
3774
|
+
hospitalType: z
|
|
3775
|
+
.string()
|
|
3776
|
+
.min(1)
|
|
3777
|
+
.max(100)
|
|
3778
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
3779
|
+
.optional()
|
|
3780
|
+
.describe(
|
|
3781
|
+
"An optional hospital-type filter (→ hospital_type, case-insensitive SUBSTRING/contains match), e.g. 'Acute', 'Critical Access'. Allowed: letters/digits/space/& . , ( ) / ' - (≤100 chars).",
|
|
3782
|
+
),
|
|
3783
|
+
size: z
|
|
3784
|
+
.number()
|
|
3785
|
+
.int()
|
|
3786
|
+
.min(1)
|
|
3787
|
+
.max(100)
|
|
3788
|
+
.optional()
|
|
3789
|
+
.describe("Max hospital rows to return (1–100, default 25). Offset-paginated."),
|
|
3790
|
+
offset: z
|
|
3791
|
+
.number()
|
|
3792
|
+
.int()
|
|
3793
|
+
.min(0)
|
|
3794
|
+
.optional()
|
|
3795
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3796
|
+
})
|
|
3797
|
+
.refine((v) => v.state !== undefined || v.facilityName !== undefined, {
|
|
3798
|
+
message: "Provide at least `state` or `facilityName` (an all-empty query would scan the entire ~5,432-hospital table and is refused; hospitalType alone is not enough to scope).",
|
|
3799
|
+
path: ["state"],
|
|
3800
|
+
});
|
|
3801
|
+
|
|
3802
|
+
// ─── CMS Facility Directory (data.cms.gov provider-data, ADR-0063) — KEYLESS ──
|
|
3803
|
+
// A four-dataset facility directory generalizing cms_hospital_compare beyond
|
|
3804
|
+
// hospitals. `facilityType` is a Zod ENUM that indexes a MODULE-CONSTANT map to a
|
|
3805
|
+
// VETTED dataset id (nursing_home → 4pq5-n9py, home_health → 6jpm-sxkc, hospice →
|
|
3806
|
+
// yc9t-dgbk, dialysis → 23ew-n7w9) — the user value never enters the URL path. A
|
|
3807
|
+
// SINGLE request: the response's top-level `count` is the EXACT per-filter total
|
|
3808
|
+
// (P1). Filters ride as DKAN conditions[] triples via URLSearchParams. name/address/
|
|
3809
|
+
// ownership columns vary per dataset → coalesced (null if none).
|
|
3810
|
+
const CmsFacilityDirectoryInput = z.object({
|
|
3811
|
+
facilityType: z
|
|
3812
|
+
.enum(["nursing_home", "home_health", "hospice", "dialysis"])
|
|
3813
|
+
.describe(
|
|
3814
|
+
"REQUIRED — which CMS provider-data dataset to search: 'nursing_home' (~14,695), 'home_health' (~12,460), 'hospice' (~6,852), or 'dialysis' (~7,490). Selects the dataset id via a constant map (the value never enters the URL path).",
|
|
3815
|
+
),
|
|
3816
|
+
state: z
|
|
3817
|
+
.string()
|
|
3818
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3819
|
+
.optional()
|
|
3820
|
+
.describe(
|
|
3821
|
+
"An optional 2-letter US state/territory code (→ state, EXACT match), e.g. 'VA', 'TX'. Validated ^[A-Za-z]{2}$.",
|
|
3822
|
+
),
|
|
3823
|
+
facilityName: z
|
|
3436
3824
|
.string()
|
|
3437
3825
|
.min(1)
|
|
3826
|
+
.max(100)
|
|
3827
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
3828
|
+
.optional()
|
|
3438
3829
|
.describe(
|
|
3439
|
-
"
|
|
3830
|
+
"An optional facility-name fragment (case-insensitive SUBSTRING/contains match against the dataset's primary-name column). Allowed: letters/digits/space/& . , ( ) / ' - (≤100 chars).",
|
|
3440
3831
|
),
|
|
3441
|
-
|
|
3832
|
+
size: z
|
|
3442
3833
|
.number()
|
|
3443
3834
|
.int()
|
|
3444
3835
|
.min(1)
|
|
3445
|
-
.max(
|
|
3836
|
+
.max(100)
|
|
3446
3837
|
.optional()
|
|
3447
|
-
.describe("Max
|
|
3838
|
+
.describe("Max facility rows to return (1–100, default 25). Offset-paginated."),
|
|
3448
3839
|
offset: z
|
|
3449
3840
|
.number()
|
|
3450
3841
|
.int()
|
|
@@ -3453,7 +3844,115 @@ const FredSearchSeriesInput = z.object({
|
|
|
3453
3844
|
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3454
3845
|
});
|
|
3455
3846
|
|
|
3456
|
-
|
|
3847
|
+
// ─── CMS DMEPOS by Supplier (data.cms.gov data-API, ADR-0064) — KEYLESS ──
|
|
3848
|
+
// SAME host/endpoint/two-request-stats-count pattern as cms_medicare_provider_services.
|
|
3849
|
+
// REQUIRE npi OR state (the supplier table is never scanned unscoped). Filter VALUES
|
|
3850
|
+
// ride as URLSearchParams filter[Col]=Val (bracket key + value encoded — the SSRF guard).
|
|
3851
|
+
const CmsDmeposSuppliersInput = z
|
|
3852
|
+
.object({
|
|
3853
|
+
npi: z
|
|
3854
|
+
.string()
|
|
3855
|
+
.regex(/^\d{10}$/)
|
|
3856
|
+
.optional()
|
|
3857
|
+
.describe(
|
|
3858
|
+
"A 10-digit supplier National Provider Identifier (→ Suplr_NPI), e.g. '1003000126'. Provide at least this OR `state`. Validated ^\\d{10}$.",
|
|
3859
|
+
),
|
|
3860
|
+
state: z
|
|
3861
|
+
.string()
|
|
3862
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3863
|
+
.optional()
|
|
3864
|
+
.describe(
|
|
3865
|
+
"A 2-letter US state/territory code (→ Suplr_Prvdr_State_Abrvtn), e.g. 'VA', 'CA'. Provide at least this OR `npi`. Validated ^[A-Za-z]{2}$.",
|
|
3866
|
+
),
|
|
3867
|
+
size: z
|
|
3868
|
+
.number()
|
|
3869
|
+
.int()
|
|
3870
|
+
.min(1)
|
|
3871
|
+
.max(100)
|
|
3872
|
+
.optional()
|
|
3873
|
+
.describe("Max supplier rows to return (1–100, default 25). Offset-paginated."),
|
|
3874
|
+
offset: z
|
|
3875
|
+
.number()
|
|
3876
|
+
.int()
|
|
3877
|
+
.min(0)
|
|
3878
|
+
.optional()
|
|
3879
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3880
|
+
})
|
|
3881
|
+
.refine((v) => v.npi !== undefined || v.state !== undefined, {
|
|
3882
|
+
message: "Provide at least `npi` or `state` (an all-empty query would scan the entire DMEPOS supplier table and is refused).",
|
|
3883
|
+
path: ["npi"],
|
|
3884
|
+
});
|
|
3885
|
+
|
|
3886
|
+
// ─── CMS Revoked Medicare Providers & Suppliers (data.cms.gov data-API, ADR-0064) ──
|
|
3887
|
+
// KEYLESS. A legally-published revocation/exclusion register (~7,059 rows) — the same
|
|
3888
|
+
// vetting class as the OFAC / SAM exclusion lists. ALL filters optional (small table —
|
|
3889
|
+
// pagination is fine unfiltered). SAME two-request stats-count P1 pattern.
|
|
3890
|
+
const CmsRevokedProvidersInput = z.object({
|
|
3891
|
+
npi: z
|
|
3892
|
+
.string()
|
|
3893
|
+
.regex(/^\d{10}$/)
|
|
3894
|
+
.optional()
|
|
3895
|
+
.describe(
|
|
3896
|
+
"An optional 10-digit National Provider Identifier (→ NPI), e.g. '1003000126'. Validated ^\\d{10}$.",
|
|
3897
|
+
),
|
|
3898
|
+
state: z
|
|
3899
|
+
.string()
|
|
3900
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3901
|
+
.optional()
|
|
3902
|
+
.describe(
|
|
3903
|
+
"An optional 2-letter US state/territory code (→ STATE_CD, EXACT match), e.g. 'FL', 'CA'. Validated ^[A-Za-z]{2}$.",
|
|
3904
|
+
),
|
|
3905
|
+
lastName: z
|
|
3906
|
+
.string()
|
|
3907
|
+
.min(1)
|
|
3908
|
+
.max(100)
|
|
3909
|
+
.regex(/^[A-Za-z0-9 .,'-]+$/)
|
|
3910
|
+
.optional()
|
|
3911
|
+
.describe(
|
|
3912
|
+
"An optional last-name filter (→ LAST_NAME, EXACT match). Allowed: letters/digits/space/. , ' - (≤100 chars).",
|
|
3913
|
+
),
|
|
3914
|
+
size: z
|
|
3915
|
+
.number()
|
|
3916
|
+
.int()
|
|
3917
|
+
.min(1)
|
|
3918
|
+
.max(100)
|
|
3919
|
+
.optional()
|
|
3920
|
+
.describe("Max revocation rows to return (1–100, default 25). Offset-paginated."),
|
|
3921
|
+
offset: z
|
|
3922
|
+
.number()
|
|
3923
|
+
.int()
|
|
3924
|
+
.min(0)
|
|
3925
|
+
.optional()
|
|
3926
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3927
|
+
});
|
|
3928
|
+
|
|
3929
|
+
// ─── FRED (Federal Reserve Economic Data) — the SECOND key-required source ──
|
|
3930
|
+
// ADR-0048. Macro context (GDP/CPI/rates/unemployment/PPI). REQUIRES a free
|
|
3931
|
+
// FRED_API_KEY; without it both tools throw an honest config error (the other 112
|
|
3932
|
+
// tools stay keyless). The key rides &api_key= ONLY. Missing observations ('.') → null.
|
|
3933
|
+
const FredSearchSeriesInput = z.object({
|
|
3934
|
+
query: z
|
|
3935
|
+
.string()
|
|
3936
|
+
.min(1)
|
|
3937
|
+
.describe(
|
|
3938
|
+
"The FRED search_text — free-text terms to discover economic series, e.g. 'unemployment rate', 'CPI', 'GDP', '10-year treasury'. Required.",
|
|
3939
|
+
),
|
|
3940
|
+
limit: z
|
|
3941
|
+
.number()
|
|
3942
|
+
.int()
|
|
3943
|
+
.min(1)
|
|
3944
|
+
.max(1000)
|
|
3945
|
+
.optional()
|
|
3946
|
+
.describe("Max series to return (default 25, max 1000). Offset-paginated."),
|
|
3947
|
+
offset: z
|
|
3948
|
+
.number()
|
|
3949
|
+
.int()
|
|
3950
|
+
.min(0)
|
|
3951
|
+
.optional()
|
|
3952
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3953
|
+
});
|
|
3954
|
+
|
|
3955
|
+
const FredSeriesObservationsInput = z.object({
|
|
3457
3956
|
seriesId: z
|
|
3458
3957
|
.string()
|
|
3459
3958
|
.regex(/^[A-Za-z0-9._-]+$/)
|
|
@@ -3489,6 +3988,236 @@ const FredSeriesObservationsInput = z.object({
|
|
|
3489
3988
|
.describe("Observation date order: 'asc' (oldest first, FRED default) or 'desc' (newest first)."),
|
|
3490
3989
|
});
|
|
3491
3990
|
|
|
3991
|
+
// ─── openFDA recall/enforcement (api.fda.gov) — KEYLESS + OPTIONAL rate-limit key ──
|
|
3992
|
+
// ADR-0054. Drug/device/food recall enforcement records. Structured filters ONLY
|
|
3993
|
+
// (no raw Lucene passthrough — injection-safe); the tool assembles the openFDA
|
|
3994
|
+
// `search=` string with proper escaping. totalAvailable = meta.results.total (P1);
|
|
3995
|
+
// a no-match query (openFDA HTTP 404 NOT_FOUND) ⇒ an honest empty (P2). An OPTIONAL
|
|
3996
|
+
// OPENFDA_API_KEY only raises the rate limit (keyless works ~1000/day).
|
|
3997
|
+
const OpenfdaEnforcementInput = z.object({
|
|
3998
|
+
category: z
|
|
3999
|
+
.enum(["drug", "device", "food"])
|
|
4000
|
+
.optional()
|
|
4001
|
+
.describe(
|
|
4002
|
+
"The recall category (default 'drug'): 'drug', 'device', or 'food'. Selects the openFDA /{category}/enforcement endpoint.",
|
|
4003
|
+
),
|
|
4004
|
+
firm: z
|
|
4005
|
+
.string()
|
|
4006
|
+
.min(1)
|
|
4007
|
+
.optional()
|
|
4008
|
+
.describe(
|
|
4009
|
+
"Recalling firm name filter (→ recalling_firm), e.g. 'pfizer'. Matched as an escaped Lucene phrase.",
|
|
4010
|
+
),
|
|
4011
|
+
product: z
|
|
4012
|
+
.string()
|
|
4013
|
+
.min(1)
|
|
4014
|
+
.optional()
|
|
4015
|
+
.describe(
|
|
4016
|
+
"Product description filter (→ product_description), e.g. 'insulin'. Matched as an escaped Lucene phrase.",
|
|
4017
|
+
),
|
|
4018
|
+
reason: z
|
|
4019
|
+
.string()
|
|
4020
|
+
.min(1)
|
|
4021
|
+
.optional()
|
|
4022
|
+
.describe(
|
|
4023
|
+
"Reason-for-recall filter (→ reason_for_recall), e.g. 'contamination'. Matched as an escaped Lucene phrase.",
|
|
4024
|
+
),
|
|
4025
|
+
classification: z
|
|
4026
|
+
.enum(["Class I", "Class II", "Class III"])
|
|
4027
|
+
.optional()
|
|
4028
|
+
.describe(
|
|
4029
|
+
"FDA recall classification filter: 'Class I' (most serious), 'Class II', or 'Class III'.",
|
|
4030
|
+
),
|
|
4031
|
+
status: z
|
|
4032
|
+
.string()
|
|
4033
|
+
.min(1)
|
|
4034
|
+
.optional()
|
|
4035
|
+
.describe(
|
|
4036
|
+
"Recall status filter (→ status), e.g. 'Ongoing', 'Terminated', 'Completed'.",
|
|
4037
|
+
),
|
|
4038
|
+
state: z
|
|
4039
|
+
.string()
|
|
4040
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
4041
|
+
.optional()
|
|
4042
|
+
.describe(
|
|
4043
|
+
"2-letter US state/territory postal code filter (→ state), e.g. 'CA'. Validated ^[A-Za-z]{2}$.",
|
|
4044
|
+
),
|
|
4045
|
+
limit: z
|
|
4046
|
+
.number()
|
|
4047
|
+
.int()
|
|
4048
|
+
.min(1)
|
|
4049
|
+
.max(100)
|
|
4050
|
+
.optional()
|
|
4051
|
+
.describe("Max recall records to return (default 25, max 100). Offset-paginated via skip."),
|
|
4052
|
+
skip: z
|
|
4053
|
+
.number()
|
|
4054
|
+
.int()
|
|
4055
|
+
.min(0)
|
|
4056
|
+
.optional()
|
|
4057
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
4058
|
+
});
|
|
4059
|
+
|
|
4060
|
+
// ─── openFDA 510(k) device clearances (api.fda.gov) — KEYLESS + OPTIONAL rate-limit key ──
|
|
4061
|
+
// ADR-0056. FDA premarket-notification (510(k)) device clearances — SAME source/envelope/
|
|
4062
|
+
// crux as openfda_enforcement (structured filters ONLY — the tool assembles + escapes the
|
|
4063
|
+
// search= string, injection-safe). totalAvailable = meta.results.total (P1); a no-match
|
|
4064
|
+
// query (openFDA HTTP 404 NOT_FOUND) ⇒ an honest empty (P2). An OPTIONAL OPENFDA_API_KEY
|
|
4065
|
+
// only raises the rate limit (keyless works ~1000/day).
|
|
4066
|
+
const OpenfdaDeviceClearancesInput = z.object({
|
|
4067
|
+
applicant: z
|
|
4068
|
+
.string()
|
|
4069
|
+
.min(1)
|
|
4070
|
+
.optional()
|
|
4071
|
+
.describe(
|
|
4072
|
+
"Applicant / manufacturer name filter (→ applicant), e.g. 'medtronic'. Matched as an escaped Lucene phrase.",
|
|
4073
|
+
),
|
|
4074
|
+
deviceName: z
|
|
4075
|
+
.string()
|
|
4076
|
+
.min(1)
|
|
4077
|
+
.optional()
|
|
4078
|
+
.describe(
|
|
4079
|
+
"Device name filter (→ device_name), e.g. 'catheter'. Matched as an escaped Lucene phrase.",
|
|
4080
|
+
),
|
|
4081
|
+
productCode: z
|
|
4082
|
+
.string()
|
|
4083
|
+
.min(1)
|
|
4084
|
+
.optional()
|
|
4085
|
+
.describe(
|
|
4086
|
+
"FDA product code filter (→ product_code), e.g. 'DXN'. Matched as an escaped Lucene phrase.",
|
|
4087
|
+
),
|
|
4088
|
+
clearanceType: z
|
|
4089
|
+
.string()
|
|
4090
|
+
.min(1)
|
|
4091
|
+
.optional()
|
|
4092
|
+
.describe(
|
|
4093
|
+
"510(k) clearance type filter (→ clearance_type), e.g. 'Traditional', 'Special', 'Abbreviated'. Matched as an escaped Lucene phrase.",
|
|
4094
|
+
),
|
|
4095
|
+
kNumber: z
|
|
4096
|
+
.string()
|
|
4097
|
+
.min(1)
|
|
4098
|
+
.optional()
|
|
4099
|
+
.describe(
|
|
4100
|
+
"510(k) clearance number (K-number) filter (→ k_number), e.g. 'K123456'. Matched as an escaped Lucene phrase.",
|
|
4101
|
+
),
|
|
4102
|
+
state: z
|
|
4103
|
+
.string()
|
|
4104
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
4105
|
+
.optional()
|
|
4106
|
+
.describe(
|
|
4107
|
+
"2-letter US state/territory postal code filter (→ state), e.g. 'CA'. Validated ^[A-Za-z]{2}$.",
|
|
4108
|
+
),
|
|
4109
|
+
limit: z
|
|
4110
|
+
.number()
|
|
4111
|
+
.int()
|
|
4112
|
+
.min(1)
|
|
4113
|
+
.max(100)
|
|
4114
|
+
.optional()
|
|
4115
|
+
.describe("Max clearance records to return (default 25, max 100). Offset-paginated via skip."),
|
|
4116
|
+
skip: z
|
|
4117
|
+
.number()
|
|
4118
|
+
.int()
|
|
4119
|
+
.min(0)
|
|
4120
|
+
.optional()
|
|
4121
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
4122
|
+
});
|
|
4123
|
+
|
|
4124
|
+
const OpenfdaDrugApprovalsInput = z.object({
|
|
4125
|
+
sponsorName: z
|
|
4126
|
+
.string()
|
|
4127
|
+
.min(1)
|
|
4128
|
+
.optional()
|
|
4129
|
+
.describe("Sponsor / applicant company name (→ sponsor_name), e.g. 'pfizer'. Matched as an escaped Lucene phrase."),
|
|
4130
|
+
brandName: z
|
|
4131
|
+
.string()
|
|
4132
|
+
.min(1)
|
|
4133
|
+
.optional()
|
|
4134
|
+
.describe("Product brand name (→ products.brand_name), e.g. 'lipitor'. Matched as an escaped Lucene phrase."),
|
|
4135
|
+
activeIngredient: z
|
|
4136
|
+
.string()
|
|
4137
|
+
.min(1)
|
|
4138
|
+
.optional()
|
|
4139
|
+
.describe("Active ingredient name (→ products.active_ingredients.name), e.g. 'atorvastatin calcium'. Matched as an escaped Lucene phrase."),
|
|
4140
|
+
applicationNumber: z
|
|
4141
|
+
.string()
|
|
4142
|
+
.min(1)
|
|
4143
|
+
.optional()
|
|
4144
|
+
.describe("FDA application number (→ application_number), e.g. 'NDA050347'. Matched as an escaped Lucene phrase."),
|
|
4145
|
+
limit: z
|
|
4146
|
+
.number()
|
|
4147
|
+
.int()
|
|
4148
|
+
.min(1)
|
|
4149
|
+
.max(100)
|
|
4150
|
+
.optional()
|
|
4151
|
+
.describe("Max application records to return (default 25, max 100). Offset-paginated via skip."),
|
|
4152
|
+
skip: z
|
|
4153
|
+
.number()
|
|
4154
|
+
.int()
|
|
4155
|
+
.min(0)
|
|
4156
|
+
.optional()
|
|
4157
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
4158
|
+
});
|
|
4159
|
+
|
|
4160
|
+
// ─── NHTSA vehicle safety (api.nhtsa.gov) — KEYLESS vehicle/parts supplier vetting ──
|
|
4161
|
+
// ADR-0057. Two tools (recalls + complaints) share make/model/modelYear inputs. NO
|
|
4162
|
+
// API key at all. ★The complaints VIN (PII) is excluded from the output. modelYear is
|
|
4163
|
+
// ^\d{4}$; make/model are letters/digits/space/hyphen only (SSRF/injection guard).
|
|
4164
|
+
const NhtsaVehicleInput = z.object({
|
|
4165
|
+
make: z
|
|
4166
|
+
.string()
|
|
4167
|
+
.regex(/^[A-Za-z0-9 -]+$/)
|
|
4168
|
+
.describe(
|
|
4169
|
+
"Vehicle make (required), e.g. 'honda', 'ford'. Letters/digits/space/hyphen only (^[A-Za-z0-9 -]+$).",
|
|
4170
|
+
),
|
|
4171
|
+
model: z
|
|
4172
|
+
.string()
|
|
4173
|
+
.regex(/^[A-Za-z0-9 -]+$/)
|
|
4174
|
+
.describe(
|
|
4175
|
+
"Vehicle model (required), e.g. 'accord', 'f-150'. Letters/digits/space/hyphen only (^[A-Za-z0-9 -]+$).",
|
|
4176
|
+
),
|
|
4177
|
+
modelYear: z
|
|
4178
|
+
.string()
|
|
4179
|
+
.regex(/^\d{4}$/)
|
|
4180
|
+
.describe("4-digit model year (required), e.g. '2020'. Validated ^\\d{4}$."),
|
|
4181
|
+
});
|
|
4182
|
+
|
|
4183
|
+
// ─── CPSC consumer-product recalls (www.saferproducts.gov) — KEYLESS goods/import vetting ──
|
|
4184
|
+
// ADR-0058. One tool. NO API key at all. The response is a bare JSON ARRAY with no
|
|
4185
|
+
// total-count field / no pagination (totalAvailable = the returned count). All filters
|
|
4186
|
+
// optional; with NO filter the tool defaults RecallDateStart to ~90 days ago (disclosed)
|
|
4187
|
+
// rather than fetch the whole dataset. dates are ^\d{4}-\d{2}-\d{2}$; recallNumber is
|
|
4188
|
+
// letters/digits/hyphen only (SSRF/injection guard).
|
|
4189
|
+
const CpscRecallsInput = z.object({
|
|
4190
|
+
dateStart: z
|
|
4191
|
+
.string()
|
|
4192
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
4193
|
+
.optional()
|
|
4194
|
+
.describe(
|
|
4195
|
+
"Recall date range START (optional), YYYY-MM-DD, e.g. '2025-01-01' (→ RecallDateStart). Validated ^\\d{4}-\\d{2}-\\d{2}$.",
|
|
4196
|
+
),
|
|
4197
|
+
dateEnd: z
|
|
4198
|
+
.string()
|
|
4199
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
4200
|
+
.optional()
|
|
4201
|
+
.describe(
|
|
4202
|
+
"Recall date range END (optional), YYYY-MM-DD, e.g. '2025-01-31' (→ RecallDateEnd). Validated ^\\d{4}-\\d{2}-\\d{2}$.",
|
|
4203
|
+
),
|
|
4204
|
+
productName: z
|
|
4205
|
+
.string()
|
|
4206
|
+
.optional()
|
|
4207
|
+
.describe("Product name substring filter (optional), e.g. 'helmet' (→ ProductName)."),
|
|
4208
|
+
manufacturer: z
|
|
4209
|
+
.string()
|
|
4210
|
+
.optional()
|
|
4211
|
+
.describe("Manufacturer name substring filter (optional) (→ Manufacturer)."),
|
|
4212
|
+
recallNumber: z
|
|
4213
|
+
.string()
|
|
4214
|
+
.regex(/^[A-Za-z0-9-]+$/)
|
|
4215
|
+
.optional()
|
|
4216
|
+
.describe(
|
|
4217
|
+
"A specific CPSC recall number (optional), e.g. '25088' (→ RecallNumber). Letters/digits/hyphen only (^[A-Za-z0-9-]+$).",
|
|
4218
|
+
),
|
|
4219
|
+
});
|
|
4220
|
+
|
|
3492
4221
|
// ─── BEA Regional Economic Accounts (apps.bea.gov) — the THIRD key-required source ──
|
|
3493
4222
|
// ADR-0051. County/state/MSA GDP-by-industry (CAGDP2/SAGDP2N) + personal income
|
|
3494
4223
|
// (CAINC1/SAINC1) — the regional/sub-national place-of-performance lane. REQUIRES a
|
|
@@ -3706,6 +4435,95 @@ const LdaSearchFilingsInput = z.object({
|
|
|
3706
4435
|
.describe("Filings per page, 1..25 (the LDA API caps at 25), default 25."),
|
|
3707
4436
|
});
|
|
3708
4437
|
|
|
4438
|
+
// ─── US federal court opinions (www.courtlistener.com) — the litigation lane ──
|
|
4439
|
+
// ADR-0055. Federal court decisions (opinions) — the judicial signal no contract/
|
|
4440
|
+
// spending/lobbying source carries (e.g. uscfc bid-protest / contract-claim opinions).
|
|
4441
|
+
// ★PROVENANCE: CourtListener (Free Law Project, a non-profit), NOT a .gov API —
|
|
4442
|
+
// PACER (the .gov source) is paywalled. KEYLESS (anonymous 200); an optional free
|
|
4443
|
+
// COURTLISTENER_API_TOKEN only raises the rate limit, riding the Authorization: Token
|
|
4444
|
+
// … header ONLY (the lda/socrata app-token lineage). `count` is the REAL total —
|
|
4445
|
+
// never results.length; CURSOR pagination (nextCursor extracted from `next`). court/
|
|
4446
|
+
// dates charclass-guarded; all filter VALUES ride URLSearchParams; type=o is FIXED.
|
|
4447
|
+
const CourtlistenerSearchOpinionsInput = z.object({
|
|
4448
|
+
query: z
|
|
4449
|
+
.string()
|
|
4450
|
+
.min(1)
|
|
4451
|
+
.optional()
|
|
4452
|
+
.describe("Full-text query (maps to q), e.g. 'bid protest' or a party name. Matches across the opinion text/metadata."),
|
|
4453
|
+
court: z
|
|
4454
|
+
.string()
|
|
4455
|
+
.regex(/^[a-z0-9]+$/)
|
|
4456
|
+
.optional()
|
|
4457
|
+
.describe("A CourtListener court id (lowercase alphanumerics ^[a-z0-9]+$), e.g. 'uscfc' (US Court of Federal Claims — contract claims/bid protests), 'cafc' (Federal Circuit — contract/patent appeals), 'scotus'."),
|
|
4458
|
+
dateFiledAfter: z
|
|
4459
|
+
.string()
|
|
4460
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
4461
|
+
.optional()
|
|
4462
|
+
.describe("Only opinions filed on/after this ISO date (→ filed_after), e.g. '2020-01-01'. Validated ^\\d{4}-\\d{2}-\\d{2}$."),
|
|
4463
|
+
dateFiledBefore: z
|
|
4464
|
+
.string()
|
|
4465
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
4466
|
+
.optional()
|
|
4467
|
+
.describe("Only opinions filed on/before this ISO date (→ filed_before), e.g. '2024-12-31'. Validated ^\\d{4}-\\d{2}-\\d{2}$."),
|
|
4468
|
+
natureOfSuit: z
|
|
4469
|
+
.string()
|
|
4470
|
+
.min(1)
|
|
4471
|
+
.optional()
|
|
4472
|
+
.describe("Nature-of-suit text — folded into the q full-text query (the v4 opinions search has no verified dedicated filter), so it matches the text anywhere in the document (disclosed in _meta.notes)."),
|
|
4473
|
+
cursor: z
|
|
4474
|
+
.string()
|
|
4475
|
+
.min(1)
|
|
4476
|
+
.optional()
|
|
4477
|
+
.describe("Opaque continuation token for the NEXT page — pass back the _meta.nextCursor from the previous response (CourtListener uses CURSOR pagination, not page/offset)."),
|
|
4478
|
+
order: z
|
|
4479
|
+
.string()
|
|
4480
|
+
.min(1)
|
|
4481
|
+
.default("dateFiled desc")
|
|
4482
|
+
.describe("Sort order (maps to order_by), default 'dateFiled desc' (most recent first). E.g. 'dateFiled asc', 'score desc'."),
|
|
4483
|
+
});
|
|
4484
|
+
|
|
4485
|
+
// ─── US tax-exempt nonprofits (projects.propublica.org) — the nonprofit lane ──
|
|
4486
|
+
// ADR-0060. IRS Form 990 public records republished KEYLESS by ProPublica Nonprofit
|
|
4487
|
+
// Explorer (a non-profit newsroom) — NOT a .gov API (the IRS has no clean query
|
|
4488
|
+
// API). ★PROVENANCE disclosed in _meta.source + a note. KEYLESS (no key of any
|
|
4489
|
+
// kind). search: q/state[id]/ntee[id]/page (0-based); total_results is the REAL
|
|
4490
|
+
// total — never organizations.length. All VALUES ride URLSearchParams (incl. the
|
|
4491
|
+
// bracket keys); state/ntee charclass/range-guarded.
|
|
4492
|
+
const NonprofitSearchInput = z.object({
|
|
4493
|
+
query: z
|
|
4494
|
+
.string()
|
|
4495
|
+
.min(1)
|
|
4496
|
+
.optional()
|
|
4497
|
+
.describe("Full-text query (maps to q) — an organization name or keyword, e.g. 'american red cross'. Matches across the org name/metadata."),
|
|
4498
|
+
state: z
|
|
4499
|
+
.string()
|
|
4500
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
4501
|
+
.optional()
|
|
4502
|
+
.describe("Filter by a 2-letter US state/territory code (maps to state[id]), e.g. 'VA'. Validated ^[A-Za-z]{2}$."),
|
|
4503
|
+
ntee: z
|
|
4504
|
+
.number()
|
|
4505
|
+
.int()
|
|
4506
|
+
.min(1)
|
|
4507
|
+
.max(10)
|
|
4508
|
+
.optional()
|
|
4509
|
+
.describe("Filter by NTEE major category, an integer 1..10 (maps to ntee[id]) — the National Taxonomy of Exempt Entities top-level group (e.g. 1 Arts, 3 Environment, 8 Health)."),
|
|
4510
|
+
page: z
|
|
4511
|
+
.number()
|
|
4512
|
+
.int()
|
|
4513
|
+
.min(0)
|
|
4514
|
+
.default(0)
|
|
4515
|
+
.describe("0-BASED page number (default 0). Page with cur_page+1 from _meta.notes / when _meta.pagination.hasMore."),
|
|
4516
|
+
});
|
|
4517
|
+
|
|
4518
|
+
// nonprofit_financials — one org's Form 990 profile + financials by EIN. KEYLESS.
|
|
4519
|
+
// ein rides the URL PATH ⇒ digits-only ^\d{1,9}$. An unknown EIN (404) ⇒ not_found.
|
|
4520
|
+
const NonprofitFinancialsInput = z.object({
|
|
4521
|
+
ein: z
|
|
4522
|
+
.string()
|
|
4523
|
+
.regex(/^\d{1,9}$/)
|
|
4524
|
+
.describe("The organization's EIN (Employer Identification Number), 1..9 digits, e.g. '530196605' (American National Red Cross). Validated ^\\d{1,9}$; rides the URL path."),
|
|
4525
|
+
});
|
|
4526
|
+
|
|
3709
4527
|
// api_key_status takes no input — it is a pure status query over process.env.
|
|
3710
4528
|
const ApiKeyStatusInput = z.object({});
|
|
3711
4529
|
|
|
@@ -4340,7 +5158,7 @@ export const TOOLS: ToolDef[] = [
|
|
|
4340
5158
|
defineTool({
|
|
4341
5159
|
name: "usas_search_subawards",
|
|
4342
5160
|
description:
|
|
4343
|
-
"Enumerate subcontracts
|
|
5161
|
+
"Enumerate federal subawards (subcontracts), optionally filtered by SUBAWARDEE name. Use for 'where does Leidos appear as a SUBcontractor, and under which primes' — surfaces the prime/sub network for teaming-map artifacts. NOTE: subRecipientName matches the SUB-recipient, NOT the prime (the keyless spending_by_award subaward view has no prime-name filter); to see the subs UNDER a specific prime, resolve that prime's awards first (usas_search_awards → usas_get_award_detail) and read their sub network. Each row carries subRecipient (the subawardee), amount, actionDate, the prime award id, and the prime award's NAICS.",
|
|
4344
5162
|
inputSchema: UsasSubawardsInput,
|
|
4345
5163
|
handler: (input) => usas.searchSubawards(input),
|
|
4346
5164
|
}),
|
|
@@ -4492,6 +5310,20 @@ export const TOOLS: ToolDef[] = [
|
|
|
4492
5310
|
inputSchema: UsasListAgenciesInput,
|
|
4493
5311
|
handler: (input) => usas.listToptierAgencies(input),
|
|
4494
5312
|
}),
|
|
5313
|
+
defineTool({
|
|
5314
|
+
name: "usas_list_disaster_codes",
|
|
5315
|
+
description:
|
|
5316
|
+
"List the Disaster Emergency Fund Codes (DEFC) — the supplemental-appropriation tags (COVID-19 relief, IIJA/infrastructure, and other emergency laws) that usas_disaster_spending filters on. Keyless USAspending references/def_codes. Returns the COMPLETE code set (no pagination): each `code` with its `group` ('covid_19' | 'infrastructure' | null), `title`, and `publicLaw`. Use this to discover the codes to pass to usas_disaster_spending. HONESTY: group is null (never fabricated) when a code belongs to no named group; totalAvailable is the exact complete count.",
|
|
5317
|
+
inputSchema: UsasListDisasterCodesInput,
|
|
5318
|
+
handler: () => usas.listDisasterCodes(),
|
|
5319
|
+
}),
|
|
5320
|
+
defineTool({
|
|
5321
|
+
name: "usas_disaster_spending",
|
|
5322
|
+
description:
|
|
5323
|
+
"Disaster / emergency-fund spending BY GEOGRAPHY — obligations or outlays tagged to one or more Disaster Emergency Fund Codes (DEFC: COVID-19, IIJA, etc.), broken out per state / county / congressional district (keyless USAspending disaster/spending_by_geography). Answers 'which geographies captured COVID/IIJA relief money' — a distinct axis the standard award search does not expose. `defCodes` REQUIRED (discover via usas_list_disaster_codes); `spendingType` obligation (default) | outlay; `geoLayer` state (default) | county | district. Each row: name, code, amount, awardCount, population, perCapita. HONESTY: amount/perCapita are number|null (a real 0 stays 0 — some DEFCs like IIJA report $0 OBLIGATIONS with a nonzero awardCount, disclosed in a note; absent → null, never a fabricated 0); the endpoint returns the COMPLETE set of geo units (no pagination) so totalAvailable = returned; an outage/4xx THROWS (never a fake empty).",
|
|
5324
|
+
inputSchema: UsasDisasterSpendingInput,
|
|
5325
|
+
handler: (input) => usas.disasterSpending(input),
|
|
5326
|
+
}),
|
|
4495
5327
|
|
|
4496
5328
|
// ━━━ Federal Register (4) ━━━
|
|
4497
5329
|
defineTool({
|
|
@@ -4659,6 +5491,13 @@ export const TOOLS: ToolDef[] = [
|
|
|
4659
5491
|
inputSchema: CisaKevLookupInput,
|
|
4660
5492
|
handler: (input) => nvd.cisaKevLookup(input),
|
|
4661
5493
|
}),
|
|
5494
|
+
defineTool({
|
|
5495
|
+
name: "nist_800_53_controls",
|
|
5496
|
+
description:
|
|
5497
|
+
"Look up NIST SP 800-53 Rev 5 security & privacy CONTROLS (keyless) — the requirement backbone for FedRAMP / CMMC / RMF compliance work. Retrieve a control by `controlId` (exact, e.g. 'AC-2', 'SC-7', 'AC-2(1)'), a `family` (2-letter code 'AC'/'SC'/'IA' or a name substring 'Access Control'), and/or a `keyword` (case-insensitive substring over title + statement); `limit`/`offset` pagination. Each row: { id (e.g. 'AC-2'), family (e.g. 'AC — Access Control'), title, statement (the labelled requirement prose), guidance (discussion), enhancements:[{id,title}] (e.g. AC-2(1)) }. Complements cve_lookup + cisa_kev_lookup (the vulnerability side) with the CONTROL/requirement side. HONESTY: source is NIST's OFFICIAL OSCAL catalog published at github.com/usnistgov/oscal-content (authoritative first-party data served from GitHub, not a .gov API host — provenance disclosed in _meta); the catalog has no query API so filtering is CLIENT-SIDE and totalAvailable is the EXACT match count; this is the REQUIREMENT text only — applicability depends on the system's FIPS-199 impact baseline (Low/Moderate/High), which the catalog does not encode (disclosed); a download failure or an implausibly-truncated catalog (< 15 families) THROWS (never a fake-empty 'control not found').",
|
|
5498
|
+
inputSchema: NistControlsInput,
|
|
5499
|
+
handler: (input) => nistControls.searchControls(input),
|
|
5500
|
+
}),
|
|
4662
5501
|
// ━━━ NPPES NPI Registry — Healthcare-Provider Vetting (1) ━━━ ADR-0036
|
|
4663
5502
|
defineTool({
|
|
4664
5503
|
name: "nppes_lookup_provider",
|
|
@@ -4709,7 +5548,7 @@ export const TOOLS: ToolDef[] = [
|
|
|
4709
5548
|
defineTool({
|
|
4710
5549
|
name: "treasury_query_dataset",
|
|
4711
5550
|
description:
|
|
4712
|
-
"Escape-hatch query over
|
|
5551
|
+
"Escape-hatch query over 7 confirmed US Treasury Fiscal Data datasets (keyless): debt_to_penny, avg_interest_rates, mts_table_1 (Monthly Treasury Statement), rates_of_exchange, debt_outstanding, interest_expense (actual interest PAID / debt-service cost), tror (Treasury Report on Receivables — federal receivables + delinquent-debt collections by agency). Choose `dataset` (enum — no free path), and optionally project `fields` (CSV), `filter` (CSV 'col:op:val', ops lt|lte|gt|gte|eq|in, AND-combined), and `sort` (CSV, '-' = desc), with page[size]/page[number] pagination. Returns raw rows plus a truthful `_meta` (totalAvailable = upstream total-count, offset pagination). Value/amount fields are raw upstream strings — the string \"null\"/empty means 'no value', never 0. Covers rates_of_exchange + debt_outstanding without a dedicated tool.",
|
|
4713
5552
|
inputSchema: TreasuryQueryDatasetInput,
|
|
4714
5553
|
handler: (input) => treasury.queryDataset(input),
|
|
4715
5554
|
}),
|
|
@@ -4928,7 +5767,7 @@ export const TOOLS: ToolDef[] = [
|
|
|
4928
5767
|
inputSchema: BlsQcewInput,
|
|
4929
5768
|
handler: (input) => bls.qcew(input),
|
|
4930
5769
|
}),
|
|
4931
|
-
// ━━━ OpenFEMA — keyless disaster declarations + emergency-assistance spend (
|
|
5770
|
+
// ━━━ OpenFEMA — keyless disaster declarations + emergency-assistance spend (3) ━━━ ADR-0016
|
|
4932
5771
|
defineTool({
|
|
4933
5772
|
name: "fema_search_public_assistance",
|
|
4934
5773
|
description:
|
|
@@ -4943,6 +5782,29 @@ export const TOOLS: ToolDef[] = [
|
|
|
4943
5782
|
inputSchema: FemaDisasterDeclarationsInput,
|
|
4944
5783
|
handler: (input) => fema.disasterDeclarations(input),
|
|
4945
5784
|
}),
|
|
5785
|
+
defineTool({
|
|
5786
|
+
name: "fema_search_hazard_mitigation",
|
|
5787
|
+
description:
|
|
5788
|
+
"Search FEMA Hazard Mitigation Assistance projects — the disaster-RESILIENCE grant axis (HMGP/FMA/PDM/BRIC mitigation grants to state/local/tribal subrecipients, distinct from the disaster-RECOVERY spend in fema_search_public_assistance). Keyless OpenFEMA, dataset HazardMitigationAssistanceProjects v4, ~56k rows. Structured filters (module-built into an OData $filter; each LIVE-VERIFIED to narrow): `state` (→ state — the FULL state NAME, e.g. 'Alabama', NOT the 2-letter code), `programArea` (HMGP/FMA/PDM/BRIC/LPDM/FMA-SL), `disasterNumber`, `status` (e.g. 'Closed'), `programFy`, `region` (FEMA region 1–10), `minProjectAmount`/`maxProjectAmount` (projectAmount ge/le). `limit` (≤1000, def 100 → $top), `offset` (→ $skip). HONESTY: the module ALWAYS sends $inlinecount=allpages so totalAvailable is the EXACT filtered total (metadata.count), never the page length; amount fields (projectAmount/federalShareObligated/initialObligationAmount/netValueBenefits) are number|null (a real 0 stays 0, absent → null); genuine-empty ⇒ complete:true/total:0; an outage/400/404 THROWS (never a fake empty). NOTE: 'state' here is the full name (this dataset 400s on a 2-letter code), whereas fema_search_public_assistance maps 'state' to the 2-letter 'stateAbbreviation'.",
|
|
5789
|
+
inputSchema: FemaSearchHazardMitigationInput,
|
|
5790
|
+
handler: (input) => fema.searchHazardMitigation(input),
|
|
5791
|
+
}),
|
|
5792
|
+
// ━━━ NWS — National Weather Service active alerts (keyless) (1) ━━━
|
|
5793
|
+
defineTool({
|
|
5794
|
+
name: "nws_active_alerts",
|
|
5795
|
+
description:
|
|
5796
|
+
"List CURRENTLY-ACTIVE National Weather Service alerts — watches, warnings, and advisories (keyless; api.weather.gov). The disaster/climate-readiness lane that pairs with the FEMA tools (declarations → public assistance → hazard mitigation → LIVE active weather): where severe-weather events are active NOW, ahead of the declarations/contracts that follow. Filters: `state` (2-letter code → server-side ?area=, e.g. 'CA'; omit for all US), `event` (case-insensitive substring, e.g. 'Flood', 'Wind'), `severity` (Extreme/Severe/Moderate/Minor/Unknown); `limit`/`offset` pagination. Each alert: { id, event, headline, severity, urgency, certainty, category, status, messageType, areaDesc, effective, onset, expires, ends, senderName, description, instruction, response }. HONESTY: this is REAL-TIME data (alerts active at request time — a live snapshot, NOT a historical archive; read effective/expires for each window, disclosed in _meta); every scalar is null-never-empty-string and dates are ISO strings; totalAvailable is the EXACT count of matched active alerts; a NO-active-alerts result is an HONEST EMPTY (returned:0), never an error; an outage/4xx/timeout THROWS and a non-FeatureCollection body ⇒ schema_drift. A descriptive User-Agent is sent per NWS policy (no key/token).",
|
|
5797
|
+
inputSchema: NwsActiveAlertsInput,
|
|
5798
|
+
handler: (input) => nws.activeAlerts(input),
|
|
5799
|
+
}),
|
|
5800
|
+
// ━━━ get.gov — CISA authoritative .gov domain registry (keyless) (1) ━━━
|
|
5801
|
+
defineTool({
|
|
5802
|
+
name: "search_gov_domains",
|
|
5803
|
+
description:
|
|
5804
|
+
"Search the authoritative US .gov domain registry (CISA get.gov) — resolve which ORGANIZATION owns a .gov domain, enumerate federal agencies, and MAP SLED entities (state/county/city/school-district/special-district/tribal) for market targeting. Keyless. scope 'all' (federal + SLED, ~16k rows, default) | 'federal'. Filters (client-side over the published CSV): organization/domain/city (case-insensitive SUBSTRING), domainType (e.g. 'Federal - Executive', 'County', 'Tribal'), state (2-letter). Each row: domain, domainType, organization, suborganization, city, state. HONESTY: source is CISA's OFFICIAL registry published at github.com/cisagov/dotgov-data (authoritative first-party data, not a .gov API host — provenance disclosed in _meta); the registry has no query API so filtering is CLIENT-SIDE and totalAvailable is the EXACT match count; the 'Security contact email' column is intentionally EXCLUDED (org mailbox — this tool resolves organizations, not contacts); an outage/4xx THROWS (never a fake empty); a header-column rename ⇒ schema_drift.",
|
|
5805
|
+
inputSchema: SearchGovDomainsInput,
|
|
5806
|
+
handler: (input) => govDomains.searchGovDomains(input),
|
|
5807
|
+
}),
|
|
4946
5808
|
// ━━━ FPDS-NG — federal contract AWARD ACTIONS (keyless ATOM) (1) ━━━ ADR-0012
|
|
4947
5809
|
// The FIRST XML/ATOM source (bounded, ReDoS-safe hand-parser — the far.ts/gao.ts
|
|
4948
5810
|
// lineage; NOT the getJson port). FPDS is the system-of-record USAspending
|
|
@@ -5174,17 +6036,97 @@ export const TOOLS: ToolDef[] = [
|
|
|
5174
6036
|
// ━━━ US Census County Business Patterns — market sizing (1) ━━━ ADR-0047
|
|
5175
6037
|
// ★The server's FIRST KEY-REQUIRED source: the Census Data API removed its
|
|
5176
6038
|
// keyless tier, so WITHOUT a CENSUS_API_KEY this tool throws an honest
|
|
5177
|
-
// invalid_input config error (
|
|
6039
|
+
// invalid_input config error (most other tools are keyless — see api_key_status). NAICS×geography
|
|
5178
6040
|
// establishments / employment / annual payroll — the demand-side market-sizing
|
|
5179
6041
|
// lane. Census negative suppression sentinels (-999999999 …) map to null (never
|
|
5180
6042
|
// a negative number / never 0). The 2D-array body is parsed by header name.
|
|
5181
6043
|
defineTool({
|
|
5182
6044
|
name: "census_business_patterns",
|
|
5183
6045
|
description:
|
|
5184
|
-
"Market sizing by NAICS × geography — establishments, employment, and annual payroll from the US Census County Business Patterns (CBP) API (api.census.gov/data/{year}/cbp). ★REQUIRES a free CENSUS_API_KEY: the Census Data API has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://api.census.gov/data/key_signup.html;
|
|
6046
|
+
"Market sizing by NAICS × geography — establishments, employment, and annual payroll from the US Census County Business Patterns (CBP) API (api.census.gov/data/{year}/cbp). ★REQUIRES a free CENSUS_API_KEY: the Census Data API has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://api.census.gov/data/key_signup.html; call api_key_status to see every source's key requirement). Input: optional `naics` (2–6 digit NAICS-2017, e.g. '5415'; omit to aggregate all sectors), `geography` (us|state|county, default us; county REQUIRES `state`), `state` (2-digit FIPS, e.g. '06'), `year` (default '2022'), optional `limit` (client-side top-N; CBP has no server pagination). Returns { rows:[{ name, geoId, naicsCode, naicsLabel, establishments, employees, annualPayrollUsd, state }] } + honest _meta. HONESTY: establishments/employees are integer counts and annualPayrollUsd is annual US dollars (×1000 from the source's $1,000-unit PAYANN); large-negative suppression sentinels map to null — NEVER a negative number and NEVER 0 (a genuine 0 stays 0; note CBP primarily uses noise-infusion + suppression flags, surfaced as reported — see the tool's suppression note); geoId/naicsCode/state are STRINGS (leading zeros survive). CBP returns the COMPLETE geography set for the filter (no pagination) ⇒ totalAvailable = the row count, complete:true. A missing/invalid key ⇒ invalid_input (a 302 to the Missing-Key page); a header-only body ⇒ honest empty (returned:0); a 5xx ⇒ THROWS; a 200 non-JSON ⇒ schema_drift. The key rides ONLY in the &key= query param — never logged or echoed.",
|
|
5185
6047
|
inputSchema: CensusBusinessPatternsInput,
|
|
5186
6048
|
handler: (input) => censusEconomic.businessPatterns(input),
|
|
5187
6049
|
}),
|
|
6050
|
+
// ━━━ EPA Envirofacts TRI facilities — environmental footprint (1) ━━━ ADR-0059
|
|
6051
|
+
// KEYLESS (data.epa.gov /efservice/tri_facility). ★Two requests: a count
|
|
6052
|
+
// sub-query yields the EXACT total (P1 — TOTALQUERYRESULTS, e.g. VA=1247), then
|
|
6053
|
+
// the data slice. All user values ride as PATH SEGMENTS → each is
|
|
6054
|
+
// charclass-validated + encodeURIComponent-encoded (the load-bearing SSRF guard).
|
|
6055
|
+
defineTool({
|
|
6056
|
+
name: "epa_tri_facilities",
|
|
6057
|
+
description:
|
|
6058
|
+
"Look up EPA Toxics Release Inventory (TRI) reporting facilities by state / facility-name / county — an environmental-footprint / place-of-performance screen (EPA Envirofacts, keyless; data.epa.gov/efservice/tri_facility). Input: `state` (2-letter, e.g. 'VA'), `facilityName` (partial match, e.g. 'chemical'), `county` (partial match) — provide at least `state` OR `facilityName` (an all-empty query is refused); optional `limit` (1–100, default 25), `offset`. Returns { facilities:[{ triFacilityId, facilityName, streetAddress, city, county, state, zip, region, closed }] } + honest _meta. ★HONESTY: totalAvailable is the EXACT count from a SEPARATE count sub-query (…/count/JSON → TOTALQUERYRESULTS), NEVER the returned-rows length; if that count fails, totalAvailable is null + a disclosing note (never length-faked). offset/limit pagination (hasMore = offset+returned < total). `closed` normalizes fac_closed_ind ('0'/'N'→false, '1'/'Y'→true, unrecognized→null — never a fabricated false); addresses/names are null-never-empty-string. A genuine no-match ⇒ honest empty (returned:0); a 4xx ⇒ invalid_input/not_found; a 5xx ⇒ THROWS; a 200 non-array/non-JSON ⇒ schema_drift. These are nominal TRI reporters, NOT a compliance/enforcement determination. KEYLESS — no key is sent.",
|
|
6059
|
+
inputSchema: EpaTriFacilitiesInput,
|
|
6060
|
+
handler: (input) => epaEnvirofacts.triFacilities(input),
|
|
6061
|
+
}),
|
|
6062
|
+
// ━━━ CMS Medicare provider-service utilization — healthcare market (1) ━━━ ADR-0061
|
|
6063
|
+
// KEYLESS (data.cms.gov /data-api/v1/dataset/{uuid}). ★Two requests: a stats
|
|
6064
|
+
// count sub-query yields the EXACT per-filter total (P1 — found_rows, e.g.
|
|
6065
|
+
// VA=278254), then the data slice (a bare JSON array). All filter VALUES ride via
|
|
6066
|
+
// URLSearchParams (bracket key + value encoded — the SSRF guard). REQUIRE npi OR
|
|
6067
|
+
// state (the 9.78M-row table is never scanned unscoped). The dataset UUID is a
|
|
6068
|
+
// SPECIFIC ANNUAL VINTAGE (surfaced in a _meta note; update yearly).
|
|
6069
|
+
defineTool({
|
|
6070
|
+
name: "cms_medicare_provider_services",
|
|
6071
|
+
description:
|
|
6072
|
+
"Look up Medicare Part-B provider utilization — for a given provider (NPI) or state, the HCPCS services rendered, beneficiaries served, and submitted / Medicare-allowed / Medicare-paid amounts (CMS 'Medicare Physician & Other Practitioners — by Provider and Service', keyless; data.cms.gov data-API). The demand-side complement to nppes_lookup_provider (who providers ARE → what they BILL) for healthcare-market / competitor / teaming due-diligence. Input: `npi` (10-digit) OR `state` (2-letter) — at least ONE is REQUIRED (the table is 9.78M rows; an all-empty query is refused; providerType/hcpcsCode alone are NOT enough to scope); optional `providerType` (exact CMS specialty, e.g. 'Family Practice'), `hcpcsCode` (e.g. '97110', 'G0463'), `size` (1–100, default 25), `offset`. Returns { services:[{ npi, providerName, credentials, providerType, city, state, zip, hcpcsCode, hcpcsDescription, totalBeneficiaries, totalServices, avgSubmittedCharge, avgMedicareAllowed, avgMedicarePayment }] } + honest _meta. ★HONESTY: totalAvailable is the EXACT count from a SEPARATE stats sub-query (…/data-viewer/stats → found_rows, e.g. VA=278254), NEVER the returned-rows length; if that count fails, totalAvailable is null + a disclosing note (never length-faked). offset/size pagination (hasMore = offset+returned < total). Aggregate/payment values are numeric-string → number|null (a genuine 0 stays 0, absent → null, never 0-faked); NPI/HCPCS/names are null-never-empty-string. A genuine no-match ⇒ honest empty (returned:0); a 4xx ⇒ invalid_input/not_found; a 5xx ⇒ THROWS; a 200 non-array/non-JSON ⇒ schema_drift. These are public PROVIDER-level AGGREGATE figures (no patient identifiers) for ONE annual vintage (the dataset year is disclosed in _meta) — a utilization snapshot, NOT a fraud/quality/fitness determination. KEYLESS — no key is sent.",
|
|
6073
|
+
inputSchema: CmsMedicareProviderServicesInput,
|
|
6074
|
+
handler: (input) => cmsUtilization.providerServices(input),
|
|
6075
|
+
}),
|
|
6076
|
+
// ━━━ CMS Hospital Compare — Hospital General Information (1) ━━━ ADR-0062
|
|
6077
|
+
// KEYLESS (data.cms.gov /provider-data/api/1/datastore/query/{datasetId}). A
|
|
6078
|
+
// SINGLE request: the response's top-level `count` is the EXACT per-filter total
|
|
6079
|
+
// (P1 — VA=96), never the slice length. Filters ride as DKAN conditions[] triples
|
|
6080
|
+
// via URLSearchParams (bracket key + value encoded — the SSRF guard): state is an
|
|
6081
|
+
// EXACT match, facilityName/hospitalType are case-insensitive substring matches,
|
|
6082
|
+
// AND-combined server-side. REQUIRE state OR facilityName (never scanned unscoped).
|
|
6083
|
+
defineTool({
|
|
6084
|
+
name: "cms_hospital_compare",
|
|
6085
|
+
description:
|
|
6086
|
+
"Look up Medicare-certified hospitals by US state and/or facility-name fragment — location, type, ownership, emergency-services flag, and CMS star rating (CMS Hospital Compare 'Hospital General Information', keyless; data.cms.gov provider-data datastore-query API, ~5,432 hospitals). A healthcare-facility directory / market-map lane (WHERE hospitals are and HOW CMS rates them). Input: `state` (2-letter, EXACT) OR `facilityName` (a name fragment, case-insensitive substring/contains match) — at least ONE is REQUIRED (an all-empty query is refused; hospitalType alone is NOT enough to scope); optional `hospitalType` (substring, e.g. 'Acute', 'Critical Access'), `size` (1–100, default 25), `offset`. Returns { hospitals:[{ facilityId, facilityName, address, city, state, zip, county, phone, hospitalType, ownership, emergencyServices, overallRating }] } + honest _meta. ★HONESTY: totalAvailable is the response's EXACT top-level `count` for the filter set (VA=96), NEVER the returned-rows length; offset/size pagination (hasMore = offset+returned < count). overallRating is CMS's 1–5 star rating as a number; 'Not Available'/blank/non-numeric ⇒ null (NEVER 0). emergencyServices normalizes 'Yes'⇒true / 'No'⇒false / else null (never a fabricated false). IDs/names/addresses are null-never-empty-string. A genuine no-match ⇒ honest empty (returned:0); a 4xx ⇒ invalid_input/not_found; a 5xx ⇒ THROWS; a 200 non-array body or one missing count/results ⇒ schema_drift. Filters are applied SERVER-SIDE (AND-combined) — nothing is silently dropped. This is a summary star rating, NOT a clinical-quality or fitness determination. KEYLESS — no key is sent.",
|
|
6087
|
+
inputSchema: CmsHospitalCompareInput,
|
|
6088
|
+
handler: (input) => cmsHospital.hospitalCompare(input),
|
|
6089
|
+
}),
|
|
6090
|
+
// ━━━ CMS Facility Directory — 4 provider-data datasets (1) ━━━ ADR-0063
|
|
6091
|
+
// KEYLESS (data.cms.gov /provider-data/api/1/datastore/query/{datasetId}). A
|
|
6092
|
+
// generalization of cms_hospital_compare beyond hospitals: facilityType (a Zod
|
|
6093
|
+
// enum) indexes a CONSTANT map to a VETTED dataset id — the user value never enters
|
|
6094
|
+
// the path (the load-bearing SSRF guard). A SINGLE request: the response's top-level
|
|
6095
|
+
// `count` is the EXACT per-filter total (P1). name/address/ownership columns vary
|
|
6096
|
+
// per dataset → coalesced (null if none — never empty-string, never fabricated).
|
|
6097
|
+
defineTool({
|
|
6098
|
+
name: "cms_facility_directory",
|
|
6099
|
+
description:
|
|
6100
|
+
"Look up Medicare/Medicaid-certified healthcare FACILITIES by type — nursing homes, home health agencies, hospices, or dialysis facilities — with their name, address, city, state, zip, and ownership (CMS provider-data, keyless; data.cms.gov datastore-query API, four datasets). A healthcare-facility directory / market-map lane that generalizes cms_hospital_compare beyond hospitals. Input: `facilityType` (REQUIRED enum — 'nursing_home' ~14,695 / 'home_health' ~12,460 / 'hospice' ~6,852 / 'dialysis' ~7,490; selects the dataset id via a constant map, the value never enters the URL path), optional `state` (2-letter, EXACT), `facilityName` (a name fragment, case-insensitive substring/contains match against the dataset's primary-name column), `size` (1–100, default 25), `offset`. Returns { facilities:[{ name, address, city, state, zip, facilityType, ownership }] } + honest _meta. ★HONESTY: totalAvailable is the response's EXACT top-level `count` for the filter set, NEVER the returned-rows length; offset/size pagination (hasMore = offset+returned < count). name/address/ownership column names DIFFER across the four datasets, so each is COALESCED over per-dataset candidates (name: provider_name/facility_name/legal_business_name; address: address/provider_address/address_line_1; ownership: ownership_type/type_of_ownership/profit_or_nonprofit) — a field absent in the chosen dataset is null (unknown), NEVER an empty string and NEVER fabricated. facilityType is echoed on each row. A genuine no-match ⇒ honest empty (returned:0); an invalid facilityType ⇒ invalid_input (blocked by the enum); a 4xx ⇒ invalid_input/not_found; a 5xx ⇒ THROWS; a 200 non-array body or one missing count/results ⇒ schema_drift. Filters are applied SERVER-SIDE (AND-combined) — nothing is silently dropped. This is a facility directory, NOT a clinical-quality or fitness determination. KEYLESS — no key is sent.",
|
|
6101
|
+
inputSchema: CmsFacilityDirectoryInput,
|
|
6102
|
+
handler: (input) => cmsFacility.facilityDirectory(input),
|
|
6103
|
+
}),
|
|
6104
|
+
// ━━━ CMS DMEPOS by Supplier — supply-side utilization (1) ━━━ ADR-0064
|
|
6105
|
+
// KEYLESS (data.cms.gov /data-api/v1/dataset/{uuid}). ★Two requests: a stats count
|
|
6106
|
+
// sub-query yields the EXACT per-filter total (P1 — found_rows), then the data slice
|
|
6107
|
+
// (a bare JSON array). All filter VALUES ride via URLSearchParams (bracket key +
|
|
6108
|
+
// value encoded — the SSRF guard). REQUIRE npi OR state (the supplier table is never
|
|
6109
|
+
// scanned unscoped). The dataset UUID is a SPECIFIC ANNUAL VINTAGE (update yearly).
|
|
6110
|
+
defineTool({
|
|
6111
|
+
name: "cms_dmepos_suppliers",
|
|
6112
|
+
description:
|
|
6113
|
+
"Look up Medicare DMEPOS (Durable Medical Equipment, Devices & Supplies) SUPPLIERS — for a given supplier (NPI) or state, the supplier's identity plus aggregate Medicare figures: HCPCS codes billed, beneficiaries served, claims, services, and submitted / Medicare-allowed / Medicare-paid amounts (CMS 'Medicare DMEPOS — by Supplier', keyless; data.cms.gov data-API). The supply-side complement to cms_medicare_provider_services for healthcare-market / competitor / teaming due-diligence on equipment suppliers. Input: `npi` (10-digit) OR `state` (2-letter) — at least ONE is REQUIRED (an all-empty query is refused; the supplier table is never scanned unscoped); optional `size` (1–100, default 25), `offset`. Returns { suppliers:[{ npi, supplierName, credentials, entityType, city, state, zip, totalHcpcsCodes, totalBeneficiaries, totalClaims, totalServices, submittedCharges, medicareAllowed, medicarePayment }] } + honest _meta. ★HONESTY: totalAvailable is the EXACT count from a SEPARATE stats sub-query (…/data-viewer/stats → found_rows), NEVER the returned-rows length; if that count fails, totalAvailable is null + a disclosing note (never length-faked). offset/size pagination (hasMore = offset+returned < total). Aggregate/payment values are numeric-string → number|null (a genuine 0 stays 0, absent → null, never 0-faked); NPI/entityType/names are null-never-empty-string; supplierName joins Last_Name_Org + First_Name ('Last, First' for individuals, the org name alone for organizations). A genuine no-match ⇒ honest empty (returned:0); a 4xx ⇒ invalid_input/not_found; a 5xx ⇒ THROWS; a 200 non-array/non-JSON ⇒ schema_drift. These are public SUPPLIER-level AGGREGATE figures (no patient identifiers) for ONE annual vintage (disclosed in _meta) — a utilization snapshot, NOT a fraud/quality/fitness determination. KEYLESS — no key is sent.",
|
|
6114
|
+
inputSchema: CmsDmeposSuppliersInput,
|
|
6115
|
+
handler: (input) => cmsSupplier.dmeposSuppliers(input),
|
|
6116
|
+
}),
|
|
6117
|
+
// ━━━ CMS Revoked Medicare Providers & Suppliers — vetting/exclusion list (1) ━━━ ADR-0064
|
|
6118
|
+
// KEYLESS (data.cms.gov /data-api/v1/dataset/{uuid}). A legally-published
|
|
6119
|
+
// revocation/exclusion register (~7,059 rows) — the SAME vetting class as the OFAC /
|
|
6120
|
+
// SAM exclusion lists already shipped (surfacing the names IS the point). ALL filters
|
|
6121
|
+
// optional (small table — pagination is fine unfiltered). SAME two-request stats-count
|
|
6122
|
+
// P1 pattern; filter VALUES ride via URLSearchParams (bracket key + value encoded).
|
|
6123
|
+
defineTool({
|
|
6124
|
+
name: "cms_revoked_providers",
|
|
6125
|
+
description:
|
|
6126
|
+
"Search CMS's PUBLIC 'Revoked Medicare Providers & Suppliers' list — the legally-published register of Medicare enrollment revocations, with the revoked provider/supplier's identity, provider type, revocation reason, effective date, and re-enrollment-bar expiration (CMS 'Revoked Providers and Suppliers', keyless; data.cms.gov data-API, ~7,059 rows). A vetting / due-diligence lane in the SAME class as the OFAC / SAM-exclusions lists — for screening a counterparty before teaming or subcontracting. Input (ALL optional — the ~7K-row list is safe to page unfiltered): `npi` (10-digit → NPI), `state` (2-letter → STATE_CD, exact), `lastName` (→ LAST_NAME, exact), `size` (1–100, default 25), `offset`. Returns { revocations:[{ enrollmentId, npi, name, state, providerType, revocationReason, revocationEffectiveDate, reenrollmentBarExpiration }] } + honest _meta (which notes this is CMS's public revocation/exclusion list — a due-diligence signal, NOT a current-eligibility, guilt, or fitness determination). ★HONESTY: totalAvailable is the EXACT count from a SEPARATE stats sub-query (…/data-viewer/stats → found_rows), NEVER the returned-rows length; if that count fails, totalAvailable is null + a disclosing note (never length-faked). offset/size pagination (hasMore = offset+returned < total). name coalesces ORG_NAME (organizations) else FIRST_NAME + LAST_NAME (individuals) — null if none, never a fabricated empty; NPI/reasons/dates are strings (null-never-empty-string). A genuine no-match ⇒ honest empty (returned:0); a 4xx ⇒ invalid_input/not_found; a 5xx ⇒ THROWS; a 200 non-array/non-JSON ⇒ schema_drift. KEYLESS — no key is sent.",
|
|
6127
|
+
inputSchema: CmsRevokedProvidersInput,
|
|
6128
|
+
handler: (input) => cmsSupplier.revokedProviders(input),
|
|
6129
|
+
}),
|
|
5188
6130
|
// ━━━ FRED (Federal Reserve Economic Data) — macro context (2) ━━━ ADR-0048
|
|
5189
6131
|
// ★The server's SECOND KEY-REQUIRED source: FRED has NO keyless tier, so WITHOUT
|
|
5190
6132
|
// a FRED_API_KEY both tools throw an honest invalid_input config error (the other
|
|
@@ -5193,7 +6135,7 @@ export const TOOLS: ToolDef[] = [
|
|
|
5193
6135
|
defineTool({
|
|
5194
6136
|
name: "fred_search_series",
|
|
5195
6137
|
description:
|
|
5196
|
-
"Discover FRED economic series (GDP, CPI, interest rates, unemployment, PPI…) by free-text search (FRED /fred/series/search; api.stlouisfed.org). ★REQUIRES a free FRED_API_KEY: FRED has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://fred.stlouisfed.org/docs/api/api_key.html;
|
|
6138
|
+
"Discover FRED economic series (GDP, CPI, interest rates, unemployment, PPI…) by free-text search (FRED /fred/series/search; api.stlouisfed.org). ★REQUIRES a free FRED_API_KEY: FRED has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://fred.stlouisfed.org/docs/api/api_key.html; fred_series_observations shares this key — call api_key_status to see every source's key requirement). Input: `query` (the search_text, required, e.g. 'unemployment rate' / 'CPI' / '10-year treasury'), optional `limit` (default 25, max 1000), `offset`. Returns { series:[{ id, title, frequency, frequencyShort, units, seasonalAdjustment, observationStart, observationEnd, lastUpdated, popularity }] } + honest _meta. Feed `id` into fred_series_observations for the time series. HONESTY: totalAvailable is FRED's EXACT reported `count` (offset pagination via hasMore/nextOffset — never fabricated); every scalar is null-never-empty-string; a genuine no-match ⇒ honest empty (returned:0); a 400 (bad/missing key) ⇒ invalid_input CARRYING FRED's error_message; a 5xx ⇒ THROWS; a 200 non-JSON / non-array `seriess` ⇒ schema_drift. The key rides ONLY in the &api_key= query param — never logged or echoed.",
|
|
5197
6139
|
inputSchema: FredSearchSeriesInput,
|
|
5198
6140
|
handler: (input) => fred.searchSeries(input),
|
|
5199
6141
|
}),
|
|
@@ -5204,10 +6146,88 @@ export const TOOLS: ToolDef[] = [
|
|
|
5204
6146
|
inputSchema: FredSeriesObservationsInput,
|
|
5205
6147
|
handler: (input) => fred.seriesObservations(input),
|
|
5206
6148
|
}),
|
|
6149
|
+
// ━━━ openFDA recall/enforcement (api.fda.gov) — product-safety recalls (1) ━━━ ADR-0054
|
|
6150
|
+
// KEYLESS with an OPTIONAL OPENFDA_API_KEY (raises the rate limit; keyless works
|
|
6151
|
+
// ~1000/day — it NEVER throws for a missing key, unlike the key-REQUIRED sources).
|
|
6152
|
+
// Structured filters ONLY (no raw Lucene passthrough — the tool assembles + escapes
|
|
6153
|
+
// the search= string, injection-safe). ★P1: totalAvailable = meta.results.total
|
|
6154
|
+
// (EXACT). ★P2 crux: a no-match query returns openFDA HTTP 404 NOT_FOUND ⇒ an honest
|
|
6155
|
+
// empty, never a throw. The optional key rides &api_key= ONLY.
|
|
6156
|
+
defineTool({
|
|
6157
|
+
name: "openfda_enforcement",
|
|
6158
|
+
description:
|
|
6159
|
+
"Search openFDA recall/enforcement records — drug/device/food product recalls with the recalling firm, product, reason, FDA classification (Class I/II/III), status, and geography (openFDA /{category}/enforcement.json; api.fda.gov). KEYLESS (an OPTIONAL free OPENFDA_API_KEY only RAISES the rate limit — keyless works at ~1000 requests/day; it NEVER throws for a missing key; get one at https://open.fda.gov/apis/authentication/; call api_key_status to see every source's key requirement). Input: `category` (drug|device|food, default drug), and STRUCTURED filters — `firm` (→recalling_firm), `product` (→product_description), `reason` (→reason_for_recall), `classification` (Class I|II|III), `status` (e.g. Ongoing/Terminated/Completed), `state` (2-letter, e.g. 'CA') — the tool safely assembles + escapes these into the openFDA search= Lucene string (NO raw passthrough — injection-safe), plus `limit` (1..100, default 25) and `skip` (offset ≥0). Returns { recalls:[{ recallingFirm, productDescription, reasonForRecall, classification, status, state, city, recallInitiationDate, recallNumber, voluntaryMandated, distributionPattern }] } + honest _meta. HONESTY: totalAvailable is openFDA's EXACT meta.results.total (skip/limit pagination via hasMore/nextOffset — never results.length); every scalar (dates included, recall_initiation_date is a YYYYMMDD string) is null-never-empty-string. ★A no-match query returns openFDA HTTP 404 NOT_FOUND ⇒ an HONEST EMPTY (returned:0, totalAvailable:0), NOT an error; a 400 syntax error ⇒ invalid_input surfacing openFDA's message; a 5xx ⇒ THROWS; a 200 non-JSON ⇒ schema_drift. The optional key rides ONLY the &api_key= query param — never logged or echoed.",
|
|
6160
|
+
inputSchema: OpenfdaEnforcementInput,
|
|
6161
|
+
handler: (input) => openfda.enforcement(input),
|
|
6162
|
+
}),
|
|
6163
|
+
// ━━━ openFDA 510(k) device clearances (api.fda.gov) — medical-device regulatory (1) ━━━ ADR-0056
|
|
6164
|
+
// KEYLESS with an OPTIONAL OPENFDA_API_KEY (raises the rate limit; keyless works
|
|
6165
|
+
// ~1000/day — never throws for a missing key). SAME source/envelope/crux as
|
|
6166
|
+
// openfda_enforcement (reuses openfda.ts's fetchOpenfda/readOpenfdaError/luceneQuote).
|
|
6167
|
+
// Structured filters ONLY (no raw Lucene passthrough — the tool assembles + escapes the
|
|
6168
|
+
// search= string, injection-safe). ★P1: totalAvailable = meta.results.total (EXACT,
|
|
6169
|
+
// ~175507). ★P2 crux: a no-match query returns openFDA HTTP 404 NOT_FOUND ⇒ an honest
|
|
6170
|
+
// empty, never a throw. The optional key rides &api_key= ONLY.
|
|
6171
|
+
defineTool({
|
|
6172
|
+
name: "openfda_device_clearances",
|
|
6173
|
+
description:
|
|
6174
|
+
"Search openFDA 510(k) DEVICE CLEARANCES — the FDA's premarket-notification (510(k)) clearances for medical devices, with the applicant/manufacturer, device name, clearance number (K-number), decision (date + description), clearance type, product code, advisory committee, and geography (openFDA /device/510k.json; api.fda.gov). KEYLESS (an OPTIONAL free OPENFDA_API_KEY only RAISES the rate limit — keyless works at ~1000 requests/day; it NEVER throws for a missing key; get one at https://open.fda.gov/apis/authentication/; call api_key_status to see every source's key requirement). Input: STRUCTURED filters — `applicant` (→applicant), `deviceName` (→device_name), `productCode` (→product_code), `clearanceType` (→clearance_type, e.g. Traditional/Special/Abbreviated), `kNumber` (→k_number, e.g. 'K123456'), `state` (2-letter, e.g. 'CA') — the tool safely assembles + escapes these into the openFDA search= Lucene string (NO raw passthrough — injection-safe), plus `limit` (1..100, default 25) and `skip` (offset ≥0). Returns { clearances:[{ applicant, deviceName, kNumber, decisionDate, decisionDescription, clearanceType, productCode, advisoryCommittee, state }] } + honest _meta. HONESTY: totalAvailable is openFDA's EXACT meta.results.total (skip/limit pagination via hasMore/nextOffset — never results.length); every scalar (dates included, decision_date is a YYYY-MM-DD string) is null-never-empty-string. ★A no-match query returns openFDA HTTP 404 NOT_FOUND ⇒ an HONEST EMPTY (returned:0, totalAvailable:0), NOT an error; a 400 syntax error ⇒ invalid_input surfacing openFDA's message; a 5xx ⇒ THROWS; a 200 non-JSON ⇒ schema_drift. The optional key rides ONLY the &api_key= query param — never logged or echoed.",
|
|
6175
|
+
inputSchema: OpenfdaDeviceClearancesInput,
|
|
6176
|
+
handler: (input) => openfdaDevice.deviceClearances(input),
|
|
6177
|
+
}),
|
|
6178
|
+
defineTool({
|
|
6179
|
+
name: "openfda_drug_approvals",
|
|
6180
|
+
description:
|
|
6181
|
+
"Search openFDA Drugs@FDA DRUG APPROVALS — FDA-approved drug applications (NDA/ANDA/BLA) with the sponsor, application number, each approved product (brand + generic/active-ingredient name, dosage form, route, marketing status), and the submission/approval history (openFDA /drug/drugsfda.json; api.fda.gov). Answers 'what drugs did sponsor X get approved, and which are still marketed' — pharma vendor product/approval intelligence. KEYLESS (an OPTIONAL free OPENFDA_API_KEY only RAISES the rate limit — keyless works at ~1000 requests/day; NEVER throws for a missing key; api_key_status lists every source's key requirement). Input: STRUCTURED filters — `sponsorName` (→sponsor_name), `brandName` (→products.brand_name), `activeIngredient` (→products.active_ingredients.name), `applicationNumber` (→application_number) — safely escaped into the openFDA search= Lucene string (NO raw passthrough — injection-safe), plus `limit` (1..100, default 25) and `skip` (offset ≥0). Returns { applications:[{ applicationNumber, sponsorName, products:[{ brandName, genericIngredients:[{name,strength}], dosageForm, route, marketingStatus }], submissions:[{ submissionType, submissionNumber, submissionStatus, submissionStatusDate, submissionClass }] }] } + honest _meta. HONESTY: totalAvailable is openFDA's EXACT meta.results.total (skip/limit pagination — never results.length); every scalar is null-never-empty-string; a 'Discontinued' marketingStatus is NOT an approval revocation (disclosed in _meta). ★A no-match query returns openFDA HTTP 404 NOT_FOUND ⇒ an HONEST EMPTY (returned:0/total:0), NOT an error; a 400 ⇒ invalid_input surfacing openFDA's message; a 5xx ⇒ THROWS; a 200 non-JSON ⇒ schema_drift. The optional key rides ONLY the &api_key= query param — never logged or echoed.",
|
|
6182
|
+
inputSchema: OpenfdaDrugApprovalsInput,
|
|
6183
|
+
handler: (input) => openfdaDrugsfda.drugApprovals(input),
|
|
6184
|
+
}),
|
|
6185
|
+
// ━━━ NHTSA vehicle safety (api.nhtsa.gov) — vehicle/parts supplier vetting (2) ━━━ ADR-0057
|
|
6186
|
+
// ★KEYLESS — no API key at all (no parameter, no header). The cross-agency
|
|
6187
|
+
// product-safety family alongside openFDA (medical). Both tools share
|
|
6188
|
+
// make/model/modelYear inputs and return the COMPLETE matching set (no pagination
|
|
6189
|
+
// ⇒ totalAvailable = the upstream Count/count, complete:true). ★The complaints VIN
|
|
6190
|
+
// (an individual-vehicle PII identifier) is EXCLUDED from the output.
|
|
6191
|
+
defineTool({
|
|
6192
|
+
name: "nhtsa_recalls",
|
|
6193
|
+
description:
|
|
6194
|
+
"Look up NHTSA vehicle safety RECALLS for a specific vehicle — the manufacturer's recall campaigns with the affected component, the safety consequence, the remedy, and 'do not drive'/'park outside'/over-the-air-update flags (NHTSA /recalls/recallsByVehicle; api.nhtsa.gov). KEYLESS — no API key is required or accepted. Input: `make` (required, e.g. 'honda'), `model` (required, e.g. 'accord'), `modelYear` (required, 4-digit, e.g. '2020'). Returns { recalls:[{ campaignNumber, manufacturer, component, summary, consequence, remedy, reportReceivedDate, parkIt, parkOutside, overTheAirUpdate }] } + honest _meta. HONESTY: totalAvailable is NHTSA's EXACT Count and NHTSA returns the COMPLETE set for the vehicle (no pagination) ⇒ complete:true; a no-match (Count 0 / a bad make/model) ⇒ an HONEST EMPTY (returned:0), NOT an error; a 4xx ⇒ invalid_input; a 5xx/timeout ⇒ THROWS; a 200 non-JSON ⇒ schema_drift. The park-it/park-outside/over-the-air-update flags are preserved as booleans (never a fabricated false); dates are strings; every scalar is null-never-empty-string. Fixed host api.nhtsa.gov (SSRF-guarded); make/model are letters/digits/space/hyphen only and modelYear is ^\\d{4}$.",
|
|
6195
|
+
inputSchema: NhtsaVehicleInput,
|
|
6196
|
+
handler: (input) => nhtsa.recalls(input),
|
|
6197
|
+
}),
|
|
6198
|
+
defineTool({
|
|
6199
|
+
name: "nhtsa_complaints",
|
|
6200
|
+
description:
|
|
6201
|
+
"Look up NHTSA consumer COMPLAINTS for a specific vehicle — owner-filed safety complaints with the affected component, crash/fire flags, injury/death counts, and incident/filing dates (NHTSA /complaints/complaintsByVehicle; api.nhtsa.gov). KEYLESS — no API key is required or accepted. Input: `make` (required, e.g. 'honda'), `model` (required, e.g. 'accord'), `modelYear` (required, 4-digit, e.g. '2020'). Returns { complaints:[{ odiNumber, manufacturer, component, summary, crash, fire, numberOfInjuries, numberOfDeaths, dateOfIncident, dateComplaintFiled }] } + honest _meta. ★PRIVACY: the NHTSA complaint VIN (an individual-vehicle identifier) is INTENTIONALLY EXCLUDED from the output — the B2G signal is the manufacturer/component/crash/fire/injury/death safety history, not the VIN. HONESTY: totalAvailable is NHTSA's EXACT count and NHTSA returns the COMPLETE set for the vehicle (no pagination) ⇒ complete:true; a no-match ⇒ an HONEST EMPTY (returned:0), NOT an error; crash/fire preserved as booleans (never a fabricated false); numberOfInjuries/numberOfDeaths via numeric coercion (a genuine 0 stays 0, NEVER null-for-0); dates are strings; a 4xx ⇒ invalid_input; a 5xx/timeout ⇒ THROWS; a 200 non-JSON ⇒ schema_drift. Fixed host api.nhtsa.gov (SSRF-guarded); make/model are letters/digits/space/hyphen only and modelYear is ^\\d{4}$.",
|
|
6202
|
+
inputSchema: NhtsaVehicleInput,
|
|
6203
|
+
handler: (input) => nhtsa.complaints(input),
|
|
6204
|
+
}),
|
|
6205
|
+
// ━━━ CPSC consumer-product recalls (www.saferproducts.gov) — goods/import vetting (1) ━━━ ADR-0058
|
|
6206
|
+
// ★KEYLESS — no API key at all (no parameter, no header). The third leg of the
|
|
6207
|
+
// cross-agency product-safety family alongside NHTSA (vehicles) and openFDA
|
|
6208
|
+
// (medical). The response is a bare JSON ARRAY with NO total-count field and NO
|
|
6209
|
+
// pagination ⇒ totalAvailable = the returned count, complete:true. All filters are
|
|
6210
|
+
// optional; with NO filter the tool bounds results to a ~90-day default window
|
|
6211
|
+
// (disclosed) rather than silently fetch the entire dataset.
|
|
6212
|
+
defineTool({
|
|
6213
|
+
name: "cpsc_recalls",
|
|
6214
|
+
description:
|
|
6215
|
+
"Look up U.S. CPSC consumer-product RECALLS — the recall title, hazard description, remedy, affected products, manufacturers, retailers, injuries, and country of manufacture (CPSC SaferProducts /RestWebServices/Recall; www.saferproducts.gov). The consumer-goods / import product-safety lane alongside nhtsa_recalls (vehicles) and openfda (medical). KEYLESS — no API key is required or accepted. Inputs (ALL optional): `dateStart`/`dateEnd` (YYYY-MM-DD recall date range), `productName` (substring), `manufacturer` (substring), `recallNumber` (a specific CPSC recall number). Returns { recalls:[{ recallNumber, recallDate, title, description, url, products:[names], numberOfUnits, manufacturers:[names], retailers:[names], hazards:[descriptions], remedies:[descriptions], injuries:[names], manufacturerCountries:[names] }] } + honest _meta. HONESTY: the CPSC response is a bare array with NO count field and NO pagination — it returns the COMPLETE matching set, so totalAvailable = the number of returned recalls and complete:true (never a fabricated total). ★With NO filter given, results are bounded to a DEFAULT ~90-day recent window (RecallDateStart, disclosed in _meta.notes) rather than a silent whole-dataset fetch. An empty result ⇒ an HONEST EMPTY (returned:0), NOT an error; a 4xx ⇒ invalid_input; a 5xx/timeout ⇒ THROWS; a 200 non-JSON OR a non-array body ⇒ schema_drift. Nested arrays are flattened to name/description strings (an empty {} object is skipped, never fabricated); NumberOfUnits is free text kept as a string; dates are strings; every scalar is null-never-empty-string. Fixed host www.saferproducts.gov (SSRF-guarded); dates are ^\\d{4}-\\d{2}-\\d{2}$ and recallNumber is letters/digits/hyphen only.",
|
|
6216
|
+
inputSchema: CpscRecallsInput,
|
|
6217
|
+
handler: (input) => cpsc.recalls(input),
|
|
6218
|
+
}),
|
|
6219
|
+
// ━━━ CBP Border Wait Times (bwt.cbp.gov) — freight/logistics (1) ━━━
|
|
6220
|
+
defineTool({
|
|
6221
|
+
name: "cbp_border_wait_times",
|
|
6222
|
+
description:
|
|
6223
|
+
"Live CBP land-border-port wait times — current commercial-vehicle (and passenger) crossing delays at every US Canadian- and Mexican-border port (keyless; bwt.cbp.gov). The FREIGHT / LOGISTICS situational-awareness lane: per-port commercial-vehicle standard + FAST lane delay (minutes), operational status, open-lane count, and maximum lanes. Filters (optional): `border` (case-insensitive substring, 'Canadian'/'Mexican'), `portName` (substring, e.g. 'Laredo'); `limit`/`offset` pagination. Each row: { portNumber, portName, crossingName, border, portStatus (Open/Closed), asOf, commercialVehicle:{ maxLanes, standard:{operationalStatus, delayMinutes, lanesOpen, updateTime}, fast:{…} } }. HONESTY: this is REAL-TIME operational data — each lane carries its own updateTime (surfaced verbatim; freshness never implied live-to-the-second); delayMinutes/lanesOpen are number|null (a real 0 stays 0; an empty/N/A value — e.g. a closed lane — is null, NEVER a fabricated 0, because a closed lane's delay is UNKNOWN, not zero); the API returns the WHOLE port set so totalAvailable is the EXACT matched-port count; an outage/4xx/timeout THROWS and a non-array body ⇒ schema_drift (never a fake empty).",
|
|
6224
|
+
inputSchema: CbpBorderWaitInput,
|
|
6225
|
+
handler: (input) => cbpBorder.borderWaitTimes(input),
|
|
6226
|
+
}),
|
|
5207
6227
|
// ━━━ BEA Regional Economic Accounts (apps.bea.gov) — regional GDP/income (1) ━━━ ADR-0051
|
|
5208
6228
|
// ★The server's THIRD KEY-REQUIRED source: the BEA Data API has NO keyless tier, so
|
|
5209
6229
|
// WITHOUT a BEA_API_KEY this tool throws an honest invalid_input config error (the
|
|
5210
|
-
// other
|
|
6230
|
+
// most other tools stay keyless — see api_key_status). County/state/MSA GDP-by-industry + personal income —
|
|
5211
6231
|
// the regional place-of-performance lane. ★The P2 crux: a missing/invalid key returns
|
|
5212
6232
|
// HTTP 200 carrying BEAAPI.Results.Error (NOT an HTTP error status), which is detected
|
|
5213
6233
|
// BEFORE the Data-array drift check and surfaced as invalid_input (never a fake empty).
|
|
@@ -5215,7 +6235,7 @@ export const TOOLS: ToolDef[] = [
|
|
|
5215
6235
|
defineTool({
|
|
5216
6236
|
name: "bea_regional_data",
|
|
5217
6237
|
description:
|
|
5218
|
-
"Regional (county / state / MSA) economic data — GDP by industry and personal income — from the US Bureau of Economic Analysis (BEA) Regional Economic Accounts (apps.bea.gov/api/data, dataset 'Regional'). ★REQUIRES a free BEA_API_KEY: the BEA Data API has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://apps.bea.gov/API/signup/;
|
|
6238
|
+
"Regional (county / state / MSA) economic data — GDP by industry and personal income — from the US Bureau of Economic Analysis (BEA) Regional Economic Accounts (apps.bea.gov/api/data, dataset 'Regional'). ★REQUIRES a free BEA_API_KEY: the BEA Data API has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://apps.bea.gov/API/signup/; call api_key_status to see every source's key requirement). Input: `tableName` (required, e.g. 'CAGDP2' county GDP by industry, 'SAGDP2N' state GDP, 'CAINC1'/'SAINC1' personal income), `geoFips` (required — 'STATE' for all states, a county FIPS like '06075', or an MSA code), `lineCode` (required — an integer industry line like '1', or 'ALL'), optional `year` ('LAST5' default, a 4-digit year, or 'ALL'), `frequency` ('A' annual default, or 'Q'). Returns { rows:[{ geoFips, geoName, timePeriod, lineCode, dataValue, unitOfMeasure, unitMult, noteRef }], notes:[{ noteRef, noteText }] } + honest _meta. ★HONESTY (the crux): a missing/invalid key — or ANY bad parameter — returns HTTP 200 carrying an Error object (NOT an HTTP error status); this is detected and surfaced as invalid_input carrying BEA's APIErrorDescription — NEVER a fake empty. dataValue is parsed from BEA's comma-formatted string ('1,234,567'→1234567); BEA suppression/not-available codes ((NA)/(D)/(NM)/(L)/*) map to null — NEVER 0 (a genuine 0 stays 0). unitMult (a power-of-10 multiplier) and unitOfMeasure are reported ALONGSIDE the raw dataValue — the value is NOT multiplied in (apply unitMult yourself). BEA returns the COMPLETE set for the filter (no pagination) ⇒ totalAvailable = the row count, complete:true; a genuine empty Data:[] ⇒ honest empty (returned:0); a 5xx ⇒ THROWS; a 200 non-JSON ⇒ schema_drift. The key rides ONLY in the UserID= query param — never logged or echoed.",
|
|
5219
6239
|
inputSchema: BeaRegionalDataInput,
|
|
5220
6240
|
handler: (input) => bea.regionalData(input),
|
|
5221
6241
|
}),
|
|
@@ -5250,7 +6270,7 @@ export const TOOLS: ToolDef[] = [
|
|
|
5250
6270
|
defineTool({
|
|
5251
6271
|
name: "dol_get_dataset",
|
|
5252
6272
|
description:
|
|
5253
|
-
"Fetch records from ONE US DOL dataset (apiprod.dol.gov /v4/get/{agency}/{endpoint}/json). ★REQUIRES a free DOL_API_KEY: the DOL DATA endpoint has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://dol.gov/
|
|
6273
|
+
"Fetch records from ONE US DOL dataset (apiprod.dol.gov /v4/get/{agency}/{endpoint}/json). ★REQUIRES a free DOL_API_KEY: the DOL DATA endpoint has NO keyless tier, so without the key this tool THROWS an honest config error (get one at https://dataportal.dol.gov/registration; the dataset CATALOG — dol_list_datasets — and agency list stay keyless). Input: `agency` (required — the `agencyAbbr` from dol_list_datasets, e.g. 'WHD', 'OSHA', 'ILAB'; rides the PATH, ^[A-Za-z0-9_]+$), `table` (required — the dataset's `apiUrl` endpoint from dol_list_datasets, e.g. 'Child_Labor_Report__2016_to_2022'; rides the PATH, ^[A-Za-z0-9_]+$), optional `limit` (default 10, max 100), `offset`, `filterField`+`filterValue` (a paired equality filter → a DOL filter_object), `fields` (best-effort column selection). Returns { records:[…verbatim dataset rows…] } + honest _meta. HONESTY: records are surfaced VERBATIM (the data-record envelope is key-gated and unverified, so field names/values are preserved as-is — a genuine 0 stays 0, a missing field stays null; the tool never coerces or fabricates). totalAvailable is a real count field ONLY when the response carries one, else null (an honest unknown — `returned` is NEVER passed off as the total); offset pagination (a full page ⇒ hasMore, page forward to confirm). A missing/invalid key (401/403) ⇒ invalid_input carrying the DOL_API_KEY guidance (never empty); a 400 ⇒ invalid_input; a genuine empty ⇒ honest empty (returned:0); a 429 ⇒ rate_limited THROWS (Retry-After honored); a 5xx/timeout ⇒ upstream_unavailable THROWS; a 200 non-JSON / no row array ⇒ schema_drift. The key rides ONLY in the X-API-KEY request header — never the URL / _meta / a log.",
|
|
5254
6274
|
inputSchema: DolGetDatasetInput,
|
|
5255
6275
|
handler: (input) => dol.getDataset(input),
|
|
5256
6276
|
}),
|
|
@@ -5268,6 +6288,43 @@ export const TOOLS: ToolDef[] = [
|
|
|
5268
6288
|
inputSchema: LdaSearchFilingsInput,
|
|
5269
6289
|
handler: (input) => lda.searchFilings(input),
|
|
5270
6290
|
}),
|
|
6291
|
+
// ━━━ US federal court opinions (www.courtlistener.com) — the litigation lane (1) ━━━ ADR-0055
|
|
6292
|
+
// Federal court decisions — the judicial signal no contract/spending/lobbying source
|
|
6293
|
+
// carries (uscfc bid-protest/contract-claim opinions, cafc contract/patent appeals).
|
|
6294
|
+
// ★PROVENANCE: CourtListener (Free Law Project, a non-profit), NOT a .gov API — PACER
|
|
6295
|
+
// (the .gov source) is paywalled. KEYLESS (anonymous 200); the OPTIONAL free
|
|
6296
|
+
// COURTLISTENER_API_TOKEN only raises the rate limit, riding the Authorization: Token …
|
|
6297
|
+
// header ONLY. ★count is the API's REAL total — never results.length; CURSOR pagination
|
|
6298
|
+
// (nextCursor extracted from `next`, host re-asserted). type=o FIXED.
|
|
6299
|
+
defineTool({
|
|
6300
|
+
name: "courtlistener_search_opinions",
|
|
6301
|
+
description:
|
|
6302
|
+
"Search US FEDERAL COURT OPINIONS (case law / litigation) via CourtListener (www.courtlistener.com/api/rest/v4/search, type=o). ★PROVENANCE: the DATA is US federal court PUBLIC RECORDS, but the API is CourtListener, run by the Free Law Project (a NON-PROFIT) — this is NOT a .gov API; CourtListener republishes these records KEYLESS because the .gov primary source (PACER) is PAYWALLED. KEYLESS (anonymous access works; an optional free COURTLISTENER_API_TOKEN only raises the rate limit; get one at https://www.courtlistener.com/help/api/rest/; call api_key_status to see every source's key requirement). All inputs optional: `query` (full-text → q), `court` (a court id, ^[a-z0-9]+$ — e.g. 'uscfc' US Court of Federal Claims for contract claims/bid protests, 'cafc' Federal Circuit for contract/patent appeals, 'scotus'), `dateFiledAfter`/`dateFiledBefore` (ISO ^\\d{4}-\\d{2}-\\d{2}$ → filed_after/filed_before), `natureOfSuit` (folded into the q query — no verified dedicated filter, disclosed in notes), `cursor` (opaque continuation — pass back _meta.nextCursor), `order` (→ order_by, default 'dateFiled desc'). Returns { opinions:[{ caseName, court, courtId, dateFiled, docketNumber, natureOfSuit, status, judge, citation, absoluteUrl }] } + honest _meta. HONESTY: totalAvailable is the API's REAL `count` (the total match count for the filter) — NOT the rows on this page; pagination is an OPAQUE CURSOR (offset/nextOffset are null/meaningless — pass _meta.nextCursor back as `cursor`; nextCursor:null/hasMore:false = last page). CourtListener v4 stops counting on deep cursor pages (count:null) ⇒ totalAvailable:null is DISCLOSED, never faked as results.length. dateFiled is a date STRING; citation may be an array/object ⇒ flattened to a safe string/string[] (never fabricated); judge/natureOfSuit/docketNumber are null when absent (never ''); absoluteUrl is the full https://www.courtlistener.com link. A genuine no-match (results:[]) ⇒ honest empty (returned:0); a 400 (bad param) ⇒ invalid_input surfacing the API's message; a 429 (unauth throttle) ⇒ rate_limited THROWS (Retry-After honored, never routed around); a 5xx/timeout ⇒ upstream_unavailable THROWS; a 200 non-JSON / non-array results / a count that is neither a number nor null ⇒ schema_drift; an off-host `next` is REFUSED (SSRF). The optional token rides ONLY in the Authorization: Token header (never the URL/_meta).",
|
|
6303
|
+
inputSchema: CourtlistenerSearchOpinionsInput,
|
|
6304
|
+
handler: (input) => courtlistener.searchOpinions(input),
|
|
6305
|
+
}),
|
|
6306
|
+
// ━━━ US tax-exempt nonprofits (projects.propublica.org) — the nonprofit lane (2) ━━━ ADR-0060
|
|
6307
|
+
// Who a 501(c) org IS (EIN, NTEE, subsection, ruling date, status) + its Form 990
|
|
6308
|
+
// FINANCIALS (revenue/expenses/assets/liabilities by year) — the nonprofit/grantee/
|
|
6309
|
+
// subcontractor vetting signal no contract/spending/grant/lobbying source carries.
|
|
6310
|
+
// ★PROVENANCE: IRS Form 990 public records republished KEYLESS by ProPublica Nonprofit
|
|
6311
|
+
// Explorer (a non-profit newsroom), NOT a .gov API (the IRS has no clean query API).
|
|
6312
|
+
// KEYLESS (no key). search total_results is the REAL total — never organizations.length;
|
|
6313
|
+
// financials totalAvailable = filings.length (the complete set). Money via num (null-never-0).
|
|
6314
|
+
defineTool({
|
|
6315
|
+
name: "nonprofit_search",
|
|
6316
|
+
description:
|
|
6317
|
+
"Search US TAX-EXEMPT NONPROFITS (501(c) organizations) by IRS Form 990 data via ProPublica Nonprofit Explorer (projects.propublica.org/nonprofits/api/v2/search). ★PROVENANCE: the DATA is IRS Form 990 filings — FEDERAL tax-exempt PUBLIC RECORDS — but the API is ProPublica Nonprofit Explorer, run by ProPublica (a NON-PROFIT newsroom) — this is NOT a .gov API; ProPublica republishes these records KEYLESS because the IRS itself has no clean query API (only bulk downloads / a web UI). KEYLESS (no key of any kind). All inputs optional: `query` (full-text org name/keyword → q), `state` (2-letter code → state[id], ^[A-Za-z]{2}$), `ntee` (NTEE major category, integer 1..10 → ntee[id]), `page` (0-BASED, default 0). Returns { organizations:[{ ein, name, city, state, nteeCode, subsectionCode }] } + honest _meta. HONESTY: totalAvailable is the API's REAL total_results (the total match count for the query) — NOT the organizations on this page; pagination is page-based and 0-INDEXED (pass page=cur_page+1 when hasMore). ein/nteeCode/subsectionCode are strings (never num-coerced). A genuine no-match (organizations:[]) ⇒ honest empty (returned:0, complete:true); a 4xx ⇒ invalid_input; a 429 ⇒ rate_limited THROWS (Retry-After honored, never routed around); a 5xx/timeout ⇒ upstream_unavailable THROWS; a 200 non-JSON / non-array organizations / non-number total_results ⇒ schema_drift. Data is IRS Form 990 data via ProPublica Nonprofit Explorer, disclosed in _meta.source and a note.",
|
|
6318
|
+
inputSchema: NonprofitSearchInput,
|
|
6319
|
+
handler: (input) => nonprofit.search(input),
|
|
6320
|
+
}),
|
|
6321
|
+
defineTool({
|
|
6322
|
+
name: "nonprofit_financials",
|
|
6323
|
+
description:
|
|
6324
|
+
"Fetch ONE US tax-exempt nonprofit's IRS Form 990 profile + FINANCIALS by EIN via ProPublica Nonprofit Explorer (projects.propublica.org/nonprofits/api/v2/organizations/{ein}.json). ★PROVENANCE: the DATA is IRS Form 990 filings (federal tax-exempt public records) but the API is ProPublica Nonprofit Explorer, run by ProPublica (a NON-PROFIT newsroom) — NOT a .gov API; ProPublica republishes these records KEYLESS because the IRS has no clean query API. KEYLESS (no key). Input: `ein` (required — the Employer Identification Number, 1..9 digits ^\\d{1,9}$, e.g. '530196605' American National Red Cross; rides the URL path). Returns { organization:{ ein, name, address, city, state, zip, nteeCode, subsectionCode, rulingDate, statusCode }, filings:[{ taxYear, formType, revenueUsd, expensesUsd, assetsUsd, liabilitiesUsd, pdfUrl }] } + honest _meta. HONESTY: the four Form 990 figures (revenueUsd/expensesUsd/assetsUsd/liabilitiesUsd, from totrevenue/totfuncexpns/totassetsend/totliabend) ride null-never-0 coercion — a genuine reported 0 stays 0, an absent figure ⇒ null (NEVER 0-faked); ein/codes are strings; rulingDate is a date string. totalAvailable = filings.length (the COMPLETE Form 990 filing set from the one detail document — no pagination). An unknown EIN (HTTP 404) ⇒ not_found (NEVER a fabricated empty org); a 4xx ⇒ invalid_input; a 429 ⇒ rate_limited THROWS; a 5xx/timeout ⇒ upstream_unavailable THROWS; a 200 non-JSON / non-object organization / non-array filings_with_data ⇒ schema_drift. Data is IRS Form 990 data via ProPublica Nonprofit Explorer, disclosed in _meta.source and a note.",
|
|
6325
|
+
inputSchema: NonprofitFinancialsInput,
|
|
6326
|
+
handler: (input) => nonprofit.financials(input),
|
|
6327
|
+
}),
|
|
5271
6328
|
// ━━━ Self-service key discovery (1) ━━━
|
|
5272
6329
|
// KEYLESS. A local status query — reads process.env (+ any .env auto-loaded at
|
|
5273
6330
|
// startup) and reports, per key, whether it is set (a BOOLEAN — the key VALUE is
|
|
@@ -5421,6 +6478,28 @@ function synthesizeDefaultMeta(
|
|
|
5421
6478
|
return buildMeta({ source, keylessMode, complete: true, truncated: false });
|
|
5422
6479
|
}
|
|
5423
6480
|
|
|
6481
|
+
// Unwrap a tool's inputSchema down to its underlying ZodObject so we can read the
|
|
6482
|
+
// set of declared top-level keys. Tools wrap the object in .refine()/.superRefine()
|
|
6483
|
+
// (ZodEffects), or occasionally .optional()/.default()/.nullable(), so peel those
|
|
6484
|
+
// layers. Returns null if no ZodObject is reachable (then unknown-key rejection is
|
|
6485
|
+
// skipped for that tool — fail open, never fail closed on our own introspection).
|
|
6486
|
+
function objectSchemaOf(schema: z.ZodTypeAny): z.AnyZodObject | null {
|
|
6487
|
+
let s: unknown = schema;
|
|
6488
|
+
for (let i = 0; i < 20; i++) {
|
|
6489
|
+
const def = (s as { _def?: { typeName?: string } } | undefined)?._def;
|
|
6490
|
+
if (!def) break;
|
|
6491
|
+
const tn = def.typeName;
|
|
6492
|
+
if (tn === "ZodObject") return s as z.AnyZodObject;
|
|
6493
|
+
if (tn === "ZodEffects") { s = (def as { schema?: unknown }).schema; continue; }
|
|
6494
|
+
if (tn === "ZodOptional" || tn === "ZodDefault" || tn === "ZodNullable") {
|
|
6495
|
+
s = (def as { innerType?: unknown }).innerType;
|
|
6496
|
+
continue;
|
|
6497
|
+
}
|
|
6498
|
+
break;
|
|
6499
|
+
}
|
|
6500
|
+
return null;
|
|
6501
|
+
}
|
|
6502
|
+
|
|
5424
6503
|
export async function runTool(
|
|
5425
6504
|
name: string,
|
|
5426
6505
|
args: Record<string, unknown>,
|
|
@@ -5434,6 +6513,27 @@ export async function runTool(
|
|
|
5434
6513
|
// gone (all 52 tools migrated) — an unknown name has no entry and throws.
|
|
5435
6514
|
const entry = TOOLS.find((t) => t.name === name);
|
|
5436
6515
|
if (entry?.handler) {
|
|
6516
|
+
// HONESTY (dogfooding 2026-07-15): reject UNKNOWN top-level input keys LOUD
|
|
6517
|
+
// instead of Zod's default silent-strip. A misspelled filter (naicsCode↔naics,
|
|
6518
|
+
// keyword↔query) was otherwise dropped and the tool scanned the WHOLE corpus,
|
|
6519
|
+
// returning an authoritative-looking WRONG answer with no error. We name the
|
|
6520
|
+
// unknown key(s) and list the valid ones so the mistake self-corrects. Skip
|
|
6521
|
+
// when the schema intentionally accepts extras (unknownKeys==="passthrough").
|
|
6522
|
+
const obj = objectSchemaOf(entry.inputSchema);
|
|
6523
|
+
if (obj && obj._def.unknownKeys !== "passthrough" && args && typeof args === "object") {
|
|
6524
|
+
const known = new Set(Object.keys(obj.shape));
|
|
6525
|
+
const unknown = Object.keys(args).filter((k) => !known.has(k));
|
|
6526
|
+
if (unknown.length > 0) {
|
|
6527
|
+
throw new ToolErrorCarrier({
|
|
6528
|
+
kind: "invalid_input",
|
|
6529
|
+
message:
|
|
6530
|
+
`Unknown input ${unknown.length > 1 ? "keys" : "key"} for ${name}: ` +
|
|
6531
|
+
`${unknown.map((k) => `'${k}'`).join(", ")}. ` +
|
|
6532
|
+
`Valid keys: ${[...known].sort().join(", ")}.`,
|
|
6533
|
+
retryable: false,
|
|
6534
|
+
});
|
|
6535
|
+
}
|
|
6536
|
+
}
|
|
5437
6537
|
const input = entry.inputSchema.parse(args);
|
|
5438
6538
|
return await entry.handler(input, { sam });
|
|
5439
6539
|
}
|