@cliwant/mcp-sam-gov 1.4.0 → 1.5.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (82) hide show
  1. package/README.ja.md +11 -11
  2. package/README.ko.md +11 -11
  3. package/README.md +21 -13
  4. package/dist/cbp-border.d.ts +51 -0
  5. package/dist/cbp-border.d.ts.map +1 -0
  6. package/dist/cbp-border.js +123 -0
  7. package/dist/cbp-border.js.map +1 -0
  8. package/dist/datagov-catalog.d.ts.map +1 -1
  9. package/dist/datagov-catalog.js +16 -2
  10. package/dist/datagov-catalog.js.map +1 -1
  11. package/dist/ecfr.d.ts +2 -2
  12. package/dist/ecfr.d.ts.map +1 -1
  13. package/dist/ecfr.js +24 -10
  14. package/dist/ecfr.js.map +1 -1
  15. package/dist/edgar.d.ts.map +1 -1
  16. package/dist/edgar.js +26 -6
  17. package/dist/edgar.js.map +1 -1
  18. package/dist/epa-envirofacts.d.ts.map +1 -1
  19. package/dist/epa-envirofacts.js +14 -1
  20. package/dist/epa-envirofacts.js.map +1 -1
  21. package/dist/errors.d.ts.map +1 -1
  22. package/dist/errors.js +11 -0
  23. package/dist/errors.js.map +1 -1
  24. package/dist/far.d.ts.map +1 -1
  25. package/dist/far.js +3 -1
  26. package/dist/far.js.map +1 -1
  27. package/dist/federal-register.d.ts +2 -2
  28. package/dist/federal-register.d.ts.map +1 -1
  29. package/dist/federal-register.js +26 -10
  30. package/dist/federal-register.js.map +1 -1
  31. package/dist/fema.d.ts +36 -0
  32. package/dist/fema.d.ts.map +1 -1
  33. package/dist/fema.js +124 -0
  34. package/dist/fema.js.map +1 -1
  35. package/dist/gov-domains.d.ts +66 -0
  36. package/dist/gov-domains.d.ts.map +1 -0
  37. package/dist/gov-domains.js +211 -0
  38. package/dist/gov-domains.js.map +1 -0
  39. package/dist/nist-controls.d.ts +48 -0
  40. package/dist/nist-controls.d.ts.map +1 -0
  41. package/dist/nist-controls.js +174 -0
  42. package/dist/nist-controls.js.map +1 -0
  43. package/dist/nws-weather.d.ts +57 -0
  44. package/dist/nws-weather.d.ts.map +1 -0
  45. package/dist/nws-weather.js +131 -0
  46. package/dist/nws-weather.js.map +1 -0
  47. package/dist/openfda-drugsfda.d.ts +72 -0
  48. package/dist/openfda-drugsfda.d.ts.map +1 -0
  49. package/dist/openfda-drugsfda.js +230 -0
  50. package/dist/openfda-drugsfda.js.map +1 -0
  51. package/dist/openfda.d.ts.map +1 -1
  52. package/dist/openfda.js +31 -8
  53. package/dist/openfda.js.map +1 -1
  54. package/dist/server.d.ts.map +1 -1
  55. package/dist/server.js +331 -10
  56. package/dist/server.js.map +1 -1
  57. package/dist/treasury.d.ts +2 -0
  58. package/dist/treasury.d.ts.map +1 -1
  59. package/dist/treasury.js +7 -0
  60. package/dist/treasury.js.map +1 -1
  61. package/dist/usaspending.d.ts +32 -1
  62. package/dist/usaspending.d.ts.map +1 -1
  63. package/dist/usaspending.js +143 -16
  64. package/dist/usaspending.js.map +1 -1
  65. package/package.json +2 -2
  66. package/src/cbp-border.ts +177 -0
  67. package/src/datagov-catalog.ts +18 -2
  68. package/src/ecfr.ts +27 -10
  69. package/src/edgar.ts +39 -7
  70. package/src/epa-envirofacts.ts +17 -1
  71. package/src/errors.ts +11 -0
  72. package/src/far.ts +3 -1
  73. package/src/federal-register.ts +29 -10
  74. package/src/fema.ts +139 -0
  75. package/src/gov-domains.ts +237 -0
  76. package/src/nist-controls.ts +219 -0
  77. package/src/nws-weather.ts +167 -0
  78. package/src/openfda-drugsfda.ts +313 -0
  79. package/src/openfda.ts +30 -7
  80. package/src/server.ts +352 -10
  81. package/src/treasury.ts +7 -0
  82. package/src/usaspending.ts +189 -17
