@cliwant/mcp-sam-gov 1.4.0 → 1.6.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/LICENSE +21 -21
- package/README.ja.md +240 -231
- package/README.ko.md +240 -231
- package/README.md +725 -706
- 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/datagov-catalog.d.ts.map +1 -1
- package/dist/datagov-catalog.js +16 -2
- package/dist/datagov-catalog.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.map +1 -1
- package/dist/epa-envirofacts.js +14 -1
- package/dist/epa-envirofacts.js.map +1 -1
- package/dist/errors.d.ts +10 -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/feedback.d.ts +64 -0
- package/dist/feedback.d.ts.map +1 -0
- package/dist/feedback.js +131 -0
- package/dist/feedback.js.map +1 -0
- 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/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/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/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-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.map +1 -1
- package/dist/openfda.js +31 -8
- package/dist/openfda.js.map +1 -1
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +374 -11
- 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 +111 -111
- package/src/attachments.ts +652 -652
- package/src/bea.ts +372 -372
- package/src/bls.ts +1943 -1943
- package/src/cache.ts +73 -73
- package/src/cbp-border.ts +177 -0
- package/src/census-economic.ts +431 -431
- package/src/census.ts +735 -735
- package/src/ckan.ts +495 -495
- package/src/clinicaltrials.ts +923 -923
- package/src/cms-facility.ts +379 -379
- package/src/cms-hospital.ts +344 -344
- package/src/cms-supplier.ts +527 -527
- package/src/cms-utilization.ts +389 -389
- package/src/cms.ts +634 -634
- package/src/coerce.ts +47 -47
- package/src/courtlistener.ts +465 -465
- package/src/cpsc.ts +333 -333
- package/src/datagov-catalog.ts +312 -296
- package/src/datagov.ts +907 -907
- package/src/datagovKey.ts +68 -68
- package/src/datasource.ts +721 -721
- package/src/disclosure.ts +61 -61
- package/src/dol.ts +515 -515
- package/src/ecfr.ts +248 -231
- package/src/echo.ts +496 -496
- package/src/edgar.ts +3046 -3014
- package/src/epa-envirofacts.ts +358 -342
- package/src/errors.ts +324 -303
- package/src/fac.ts +529 -529
- package/src/far.ts +1009 -1007
- package/src/fdic.ts +2052 -2052
- package/src/federal-register.ts +725 -706
- package/src/feedback.ts +160 -0
- package/src/fema.ts +680 -541
- package/src/fpds.ts +620 -620
- package/src/fred.ts +464 -464
- package/src/gao.ts +744 -744
- package/src/gov-domains.ts +237 -0
- package/src/govinfo.ts +497 -497
- package/src/grants.ts +290 -290
- package/src/gsa-csv.ts +992 -992
- package/src/gsa-perdiem.ts +361 -361
- package/src/integrity.ts +928 -928
- package/src/keys.ts +268 -268
- package/src/lda.ts +385 -385
- package/src/meta.ts +292 -292
- package/src/nhtsa.ts +352 -352
- package/src/nih.ts +375 -375
- package/src/nist-controls.ts +219 -0
- package/src/nonprofit.ts +460 -460
- package/src/nppes.ts +834 -834
- package/src/nsf.ts +706 -706
- package/src/nvd.ts +1124 -1124
- package/src/nws-weather.ts +167 -0
- package/src/ofac.ts +1166 -1166
- package/src/openfda-device.ts +356 -356
- package/src/openfda-drugsfda.ts +313 -0
- package/src/openfda.ts +518 -495
- package/src/pricing.ts +1075 -1075
- package/src/sam-gov/client.ts +774 -774
- package/src/sam-gov/index.ts +32 -32
- package/src/sam-gov/types.ts +152 -152
- package/src/sba.ts +357 -357
- package/src/server.ts +6688 -6297
- package/src/snapshot.ts +223 -223
- package/src/socrata.ts +532 -532
- package/src/treasury.ts +582 -575
- package/src/usaspending.ts +2852 -2680
- package/src/usitc.ts +420 -420
|
@@ -0,0 +1,174 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nist-controls.ts — NIST SP 800-53 Rev 5 security & privacy CONTROLS catalog
|
|
3
|
+
* (OSCAL, keyless). The cyber-compliance controls backbone for FedRAMP / CMMC / RMF
|
|
4
|
+
* work: look up a control (AC-2, SC-7, …) or a family (Access Control, System &
|
|
5
|
+
* Communications Protection, …) and get its title, requirement STATEMENT, discussion
|
|
6
|
+
* guidance, and control enhancements. No other tool here exposes the controls catalog
|
|
7
|
+
* (we have NVD CVEs + CISA KEV, but not the requirement side).
|
|
8
|
+
*
|
|
9
|
+
* SOURCE: NIST's OFFICIAL OSCAL content, published at github.com/usnistgov/oscal-content
|
|
10
|
+
* (the canonical machine-readable release; the .gov PDF is the human copy). NOT a
|
|
11
|
+
* .gov API host, so provenance is disclosed on every response (the ProPublica /
|
|
12
|
+
* CourtListener / get.gov republisher idiom — here NIST is the first-party author).
|
|
13
|
+
*
|
|
14
|
+
* PATTERN: the CISA-KEV static-file idiom (nvd.ts) — a fixed-host const URL fetched
|
|
15
|
+
* via getJson (redirect:"error" + 30s timeout), memoized 6h, with a plausibility
|
|
16
|
+
* FLOOR (a truncated catalog must NEVER read as "control not found"), then
|
|
17
|
+
* client-side filter/lookup. An outage/4xx/timeout THROWS (never a fake empty).
|
|
18
|
+
*
|
|
19
|
+
* SSRF: fixed host `raw.githubusercontent.com` + a fixed, pinned path (no free
|
|
20
|
+
* host/path). Filtering is CLIENT-SIDE over the parsed catalog.
|
|
21
|
+
*/
|
|
22
|
+
import { getJson, driftError } from "./datasource.js";
|
|
23
|
+
import { memoize } from "./cache.js";
|
|
24
|
+
import { withMeta } from "./meta.js";
|
|
25
|
+
export const OSCAL_HOST = "raw.githubusercontent.com";
|
|
26
|
+
const OSCAL_URL = "https://raw.githubusercontent.com/usnistgov/oscal-content/main/nist.gov/SP800-53/rev5/json/NIST_SP-800-53_rev5_catalog.json";
|
|
27
|
+
const OSCAL_LABEL = "nist-oscal:sp800-53r5";
|
|
28
|
+
const OSCAL_TIMEOUT_MS = 30_000;
|
|
29
|
+
const OSCAL_CACHE_TTL_MS = 6 * 60 * 60 * 1000;
|
|
30
|
+
// Plausibility floor: 800-53 Rev5 has 20 control families and ~1000 controls. A
|
|
31
|
+
// truncated catalog with far fewer must THROW, never read as "control not found".
|
|
32
|
+
const FAMILY_FLOOR = 15;
|
|
33
|
+
const PROVENANCE_NOTE = "Source: NIST SP 800-53 Rev 5 OSCAL catalog, published at github.com/usnistgov/oscal-content (NIST's canonical machine-readable release; authoritative first-party data served from GitHub, not a .gov API host).";
|
|
34
|
+
const CLIENT_FILTER_NOTE = "The catalog has no query API — the full published OSCAL JSON is fetched (cached 6h) and filtered CLIENT-SIDE (controlId exact, family exact, keyword = case-insensitive substring over title+statement). totalAvailable is the EXACT match count.";
|
|
35
|
+
const REFERENCE_NOTE = "This is the REQUIREMENT catalog (control text), NOT an assessment or an authorization. A control's applicability depends on the system's FIPS-199 impact baseline (Low/Moderate/High) and overlay — which this catalog does not encode.";
|
|
36
|
+
/** OSCAL control id ("ac-2", "ac-2.1") → display id ("AC-2", "AC-2(1)"). */
|
|
37
|
+
function displayId(rawId) {
|
|
38
|
+
const m = /^([a-z]+)-(\d+)(?:\.(\d+))?$/i.exec(rawId.trim());
|
|
39
|
+
if (!m)
|
|
40
|
+
return rawId.toUpperCase();
|
|
41
|
+
const fam = (m[1] ?? "").toUpperCase();
|
|
42
|
+
// Strip leading zeros so a zero-padded input ('AC-02') normalizes to the catalog's
|
|
43
|
+
// canonical unpadded form ('AC-2') — else an exact controlId lookup would miss.
|
|
44
|
+
const num = String(Number(m[2] ?? "0"));
|
|
45
|
+
const enh = m[3] !== undefined ? String(Number(m[3])) : undefined;
|
|
46
|
+
return enh !== undefined ? `${fam}-${num}(${enh})` : `${fam}-${num}`;
|
|
47
|
+
}
|
|
48
|
+
/** Recursively collect a statement part's prose as labelled, indented lines. */
|
|
49
|
+
function collectProse(part, depth, out) {
|
|
50
|
+
const label = part.props?.find((p) => p.name === "label")?.value;
|
|
51
|
+
const indent = " ".repeat(depth);
|
|
52
|
+
const prefix = label ? `${label} ` : "";
|
|
53
|
+
if (part.prose && part.prose.trim().length > 0) {
|
|
54
|
+
out.push(`${indent}${prefix}${part.prose.trim()}`);
|
|
55
|
+
}
|
|
56
|
+
else if (label) {
|
|
57
|
+
out.push(`${indent}${prefix}`.trimEnd());
|
|
58
|
+
}
|
|
59
|
+
for (const sub of part.parts ?? [])
|
|
60
|
+
collectProse(sub, depth + 1, out);
|
|
61
|
+
}
|
|
62
|
+
function partProse(control, name) {
|
|
63
|
+
const part = (control.parts ?? []).find((p) => p.name === name);
|
|
64
|
+
if (!part)
|
|
65
|
+
return null;
|
|
66
|
+
const out = [];
|
|
67
|
+
collectProse(part, 0, out);
|
|
68
|
+
const text = out.join("\n").trim();
|
|
69
|
+
return text.length > 0 ? text : null;
|
|
70
|
+
}
|
|
71
|
+
function mapControl(control, family) {
|
|
72
|
+
return {
|
|
73
|
+
id: displayId(control.id ?? ""),
|
|
74
|
+
family,
|
|
75
|
+
title: (control.title ?? "").trim(),
|
|
76
|
+
statement: partProse(control, "statement") ?? "",
|
|
77
|
+
guidance: partProse(control, "guidance"),
|
|
78
|
+
enhancements: (control.controls ?? []).map((e) => ({
|
|
79
|
+
id: displayId(e.id ?? ""),
|
|
80
|
+
title: (e.title ?? "").trim(),
|
|
81
|
+
})),
|
|
82
|
+
};
|
|
83
|
+
}
|
|
84
|
+
/**
|
|
85
|
+
* Fetch + parse the OSCAL catalog into a flat list of TOP-LEVEL controls, memoized
|
|
86
|
+
* 6h. Header/shape guarded: `catalog.groups` MUST be an array with ≥ FAMILY_FLOOR
|
|
87
|
+
* families (else driftError — a truncated catalog is NEVER a fake empty). Enhancements
|
|
88
|
+
* are carried on each control (not indexed as top-level entries).
|
|
89
|
+
*/
|
|
90
|
+
async function loadControls() {
|
|
91
|
+
return memoize("nist:sp800-53r5", async () => {
|
|
92
|
+
const built = new URL(OSCAL_URL);
|
|
93
|
+
if (built.hostname !== OSCAL_HOST || built.protocol !== "https:") {
|
|
94
|
+
throw driftError(OSCAL_LABEL, `Constructed OSCAL URL host ${JSON.stringify(built.hostname)} is not ${OSCAL_HOST} over https — refusing to fetch (SSRF safety).`);
|
|
95
|
+
}
|
|
96
|
+
const body = (await getJson(OSCAL_URL, {
|
|
97
|
+
label: OSCAL_LABEL,
|
|
98
|
+
redirect: "error",
|
|
99
|
+
timeoutMs: OSCAL_TIMEOUT_MS,
|
|
100
|
+
}));
|
|
101
|
+
const groups = body.catalog?.groups;
|
|
102
|
+
if (!Array.isArray(groups) || groups.length < FAMILY_FLOOR) {
|
|
103
|
+
throw driftError(OSCAL_LABEL, `OSCAL catalog.groups missing or implausibly small (${Array.isArray(groups) ? groups.length : "not-an-array"} < ${FAMILY_FLOOR} families) — treating as schema drift / truncation, never a fake-empty catalog.`);
|
|
104
|
+
}
|
|
105
|
+
const controls = [];
|
|
106
|
+
for (const g of groups) {
|
|
107
|
+
const family = `${(g.id ?? "").toUpperCase()} — ${(g.title ?? "").trim()}`;
|
|
108
|
+
for (const c of g.controls ?? [])
|
|
109
|
+
controls.push(mapControl(c, family));
|
|
110
|
+
}
|
|
111
|
+
return controls;
|
|
112
|
+
}, OSCAL_CACHE_TTL_MS);
|
|
113
|
+
}
|
|
114
|
+
// ─── Tool: nist_800_53_controls ───────────────────────────────────
|
|
115
|
+
/**
|
|
116
|
+
* Look up NIST SP 800-53 Rev 5 controls by controlId (exact, e.g. "AC-2"), family
|
|
117
|
+
* (exact family letter or name, e.g. "AC" / "Access Control"), and/or keyword
|
|
118
|
+
* (case-insensitive substring over title + statement). Client-side filters over the
|
|
119
|
+
* cached OSCAL catalog; honest `_meta` (exact match total; provenance disclosed).
|
|
120
|
+
*/
|
|
121
|
+
export async function searchControls(args) {
|
|
122
|
+
const limit = args.limit ?? 25;
|
|
123
|
+
const offset = args.offset ?? 0;
|
|
124
|
+
const all = await loadControls();
|
|
125
|
+
const filtersApplied = [];
|
|
126
|
+
const idQ = args.controlId !== undefined ? displayId(args.controlId) : undefined;
|
|
127
|
+
const famQ = args.family?.trim().toLowerCase();
|
|
128
|
+
const kwQ = args.keyword?.trim().toLowerCase();
|
|
129
|
+
if (args.controlId !== undefined)
|
|
130
|
+
filtersApplied.push("controlId");
|
|
131
|
+
if (args.family !== undefined)
|
|
132
|
+
filtersApplied.push("family");
|
|
133
|
+
if (args.keyword !== undefined)
|
|
134
|
+
filtersApplied.push("keyword");
|
|
135
|
+
const matched = all.filter((c) => {
|
|
136
|
+
if (idQ && c.id.toUpperCase() !== idQ.toUpperCase())
|
|
137
|
+
return false;
|
|
138
|
+
if (famQ) {
|
|
139
|
+
// family field is "AC — Access Control"; match either the letter code or a
|
|
140
|
+
// substring of the title (both case-insensitive).
|
|
141
|
+
const fam = c.family.toLowerCase();
|
|
142
|
+
const code = (fam.split("—")[0] ?? "").trim();
|
|
143
|
+
if (code !== famQ && !fam.includes(famQ))
|
|
144
|
+
return false;
|
|
145
|
+
}
|
|
146
|
+
if (kwQ) {
|
|
147
|
+
// Search the title + requirement statement AND each enhancement's title, so a
|
|
148
|
+
// term that lives only in an enhancement (e.g. "multi-factor" → IA-2(1)) still
|
|
149
|
+
// surfaces the parent control. filtersApplied still lists 'keyword'.
|
|
150
|
+
const hay = `${c.title}\n${c.statement}\n${c.enhancements.map((e) => e.title).join("\n")}`.toLowerCase();
|
|
151
|
+
if (!hay.includes(kwQ))
|
|
152
|
+
return false;
|
|
153
|
+
}
|
|
154
|
+
return true;
|
|
155
|
+
});
|
|
156
|
+
const totalAvailable = matched.length;
|
|
157
|
+
const page = matched.slice(offset, offset + limit);
|
|
158
|
+
const returned = page.length;
|
|
159
|
+
const hasMore = offset + returned < totalAvailable;
|
|
160
|
+
const nextOffset = hasMore ? offset + returned : null;
|
|
161
|
+
return withMeta({ controls: page }, {
|
|
162
|
+
source: "NIST SP 800-53 Rev 5 (OSCAL catalog, keyless)",
|
|
163
|
+
keylessMode: true,
|
|
164
|
+
returned,
|
|
165
|
+
totalAvailable,
|
|
166
|
+
truncated: hasMore,
|
|
167
|
+
filtersApplied,
|
|
168
|
+
filtersDropped: [],
|
|
169
|
+
fieldsUnavailable: [],
|
|
170
|
+
pagination: { offset, limit, hasMore, nextOffset },
|
|
171
|
+
notes: [PROVENANCE_NOTE, CLIENT_FILTER_NOTE, REFERENCE_NOTE],
|
|
172
|
+
});
|
|
173
|
+
}
|
|
174
|
+
//# sourceMappingURL=nist-controls.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nist-controls.js","sourceRoot":"","sources":["../src/nist-controls.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nws-weather.ts — National Weather Service active alerts (api.weather.gov, KEYLESS)
|
|
3
|
+
* — the DISASTER / CLIMATE-READINESS lane. Current watches, warnings, and advisories
|
|
4
|
+
* (event, severity, urgency, area, effective/expires window, instructions). Pairs
|
|
5
|
+
* directly with the FEMA tools (disaster declarations → public assistance → hazard
|
|
6
|
+
* mitigation → LIVE active weather) to complete a disaster-response-readiness view:
|
|
7
|
+
* where severe-weather events are active NOW, ahead of the declarations/contracts
|
|
8
|
+
* that follow.
|
|
9
|
+
*
|
|
10
|
+
* SOURCE: NWS api.weather.gov (a .gov host), keyless. Requires a descriptive
|
|
11
|
+
* User-Agent (NWS policy) — sent from a fixed constant; no token. Returns a GeoJSON
|
|
12
|
+
* FeatureCollection. This is REAL-TIME data (currently-active alerts) — freshness is
|
|
13
|
+
* disclosed and never implied to be a historical series.
|
|
14
|
+
*
|
|
15
|
+
* HONESTY: fixed host + redirect:"error" (SSRF); `state` is a 2-letter charclass (the
|
|
16
|
+
* server-side ?area= filter); event/severity are applied CLIENT-SIDE over the returned
|
|
17
|
+
* set; a non-FeatureCollection / non-array body ⇒ driftError; an outage/4xx/timeout
|
|
18
|
+
* THROWS. A genuine no-active-alerts result ⇒ an HONEST EMPTY (returned:0), never an
|
|
19
|
+
* error. totalAvailable = the EXACT count of matched active alerts. Every scalar via
|
|
20
|
+
* `str` (null-never-empty-string); dates preserved as ISO strings.
|
|
21
|
+
*/
|
|
22
|
+
import { type MetaBundle } from "./meta.js";
|
|
23
|
+
export declare const NWS_HOST = "api.weather.gov";
|
|
24
|
+
export type NwsAlert = {
|
|
25
|
+
id: string | null;
|
|
26
|
+
event: string | null;
|
|
27
|
+
headline: string | null;
|
|
28
|
+
severity: string | null;
|
|
29
|
+
urgency: string | null;
|
|
30
|
+
certainty: string | null;
|
|
31
|
+
category: string | null;
|
|
32
|
+
status: string | null;
|
|
33
|
+
messageType: string | null;
|
|
34
|
+
areaDesc: string | null;
|
|
35
|
+
effective: string | null;
|
|
36
|
+
onset: string | null;
|
|
37
|
+
expires: string | null;
|
|
38
|
+
ends: string | null;
|
|
39
|
+
senderName: string | null;
|
|
40
|
+
description: string | null;
|
|
41
|
+
instruction: string | null;
|
|
42
|
+
response: string | null;
|
|
43
|
+
};
|
|
44
|
+
/**
|
|
45
|
+
* List CURRENTLY-ACTIVE NWS weather alerts, optionally scoped by `state` (server-side
|
|
46
|
+
* ?area=) and filtered client-side by `event` (substring) and/or `severity` (exact).
|
|
47
|
+
* Honest `_meta` (exact match total + real-time freshness; a no-alerts result is an
|
|
48
|
+
* honest empty).
|
|
49
|
+
*/
|
|
50
|
+
export declare function activeAlerts(args: {
|
|
51
|
+
state?: string;
|
|
52
|
+
event?: string;
|
|
53
|
+
severity?: string;
|
|
54
|
+
limit?: number;
|
|
55
|
+
offset?: number;
|
|
56
|
+
}): Promise<MetaBundle>;
|
|
57
|
+
//# sourceMappingURL=nws-weather.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nws-weather.d.ts","sourceRoot":"","sources":["../src/nws-weather.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;GAoBG;AAKH,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAEzE,eAAO,MAAM,QAAQ,oBAAoB,CAAC;AAa1C,MAAM,MAAM,QAAQ,GAAG;IACrB,EAAE,EAAE,MAAM,GAAG,IAAI,CAAC;IAClB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;IACxB,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,OAAO,EAAE,MAAM,GAAG,IAAI,CAAC;IACvB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAC;CACzB,CAAC;AA+CF;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,IAAI,EAAE;IACvC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB,GAAG,OAAO,CAAC,UAAU,CAAC,CAgDtB"}
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* nws-weather.ts — National Weather Service active alerts (api.weather.gov, KEYLESS)
|
|
3
|
+
* — the DISASTER / CLIMATE-READINESS lane. Current watches, warnings, and advisories
|
|
4
|
+
* (event, severity, urgency, area, effective/expires window, instructions). Pairs
|
|
5
|
+
* directly with the FEMA tools (disaster declarations → public assistance → hazard
|
|
6
|
+
* mitigation → LIVE active weather) to complete a disaster-response-readiness view:
|
|
7
|
+
* where severe-weather events are active NOW, ahead of the declarations/contracts
|
|
8
|
+
* that follow.
|
|
9
|
+
*
|
|
10
|
+
* SOURCE: NWS api.weather.gov (a .gov host), keyless. Requires a descriptive
|
|
11
|
+
* User-Agent (NWS policy) — sent from a fixed constant; no token. Returns a GeoJSON
|
|
12
|
+
* FeatureCollection. This is REAL-TIME data (currently-active alerts) — freshness is
|
|
13
|
+
* disclosed and never implied to be a historical series.
|
|
14
|
+
*
|
|
15
|
+
* HONESTY: fixed host + redirect:"error" (SSRF); `state` is a 2-letter charclass (the
|
|
16
|
+
* server-side ?area= filter); event/severity are applied CLIENT-SIDE over the returned
|
|
17
|
+
* set; a non-FeatureCollection / non-array body ⇒ driftError; an outage/4xx/timeout
|
|
18
|
+
* THROWS. A genuine no-active-alerts result ⇒ an HONEST EMPTY (returned:0), never an
|
|
19
|
+
* error. totalAvailable = the EXACT count of matched active alerts. Every scalar via
|
|
20
|
+
* `str` (null-never-empty-string); dates preserved as ISO strings.
|
|
21
|
+
*/
|
|
22
|
+
import { ToolErrorCarrier } from "./errors.js";
|
|
23
|
+
import { getJson, driftError } from "./datasource.js";
|
|
24
|
+
import { str } from "./coerce.js";
|
|
25
|
+
import { withMeta } from "./meta.js";
|
|
26
|
+
export const NWS_HOST = "api.weather.gov";
|
|
27
|
+
const NWS_ACTIVE_URL = "https://api.weather.gov/alerts/active";
|
|
28
|
+
const NWS_LABEL = "nws:/alerts/active";
|
|
29
|
+
const NWS_TIMEOUT_MS = 15_000;
|
|
30
|
+
// NWS asks every client to send a descriptive User-Agent (with contact). No token.
|
|
31
|
+
const NWS_USER_AGENT = "cliwant-mcp-sam-gov (https://github.com/cliwant/mcp-sam-gov)";
|
|
32
|
+
const STATE_RE = /^[A-Za-z]{2}$/;
|
|
33
|
+
const PROVENANCE_NOTE = "Source: NWS api.weather.gov active-alerts feed (keyless; a descriptive User-Agent is sent per NWS policy).";
|
|
34
|
+
const FRESHNESS_NOTE = "REAL-TIME: these are the alerts ACTIVE at request time (a live snapshot, not a historical archive). Read effective/onset/expires/ends for each alert's window; an expired alert is not returned. A no-active-alerts result is an HONEST empty (returned:0), never an error.";
|
|
35
|
+
function mapAlert(feature) {
|
|
36
|
+
const p = (feature ?? {}).properties ?? {};
|
|
37
|
+
return {
|
|
38
|
+
id: str(p.id),
|
|
39
|
+
event: str(p.event),
|
|
40
|
+
headline: str(p.headline),
|
|
41
|
+
severity: str(p.severity),
|
|
42
|
+
urgency: str(p.urgency),
|
|
43
|
+
certainty: str(p.certainty),
|
|
44
|
+
category: str(p.category),
|
|
45
|
+
status: str(p.status),
|
|
46
|
+
messageType: str(p.messageType),
|
|
47
|
+
areaDesc: str(p.areaDesc),
|
|
48
|
+
effective: str(p.effective),
|
|
49
|
+
onset: str(p.onset),
|
|
50
|
+
expires: str(p.expires),
|
|
51
|
+
ends: str(p.ends),
|
|
52
|
+
senderName: str(p.senderName),
|
|
53
|
+
description: str(p.description),
|
|
54
|
+
instruction: str(p.instruction),
|
|
55
|
+
response: str(p.response),
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
async function loadActiveAlerts(state) {
|
|
59
|
+
const params = new URLSearchParams();
|
|
60
|
+
if (state !== undefined)
|
|
61
|
+
params.set("area", state.toUpperCase());
|
|
62
|
+
const url = params.toString() ? `${NWS_ACTIVE_URL}?${params.toString()}` : NWS_ACTIVE_URL;
|
|
63
|
+
const built = new URL(url);
|
|
64
|
+
if (built.hostname !== NWS_HOST || built.protocol !== "https:") {
|
|
65
|
+
throw driftError(NWS_LABEL, `Constructed NWS URL host ${JSON.stringify(built.hostname)} is not ${NWS_HOST} over https — refusing to fetch (SSRF safety).`);
|
|
66
|
+
}
|
|
67
|
+
const body = (await getJson(url, {
|
|
68
|
+
label: NWS_LABEL,
|
|
69
|
+
redirect: "error",
|
|
70
|
+
timeoutMs: NWS_TIMEOUT_MS,
|
|
71
|
+
headers: { "User-Agent": NWS_USER_AGENT, Accept: "application/geo+json" },
|
|
72
|
+
}));
|
|
73
|
+
if (body.type !== "FeatureCollection" || !Array.isArray(body.features)) {
|
|
74
|
+
throw driftError(NWS_LABEL, "NWS active-alerts body is not a GeoJSON FeatureCollection with a features[] array — schema drift, never a fake-empty result.");
|
|
75
|
+
}
|
|
76
|
+
return body.features.map(mapAlert);
|
|
77
|
+
}
|
|
78
|
+
// ─── Tool: nws_active_alerts ──────────────────────────────────────
|
|
79
|
+
/**
|
|
80
|
+
* List CURRENTLY-ACTIVE NWS weather alerts, optionally scoped by `state` (server-side
|
|
81
|
+
* ?area=) and filtered client-side by `event` (substring) and/or `severity` (exact).
|
|
82
|
+
* Honest `_meta` (exact match total + real-time freshness; a no-alerts result is an
|
|
83
|
+
* honest empty).
|
|
84
|
+
*/
|
|
85
|
+
export async function activeAlerts(args) {
|
|
86
|
+
if (args.state !== undefined && !STATE_RE.test(args.state)) {
|
|
87
|
+
throw new ToolErrorCarrier({
|
|
88
|
+
kind: "invalid_input",
|
|
89
|
+
retryable: false,
|
|
90
|
+
message: `Invalid state ${JSON.stringify(args.state)} — expected a 2-letter US state/territory code (^[A-Za-z]{2}$), e.g. "CA".`,
|
|
91
|
+
upstreamEndpoint: NWS_LABEL,
|
|
92
|
+
});
|
|
93
|
+
}
|
|
94
|
+
const limit = args.limit ?? 50;
|
|
95
|
+
const offset = args.offset ?? 0;
|
|
96
|
+
const all = await loadActiveAlerts(args.state);
|
|
97
|
+
const filtersApplied = [];
|
|
98
|
+
if (args.state !== undefined)
|
|
99
|
+
filtersApplied.push("state");
|
|
100
|
+
const eventQ = args.event?.trim().toLowerCase();
|
|
101
|
+
const sevQ = args.severity?.trim().toLowerCase();
|
|
102
|
+
if (args.event !== undefined)
|
|
103
|
+
filtersApplied.push("event");
|
|
104
|
+
if (args.severity !== undefined)
|
|
105
|
+
filtersApplied.push("severity");
|
|
106
|
+
const matched = all.filter((a) => {
|
|
107
|
+
if (eventQ && !(a.event ?? "").toLowerCase().includes(eventQ))
|
|
108
|
+
return false;
|
|
109
|
+
if (sevQ && (a.severity ?? "").toLowerCase() !== sevQ)
|
|
110
|
+
return false;
|
|
111
|
+
return true;
|
|
112
|
+
});
|
|
113
|
+
const totalAvailable = matched.length;
|
|
114
|
+
const page = matched.slice(offset, offset + limit);
|
|
115
|
+
const returned = page.length;
|
|
116
|
+
const hasMore = offset + returned < totalAvailable;
|
|
117
|
+
const nextOffset = hasMore ? offset + returned : null;
|
|
118
|
+
return withMeta({ alerts: page }, {
|
|
119
|
+
source: "api.weather.gov active alerts (NWS, keyless)",
|
|
120
|
+
keylessMode: true,
|
|
121
|
+
returned,
|
|
122
|
+
totalAvailable,
|
|
123
|
+
truncated: hasMore,
|
|
124
|
+
filtersApplied,
|
|
125
|
+
filtersDropped: [],
|
|
126
|
+
fieldsUnavailable: [],
|
|
127
|
+
pagination: { offset, limit, hasMore, nextOffset },
|
|
128
|
+
notes: [PROVENANCE_NOTE, FRESHNESS_NOTE],
|
|
129
|
+
});
|
|
130
|
+
}
|
|
131
|
+
//# sourceMappingURL=nws-weather.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"nws-weather.js","sourceRoot":"","sources":["../src/nws-weather.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* openfda-drugsfda.ts — openFDA DRUG APPROVALS (Drugs@FDA, api.fda.gov) — the
|
|
3
|
+
* PHARMA REGULATORY-APPROVAL lane. FDA-approved drug applications (NDA/ANDA/BLA):
|
|
4
|
+
* the sponsor, the application number, each approved product (brand/generic name,
|
|
5
|
+
* dosage form, route, marketing status), and the submission/approval history.
|
|
6
|
+
* Answers "what drugs did sponsor X get approved, and which are still marketed" —
|
|
7
|
+
* pharma vendor product/approval intelligence.
|
|
8
|
+
*
|
|
9
|
+
* ★ SAME SOURCE + ENVELOPE + CRUX as openfda.ts / openfda-device.ts (ADR-0054/0056).
|
|
10
|
+
* Same host (api.fda.gov), same envelope `{ meta:{ results:{ skip,limit,total }},
|
|
11
|
+
* results:[…] }`, same ★no-match→HTTP-404-NOT_FOUND-as-honest-empty crux, same
|
|
12
|
+
* optional query-key K-test, same fixed-host SSRF idiom, same structured-only (no
|
|
13
|
+
* raw Lucene passthrough) search. REUSES openfda.ts's `fetchOpenfda`,
|
|
14
|
+
* `readOpenfdaError`, `luceneQuote`, `openfdaApiKey`, `OPENFDA_HOST` verbatim.
|
|
15
|
+
*
|
|
16
|
+
* GET https://api.fda.gov/drug/drugsfda.json?search=<lucene>&limit=&skip=[&api_key=]
|
|
17
|
+
* → { meta:{ results:{ skip,limit,total }}, results:[ { application_number,
|
|
18
|
+
* sponsor_name, products:[…], submissions:[…] } ] }.
|
|
19
|
+
*
|
|
20
|
+
* ★ HONESTY (mirrors the siblings exactly): [P1] totalAvailable = meta.results.total
|
|
21
|
+
* EXACT (never results.length); skip/limit offset pagination. [★P2] 404 NOT_FOUND
|
|
22
|
+
* ⇒ honest empty (never thrown); other 4xx ⇒ invalid_input surfacing openFDA's
|
|
23
|
+
* message; 5xx/429 ⇒ THROW. [P3] every scalar via `str` (null-never-""); nested
|
|
24
|
+
* products/submissions arrays default to [] and each field is str (never fabricated).
|
|
25
|
+
* [P4] meta.results / results absent-or-mis-shaped ⇒ driftError. [K-test] the
|
|
26
|
+
* OPTIONAL OPENFDA_API_KEY rides ONLY &api_key=; label/source name mode only.
|
|
27
|
+
* [SSRF] fixed host; all VALUES Lucene-escaped + phrase-quoted via URLSearchParams.
|
|
28
|
+
*/
|
|
29
|
+
import { type MetaBundle } from "./meta.js";
|
|
30
|
+
export type DrugsfdaProduct = {
|
|
31
|
+
brandName: string | null;
|
|
32
|
+
genericIngredients: {
|
|
33
|
+
name: string | null;
|
|
34
|
+
strength: string | null;
|
|
35
|
+
}[];
|
|
36
|
+
dosageForm: string | null;
|
|
37
|
+
route: string | null;
|
|
38
|
+
marketingStatus: string | null;
|
|
39
|
+
};
|
|
40
|
+
export type DrugsfdaSubmission = {
|
|
41
|
+
submissionType: string | null;
|
|
42
|
+
submissionNumber: string | null;
|
|
43
|
+
submissionStatus: string | null;
|
|
44
|
+
submissionStatusDate: string | null;
|
|
45
|
+
submissionClass: string | null;
|
|
46
|
+
};
|
|
47
|
+
export type DrugsfdaApplication = {
|
|
48
|
+
applicationNumber: string | null;
|
|
49
|
+
sponsorName: string | null;
|
|
50
|
+
products: DrugsfdaProduct[];
|
|
51
|
+
submissions: DrugsfdaSubmission[];
|
|
52
|
+
};
|
|
53
|
+
export type DrugsfdaFilters = {
|
|
54
|
+
sponsorName?: string;
|
|
55
|
+
brandName?: string;
|
|
56
|
+
activeIngredient?: string;
|
|
57
|
+
applicationNumber?: string;
|
|
58
|
+
};
|
|
59
|
+
export declare function buildDrugsfdaSearch(f: DrugsfdaFilters): string;
|
|
60
|
+
export type DrugApprovalsArgs = DrugsfdaFilters & {
|
|
61
|
+
limit?: number;
|
|
62
|
+
skip?: number;
|
|
63
|
+
};
|
|
64
|
+
/**
|
|
65
|
+
* Search openFDA Drugs@FDA drug-approval applications with structured filters →
|
|
66
|
+
* curated application rows (sponsor, application number, approved products, submission
|
|
67
|
+
* history) + honest `_meta`. KEYLESS (OPTIONAL OPENFDA_API_KEY only raises the rate
|
|
68
|
+
* limit). totalAvailable = meta.results.total (EXACT); skip/limit pagination. A
|
|
69
|
+
* no-match query (openFDA HTTP 404 NOT_FOUND) ⇒ an honest empty, never a throw.
|
|
70
|
+
*/
|
|
71
|
+
export declare function drugApprovals(args: DrugApprovalsArgs): Promise<MetaBundle>;
|
|
72
|
+
//# sourceMappingURL=openfda-drugsfda.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"openfda-drugsfda.d.ts","sourceRoot":"","sources":["../src/openfda-drugsfda.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAKH,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AA4BzE,MAAM,MAAM,eAAe,GAAG;IAC5B,SAAS,EAAE,MAAM,GAAG,IAAI,CAAC;IACzB,kBAAkB,EAAE;QAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;QAAC,QAAQ,EAAE,MAAM,GAAG,IAAI,CAAA;KAAE,EAAE,CAAC;IACvE,UAAU,EAAE,MAAM,GAAG,IAAI,CAAC;IAC1B,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AACF,MAAM,MAAM,kBAAkB,GAAG;IAC/B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,gBAAgB,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;CAChC,CAAC;AACF,MAAM,MAAM,mBAAmB,GAAG;IAChC,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,WAAW,EAAE,MAAM,GAAG,IAAI,CAAC;IAC3B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,WAAW,EAAE,kBAAkB,EAAE,CAAC;CACnC,CAAC;AAsCF,MAAM,MAAM,eAAe,GAAG;IAC5B,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,gBAAgB,CAAC,EAAE,MAAM,CAAC;IAC1B,iBAAiB,CAAC,EAAE,MAAM,CAAC;CAC5B,CAAC;AAEF,wBAAgB,mBAAmB,CAAC,CAAC,EAAE,eAAe,GAAG,MAAM,CAW9D;AAGD,MAAM,MAAM,iBAAiB,GAAG,eAAe,GAAG;IAChD,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,aAAa,CAAC,IAAI,EAAE,iBAAiB,GAAG,OAAO,CAAC,UAAU,CAAC,CAmHhF"}
|