@cliwant/mcp-sam-gov 1.3.0 → 1.4.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 +15 -7
- package/README.ko.md +15 -7
- package/README.md +48 -8
- package/dist/bea.d.ts +1 -1
- package/dist/bea.js +1 -1
- 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/dol.d.ts +2 -2
- package/dist/dol.js +5 -5
- package/dist/dol.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 +292 -0
- package/dist/epa-envirofacts.js.map +1 -0
- package/dist/fred.d.ts +1 -1
- package/dist/fred.js +1 -1
- 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/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/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.d.ts +133 -0
- package/dist/openfda.d.ts.map +1 -0
- package/dist/openfda.js +402 -0
- package/dist/openfda.js.map +1 -0
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +666 -7
- package/dist/server.js.map +1 -1
- package/package.json +2 -1
- package/src/bea.ts +1 -1
- 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/dol.ts +5 -5
- package/src/epa-envirofacts.ts +342 -0
- package/src/fred.ts +1 -1
- package/src/keys.ts +27 -6
- package/src/nhtsa.ts +352 -0
- package/src/nonprofit.ts +460 -0
- package/src/openfda-device.ts +356 -0
- package/src/openfda.ts +495 -0
- package/src/server.ts +765 -7
package/dist/server.js
CHANGED
|
@@ -45,11 +45,18 @@ import * as nsf from "./nsf.js";
|
|
|
45
45
|
import * as clinicaltrials from "./clinicaltrials.js";
|
|
46
46
|
import * as census from "./census.js";
|
|
47
47
|
import * as censusEconomic from "./census-economic.js";
|
|
48
|
+
import * as epaEnvirofacts from "./epa-envirofacts.js";
|
|
49
|
+
import * as cmsUtilization from "./cms-utilization.js";
|
|
50
|
+
import * as cmsHospital from "./cms-hospital.js";
|
|
51
|
+
import * as cmsFacility from "./cms-facility.js";
|
|
52
|
+
import * as cmsSupplier from "./cms-supplier.js";
|
|
48
53
|
import * as fred from "./fred.js";
|
|
49
54
|
import * as bea from "./bea.js";
|
|
50
55
|
import * as gsaPerdiem from "./gsa-perdiem.js";
|
|
51
56
|
import * as dol from "./dol.js";
|
|
52
57
|
import * as lda from "./lda.js";
|
|
58
|
+
import * as courtlistener from "./courtlistener.js";
|
|
59
|
+
import * as nonprofit from "./nonprofit.js";
|
|
53
60
|
import * as fema from "./fema.js";
|
|
54
61
|
import * as fdic from "./fdic.js";
|
|
55
62
|
import * as bls from "./bls.js";
|
|
@@ -59,6 +66,10 @@ import * as nppes from "./nppes.js";
|
|
|
59
66
|
import * as cms from "./cms.js";
|
|
60
67
|
import * as fac from "./fac.js";
|
|
61
68
|
import * as usitc from "./usitc.js";
|
|
69
|
+
import * as openfda from "./openfda.js";
|
|
70
|
+
import * as openfdaDevice from "./openfda-device.js";
|
|
71
|
+
import * as nhtsa from "./nhtsa.js";
|
|
72
|
+
import * as cpsc from "./cpsc.js";
|
|
62
73
|
import { fetchAttachmentText } from "./attachments.js";
|
|
63
74
|
import * as keys from "./keys.js";
|
|
64
75
|
import { toToolError, ToolErrorCarrier, errorFromResponse } from "./errors.js";
|
|
@@ -68,7 +79,7 @@ import { realpathSync } from "node:fs";
|
|
|
68
79
|
const SERVER_NAME = "mcp-sam-gov";
|
|
69
80
|
// Kept in lockstep with package.json / manifest.json / server.json.
|
|
70
81
|
// Keep in sync with package.json "version" (asserted at release; see CHANGELOG).
|
|
71
|
-
const SERVER_VERSION = "1.
|
|
82
|
+
const SERVER_VERSION = "1.4.0";
|
|
72
83
|
// ─── Tool input schemas (Zod) ────────────────────────────────────
|
|
73
84
|
const SamSearchInput = z.object({
|
|
74
85
|
query: z.string().optional().describe("Free-text title query"),
|
|
@@ -2824,6 +2835,248 @@ const CensusBusinessPatternsInput = z.object({
|
|
|
2824
2835
|
.optional()
|
|
2825
2836
|
.describe("OPTIONAL client-side top-N cap on the returned rows. CBP has NO server-side pagination, so this slices AFTER the full set is fetched and DISCLOSES the omission (totalAvailable stays the full count). Omit to return every matching row."),
|
|
2826
2837
|
});
|
|
2838
|
+
// ─── EPA Envirofacts TRI facilities (ADR-0059) — keyless, PATH-segment SSRF ──
|
|
2839
|
+
// data.epa.gov /efservice/tri_facility. Two requests: a count sub-query for the
|
|
2840
|
+
// EXACT total (P1) + the data slice. All user values ride as PATH SEGMENTS, so each
|
|
2841
|
+
// is charclass-validated + encodeURIComponent-encoded (the load-bearing SSRF guard).
|
|
2842
|
+
const EpaTriFacilitiesInput = z
|
|
2843
|
+
.object({
|
|
2844
|
+
state: z
|
|
2845
|
+
.string()
|
|
2846
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
2847
|
+
.optional()
|
|
2848
|
+
.describe("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)."),
|
|
2849
|
+
facilityName: z
|
|
2850
|
+
.string()
|
|
2851
|
+
.min(1)
|
|
2852
|
+
.max(100)
|
|
2853
|
+
.regex(/^[A-Za-z0-9 &.\-]+$/)
|
|
2854
|
+
.optional()
|
|
2855
|
+
.describe("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)."),
|
|
2856
|
+
county: z
|
|
2857
|
+
.string()
|
|
2858
|
+
.min(1)
|
|
2859
|
+
.max(100)
|
|
2860
|
+
.regex(/^[A-Za-z0-9 &.\-]+$/)
|
|
2861
|
+
.optional()
|
|
2862
|
+
.describe("A partial county-name match (→ county_name/CONTAINING/…), e.g. 'FAIRFAX'. Optional additional filter; same charclass as facilityName."),
|
|
2863
|
+
limit: z
|
|
2864
|
+
.number()
|
|
2865
|
+
.int()
|
|
2866
|
+
.min(1)
|
|
2867
|
+
.max(100)
|
|
2868
|
+
.optional()
|
|
2869
|
+
.describe("Max facilities to return (1–100, default 25). Offset-paginated."),
|
|
2870
|
+
offset: z
|
|
2871
|
+
.number()
|
|
2872
|
+
.int()
|
|
2873
|
+
.min(0)
|
|
2874
|
+
.optional()
|
|
2875
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
2876
|
+
})
|
|
2877
|
+
.refine((v) => v.state !== undefined || v.facilityName !== undefined, {
|
|
2878
|
+
message: "Provide at least `state` or `facilityName` (an all-empty query would scan the whole national TRI table and is refused).",
|
|
2879
|
+
path: ["state"],
|
|
2880
|
+
});
|
|
2881
|
+
// ─── CMS Medicare provider-service utilization (ADR-0061) — keyless, two-request ──
|
|
2882
|
+
// data.cms.gov /data-api/v1/dataset/{uuid}. Two requests: a stats count sub-query
|
|
2883
|
+
// for the EXACT total (P1 — found_rows) + the data slice (a bare JSON array). Filter
|
|
2884
|
+
// VALUES ride via URLSearchParams (bracket key + value encoded). REQUIRE npi OR
|
|
2885
|
+
// state (the 9.78M-row table is never scanned unscoped).
|
|
2886
|
+
const CmsMedicareProviderServicesInput = z
|
|
2887
|
+
.object({
|
|
2888
|
+
npi: z
|
|
2889
|
+
.string()
|
|
2890
|
+
.regex(/^\d{10}$/)
|
|
2891
|
+
.optional()
|
|
2892
|
+
.describe("A 10-digit National Provider Identifier (→ Rndrng_NPI), e.g. '1003000126'. Provide at least this OR `state`. Validated ^\\d{10}$."),
|
|
2893
|
+
state: z
|
|
2894
|
+
.string()
|
|
2895
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
2896
|
+
.optional()
|
|
2897
|
+
.describe("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}$."),
|
|
2898
|
+
providerType: z
|
|
2899
|
+
.string()
|
|
2900
|
+
.min(1)
|
|
2901
|
+
.max(100)
|
|
2902
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
2903
|
+
.optional()
|
|
2904
|
+
.describe("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)."),
|
|
2905
|
+
hcpcsCode: z
|
|
2906
|
+
.string()
|
|
2907
|
+
.regex(/^[A-Za-z0-9]{1,10}$/)
|
|
2908
|
+
.optional()
|
|
2909
|
+
.describe("An optional HCPCS/CPT service code filter (→ HCPCS_Cd), e.g. '97110', 'G0463'. Validated ^[A-Za-z0-9]{1,10}$."),
|
|
2910
|
+
size: z
|
|
2911
|
+
.number()
|
|
2912
|
+
.int()
|
|
2913
|
+
.min(1)
|
|
2914
|
+
.max(100)
|
|
2915
|
+
.optional()
|
|
2916
|
+
.describe("Max provider-service rows to return (1–100, default 25). Offset-paginated."),
|
|
2917
|
+
offset: z
|
|
2918
|
+
.number()
|
|
2919
|
+
.int()
|
|
2920
|
+
.min(0)
|
|
2921
|
+
.optional()
|
|
2922
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
2923
|
+
})
|
|
2924
|
+
.refine((v) => v.npi !== undefined || v.state !== undefined, {
|
|
2925
|
+
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).",
|
|
2926
|
+
path: ["npi"],
|
|
2927
|
+
});
|
|
2928
|
+
// ─── CMS Hospital Compare "Hospital General Information" (ADR-0062) — keyless ──
|
|
2929
|
+
// data.cms.gov /provider-data/api/1/datastore/query/{datasetId}/0. A SINGLE request:
|
|
2930
|
+
// the response's top-level `count` is the EXACT per-filter total (P1). Filters ride
|
|
2931
|
+
// as DKAN conditions[] triples via URLSearchParams (bracket key + value encoded).
|
|
2932
|
+
// REQUIRE state OR facilityName (the ~5,432-hospital table is never scanned unscoped).
|
|
2933
|
+
const CmsHospitalCompareInput = z
|
|
2934
|
+
.object({
|
|
2935
|
+
state: z
|
|
2936
|
+
.string()
|
|
2937
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
2938
|
+
.optional()
|
|
2939
|
+
.describe("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}$."),
|
|
2940
|
+
facilityName: z
|
|
2941
|
+
.string()
|
|
2942
|
+
.min(1)
|
|
2943
|
+
.max(100)
|
|
2944
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
2945
|
+
.optional()
|
|
2946
|
+
.describe("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)."),
|
|
2947
|
+
hospitalType: z
|
|
2948
|
+
.string()
|
|
2949
|
+
.min(1)
|
|
2950
|
+
.max(100)
|
|
2951
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
2952
|
+
.optional()
|
|
2953
|
+
.describe("An optional hospital-type filter (→ hospital_type, case-insensitive SUBSTRING/contains match), e.g. 'Acute', 'Critical Access'. Allowed: letters/digits/space/& . , ( ) / ' - (≤100 chars)."),
|
|
2954
|
+
size: z
|
|
2955
|
+
.number()
|
|
2956
|
+
.int()
|
|
2957
|
+
.min(1)
|
|
2958
|
+
.max(100)
|
|
2959
|
+
.optional()
|
|
2960
|
+
.describe("Max hospital rows to return (1–100, default 25). Offset-paginated."),
|
|
2961
|
+
offset: z
|
|
2962
|
+
.number()
|
|
2963
|
+
.int()
|
|
2964
|
+
.min(0)
|
|
2965
|
+
.optional()
|
|
2966
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
2967
|
+
})
|
|
2968
|
+
.refine((v) => v.state !== undefined || v.facilityName !== undefined, {
|
|
2969
|
+
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).",
|
|
2970
|
+
path: ["state"],
|
|
2971
|
+
});
|
|
2972
|
+
// ─── CMS Facility Directory (data.cms.gov provider-data, ADR-0063) — KEYLESS ──
|
|
2973
|
+
// A four-dataset facility directory generalizing cms_hospital_compare beyond
|
|
2974
|
+
// hospitals. `facilityType` is a Zod ENUM that indexes a MODULE-CONSTANT map to a
|
|
2975
|
+
// VETTED dataset id (nursing_home → 4pq5-n9py, home_health → 6jpm-sxkc, hospice →
|
|
2976
|
+
// yc9t-dgbk, dialysis → 23ew-n7w9) — the user value never enters the URL path. A
|
|
2977
|
+
// SINGLE request: the response's top-level `count` is the EXACT per-filter total
|
|
2978
|
+
// (P1). Filters ride as DKAN conditions[] triples via URLSearchParams. name/address/
|
|
2979
|
+
// ownership columns vary per dataset → coalesced (null if none).
|
|
2980
|
+
const CmsFacilityDirectoryInput = z.object({
|
|
2981
|
+
facilityType: z
|
|
2982
|
+
.enum(["nursing_home", "home_health", "hospice", "dialysis"])
|
|
2983
|
+
.describe("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)."),
|
|
2984
|
+
state: z
|
|
2985
|
+
.string()
|
|
2986
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
2987
|
+
.optional()
|
|
2988
|
+
.describe("An optional 2-letter US state/territory code (→ state, EXACT match), e.g. 'VA', 'TX'. Validated ^[A-Za-z]{2}$."),
|
|
2989
|
+
facilityName: z
|
|
2990
|
+
.string()
|
|
2991
|
+
.min(1)
|
|
2992
|
+
.max(100)
|
|
2993
|
+
.regex(/^[A-Za-z0-9 &.,()/'-]+$/)
|
|
2994
|
+
.optional()
|
|
2995
|
+
.describe("An optional facility-name fragment (case-insensitive SUBSTRING/contains match against the dataset's primary-name column). Allowed: letters/digits/space/& . , ( ) / ' - (≤100 chars)."),
|
|
2996
|
+
size: z
|
|
2997
|
+
.number()
|
|
2998
|
+
.int()
|
|
2999
|
+
.min(1)
|
|
3000
|
+
.max(100)
|
|
3001
|
+
.optional()
|
|
3002
|
+
.describe("Max facility rows to return (1–100, default 25). Offset-paginated."),
|
|
3003
|
+
offset: z
|
|
3004
|
+
.number()
|
|
3005
|
+
.int()
|
|
3006
|
+
.min(0)
|
|
3007
|
+
.optional()
|
|
3008
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3009
|
+
});
|
|
3010
|
+
// ─── CMS DMEPOS by Supplier (data.cms.gov data-API, ADR-0064) — KEYLESS ──
|
|
3011
|
+
// SAME host/endpoint/two-request-stats-count pattern as cms_medicare_provider_services.
|
|
3012
|
+
// REQUIRE npi OR state (the supplier table is never scanned unscoped). Filter VALUES
|
|
3013
|
+
// ride as URLSearchParams filter[Col]=Val (bracket key + value encoded — the SSRF guard).
|
|
3014
|
+
const CmsDmeposSuppliersInput = z
|
|
3015
|
+
.object({
|
|
3016
|
+
npi: z
|
|
3017
|
+
.string()
|
|
3018
|
+
.regex(/^\d{10}$/)
|
|
3019
|
+
.optional()
|
|
3020
|
+
.describe("A 10-digit supplier National Provider Identifier (→ Suplr_NPI), e.g. '1003000126'. Provide at least this OR `state`. Validated ^\\d{10}$."),
|
|
3021
|
+
state: z
|
|
3022
|
+
.string()
|
|
3023
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3024
|
+
.optional()
|
|
3025
|
+
.describe("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}$."),
|
|
3026
|
+
size: z
|
|
3027
|
+
.number()
|
|
3028
|
+
.int()
|
|
3029
|
+
.min(1)
|
|
3030
|
+
.max(100)
|
|
3031
|
+
.optional()
|
|
3032
|
+
.describe("Max supplier rows to return (1–100, default 25). Offset-paginated."),
|
|
3033
|
+
offset: z
|
|
3034
|
+
.number()
|
|
3035
|
+
.int()
|
|
3036
|
+
.min(0)
|
|
3037
|
+
.optional()
|
|
3038
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3039
|
+
})
|
|
3040
|
+
.refine((v) => v.npi !== undefined || v.state !== undefined, {
|
|
3041
|
+
message: "Provide at least `npi` or `state` (an all-empty query would scan the entire DMEPOS supplier table and is refused).",
|
|
3042
|
+
path: ["npi"],
|
|
3043
|
+
});
|
|
3044
|
+
// ─── CMS Revoked Medicare Providers & Suppliers (data.cms.gov data-API, ADR-0064) ──
|
|
3045
|
+
// KEYLESS. A legally-published revocation/exclusion register (~7,059 rows) — the same
|
|
3046
|
+
// vetting class as the OFAC / SAM exclusion lists. ALL filters optional (small table —
|
|
3047
|
+
// pagination is fine unfiltered). SAME two-request stats-count P1 pattern.
|
|
3048
|
+
const CmsRevokedProvidersInput = z.object({
|
|
3049
|
+
npi: z
|
|
3050
|
+
.string()
|
|
3051
|
+
.regex(/^\d{10}$/)
|
|
3052
|
+
.optional()
|
|
3053
|
+
.describe("An optional 10-digit National Provider Identifier (→ NPI), e.g. '1003000126'. Validated ^\\d{10}$."),
|
|
3054
|
+
state: z
|
|
3055
|
+
.string()
|
|
3056
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3057
|
+
.optional()
|
|
3058
|
+
.describe("An optional 2-letter US state/territory code (→ STATE_CD, EXACT match), e.g. 'FL', 'CA'. Validated ^[A-Za-z]{2}$."),
|
|
3059
|
+
lastName: z
|
|
3060
|
+
.string()
|
|
3061
|
+
.min(1)
|
|
3062
|
+
.max(100)
|
|
3063
|
+
.regex(/^[A-Za-z0-9 .,'-]+$/)
|
|
3064
|
+
.optional()
|
|
3065
|
+
.describe("An optional last-name filter (→ LAST_NAME, EXACT match). Allowed: letters/digits/space/. , ' - (≤100 chars)."),
|
|
3066
|
+
size: z
|
|
3067
|
+
.number()
|
|
3068
|
+
.int()
|
|
3069
|
+
.min(1)
|
|
3070
|
+
.max(100)
|
|
3071
|
+
.optional()
|
|
3072
|
+
.describe("Max revocation rows to return (1–100, default 25). Offset-paginated."),
|
|
3073
|
+
offset: z
|
|
3074
|
+
.number()
|
|
3075
|
+
.int()
|
|
3076
|
+
.min(0)
|
|
3077
|
+
.optional()
|
|
3078
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3079
|
+
});
|
|
2827
3080
|
// ─── FRED (Federal Reserve Economic Data) — the SECOND key-required source ──
|
|
2828
3081
|
// ADR-0048. Macro context (GDP/CPI/rates/unemployment/PPI). REQUIRES a free
|
|
2829
3082
|
// FRED_API_KEY; without it both tools throw an honest config error (the other 112
|
|
@@ -2880,6 +3133,160 @@ const FredSeriesObservationsInput = z.object({
|
|
|
2880
3133
|
.optional()
|
|
2881
3134
|
.describe("Observation date order: 'asc' (oldest first, FRED default) or 'desc' (newest first)."),
|
|
2882
3135
|
});
|
|
3136
|
+
// ─── openFDA recall/enforcement (api.fda.gov) — KEYLESS + OPTIONAL rate-limit key ──
|
|
3137
|
+
// ADR-0054. Drug/device/food recall enforcement records. Structured filters ONLY
|
|
3138
|
+
// (no raw Lucene passthrough — injection-safe); the tool assembles the openFDA
|
|
3139
|
+
// `search=` string with proper escaping. totalAvailable = meta.results.total (P1);
|
|
3140
|
+
// a no-match query (openFDA HTTP 404 NOT_FOUND) ⇒ an honest empty (P2). An OPTIONAL
|
|
3141
|
+
// OPENFDA_API_KEY only raises the rate limit (keyless works ~1000/day).
|
|
3142
|
+
const OpenfdaEnforcementInput = z.object({
|
|
3143
|
+
category: z
|
|
3144
|
+
.enum(["drug", "device", "food"])
|
|
3145
|
+
.optional()
|
|
3146
|
+
.describe("The recall category (default 'drug'): 'drug', 'device', or 'food'. Selects the openFDA /{category}/enforcement endpoint."),
|
|
3147
|
+
firm: z
|
|
3148
|
+
.string()
|
|
3149
|
+
.min(1)
|
|
3150
|
+
.optional()
|
|
3151
|
+
.describe("Recalling firm name filter (→ recalling_firm), e.g. 'pfizer'. Matched as an escaped Lucene phrase."),
|
|
3152
|
+
product: z
|
|
3153
|
+
.string()
|
|
3154
|
+
.min(1)
|
|
3155
|
+
.optional()
|
|
3156
|
+
.describe("Product description filter (→ product_description), e.g. 'insulin'. Matched as an escaped Lucene phrase."),
|
|
3157
|
+
reason: z
|
|
3158
|
+
.string()
|
|
3159
|
+
.min(1)
|
|
3160
|
+
.optional()
|
|
3161
|
+
.describe("Reason-for-recall filter (→ reason_for_recall), e.g. 'contamination'. Matched as an escaped Lucene phrase."),
|
|
3162
|
+
classification: z
|
|
3163
|
+
.enum(["Class I", "Class II", "Class III"])
|
|
3164
|
+
.optional()
|
|
3165
|
+
.describe("FDA recall classification filter: 'Class I' (most serious), 'Class II', or 'Class III'."),
|
|
3166
|
+
status: z
|
|
3167
|
+
.string()
|
|
3168
|
+
.min(1)
|
|
3169
|
+
.optional()
|
|
3170
|
+
.describe("Recall status filter (→ status), e.g. 'Ongoing', 'Terminated', 'Completed'."),
|
|
3171
|
+
state: z
|
|
3172
|
+
.string()
|
|
3173
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3174
|
+
.optional()
|
|
3175
|
+
.describe("2-letter US state/territory postal code filter (→ state), e.g. 'CA'. Validated ^[A-Za-z]{2}$."),
|
|
3176
|
+
limit: z
|
|
3177
|
+
.number()
|
|
3178
|
+
.int()
|
|
3179
|
+
.min(1)
|
|
3180
|
+
.max(100)
|
|
3181
|
+
.optional()
|
|
3182
|
+
.describe("Max recall records to return (default 25, max 100). Offset-paginated via skip."),
|
|
3183
|
+
skip: z
|
|
3184
|
+
.number()
|
|
3185
|
+
.int()
|
|
3186
|
+
.min(0)
|
|
3187
|
+
.optional()
|
|
3188
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3189
|
+
});
|
|
3190
|
+
// ─── openFDA 510(k) device clearances (api.fda.gov) — KEYLESS + OPTIONAL rate-limit key ──
|
|
3191
|
+
// ADR-0056. FDA premarket-notification (510(k)) device clearances — SAME source/envelope/
|
|
3192
|
+
// crux as openfda_enforcement (structured filters ONLY — the tool assembles + escapes the
|
|
3193
|
+
// search= string, injection-safe). totalAvailable = meta.results.total (P1); a no-match
|
|
3194
|
+
// query (openFDA HTTP 404 NOT_FOUND) ⇒ an honest empty (P2). An OPTIONAL OPENFDA_API_KEY
|
|
3195
|
+
// only raises the rate limit (keyless works ~1000/day).
|
|
3196
|
+
const OpenfdaDeviceClearancesInput = z.object({
|
|
3197
|
+
applicant: z
|
|
3198
|
+
.string()
|
|
3199
|
+
.min(1)
|
|
3200
|
+
.optional()
|
|
3201
|
+
.describe("Applicant / manufacturer name filter (→ applicant), e.g. 'medtronic'. Matched as an escaped Lucene phrase."),
|
|
3202
|
+
deviceName: z
|
|
3203
|
+
.string()
|
|
3204
|
+
.min(1)
|
|
3205
|
+
.optional()
|
|
3206
|
+
.describe("Device name filter (→ device_name), e.g. 'catheter'. Matched as an escaped Lucene phrase."),
|
|
3207
|
+
productCode: z
|
|
3208
|
+
.string()
|
|
3209
|
+
.min(1)
|
|
3210
|
+
.optional()
|
|
3211
|
+
.describe("FDA product code filter (→ product_code), e.g. 'DXN'. Matched as an escaped Lucene phrase."),
|
|
3212
|
+
clearanceType: z
|
|
3213
|
+
.string()
|
|
3214
|
+
.min(1)
|
|
3215
|
+
.optional()
|
|
3216
|
+
.describe("510(k) clearance type filter (→ clearance_type), e.g. 'Traditional', 'Special', 'Abbreviated'. Matched as an escaped Lucene phrase."),
|
|
3217
|
+
kNumber: z
|
|
3218
|
+
.string()
|
|
3219
|
+
.min(1)
|
|
3220
|
+
.optional()
|
|
3221
|
+
.describe("510(k) clearance number (K-number) filter (→ k_number), e.g. 'K123456'. Matched as an escaped Lucene phrase."),
|
|
3222
|
+
state: z
|
|
3223
|
+
.string()
|
|
3224
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3225
|
+
.optional()
|
|
3226
|
+
.describe("2-letter US state/territory postal code filter (→ state), e.g. 'CA'. Validated ^[A-Za-z]{2}$."),
|
|
3227
|
+
limit: z
|
|
3228
|
+
.number()
|
|
3229
|
+
.int()
|
|
3230
|
+
.min(1)
|
|
3231
|
+
.max(100)
|
|
3232
|
+
.optional()
|
|
3233
|
+
.describe("Max clearance records to return (default 25, max 100). Offset-paginated via skip."),
|
|
3234
|
+
skip: z
|
|
3235
|
+
.number()
|
|
3236
|
+
.int()
|
|
3237
|
+
.min(0)
|
|
3238
|
+
.optional()
|
|
3239
|
+
.describe("Row offset for pagination (default 0). Page with _meta.pagination.nextOffset."),
|
|
3240
|
+
});
|
|
3241
|
+
// ─── NHTSA vehicle safety (api.nhtsa.gov) — KEYLESS vehicle/parts supplier vetting ──
|
|
3242
|
+
// ADR-0057. Two tools (recalls + complaints) share make/model/modelYear inputs. NO
|
|
3243
|
+
// API key at all. ★The complaints VIN (PII) is excluded from the output. modelYear is
|
|
3244
|
+
// ^\d{4}$; make/model are letters/digits/space/hyphen only (SSRF/injection guard).
|
|
3245
|
+
const NhtsaVehicleInput = z.object({
|
|
3246
|
+
make: z
|
|
3247
|
+
.string()
|
|
3248
|
+
.regex(/^[A-Za-z0-9 -]+$/)
|
|
3249
|
+
.describe("Vehicle make (required), e.g. 'honda', 'ford'. Letters/digits/space/hyphen only (^[A-Za-z0-9 -]+$)."),
|
|
3250
|
+
model: z
|
|
3251
|
+
.string()
|
|
3252
|
+
.regex(/^[A-Za-z0-9 -]+$/)
|
|
3253
|
+
.describe("Vehicle model (required), e.g. 'accord', 'f-150'. Letters/digits/space/hyphen only (^[A-Za-z0-9 -]+$)."),
|
|
3254
|
+
modelYear: z
|
|
3255
|
+
.string()
|
|
3256
|
+
.regex(/^\d{4}$/)
|
|
3257
|
+
.describe("4-digit model year (required), e.g. '2020'. Validated ^\\d{4}$."),
|
|
3258
|
+
});
|
|
3259
|
+
// ─── CPSC consumer-product recalls (www.saferproducts.gov) — KEYLESS goods/import vetting ──
|
|
3260
|
+
// ADR-0058. One tool. NO API key at all. The response is a bare JSON ARRAY with no
|
|
3261
|
+
// total-count field / no pagination (totalAvailable = the returned count). All filters
|
|
3262
|
+
// optional; with NO filter the tool defaults RecallDateStart to ~90 days ago (disclosed)
|
|
3263
|
+
// rather than fetch the whole dataset. dates are ^\d{4}-\d{2}-\d{2}$; recallNumber is
|
|
3264
|
+
// letters/digits/hyphen only (SSRF/injection guard).
|
|
3265
|
+
const CpscRecallsInput = z.object({
|
|
3266
|
+
dateStart: z
|
|
3267
|
+
.string()
|
|
3268
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
3269
|
+
.optional()
|
|
3270
|
+
.describe("Recall date range START (optional), YYYY-MM-DD, e.g. '2025-01-01' (→ RecallDateStart). Validated ^\\d{4}-\\d{2}-\\d{2}$."),
|
|
3271
|
+
dateEnd: z
|
|
3272
|
+
.string()
|
|
3273
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
3274
|
+
.optional()
|
|
3275
|
+
.describe("Recall date range END (optional), YYYY-MM-DD, e.g. '2025-01-31' (→ RecallDateEnd). Validated ^\\d{4}-\\d{2}-\\d{2}$."),
|
|
3276
|
+
productName: z
|
|
3277
|
+
.string()
|
|
3278
|
+
.optional()
|
|
3279
|
+
.describe("Product name substring filter (optional), e.g. 'helmet' (→ ProductName)."),
|
|
3280
|
+
manufacturer: z
|
|
3281
|
+
.string()
|
|
3282
|
+
.optional()
|
|
3283
|
+
.describe("Manufacturer name substring filter (optional) (→ Manufacturer)."),
|
|
3284
|
+
recallNumber: z
|
|
3285
|
+
.string()
|
|
3286
|
+
.regex(/^[A-Za-z0-9-]+$/)
|
|
3287
|
+
.optional()
|
|
3288
|
+
.describe("A specific CPSC recall number (optional), e.g. '25088' (→ RecallNumber). Letters/digits/hyphen only (^[A-Za-z0-9-]+$)."),
|
|
3289
|
+
});
|
|
2883
3290
|
// ─── BEA Regional Economic Accounts (apps.bea.gov) — the THIRD key-required source ──
|
|
2884
3291
|
// ADR-0051. County/state/MSA GDP-by-industry (CAGDP2/SAGDP2N) + personal income
|
|
2885
3292
|
// (CAINC1/SAINC1) — the regional/sub-national place-of-performance lane. REQUIRES a
|
|
@@ -3066,6 +3473,92 @@ const LdaSearchFilingsInput = z.object({
|
|
|
3066
3473
|
.default(25)
|
|
3067
3474
|
.describe("Filings per page, 1..25 (the LDA API caps at 25), default 25."),
|
|
3068
3475
|
});
|
|
3476
|
+
// ─── US federal court opinions (www.courtlistener.com) — the litigation lane ──
|
|
3477
|
+
// ADR-0055. Federal court decisions (opinions) — the judicial signal no contract/
|
|
3478
|
+
// spending/lobbying source carries (e.g. uscfc bid-protest / contract-claim opinions).
|
|
3479
|
+
// ★PROVENANCE: CourtListener (Free Law Project, a non-profit), NOT a .gov API —
|
|
3480
|
+
// PACER (the .gov source) is paywalled. KEYLESS (anonymous 200); an optional free
|
|
3481
|
+
// COURTLISTENER_API_TOKEN only raises the rate limit, riding the Authorization: Token
|
|
3482
|
+
// … header ONLY (the lda/socrata app-token lineage). `count` is the REAL total —
|
|
3483
|
+
// never results.length; CURSOR pagination (nextCursor extracted from `next`). court/
|
|
3484
|
+
// dates charclass-guarded; all filter VALUES ride URLSearchParams; type=o is FIXED.
|
|
3485
|
+
const CourtlistenerSearchOpinionsInput = z.object({
|
|
3486
|
+
query: z
|
|
3487
|
+
.string()
|
|
3488
|
+
.min(1)
|
|
3489
|
+
.optional()
|
|
3490
|
+
.describe("Full-text query (maps to q), e.g. 'bid protest' or a party name. Matches across the opinion text/metadata."),
|
|
3491
|
+
court: z
|
|
3492
|
+
.string()
|
|
3493
|
+
.regex(/^[a-z0-9]+$/)
|
|
3494
|
+
.optional()
|
|
3495
|
+
.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'."),
|
|
3496
|
+
dateFiledAfter: z
|
|
3497
|
+
.string()
|
|
3498
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
3499
|
+
.optional()
|
|
3500
|
+
.describe("Only opinions filed on/after this ISO date (→ filed_after), e.g. '2020-01-01'. Validated ^\\d{4}-\\d{2}-\\d{2}$."),
|
|
3501
|
+
dateFiledBefore: z
|
|
3502
|
+
.string()
|
|
3503
|
+
.regex(/^\d{4}-\d{2}-\d{2}$/)
|
|
3504
|
+
.optional()
|
|
3505
|
+
.describe("Only opinions filed on/before this ISO date (→ filed_before), e.g. '2024-12-31'. Validated ^\\d{4}-\\d{2}-\\d{2}$."),
|
|
3506
|
+
natureOfSuit: z
|
|
3507
|
+
.string()
|
|
3508
|
+
.min(1)
|
|
3509
|
+
.optional()
|
|
3510
|
+
.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)."),
|
|
3511
|
+
cursor: z
|
|
3512
|
+
.string()
|
|
3513
|
+
.min(1)
|
|
3514
|
+
.optional()
|
|
3515
|
+
.describe("Opaque continuation token for the NEXT page — pass back the _meta.nextCursor from the previous response (CourtListener uses CURSOR pagination, not page/offset)."),
|
|
3516
|
+
order: z
|
|
3517
|
+
.string()
|
|
3518
|
+
.min(1)
|
|
3519
|
+
.default("dateFiled desc")
|
|
3520
|
+
.describe("Sort order (maps to order_by), default 'dateFiled desc' (most recent first). E.g. 'dateFiled asc', 'score desc'."),
|
|
3521
|
+
});
|
|
3522
|
+
// ─── US tax-exempt nonprofits (projects.propublica.org) — the nonprofit lane ──
|
|
3523
|
+
// ADR-0060. IRS Form 990 public records republished KEYLESS by ProPublica Nonprofit
|
|
3524
|
+
// Explorer (a non-profit newsroom) — NOT a .gov API (the IRS has no clean query
|
|
3525
|
+
// API). ★PROVENANCE disclosed in _meta.source + a note. KEYLESS (no key of any
|
|
3526
|
+
// kind). search: q/state[id]/ntee[id]/page (0-based); total_results is the REAL
|
|
3527
|
+
// total — never organizations.length. All VALUES ride URLSearchParams (incl. the
|
|
3528
|
+
// bracket keys); state/ntee charclass/range-guarded.
|
|
3529
|
+
const NonprofitSearchInput = z.object({
|
|
3530
|
+
query: z
|
|
3531
|
+
.string()
|
|
3532
|
+
.min(1)
|
|
3533
|
+
.optional()
|
|
3534
|
+
.describe("Full-text query (maps to q) — an organization name or keyword, e.g. 'american red cross'. Matches across the org name/metadata."),
|
|
3535
|
+
state: z
|
|
3536
|
+
.string()
|
|
3537
|
+
.regex(/^[A-Za-z]{2}$/)
|
|
3538
|
+
.optional()
|
|
3539
|
+
.describe("Filter by a 2-letter US state/territory code (maps to state[id]), e.g. 'VA'. Validated ^[A-Za-z]{2}$."),
|
|
3540
|
+
ntee: z
|
|
3541
|
+
.number()
|
|
3542
|
+
.int()
|
|
3543
|
+
.min(1)
|
|
3544
|
+
.max(10)
|
|
3545
|
+
.optional()
|
|
3546
|
+
.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)."),
|
|
3547
|
+
page: z
|
|
3548
|
+
.number()
|
|
3549
|
+
.int()
|
|
3550
|
+
.min(0)
|
|
3551
|
+
.default(0)
|
|
3552
|
+
.describe("0-BASED page number (default 0). Page with cur_page+1 from _meta.notes / when _meta.pagination.hasMore."),
|
|
3553
|
+
});
|
|
3554
|
+
// nonprofit_financials — one org's Form 990 profile + financials by EIN. KEYLESS.
|
|
3555
|
+
// ein rides the URL PATH ⇒ digits-only ^\d{1,9}$. An unknown EIN (404) ⇒ not_found.
|
|
3556
|
+
const NonprofitFinancialsInput = z.object({
|
|
3557
|
+
ein: z
|
|
3558
|
+
.string()
|
|
3559
|
+
.regex(/^\d{1,9}$/)
|
|
3560
|
+
.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."),
|
|
3561
|
+
});
|
|
3069
3562
|
// api_key_status takes no input — it is a pure status query over process.env.
|
|
3070
3563
|
const ApiKeyStatusInput = z.object({});
|
|
3071
3564
|
// Build a ToolDef whose `handler` is type-checked against the schema's inferred
|
|
@@ -4331,16 +4824,90 @@ export const TOOLS = [
|
|
|
4331
4824
|
// ━━━ US Census County Business Patterns — market sizing (1) ━━━ ADR-0047
|
|
4332
4825
|
// ★The server's FIRST KEY-REQUIRED source: the Census Data API removed its
|
|
4333
4826
|
// keyless tier, so WITHOUT a CENSUS_API_KEY this tool throws an honest
|
|
4334
|
-
// invalid_input config error (
|
|
4827
|
+
// invalid_input config error (most other tools are keyless — see api_key_status). NAICS×geography
|
|
4335
4828
|
// establishments / employment / annual payroll — the demand-side market-sizing
|
|
4336
4829
|
// lane. Census negative suppression sentinels (-999999999 …) map to null (never
|
|
4337
4830
|
// a negative number / never 0). The 2D-array body is parsed by header name.
|
|
4338
4831
|
defineTool({
|
|
4339
4832
|
name: "census_business_patterns",
|
|
4340
|
-
description: "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;
|
|
4833
|
+
description: "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.",
|
|
4341
4834
|
inputSchema: CensusBusinessPatternsInput,
|
|
4342
4835
|
handler: (input) => censusEconomic.businessPatterns(input),
|
|
4343
4836
|
}),
|
|
4837
|
+
// ━━━ EPA Envirofacts TRI facilities — environmental footprint (1) ━━━ ADR-0059
|
|
4838
|
+
// KEYLESS (data.epa.gov /efservice/tri_facility). ★Two requests: a count
|
|
4839
|
+
// sub-query yields the EXACT total (P1 — TOTALQUERYRESULTS, e.g. VA=1247), then
|
|
4840
|
+
// the data slice. All user values ride as PATH SEGMENTS → each is
|
|
4841
|
+
// charclass-validated + encodeURIComponent-encoded (the load-bearing SSRF guard).
|
|
4842
|
+
defineTool({
|
|
4843
|
+
name: "epa_tri_facilities",
|
|
4844
|
+
description: "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.",
|
|
4845
|
+
inputSchema: EpaTriFacilitiesInput,
|
|
4846
|
+
handler: (input) => epaEnvirofacts.triFacilities(input),
|
|
4847
|
+
}),
|
|
4848
|
+
// ━━━ CMS Medicare provider-service utilization — healthcare market (1) ━━━ ADR-0061
|
|
4849
|
+
// KEYLESS (data.cms.gov /data-api/v1/dataset/{uuid}). ★Two requests: a stats
|
|
4850
|
+
// count sub-query yields the EXACT per-filter total (P1 — found_rows, e.g.
|
|
4851
|
+
// VA=278254), then the data slice (a bare JSON array). All filter VALUES ride via
|
|
4852
|
+
// URLSearchParams (bracket key + value encoded — the SSRF guard). REQUIRE npi OR
|
|
4853
|
+
// state (the 9.78M-row table is never scanned unscoped). The dataset UUID is a
|
|
4854
|
+
// SPECIFIC ANNUAL VINTAGE (surfaced in a _meta note; update yearly).
|
|
4855
|
+
defineTool({
|
|
4856
|
+
name: "cms_medicare_provider_services",
|
|
4857
|
+
description: "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.",
|
|
4858
|
+
inputSchema: CmsMedicareProviderServicesInput,
|
|
4859
|
+
handler: (input) => cmsUtilization.providerServices(input),
|
|
4860
|
+
}),
|
|
4861
|
+
// ━━━ CMS Hospital Compare — Hospital General Information (1) ━━━ ADR-0062
|
|
4862
|
+
// KEYLESS (data.cms.gov /provider-data/api/1/datastore/query/{datasetId}). A
|
|
4863
|
+
// SINGLE request: the response's top-level `count` is the EXACT per-filter total
|
|
4864
|
+
// (P1 — VA=96), never the slice length. Filters ride as DKAN conditions[] triples
|
|
4865
|
+
// via URLSearchParams (bracket key + value encoded — the SSRF guard): state is an
|
|
4866
|
+
// EXACT match, facilityName/hospitalType are case-insensitive substring matches,
|
|
4867
|
+
// AND-combined server-side. REQUIRE state OR facilityName (never scanned unscoped).
|
|
4868
|
+
defineTool({
|
|
4869
|
+
name: "cms_hospital_compare",
|
|
4870
|
+
description: "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.",
|
|
4871
|
+
inputSchema: CmsHospitalCompareInput,
|
|
4872
|
+
handler: (input) => cmsHospital.hospitalCompare(input),
|
|
4873
|
+
}),
|
|
4874
|
+
// ━━━ CMS Facility Directory — 4 provider-data datasets (1) ━━━ ADR-0063
|
|
4875
|
+
// KEYLESS (data.cms.gov /provider-data/api/1/datastore/query/{datasetId}). A
|
|
4876
|
+
// generalization of cms_hospital_compare beyond hospitals: facilityType (a Zod
|
|
4877
|
+
// enum) indexes a CONSTANT map to a VETTED dataset id — the user value never enters
|
|
4878
|
+
// the path (the load-bearing SSRF guard). A SINGLE request: the response's top-level
|
|
4879
|
+
// `count` is the EXACT per-filter total (P1). name/address/ownership columns vary
|
|
4880
|
+
// per dataset → coalesced (null if none — never empty-string, never fabricated).
|
|
4881
|
+
defineTool({
|
|
4882
|
+
name: "cms_facility_directory",
|
|
4883
|
+
description: "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.",
|
|
4884
|
+
inputSchema: CmsFacilityDirectoryInput,
|
|
4885
|
+
handler: (input) => cmsFacility.facilityDirectory(input),
|
|
4886
|
+
}),
|
|
4887
|
+
// ━━━ CMS DMEPOS by Supplier — supply-side utilization (1) ━━━ ADR-0064
|
|
4888
|
+
// KEYLESS (data.cms.gov /data-api/v1/dataset/{uuid}). ★Two requests: a stats count
|
|
4889
|
+
// sub-query yields the EXACT per-filter total (P1 — found_rows), then the data slice
|
|
4890
|
+
// (a bare JSON array). All filter VALUES ride via URLSearchParams (bracket key +
|
|
4891
|
+
// value encoded — the SSRF guard). REQUIRE npi OR state (the supplier table is never
|
|
4892
|
+
// scanned unscoped). The dataset UUID is a SPECIFIC ANNUAL VINTAGE (update yearly).
|
|
4893
|
+
defineTool({
|
|
4894
|
+
name: "cms_dmepos_suppliers",
|
|
4895
|
+
description: "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.",
|
|
4896
|
+
inputSchema: CmsDmeposSuppliersInput,
|
|
4897
|
+
handler: (input) => cmsSupplier.dmeposSuppliers(input),
|
|
4898
|
+
}),
|
|
4899
|
+
// ━━━ CMS Revoked Medicare Providers & Suppliers — vetting/exclusion list (1) ━━━ ADR-0064
|
|
4900
|
+
// KEYLESS (data.cms.gov /data-api/v1/dataset/{uuid}). A legally-published
|
|
4901
|
+
// revocation/exclusion register (~7,059 rows) — the SAME vetting class as the OFAC /
|
|
4902
|
+
// SAM exclusion lists already shipped (surfacing the names IS the point). ALL filters
|
|
4903
|
+
// optional (small table — pagination is fine unfiltered). SAME two-request stats-count
|
|
4904
|
+
// P1 pattern; filter VALUES ride via URLSearchParams (bracket key + value encoded).
|
|
4905
|
+
defineTool({
|
|
4906
|
+
name: "cms_revoked_providers",
|
|
4907
|
+
description: "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.",
|
|
4908
|
+
inputSchema: CmsRevokedProvidersInput,
|
|
4909
|
+
handler: (input) => cmsSupplier.revokedProviders(input),
|
|
4910
|
+
}),
|
|
4344
4911
|
// ━━━ FRED (Federal Reserve Economic Data) — macro context (2) ━━━ ADR-0048
|
|
4345
4912
|
// ★The server's SECOND KEY-REQUIRED source: FRED has NO keyless tier, so WITHOUT
|
|
4346
4913
|
// a FRED_API_KEY both tools throw an honest invalid_input config error (the other
|
|
@@ -4348,7 +4915,7 @@ export const TOOLS = [
|
|
|
4348
4915
|
// bid escalation / market timing. A missing observation ('.') maps to null (never 0).
|
|
4349
4916
|
defineTool({
|
|
4350
4917
|
name: "fred_search_series",
|
|
4351
|
-
description: "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;
|
|
4918
|
+
description: "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.",
|
|
4352
4919
|
inputSchema: FredSearchSeriesInput,
|
|
4353
4920
|
handler: (input) => fred.searchSeries(input),
|
|
4354
4921
|
}),
|
|
@@ -4358,17 +4925,75 @@ export const TOOLS = [
|
|
|
4358
4925
|
inputSchema: FredSeriesObservationsInput,
|
|
4359
4926
|
handler: (input) => fred.seriesObservations(input),
|
|
4360
4927
|
}),
|
|
4928
|
+
// ━━━ openFDA recall/enforcement (api.fda.gov) — product-safety recalls (1) ━━━ ADR-0054
|
|
4929
|
+
// KEYLESS with an OPTIONAL OPENFDA_API_KEY (raises the rate limit; keyless works
|
|
4930
|
+
// ~1000/day — it NEVER throws for a missing key, unlike the key-REQUIRED sources).
|
|
4931
|
+
// Structured filters ONLY (no raw Lucene passthrough — the tool assembles + escapes
|
|
4932
|
+
// the search= string, injection-safe). ★P1: totalAvailable = meta.results.total
|
|
4933
|
+
// (EXACT). ★P2 crux: a no-match query returns openFDA HTTP 404 NOT_FOUND ⇒ an honest
|
|
4934
|
+
// empty, never a throw. The optional key rides &api_key= ONLY.
|
|
4935
|
+
defineTool({
|
|
4936
|
+
name: "openfda_enforcement",
|
|
4937
|
+
description: "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.",
|
|
4938
|
+
inputSchema: OpenfdaEnforcementInput,
|
|
4939
|
+
handler: (input) => openfda.enforcement(input),
|
|
4940
|
+
}),
|
|
4941
|
+
// ━━━ openFDA 510(k) device clearances (api.fda.gov) — medical-device regulatory (1) ━━━ ADR-0056
|
|
4942
|
+
// KEYLESS with an OPTIONAL OPENFDA_API_KEY (raises the rate limit; keyless works
|
|
4943
|
+
// ~1000/day — never throws for a missing key). SAME source/envelope/crux as
|
|
4944
|
+
// openfda_enforcement (reuses openfda.ts's fetchOpenfda/readOpenfdaError/luceneQuote).
|
|
4945
|
+
// Structured filters ONLY (no raw Lucene passthrough — the tool assembles + escapes the
|
|
4946
|
+
// search= string, injection-safe). ★P1: totalAvailable = meta.results.total (EXACT,
|
|
4947
|
+
// ~175507). ★P2 crux: a no-match query returns openFDA HTTP 404 NOT_FOUND ⇒ an honest
|
|
4948
|
+
// empty, never a throw. The optional key rides &api_key= ONLY.
|
|
4949
|
+
defineTool({
|
|
4950
|
+
name: "openfda_device_clearances",
|
|
4951
|
+
description: "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.",
|
|
4952
|
+
inputSchema: OpenfdaDeviceClearancesInput,
|
|
4953
|
+
handler: (input) => openfdaDevice.deviceClearances(input),
|
|
4954
|
+
}),
|
|
4955
|
+
// ━━━ NHTSA vehicle safety (api.nhtsa.gov) — vehicle/parts supplier vetting (2) ━━━ ADR-0057
|
|
4956
|
+
// ★KEYLESS — no API key at all (no parameter, no header). The cross-agency
|
|
4957
|
+
// product-safety family alongside openFDA (medical). Both tools share
|
|
4958
|
+
// make/model/modelYear inputs and return the COMPLETE matching set (no pagination
|
|
4959
|
+
// ⇒ totalAvailable = the upstream Count/count, complete:true). ★The complaints VIN
|
|
4960
|
+
// (an individual-vehicle PII identifier) is EXCLUDED from the output.
|
|
4961
|
+
defineTool({
|
|
4962
|
+
name: "nhtsa_recalls",
|
|
4963
|
+
description: "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}$.",
|
|
4964
|
+
inputSchema: NhtsaVehicleInput,
|
|
4965
|
+
handler: (input) => nhtsa.recalls(input),
|
|
4966
|
+
}),
|
|
4967
|
+
defineTool({
|
|
4968
|
+
name: "nhtsa_complaints",
|
|
4969
|
+
description: "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}$.",
|
|
4970
|
+
inputSchema: NhtsaVehicleInput,
|
|
4971
|
+
handler: (input) => nhtsa.complaints(input),
|
|
4972
|
+
}),
|
|
4973
|
+
// ━━━ CPSC consumer-product recalls (www.saferproducts.gov) — goods/import vetting (1) ━━━ ADR-0058
|
|
4974
|
+
// ★KEYLESS — no API key at all (no parameter, no header). The third leg of the
|
|
4975
|
+
// cross-agency product-safety family alongside NHTSA (vehicles) and openFDA
|
|
4976
|
+
// (medical). The response is a bare JSON ARRAY with NO total-count field and NO
|
|
4977
|
+
// pagination ⇒ totalAvailable = the returned count, complete:true. All filters are
|
|
4978
|
+
// optional; with NO filter the tool bounds results to a ~90-day default window
|
|
4979
|
+
// (disclosed) rather than silently fetch the entire dataset.
|
|
4980
|
+
defineTool({
|
|
4981
|
+
name: "cpsc_recalls",
|
|
4982
|
+
description: "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.",
|
|
4983
|
+
inputSchema: CpscRecallsInput,
|
|
4984
|
+
handler: (input) => cpsc.recalls(input),
|
|
4985
|
+
}),
|
|
4361
4986
|
// ━━━ BEA Regional Economic Accounts (apps.bea.gov) — regional GDP/income (1) ━━━ ADR-0051
|
|
4362
4987
|
// ★The server's THIRD KEY-REQUIRED source: the BEA Data API has NO keyless tier, so
|
|
4363
4988
|
// WITHOUT a BEA_API_KEY this tool throws an honest invalid_input config error (the
|
|
4364
|
-
// other
|
|
4989
|
+
// most other tools stay keyless — see api_key_status). County/state/MSA GDP-by-industry + personal income —
|
|
4365
4990
|
// the regional place-of-performance lane. ★The P2 crux: a missing/invalid key returns
|
|
4366
4991
|
// HTTP 200 carrying BEAAPI.Results.Error (NOT an HTTP error status), which is detected
|
|
4367
4992
|
// BEFORE the Data-array drift check and surfaced as invalid_input (never a fake empty).
|
|
4368
4993
|
// DataValue is a comma-formatted string; suppression codes ((NA)/(D)/(NM)/(L)/*) → null.
|
|
4369
4994
|
defineTool({
|
|
4370
4995
|
name: "bea_regional_data",
|
|
4371
|
-
description: "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/;
|
|
4996
|
+
description: "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.",
|
|
4372
4997
|
inputSchema: BeaRegionalDataInput,
|
|
4373
4998
|
handler: (input) => bea.regionalData(input),
|
|
4374
4999
|
}),
|
|
@@ -4400,7 +5025,7 @@ export const TOOLS = [
|
|
|
4400
5025
|
}),
|
|
4401
5026
|
defineTool({
|
|
4402
5027
|
name: "dol_get_dataset",
|
|
4403
|
-
description: "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/
|
|
5028
|
+
description: "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.",
|
|
4404
5029
|
inputSchema: DolGetDatasetInput,
|
|
4405
5030
|
handler: (input) => dol.getDataset(input),
|
|
4406
5031
|
}),
|
|
@@ -4417,6 +5042,40 @@ export const TOOLS = [
|
|
|
4417
5042
|
inputSchema: LdaSearchFilingsInput,
|
|
4418
5043
|
handler: (input) => lda.searchFilings(input),
|
|
4419
5044
|
}),
|
|
5045
|
+
// ━━━ US federal court opinions (www.courtlistener.com) — the litigation lane (1) ━━━ ADR-0055
|
|
5046
|
+
// Federal court decisions — the judicial signal no contract/spending/lobbying source
|
|
5047
|
+
// carries (uscfc bid-protest/contract-claim opinions, cafc contract/patent appeals).
|
|
5048
|
+
// ★PROVENANCE: CourtListener (Free Law Project, a non-profit), NOT a .gov API — PACER
|
|
5049
|
+
// (the .gov source) is paywalled. KEYLESS (anonymous 200); the OPTIONAL free
|
|
5050
|
+
// COURTLISTENER_API_TOKEN only raises the rate limit, riding the Authorization: Token …
|
|
5051
|
+
// header ONLY. ★count is the API's REAL total — never results.length; CURSOR pagination
|
|
5052
|
+
// (nextCursor extracted from `next`, host re-asserted). type=o FIXED.
|
|
5053
|
+
defineTool({
|
|
5054
|
+
name: "courtlistener_search_opinions",
|
|
5055
|
+
description: "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).",
|
|
5056
|
+
inputSchema: CourtlistenerSearchOpinionsInput,
|
|
5057
|
+
handler: (input) => courtlistener.searchOpinions(input),
|
|
5058
|
+
}),
|
|
5059
|
+
// ━━━ US tax-exempt nonprofits (projects.propublica.org) — the nonprofit lane (2) ━━━ ADR-0060
|
|
5060
|
+
// Who a 501(c) org IS (EIN, NTEE, subsection, ruling date, status) + its Form 990
|
|
5061
|
+
// FINANCIALS (revenue/expenses/assets/liabilities by year) — the nonprofit/grantee/
|
|
5062
|
+
// subcontractor vetting signal no contract/spending/grant/lobbying source carries.
|
|
5063
|
+
// ★PROVENANCE: IRS Form 990 public records republished KEYLESS by ProPublica Nonprofit
|
|
5064
|
+
// Explorer (a non-profit newsroom), NOT a .gov API (the IRS has no clean query API).
|
|
5065
|
+
// KEYLESS (no key). search total_results is the REAL total — never organizations.length;
|
|
5066
|
+
// financials totalAvailable = filings.length (the complete set). Money via num (null-never-0).
|
|
5067
|
+
defineTool({
|
|
5068
|
+
name: "nonprofit_search",
|
|
5069
|
+
description: "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.",
|
|
5070
|
+
inputSchema: NonprofitSearchInput,
|
|
5071
|
+
handler: (input) => nonprofit.search(input),
|
|
5072
|
+
}),
|
|
5073
|
+
defineTool({
|
|
5074
|
+
name: "nonprofit_financials",
|
|
5075
|
+
description: "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.",
|
|
5076
|
+
inputSchema: NonprofitFinancialsInput,
|
|
5077
|
+
handler: (input) => nonprofit.financials(input),
|
|
5078
|
+
}),
|
|
4420
5079
|
// ━━━ Self-service key discovery (1) ━━━
|
|
4421
5080
|
// KEYLESS. A local status query — reads process.env (+ any .env auto-loaded at
|
|
4422
5081
|
// startup) and reports, per key, whether it is set (a BOOLEAN — the key VALUE is
|