@@ -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"}
@@ -0,0 +1,230 @@
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 { ToolErrorCarrier, errorFromResponse } from "./errors.js";
30
+ import { driftError } from "./datasource.js";
31
+ import { str } from "./coerce.js";
32
+ import { withMeta } from "./meta.js";
33
+ import { OPENFDA_HOST, fetchOpenfda, readOpenfdaError, luceneQuote, openfdaApiKey, } from "./openfda.js";
34
+ const DEFAULT_LIMIT = 25;
35
+ const MAX_LIMIT = 100;
36
+ /** host+path-only label (→ ToolError.upstreamEndpoint); NEVER carries the key. */
37
+ const DRUGSFDA_LABEL = "openfda:/drug/drugsfda";
38
+ // ─── Honesty notes ────────────────────────────────────────────────
39
+ const NOT_DETERMINATION_NOTE = "openFDA Drugs@FDA records are FDA-published drug-application data; treat approval/marketing status as of the source's last publication. This is reference data, not a live regulatory determination.";
40
+ const KEYLESS_NOTE = "Keyless: no OPENFDA_API_KEY is set. openFDA allows ~1000 requests/day without a key; a free key raises the rate limit (get one at https://open.fda.gov/apis/authentication/).";
41
+ const KEYED_NOTE = "OPENFDA_API_KEY is set — it rides ONLY the &api_key= query parameter to api.fda.gov (openFDA has no header option), raising the rate limit. Its value is NEVER logged, echoed, or placed in this response.";
42
+ const NO_FILTER_NOTE = "No structured filters were applied — this is an unscoped scan of the WHOLE Drugs@FDA collection. Add sponsorName / brandName / activeIngredient / applicationNumber to scope the result set.";
43
+ const MARKETING_NOTE = "products[].marketingStatus is the FDA marketing category (e.g. 'Prescription', 'Over-the-counter', 'Discontinued', 'None (Tentative Approval)') — a 'Discontinued' product is NOT an approval revocation. submissions[] is the application's action history (ORIG approval + subsequent supplements).";
44
+ /** Map ONE Drugs@FDA result row → the curated shape. Every scalar via `str`. */
45
+ function mapApplication(row) {
46
+ const r = (row ?? {});
47
+ const products = Array.isArray(r.products) ? r.products : [];
48
+ const submissions = Array.isArray(r.submissions) ? r.submissions : [];
49
+ return {
50
+ applicationNumber: str(r.application_number),
51
+ sponsorName: str(r.sponsor_name),
52
+ products: products.map((p) => {
53
+ const pr = (p ?? {});
54
+ const ings = Array.isArray(pr.active_ingredients) ? pr.active_ingredients : [];
55
+ return {
56
+ brandName: str(pr.brand_name),
57
+ genericIngredients: ings.map((a) => {
58
+ const ai = (a ?? {});
59
+ return { name: str(ai.name), strength: str(ai.strength) };
60
+ }),
61
+ dosageForm: str(pr.dosage_form),
62
+ route: str(pr.route),
63
+ marketingStatus: str(pr.marketing_status),
64
+ };
65
+ }),
66
+ submissions: submissions.map((s) => {
67
+ const su = (s ?? {});
68
+ return {
69
+ submissionType: str(su.submission_type),
70
+ submissionNumber: str(su.submission_number),
71
+ submissionStatus: str(su.submission_status),
72
+ submissionStatusDate: str(su.submission_status_date),
73
+ submissionClass: str(su.submission_class_code_description),
74
+ };
75
+ }),
76
+ };
77
+ }
78
+ export function buildDrugsfdaSearch(f) {
79
+ const clauses = [];
80
+ if (f.sponsorName !== undefined)
81
+ clauses.push(`sponsor_name:${luceneQuote(f.sponsorName)}`);
82
+ if (f.brandName !== undefined)
83
+ clauses.push(`products.brand_name:${luceneQuote(f.brandName)}`);
84
+ if (f.activeIngredient !== undefined)
85
+ clauses.push(`products.active_ingredients.name:${luceneQuote(f.activeIngredient)}`);
86
+ if (f.applicationNumber !== undefined)
87
+ clauses.push(`application_number:${luceneQuote(f.applicationNumber)}`);
88
+ return clauses.join(" AND ");
89
+ }
90
+ /**
91
+ * Search openFDA Drugs@FDA drug-approval applications with structured filters →
92
+ * curated application rows (sponsor, application number, approved products, submission
93
+ * history) + honest `_meta`. KEYLESS (OPTIONAL OPENFDA_API_KEY only raises the rate
94
+ * limit). totalAvailable = meta.results.total (EXACT); skip/limit pagination. A
95
+ * no-match query (openFDA HTTP 404 NOT_FOUND) ⇒ an honest empty, never a throw.
96
+ */
97
+ export async function drugApprovals(args) {
98
+ const limit = clampLimit(args.limit);
99
+ const skip = clampSkip(args.skip);
100
+ const filters = {
101
+ sponsorName: args.sponsorName,
102
+ brandName: args.brandName,
103
+ activeIngredient: args.activeIngredient,
104
+ applicationNumber: args.applicationNumber,
105
+ };
106
+ const search = buildDrugsfdaSearch(filters);
107
+ const filtersApplied = [];
108
+ if (args.sponsorName !== undefined)
109
+ filtersApplied.push("sponsorName");
110
+ if (args.brandName !== undefined)
111
+ filtersApplied.push("brandName");
112
+ if (args.activeIngredient !== undefined)
113
+ filtersApplied.push("activeIngredient");
114
+ if (args.applicationNumber !== undefined)
115
+ filtersApplied.push("applicationNumber");
116
+ const params = new URLSearchParams();
117
+ if (search !== "")
118
+ params.set("search", search);
119
+ params.set("limit", String(limit));
120
+ params.set("skip", String(skip));
121
+ const key = openfdaApiKey();
122
+ if (key !== undefined)
123
+ params.set("api_key", key); // OPTIONAL — &api_key= ONLY
124
+ const url = `https://${OPENFDA_HOST}/drug/drugsfda.json?${params.toString()}`;
125
+ const res = await fetchOpenfda(url, DRUGSFDA_LABEL);
126
+ if (res.status === 404) {
127
+ const { code, message } = await readOpenfdaError(res);
128
+ if (code === "NOT_FOUND") {
129
+ return emptyResult(limit, skip, filtersApplied, key !== undefined);
130
+ }
131
+ throw new ToolErrorCarrier({
132
+ ...errorFromResponse(res, DRUGSFDA_LABEL),
133
+ message: message
134
+ ? `openFDA returned HTTP 404 at ${DRUGSFDA_LABEL}: ${message}`
135
+ : `Resource not found at ${DRUGSFDA_LABEL} (HTTP 404).`,
136
+ });
137
+ }
138
+ if (res.status >= 400 && res.status < 500 && res.status !== 429) {
139
+ const { message } = await readOpenfdaError(res);
140
+ throw new ToolErrorCarrier({
141
+ kind: "invalid_input",
142
+ retryable: false,
143
+ message: message
144
+ ? `openFDA rejected the request (HTTP ${res.status}) at ${DRUGSFDA_LABEL}: ${message}`
145
+ : `Bad request (HTTP ${res.status}) at ${DRUGSFDA_LABEL}.`,
146
+ upstreamStatus: res.status,
147
+ upstreamEndpoint: DRUGSFDA_LABEL,
148
+ });
149
+ }
150
+ if (!res.ok) {
151
+ throw new ToolErrorCarrier(errorFromResponse(res, DRUGSFDA_LABEL));
152
+ }
153
+ let body;
154
+ try {
155
+ body = await res.json();
156
+ }
157
+ catch (e) {
158
+ if (e instanceof SyntaxError) {
159
+ throw driftError(DRUGSFDA_LABEL, `openFDA ${DRUGSFDA_LABEL} returned a non-JSON body at HTTP 200 — schema drift (never read as an empty result).`);
160
+ }
161
+ throw e;
162
+ }
163
+ const b = (body ?? {});
164
+ const meta = (b.meta ?? {});
165
+ const metaResults = meta.results;
166
+ if (metaResults === undefined || metaResults === null || typeof metaResults !== "object") {
167
+ throw driftError(DRUGSFDA_LABEL, `openFDA ${DRUGSFDA_LABEL} shape drift — meta.results (the skip/limit/total carrier) is missing.`);
168
+ }
169
+ if (!Array.isArray(b.results)) {
170
+ throw driftError(DRUGSFDA_LABEL, `openFDA ${DRUGSFDA_LABEL} shape drift — results must be an array.`);
171
+ }
172
+ const applications = b.results.map(mapApplication);
173
+ const returned = applications.length;
174
+ const rawTotal = metaResults.total;
175
+ const totalAvailable = typeof rawTotal === "number" && Number.isFinite(rawTotal) ? rawTotal : null;
176
+ const hasMore = totalAvailable !== null && skip + returned < totalAvailable;
177
+ const nextOffset = hasMore ? skip + returned : null;
178
+ const notes = [NOT_DETERMINATION_NOTE, MARKETING_NOTE, keyNote(key !== undefined)];
179
+ if (filtersApplied.length === 0)
180
+ notes.push(NO_FILTER_NOTE);
181
+ return withMeta({ applications }, {
182
+ source: `${OPENFDA_HOST} /drug/drugsfda (openFDA Drugs@FDA approvals; ${key !== undefined ? "OPENFDA_API_KEY rate-limit key applied" : "keyless"})`,
183
+ keylessMode: true,
184
+ returned,
185
+ totalAvailable,
186
+ filtersApplied,
187
+ filtersDropped: [],
188
+ fieldsUnavailable: [],
189
+ pagination: { offset: skip, limit, hasMore, nextOffset },
190
+ notes,
191
+ });
192
+ }
193
+ // ─── Small helpers ────────────────────────────────────────────────
194
+ function keyNote(hasKey) {
195
+ return hasKey ? KEYED_NOTE : KEYLESS_NOTE;
196
+ }
197
+ function emptyResult(limit, skip, filtersApplied, hasKey) {
198
+ return withMeta({ applications: [] }, {
199
+ source: `${OPENFDA_HOST} /drug/drugsfda (openFDA Drugs@FDA approvals; ${hasKey ? "OPENFDA_API_KEY rate-limit key applied" : "keyless"})`,
200
+ keylessMode: true,
201
+ returned: 0,
202
+ totalAvailable: 0,
203
+ filtersApplied,
204
+ filtersDropped: [],
205
+ fieldsUnavailable: [],
206
+ pagination: { offset: skip, limit, hasMore: false, nextOffset: null },
207
+ notes: [
208
+ "No Drugs@FDA applications matched this query (openFDA returned HTTP 404 NOT_FOUND — the source's honest no-match). This is an exact empty, not an error.",
209
+ NOT_DETERMINATION_NOTE,
210
+ keyNote(hasKey),
211
+ ],
212
+ });
213
+ }
214
+ function clampLimit(v) {
215
+ if (typeof v !== "number" || !Number.isFinite(v))
216
+ return DEFAULT_LIMIT;
217
+ const n = Math.floor(v);
218
+ if (n < 1)
219
+ return 1;
220
+ if (n > MAX_LIMIT)
221
+ return MAX_LIMIT;
222
+ return n;
223
+ }
224
+ function clampSkip(v) {
225
+ if (typeof v !== "number" || !Number.isFinite(v))
226
+ return 0;
227
+ const n = Math.floor(v);
228
+ return n < 0 ? 0 : n;
229
+ }
230
+ //# sourceMappingURL=openfda-drugsfda.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"openfda-drugsfda.js","sourceRoot":"","sources":["../src/openfda-drugsfda.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AAEH,OAAO,EAAE,gBAAgB,EAAE,iBAAiB,EAAE,MAAM,aAAa,CAAC;AAClE,OAAO,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAC7C,OAAO,EAAE,GAAG,EAAE,MAAM,aAAa,CAAC;AAClC,OAAO,EAAE,QAAQ,EAAsC,MAAM,WAAW,CAAC;AACzE,OAAO,EACL,YAAY,EACZ,YAAY,EACZ,gBAAgB,EAChB,WAAW,EACX,aAAa,GACd,MAAM,cAAc,CAAC;AAEtB,MAAM,aAAa,GAAG,EAAE,CAAC;AACzB,MAAM,SAAS,GAAG,GAAG,CAAC;AAEtB,kFAAkF;AAClF,MAAM,cAAc,GAAG,wBAAwB,CAAC;AAEhD,qEAAqE;AACrE,MAAM,sBAAsB,GAC1B,sMAAsM,CAAC;AACzM,MAAM,YAAY,GAChB,+KAA+K,CAAC;AAClL,MAAM,UAAU,GACd,4MAA4M,CAAC;AAC/M,MAAM,cAAc,GAClB,8LAA8L,CAAC;AACjM,MAAM,cAAc,GAClB,uSAAuS,CAAC;AAwB1S,gFAAgF;AAChF,SAAS,cAAc,CAAC,GAAY;IAClC,MAAM,CAAC,GAAG,CAAC,GAAG,IAAI,EAAE,CAA4B,CAAC;IACjD,MAAM,QAAQ,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,EAAE,CAAC;IAC7D,MAAM,WAAW,GAAG,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IACtE,OAAO;QACL,iBAAiB,EAAE,GAAG,CAAC,CAAC,CAAC,kBAAkB,CAAC;QAC5C,WAAW,EAAE,GAAG,CAAC,CAAC,CAAC,YAAY,CAAC;QAChC,QAAQ,EAAE,QAAQ,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YAC3B,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAA4B,CAAC;YAChD,MAAM,IAAI,GAAG,KAAK,CAAC,OAAO,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,CAAC,EAAE,CAAC,kBAAkB,CAAC,CAAC,CAAC,EAAE,CAAC;YAC/E,OAAO;gBACL,SAAS,EAAE,GAAG,CAAC,EAAE,CAAC,UAAU,CAAC;gBAC7B,kBAAkB,EAAE,IAAI,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;oBACjC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAA4B,CAAC;oBAChD,OAAO,EAAE,IAAI,EAAE,GAAG,CAAC,EAAE,CAAC,IAAI,CAAC,EAAE,QAAQ,EAAE,GAAG,CAAC,EAAE,CAAC,QAAQ,CAAC,EAAE,CAAC;gBAC5D,CAAC,CAAC;gBACF,UAAU,EAAE,GAAG,CAAC,EAAE,CAAC,WAAW,CAAC;gBAC/B,KAAK,EAAE,GAAG,CAAC,EAAE,CAAC,KAAK,CAAC;gBACpB,eAAe,EAAE,GAAG,CAAC,EAAE,CAAC,gBAAgB,CAAC;aAC1C,CAAC;QACJ,CAAC,CAAC;QACF,WAAW,EAAE,WAAW,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE;YACjC,MAAM,EAAE,GAAG,CAAC,CAAC,IAAI,EAAE,CAA4B,CAAC;YAChD,OAAO;gBACL,cAAc,EAAE,GAAG,CAAC,EAAE,CAAC,eAAe,CAAC;gBACvC,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAC,iBAAiB,CAAC;gBAC3C,gBAAgB,EAAE,GAAG,CAAC,EAAE,CAAC,iBAAiB,CAAC;gBAC3C,oBAAoB,EAAE,GAAG,CAAC,EAAE,CAAC,sBAAsB,CAAC;gBACpD,eAAe,EAAE,GAAG,CAAC,EAAE,CAAC,iCAAiC,CAAC;aAC3D,CAAC;QACJ,CAAC,CAAC;KACH,CAAC;AACJ,CAAC;AAUD,MAAM,UAAU,mBAAmB,CAAC,CAAkB;IACpD,MAAM,OAAO,GAAa,EAAE,CAAC;IAC7B,IAAI,CAAC,CAAC,WAAW,KAAK,SAAS;QAC7B,OAAO,CAAC,IAAI,CAAC,gBAAgB,WAAW,CAAC,CAAC,CAAC,WAAW,CAAC,EAAE,CAAC,CAAC;IAC7D,IAAI,CAAC,CAAC,SAAS,KAAK,SAAS;QAC3B,OAAO,CAAC,IAAI,CAAC,uBAAuB,WAAW,CAAC,CAAC,CAAC,SAAS,CAAC,EAAE,CAAC,CAAC;IAClE,IAAI,CAAC,CAAC,gBAAgB,KAAK,SAAS;QAClC,OAAO,CAAC,IAAI,CAAC,oCAAoC,WAAW,CAAC,CAAC,CAAC,gBAAgB,CAAC,EAAE,CAAC,CAAC;IACtF,IAAI,CAAC,CAAC,iBAAiB,KAAK,SAAS;QACnC,OAAO,CAAC,IAAI,CAAC,sBAAsB,WAAW,CAAC,CAAC,CAAC,iBAAiB,CAAC,EAAE,CAAC,CAAC;IACzE,OAAO,OAAO,CAAC,IAAI,CAAC,OAAO,CAAC,CAAC;AAC/B,CAAC;AAQD;;;;;;GAMG;AACH,MAAM,CAAC,KAAK,UAAU,aAAa,CAAC,IAAuB;IACzD,MAAM,KAAK,GAAG,UAAU,CAAC,IAAI,CAAC,KAAK,CAAC,CAAC;IACrC,MAAM,IAAI,GAAG,SAAS,CAAC,IAAI,CAAC,IAAI,CAAC,CAAC;IAElC,MAAM,OAAO,GAAoB;QAC/B,WAAW,EAAE,IAAI,CAAC,WAAW;QAC7B,SAAS,EAAE,IAAI,CAAC,SAAS;QACzB,gBAAgB,EAAE,IAAI,CAAC,gBAAgB;QACvC,iBAAiB,EAAE,IAAI,CAAC,iBAAiB;KAC1C,CAAC;IACF,MAAM,MAAM,GAAG,mBAAmB,CAAC,OAAO,CAAC,CAAC;IAC5C,MAAM,cAAc,GAAa,EAAE,CAAC;IACpC,IAAI,IAAI,CAAC,WAAW,KAAK,SAAS;QAAE,cAAc,CAAC,IAAI,CAAC,aAAa,CAAC,CAAC;IACvE,IAAI,IAAI,CAAC,SAAS,KAAK,SAAS;QAAE,cAAc,CAAC,IAAI,CAAC,WAAW,CAAC,CAAC;IACnE,IAAI,IAAI,CAAC,gBAAgB,KAAK,SAAS;QAAE,cAAc,CAAC,IAAI,CAAC,kBAAkB,CAAC,CAAC;IACjF,IAAI,IAAI,CAAC,iBAAiB,KAAK,SAAS;QAAE,cAAc,CAAC,IAAI,CAAC,mBAAmB,CAAC,CAAC;IAEnF,MAAM,MAAM,GAAG,IAAI,eAAe,EAAE,CAAC;IACrC,IAAI,MAAM,KAAK,EAAE;QAAE,MAAM,CAAC,GAAG,CAAC,QAAQ,EAAE,MAAM,CAAC,CAAC;IAChD,MAAM,CAAC,GAAG,CAAC,OAAO,EAAE,MAAM,CAAC,KAAK,CAAC,CAAC,CAAC;IACnC,MAAM,CAAC,GAAG,CAAC,MAAM,EAAE,MAAM,CAAC,IAAI,CAAC,CAAC,CAAC;IACjC,MAAM,GAAG,GAAG,aAAa,EAAE,CAAC;IAC5B,IAAI,GAAG,KAAK,SAAS;QAAE,MAAM,CAAC,GAAG,CAAC,SAAS,EAAE,GAAG,CAAC,CAAC,CAAC,4BAA4B;IAE/E,MAAM,GAAG,GAAG,WAAW,YAAY,uBAAuB,MAAM,CAAC,QAAQ,EAAE,EAAE,CAAC;IAE9E,MAAM,GAAG,GAAG,MAAM,YAAY,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC;IAEpD,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QACvB,MAAM,EAAE,IAAI,EAAE,OAAO,EAAE,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;QACtD,IAAI,IAAI,KAAK,WAAW,EAAE,CAAC;YACzB,OAAO,WAAW,CAAC,KAAK,EAAE,IAAI,EAAE,cAAc,EAAE,GAAG,KAAK,SAAS,CAAC,CAAC;QACrE,CAAC;QACD,MAAM,IAAI,gBAAgB,CAAC;YACzB,GAAG,iBAAiB,CAAC,GAAG,EAAE,cAAc,CAAC;YACzC,OAAO,EAAE,OAAO;gBACd,CAAC,CAAC,gCAAgC,cAAc,KAAK,OAAO,EAAE;gBAC9D,CAAC,CAAC,yBAAyB,cAAc,cAAc;SAC1D,CAAC,CAAC;IACL,CAAC;IAED,IAAI,GAAG,CAAC,MAAM,IAAI,GAAG,IAAI,GAAG,CAAC,MAAM,GAAG,GAAG,IAAI,GAAG,CAAC,MAAM,KAAK,GAAG,EAAE,CAAC;QAChE,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,gBAAgB,CAAC,GAAG,CAAC,CAAC;QAChD,MAAM,IAAI,gBAAgB,CAAC;YACzB,IAAI,EAAE,eAAe;YACrB,SAAS,EAAE,KAAK;YAChB,OAAO,EAAE,OAAO;gBACd,CAAC,CAAC,sCAAsC,GAAG,CAAC,MAAM,QAAQ,cAAc,KAAK,OAAO,EAAE;gBACtF,CAAC,CAAC,qBAAqB,GAAG,CAAC,MAAM,QAAQ,cAAc,GAAG;YAC5D,cAAc,EAAE,GAAG,CAAC,MAAM;YAC1B,gBAAgB,EAAE,cAAc;SACjC,CAAC,CAAC;IACL,CAAC;IAED,IAAI,CAAC,GAAG,CAAC,EAAE,EAAE,CAAC;QACZ,MAAM,IAAI,gBAAgB,CAAC,iBAAiB,CAAC,GAAG,EAAE,cAAc,CAAC,CAAC,CAAC;IACrE,CAAC;IAED,IAAI,IAAa,CAAC;IAClB,IAAI,CAAC;QACH,IAAI,GAAG,MAAM,GAAG,CAAC,IAAI,EAAE,CAAC;IAC1B,CAAC;IAAC,OAAO,CAAC,EAAE,CAAC;QACX,IAAI,CAAC,YAAY,WAAW,EAAE,CAAC;YAC7B,MAAM,UAAU,CACd,cAAc,EACd,WAAW,cAAc,uFAAuF,CACjH,CAAC;QACJ,CAAC;QACD,MAAM,CAAC,CAAC;IACV,CAAC;IAED,MAAM,CAAC,GAAG,CAAC,IAAI,IAAI,EAAE,CAA0C,CAAC;IAChE,MAAM,IAAI,GAAG,CAAC,CAAC,CAAC,IAAI,IAAI,EAAE,CAA0B,CAAC;IACrD,MAAM,WAAW,GAAG,IAAI,CAAC,OAA0C,CAAC;IACpE,IAAI,WAAW,KAAK,SAAS,IAAI,WAAW,KAAK,IAAI,IAAI,OAAO,WAAW,KAAK,QAAQ,EAAE,CAAC;QACzF,MAAM,UAAU,CACd,cAAc,EACd,WAAW,cAAc,wEAAwE,CAClG,CAAC;IACJ,CAAC;IACD,IAAI,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,OAAO,CAAC,EAAE,CAAC;QAC9B,MAAM,UAAU,CACd,cAAc,EACd,WAAW,cAAc,0CAA0C,CACpE,CAAC;IACJ,CAAC;IAED,MAAM,YAAY,GAAI,CAAC,CAAC,OAAqB,CAAC,GAAG,CAAC,cAAc,CAAC,CAAC;IAClE,MAAM,QAAQ,GAAG,YAAY,CAAC,MAAM,CAAC;IAErC,MAAM,QAAQ,GAAG,WAAW,CAAC,KAAK,CAAC;IACnC,MAAM,cAAc,GAClB,OAAO,QAAQ,KAAK,QAAQ,IAAI,MAAM,CAAC,QAAQ,CAAC,QAAQ,CAAC,CAAC,CAAC,CAAC,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAC9E,MAAM,OAAO,GAAG,cAAc,KAAK,IAAI,IAAI,IAAI,GAAG,QAAQ,GAAG,cAAc,CAAC;IAC5E,MAAM,UAAU,GAAG,OAAO,CAAC,CAAC,CAAC,IAAI,GAAG,QAAQ,CAAC,CAAC,CAAC,IAAI,CAAC;IAEpD,MAAM,KAAK,GAAa,CAAC,sBAAsB,EAAE,cAAc,EAAE,OAAO,CAAC,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC;IAC7F,IAAI,cAAc,CAAC,MAAM,KAAK,CAAC;QAAE,KAAK,CAAC,IAAI,CAAC,cAAc,CAAC,CAAC;IAE5D,OAAO,QAAQ,CACb,EAAE,YAAY,EAAE,EAChB;QACE,MAAM,EAAE,GAAG,YAAY,iDACrB,GAAG,KAAK,SAAS,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,SACjE,GAAG;QACH,WAAW,EAAE,IAAI;QACjB,QAAQ;QACR,cAAc;QACd,cAAc;QACd,cAAc,EAAE,EAAE;QAClB,iBAAiB,EAAE,EAAE;QACrB,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,UAAU,EAAE;QACxD,KAAK;KAC0B,CAClC,CAAC;AACJ,CAAC;AAED,qEAAqE;AACrE,SAAS,OAAO,CAAC,MAAe;IAC9B,OAAO,MAAM,CAAC,CAAC,CAAC,UAAU,CAAC,CAAC,CAAC,YAAY,CAAC;AAC5C,CAAC;AAED,SAAS,WAAW,CAClB,KAAa,EACb,IAAY,EACZ,cAAwB,EACxB,MAAe;IAEf,OAAO,QAAQ,CACb,EAAE,YAAY,EAAE,EAA2B,EAAE,EAC7C;QACE,MAAM,EAAE,GAAG,YAAY,iDACrB,MAAM,CAAC,CAAC,CAAC,wCAAwC,CAAC,CAAC,CAAC,SACtD,GAAG;QACH,WAAW,EAAE,IAAI;QACjB,QAAQ,EAAE,CAAC;QACX,cAAc,EAAE,CAAC;QACjB,cAAc;QACd,cAAc,EAAE,EAAE;QAClB,iBAAiB,EAAE,EAAE;QACrB,UAAU,EAAE,EAAE,MAAM,EAAE,IAAI,EAAE,KAAK,EAAE,OAAO,EAAE,KAAK,EAAE,UAAU,EAAE,IAAI,EAAE;QACrE,KAAK,EAAE;YACL,0JAA0J;YAC1J,sBAAsB;YACtB,OAAO,CAAC,MAAM,CAAC;SAChB;KAC8B,CAClC,CAAC;AACJ,CAAC;AAED,SAAS,UAAU,CAAC,CAAU;IAC5B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,aAAa,CAAC;IACvE,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,IAAI,CAAC,GAAG,CAAC;QAAE,OAAO,CAAC,CAAC;IACpB,IAAI,CAAC,GAAG,SAAS;QAAE,OAAO,SAAS,CAAC;IACpC,OAAO,CAAC,CAAC;AACX,CAAC;AAED,SAAS,SAAS,CAAC,CAAU;IAC3B,IAAI,OAAO,CAAC,KAAK,QAAQ,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,CAAC,CAAC;QAAE,OAAO,CAAC,CAAC;IAC3D,MAAM,CAAC,GAAG,IAAI,CAAC,KAAK,CAAC,CAAC,CAAC,CAAC;IACxB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC,CAAC;AACvB,CAAC"}
@@ -1 +1 @@
1
- {"version":3,"file":"openfda.d.ts","sourceRoot":"","sources":["../src/openfda.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAKH,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAGzE,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAG1C,eAAO,MAAM,kBAAkB,qCAAsC,CAAC;AACtE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIlE,eAAO,MAAM,uBAAuB,+CAAgD,CAAC;AA2BrF,wFAAwF;AACxF,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAIlD;AAGD,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC;AAqBF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,iEAAiE;AACjE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAarD;AAGD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyChF;AAED,+FAA+F;AAC/F,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAY1D;AAGD,MAAM,MAAM,sBAAsB,GAAG,cAAc,GAAG;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,UAAU,CAAC,CA8KrB"}
1
+ {"version":3,"file":"openfda.d.ts","sourceRoot":"","sources":["../src/openfda.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;GAwDG;AAKH,OAAO,EAAY,KAAK,UAAU,EAAqB,MAAM,WAAW,CAAC;AAGzE,eAAO,MAAM,YAAY,gBAAgB,CAAC;AAG1C,eAAO,MAAM,kBAAkB,qCAAsC,CAAC;AACtE,MAAM,MAAM,eAAe,GAAG,CAAC,OAAO,kBAAkB,CAAC,CAAC,MAAM,CAAC,CAAC;AAIlE,eAAO,MAAM,uBAAuB,+CAAgD,CAAC;AAmCrF,wFAAwF;AACxF,wBAAgB,aAAa,IAAI,MAAM,GAAG,SAAS,CAIlD;AAGD,MAAM,MAAM,aAAa,GAAG;IAC1B,aAAa,EAAE,MAAM,GAAG,IAAI,CAAC;IAC7B,kBAAkB,EAAE,MAAM,GAAG,IAAI,CAAC;IAClC,eAAe,EAAE,MAAM,GAAG,IAAI,CAAC;IAC/B,cAAc,EAAE,MAAM,GAAG,IAAI,CAAC;IAC9B,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IACtB,KAAK,EAAE,MAAM,GAAG,IAAI,CAAC;IACrB,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IACpB,oBAAoB,EAAE,MAAM,GAAG,IAAI,CAAC;IACpC,YAAY,EAAE,MAAM,GAAG,IAAI,CAAC;IAC5B,iBAAiB,EAAE,MAAM,GAAG,IAAI,CAAC;IACjC,mBAAmB,EAAE,MAAM,GAAG,IAAI,CAAC;CACpC,CAAC;AAqBF;;;;;;;GAOG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,MAAM,GAAG,MAAM,CAE7C;AAED,iEAAiE;AACjE,MAAM,MAAM,cAAc,GAAG;IAC3B,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,OAAO,CAAC,EAAE,MAAM,CAAC;IACjB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB,CAAC;AAEF;;;;;GAKG;AACH,wBAAgB,WAAW,CAAC,CAAC,EAAE,cAAc,GAAG,MAAM,CAarD;AAGD;;;;;;;;;;GAUG;AACH,wBAAsB,YAAY,CAAC,GAAG,EAAE,MAAM,EAAE,KAAK,EAAE,MAAM,GAAG,OAAO,CAAC,QAAQ,CAAC,CAyChF;AAED,+FAA+F;AAC/F,wBAAsB,gBAAgB,CACpC,GAAG,EAAE,QAAQ,GACZ,OAAO,CAAC;IAAE,IAAI,EAAE,MAAM,GAAG,IAAI,CAAC;IAAC,OAAO,EAAE,MAAM,GAAG,IAAI,CAAA;CAAE,CAAC,CAY1D;AAGD,MAAM,MAAM,sBAAsB,GAAG,cAAc,GAAG;IACpD,QAAQ,CAAC,EAAE,MAAM,CAAC;IAClB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,IAAI,CAAC,EAAE,MAAM,CAAC;CACf,CAAC;AAEF;;;;;;GAMG;AACH,wBAAsB,WAAW,CAC/B,IAAI,EAAE,sBAAsB,GAC3B,OAAO,CAAC,UAAU,CAAC,CAuLrB"}
package/dist/openfda.js CHANGED
@@ -81,6 +81,13 @@ const NOT_DETERMINATION_NOTE = "openFDA recall/enforcement records are FDA-publi
81
81
  const KEYLESS_NOTE = "Keyless: no OPENFDA_API_KEY is set. openFDA allows ~1000 requests/day without a key; a free key raises the rate limit (get one at https://open.fda.gov/apis/authentication/).";
82
82
  const KEYED_NOTE = "OPENFDA_API_KEY is set — it rides ONLY the &api_key= query parameter to api.fda.gov (openFDA has no header option), raising the rate limit. Its value is NEVER logged, echoed, or placed in this response.";
83
83
  const NO_FILTER_NOTE = "No structured filters were applied — this is an unscoped scan of the WHOLE recall/enforcement category. Add firm / product / reason / classification / status / state to scope the result set.";
84
+ // dogfooding 2026-07-16: `category` defaults to 'drug'. A DEVICE- or FOOD-intent
85
+ // caller who omits it silently searches the DRUG dataset (a separate openFDA index
86
+ // with its own, much smaller total — e.g. "pacemaker" returns 1 drug recall vs 197
87
+ // device recalls). The default is convenient but load-bearing, so when it is TAKEN
88
+ // (not explicitly chosen) we say so and point to the alternatives. `category` is
89
+ // also echoed in filtersApplied so the drug/device/food choice is never invisible.
90
+ const CATEGORY_DEFAULT_NOTE = "category was NOT specified and DEFAULTED to 'drug' — this searched the DRUG recall dataset (/drug/enforcement) ONLY. For medical DEVICES pass category:'device'; for FOOD pass category:'food'. Each category is a SEPARATE openFDA dataset with its own total, so a device/food-intent query left on the default MISSES all of those recalls.";
84
91
  // ─── The OPTIONAL key seam (value NEVER leaked past the &api_key= param) ──
85
92
  /** Read OPENFDA_API_KEY from env; trim; return the value or undefined (unset/blank). */
86
93
  export function openfdaApiKey() {
@@ -265,6 +272,14 @@ export async function enforcement(args) {
265
272
  filtersApplied.push("status");
266
273
  if (args.state !== undefined)
267
274
  filtersApplied.push("state");
275
+ // NO_FILTER_NOTE gates on the STRUCTURED filters only — capture that BEFORE the
276
+ // always-present category selector is appended below.
277
+ const hasStructuredFilter = filtersApplied.length > 0;
278
+ // The category (drug|device|food) is the ENDPOINT selector — always applied and
279
+ // consequential — so echo it in filtersApplied to make the choice VISIBLE (a
280
+ // device query left on the drug default must not look identical to a device query).
281
+ const categoryDefaulted = args.category === undefined;
282
+ filtersApplied.push(`category:${category}`);
268
283
  const params = new URLSearchParams();
269
284
  if (search !== "")
270
285
  params.set("search", search);
@@ -283,7 +298,7 @@ export async function enforcement(args) {
283
298
  const { code, message } = await readOpenfdaError(res);
284
299
  if (code === "NOT_FOUND") {
285
300
  // Honest empty — NOT thrown, NOT not_found (the openFDA no-match idiom).
286
- return emptyResult(category, limit, skip, filtersApplied, key !== undefined);
301
+ return emptyResult(category, limit, skip, filtersApplied, key !== undefined, categoryDefaulted);
287
302
  }
288
303
  // A non-NOT_FOUND 404 ⇒ the shared not_found taxonomy (never a fake-empty).
289
304
  throw new ToolErrorCarrier({
@@ -346,8 +361,10 @@ export async function enforcement(args) {
346
361
  const hasMore = totalAvailable !== null && skip + returned < totalAvailable;
347
362
  const nextOffset = hasMore ? skip + returned : null;
348
363
  const notes = [NOT_DETERMINATION_NOTE, keyNote(key !== undefined)];
349
- if (filtersApplied.length === 0)
364
+ if (!hasStructuredFilter)
350
365
  notes.push(NO_FILTER_NOTE);
366
+ if (categoryDefaulted)
367
+ notes.push(CATEGORY_DEFAULT_NOTE);
351
368
  return withMeta({ recalls }, {
352
369
  // MODE only — never the key value (K-test).
353
370
  source: `${OPENFDA_HOST} /${category}/enforcement (openFDA recall enforcement; ${key !== undefined ? "OPENFDA_API_KEY rate-limit key applied" : "keyless"})`,
@@ -366,7 +383,17 @@ function keyNote(hasKey) {
366
383
  return hasKey ? KEYED_NOTE : KEYLESS_NOTE;
367
384
  }
368
385
  /** An honest empty result (★P2: a 404 NOT_FOUND no-match) — returned:0, total:0. */
369
- function emptyResult(category, limit, skip, filtersApplied, hasKey) {
386
+ function emptyResult(category, limit, skip, filtersApplied, hasKey, categoryDefaulted) {
387
+ const notes = [
388
+ "No recall/enforcement records matched this query (openFDA returned HTTP 404 NOT_FOUND — the source's honest no-match). This is an exact empty, not an error.",
389
+ NOT_DETERMINATION_NOTE,
390
+ keyNote(hasKey),
391
+ ];
392
+ // A defaulted category on an EMPTY result is the most misleading case — a device
393
+ // analyst reads "0 recalls" as "clean" when they actually searched the wrong
394
+ // dataset. Surface the default + the alternatives.
395
+ if (categoryDefaulted)
396
+ notes.push(CATEGORY_DEFAULT_NOTE);
370
397
  return withMeta({ recalls: [] }, {
371
398
  source: `${OPENFDA_HOST} /${category}/enforcement (openFDA recall enforcement; ${hasKey ? "OPENFDA_API_KEY rate-limit key applied" : "keyless"})`,
372
399
  keylessMode: true,
@@ -376,11 +403,7 @@ function emptyResult(category, limit, skip, filtersApplied, hasKey) {
376
403
  filtersDropped: [],
377
404
  fieldsUnavailable: [],
378
405
  pagination: { offset: skip, limit, hasMore: false, nextOffset: null },
379
- notes: [
380
- "No recall/enforcement records matched this query (openFDA returned HTTP 404 NOT_FOUND — the source's honest no-match). This is an exact empty, not an error.",
381
- NOT_DETERMINATION_NOTE,
382
- keyNote(hasKey),
383
- ],
406
+ notes,
384
407
  });
385
408
  }
386
409
  function clampLimit(v) {