@ythalorossy/openfda 1.0.19 → 1.1.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 +20 -20
- package/README.md +101 -94
- package/dist/index.js +404 -311
- package/package.json +5 -4
package/dist/index.js
CHANGED
|
@@ -1,28 +1,79 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
var
|
|
3
|
-
var
|
|
4
|
-
var
|
|
5
|
-
import { McpServer as
|
|
6
|
-
import { StdioServerTransport as
|
|
2
|
+
var M = Object.defineProperty;
|
|
3
|
+
var G = (e, t, n) => t in e ? M(e, t, { enumerable: !0, configurable: !0, writable: !0, value: n }) : e[t] = n;
|
|
4
|
+
var D = (e, t, n) => G(e, typeof t != "symbol" ? t + "" : t, n);
|
|
5
|
+
import { McpServer as K } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
|
+
import { StdioServerTransport as L } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
import d from "zod";
|
|
8
|
-
|
|
8
|
+
const j = `OPENFDA_API_KEY is not set. This server reads the key from its process environment; a .env file is not loaded. Add it to the "env" block of your MCP client configuration. Get a free key at https://open.fda.gov/apis/authentication/
|
|
9
|
+
|
|
10
|
+
To run without a key on the unauthenticated tier (40 requests/minute, 1,000/day per IP, and no rate-limit headers), set OPENFDA_ALLOW_KEYLESS=1.`, q = "openfda: OPENFDA_ALLOW_KEYLESS=1 — using the unauthenticated tier (40 requests/minute, no rate-limit visibility). Set OPENFDA_API_KEY for 240 requests/minute.", Y = "openfda: OPENFDA_API_KEY is not set; every tool call will return a configuration error. See https://open.fda.gov/apis/authentication/";
|
|
11
|
+
function E(e = process.env) {
|
|
12
|
+
var n;
|
|
13
|
+
const t = (n = e.OPENFDA_API_KEY) == null ? void 0 : n.trim();
|
|
14
|
+
return t ? { ok: !0, apiKey: t } : e.OPENFDA_ALLOW_KEYLESS === "1" ? { ok: !0, apiKey: null } : { ok: !1, message: j };
|
|
15
|
+
}
|
|
16
|
+
function B(e = process.env) {
|
|
17
|
+
const t = E(e);
|
|
18
|
+
t.ok ? t.apiKey === null && console.error(q) : console.error(Y);
|
|
19
|
+
}
|
|
20
|
+
class J {
|
|
9
21
|
constructor(t) {
|
|
10
|
-
|
|
22
|
+
D(this, "registerTool", (t) => this.server.registerTool(
|
|
11
23
|
t.name,
|
|
12
24
|
{
|
|
13
25
|
title: t.name,
|
|
14
26
|
description: t.description,
|
|
15
27
|
inputSchema: t.inputSchema
|
|
16
28
|
},
|
|
17
|
-
|
|
29
|
+
async (n) => {
|
|
30
|
+
const a = E();
|
|
31
|
+
return a.ok ? t.handler(n) : {
|
|
32
|
+
content: [{ type: "text", text: a.message }],
|
|
33
|
+
isError: !0
|
|
34
|
+
};
|
|
35
|
+
}
|
|
18
36
|
));
|
|
19
37
|
this.server = t;
|
|
20
38
|
}
|
|
21
39
|
}
|
|
22
|
-
|
|
40
|
+
const m = (e) => Array.isArray(e) ? e : [], P = (e) => {
|
|
41
|
+
const t = m(e.warnings);
|
|
42
|
+
return t.length > 0 ? t : m(e.warnings_and_cautions);
|
|
43
|
+
};
|
|
44
|
+
function W(e) {
|
|
45
|
+
return {
|
|
46
|
+
boxed_warning: m(e.boxed_warning),
|
|
47
|
+
warnings: P(e),
|
|
48
|
+
warnings_and_cautions: m(e.warnings_and_cautions),
|
|
49
|
+
contraindications: m(e.contraindications),
|
|
50
|
+
drug_interactions: m(e.drug_interactions),
|
|
51
|
+
precautions: m(e.precautions),
|
|
52
|
+
adverse_reactions: m(e.adverse_reactions),
|
|
53
|
+
overdosage: m(e.overdosage),
|
|
54
|
+
do_not_use: m(e.do_not_use),
|
|
55
|
+
ask_doctor: m(e.ask_doctor),
|
|
56
|
+
stop_use: m(e.stop_use),
|
|
57
|
+
pregnancy_or_breast_feeding: m(e.pregnancy_or_breast_feeding)
|
|
58
|
+
};
|
|
59
|
+
}
|
|
60
|
+
function V(e) {
|
|
61
|
+
return {
|
|
62
|
+
indications_and_usage: m(e.indications_and_usage),
|
|
63
|
+
boxed_warning: m(e.boxed_warning),
|
|
64
|
+
warnings: P(e),
|
|
65
|
+
warnings_and_cautions: m(e.warnings_and_cautions),
|
|
66
|
+
do_not_use: m(e.do_not_use),
|
|
67
|
+
ask_doctor: m(e.ask_doctor),
|
|
68
|
+
ask_doctor_or_pharmacist: m(e.ask_doctor_or_pharmacist),
|
|
69
|
+
stop_use: m(e.stop_use),
|
|
70
|
+
pregnancy_or_breast_feeding: m(e.pregnancy_or_breast_feeding)
|
|
71
|
+
};
|
|
72
|
+
}
|
|
73
|
+
class b {
|
|
23
74
|
constructor() {
|
|
24
|
-
|
|
25
|
-
|
|
75
|
+
D(this, "urlBase", "https://api.fda.gov");
|
|
76
|
+
D(this, "params", /* @__PURE__ */ new Map());
|
|
26
77
|
}
|
|
27
78
|
dataset(t) {
|
|
28
79
|
return this.params.set("dataset", t), this;
|
|
@@ -37,178 +88,179 @@ class g {
|
|
|
37
88
|
return this.params.set("limit", t), this;
|
|
38
89
|
}
|
|
39
90
|
build() {
|
|
40
|
-
const t = this.params.get("dataset"),
|
|
41
|
-
if (!t || !
|
|
91
|
+
const t = this.params.get("dataset"), n = this.params.get("context"), a = this.params.get("search"), i = this.params.get("limit") ?? 1;
|
|
92
|
+
if (!t || !n || !a)
|
|
42
93
|
throw new Error("Missing required parameters: context or search");
|
|
43
|
-
|
|
94
|
+
const o = E(), s = new URLSearchParams();
|
|
95
|
+
return o.ok && o.apiKey && s.set("api_key", o.apiKey), s.set("search", String(a)), s.set("limit", String(i)), `${this.urlBase}/${t}/${n}.json?${s}`;
|
|
44
96
|
}
|
|
45
97
|
}
|
|
46
|
-
const
|
|
98
|
+
const C = (e) => e.replace(/([?&]api_key=)[^&\s]*/gi, "$1<REDACTED>"), z = {
|
|
47
99
|
maxRetries: 3,
|
|
48
100
|
retryDelay: 1e3,
|
|
49
101
|
// 1 second
|
|
50
102
|
timeout: 3e4
|
|
51
103
|
// 30 seconds
|
|
52
104
|
};
|
|
53
|
-
function
|
|
54
|
-
return !!(
|
|
105
|
+
function T(e) {
|
|
106
|
+
return !!(e.name === "TypeError" && e.message.includes("fetch") || e.name === "AbortError" || e.status >= 500 && e.status <= 599 || e.status === 429);
|
|
55
107
|
}
|
|
56
|
-
function
|
|
57
|
-
return new Promise((t) => setTimeout(t,
|
|
108
|
+
function F(e) {
|
|
109
|
+
return new Promise((t) => setTimeout(t, e));
|
|
58
110
|
}
|
|
59
|
-
async function
|
|
60
|
-
const { maxRetries:
|
|
111
|
+
async function k(e, t = {}) {
|
|
112
|
+
const { maxRetries: n, retryDelay: a, timeout: i } = { ...z, ...t }, o = {
|
|
61
113
|
"User-Agent": "@ythalorossy/openfda",
|
|
62
114
|
Accept: "application/json"
|
|
63
115
|
};
|
|
64
|
-
let
|
|
65
|
-
for (let p = 0; p <=
|
|
116
|
+
let s = null;
|
|
117
|
+
for (let p = 0; p <= n; p++)
|
|
66
118
|
try {
|
|
67
|
-
const
|
|
119
|
+
const c = new AbortController(), f = setTimeout(() => c.abort(), i), r = await fetch(e, {
|
|
68
120
|
headers: o,
|
|
69
|
-
signal:
|
|
121
|
+
signal: c.signal
|
|
70
122
|
});
|
|
71
|
-
if (clearTimeout(
|
|
72
|
-
const
|
|
123
|
+
if (clearTimeout(f), !r.ok) {
|
|
124
|
+
const u = await r.text().catch(() => "Unable to read error response"), l = {
|
|
73
125
|
type: "http",
|
|
74
|
-
message: `HTTP ${
|
|
75
|
-
status:
|
|
76
|
-
details:
|
|
126
|
+
message: `HTTP ${r.status}: ${r.statusText}`,
|
|
127
|
+
status: r.status,
|
|
128
|
+
details: u
|
|
77
129
|
};
|
|
78
|
-
switch (
|
|
130
|
+
switch (r.status) {
|
|
79
131
|
case 400:
|
|
80
|
-
|
|
132
|
+
l.message = "Bad Request: Invalid search query or parameters";
|
|
81
133
|
break;
|
|
82
134
|
case 401:
|
|
83
|
-
|
|
135
|
+
l.message = "Unauthorized: Invalid or missing API key";
|
|
84
136
|
break;
|
|
85
137
|
case 403:
|
|
86
|
-
|
|
138
|
+
l.message = "Forbidden: API key may be invalid or quota exceeded";
|
|
87
139
|
break;
|
|
88
140
|
case 404:
|
|
89
|
-
|
|
141
|
+
l.message = "Not Found: No results found for the specified query";
|
|
90
142
|
break;
|
|
91
143
|
case 429:
|
|
92
|
-
|
|
144
|
+
l.message = "Rate Limited: Too many requests. Retrying...";
|
|
93
145
|
break;
|
|
94
146
|
case 500:
|
|
95
|
-
|
|
147
|
+
l.message = "Server Error: OpenFDA service is experiencing issues";
|
|
96
148
|
break;
|
|
97
149
|
default:
|
|
98
|
-
|
|
150
|
+
l.message = `HTTP Error ${r.status}: ${r.statusText}`;
|
|
99
151
|
}
|
|
100
|
-
if (
|
|
152
|
+
if (s = l, r.status >= 400 && r.status < 500 && r.status !== 429)
|
|
101
153
|
break;
|
|
102
|
-
if (p <
|
|
103
|
-
const
|
|
104
|
-
await
|
|
154
|
+
if (p < n && T({ status: r.status })) {
|
|
155
|
+
const h = a * Math.pow(2, p);
|
|
156
|
+
await F(h);
|
|
105
157
|
continue;
|
|
106
158
|
}
|
|
107
159
|
break;
|
|
108
160
|
}
|
|
109
|
-
let
|
|
161
|
+
let g;
|
|
110
162
|
try {
|
|
111
|
-
|
|
112
|
-
} catch (
|
|
113
|
-
|
|
163
|
+
g = await r.json();
|
|
164
|
+
} catch (u) {
|
|
165
|
+
s = {
|
|
114
166
|
type: "parsing",
|
|
115
|
-
message:
|
|
116
|
-
|
|
167
|
+
message: C(
|
|
168
|
+
`Failed to parse JSON response: ${u instanceof Error ? u.message : "Unknown parsing error"}`
|
|
169
|
+
),
|
|
170
|
+
details: u
|
|
117
171
|
};
|
|
118
172
|
break;
|
|
119
173
|
}
|
|
120
|
-
if (!
|
|
121
|
-
|
|
174
|
+
if (!g) {
|
|
175
|
+
s = {
|
|
122
176
|
type: "empty_response",
|
|
123
177
|
message: "Received empty response from OpenFDA API"
|
|
124
178
|
};
|
|
125
179
|
break;
|
|
126
180
|
}
|
|
127
|
-
return { data:
|
|
128
|
-
} catch (
|
|
129
|
-
let
|
|
130
|
-
if (
|
|
181
|
+
return { data: g, error: null };
|
|
182
|
+
} catch (c) {
|
|
183
|
+
let f;
|
|
184
|
+
if (c.name === "AbortError" ? f = {
|
|
131
185
|
type: "timeout",
|
|
132
|
-
message: `Request timeout after ${
|
|
133
|
-
details:
|
|
134
|
-
} :
|
|
186
|
+
message: `Request timeout after ${i}ms`,
|
|
187
|
+
details: c
|
|
188
|
+
} : c instanceof TypeError && c.message.includes("fetch") ? f = {
|
|
135
189
|
type: "network",
|
|
136
190
|
message: "Network error: Unable to connect to OpenFDA API",
|
|
137
|
-
details:
|
|
138
|
-
} :
|
|
191
|
+
details: c.message
|
|
192
|
+
} : f = {
|
|
139
193
|
type: "unknown",
|
|
140
|
-
message:
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
194
|
+
message: C(
|
|
195
|
+
`Unexpected error: ${c.message || "Unknown error occurred"}`
|
|
196
|
+
),
|
|
197
|
+
details: c
|
|
198
|
+
}, s = f, p < n && T(c)) {
|
|
199
|
+
const r = a * Math.pow(2, p);
|
|
200
|
+
await F(r);
|
|
145
201
|
continue;
|
|
146
202
|
}
|
|
147
203
|
break;
|
|
148
204
|
}
|
|
149
|
-
return { data: null, error:
|
|
205
|
+
return { data: null, error: s };
|
|
150
206
|
}
|
|
151
|
-
const
|
|
207
|
+
const X = [
|
|
208
|
+
"openfda.brand_name",
|
|
209
|
+
"openfda.generic_name",
|
|
210
|
+
"openfda.substance_name",
|
|
211
|
+
"spl_product_data_elements"
|
|
212
|
+
];
|
|
213
|
+
async function O(e, t = 1) {
|
|
214
|
+
let n;
|
|
215
|
+
for (const a of X) {
|
|
216
|
+
const i = new b().dataset("drug").context("label").search(`${a}:"${e}"`).limit(t).build(), { data: o, error: s } = await k(i);
|
|
217
|
+
if (s) {
|
|
218
|
+
n = s;
|
|
219
|
+
continue;
|
|
220
|
+
}
|
|
221
|
+
if (o != null && o.results && o.results.length > 0)
|
|
222
|
+
return { found: !0, matched_via: a, data: o };
|
|
223
|
+
}
|
|
224
|
+
return { found: !1, error: n };
|
|
225
|
+
}
|
|
226
|
+
const U = (e) => `No label found for "${e}".
|
|
227
|
+
|
|
228
|
+
Searched, in order: ${X.join(", ")}.
|
|
229
|
+
|
|
230
|
+
Suggestions:
|
|
231
|
+
- Check the spelling.
|
|
232
|
+
- Try the generic name instead of the brand (e.g. "amiodarone" rather than "Cordarone").
|
|
233
|
+
- Try the originator brand rather than a repackager's name.
|
|
234
|
+
- Some discontinued brands have no current FDA label.`, H = {
|
|
152
235
|
name: "get-drug-by-name",
|
|
153
|
-
description: "Get drug
|
|
236
|
+
description: "Get drug information by brand name, generic name, or active substance. Returns the brand name, generic name, manufacturer name, product NDC, product type, route, substance name, indications and usage, warnings, do not use, ask doctor, ask doctor or pharmacist, stop use, pregnancy or breast feeding. The response reports which field matched via matched_via.",
|
|
154
237
|
inputSchema: d.object({
|
|
155
238
|
drugName: d.string().describe("Drug name")
|
|
156
239
|
}),
|
|
157
|
-
async handler({ drugName:
|
|
158
|
-
const t =
|
|
159
|
-
if (
|
|
160
|
-
|
|
161
|
-
switch (s.type) {
|
|
162
|
-
case "http":
|
|
163
|
-
s.status === 404 ? a += `${t}
|
|
164
|
-
|
|
165
|
-
Suggestions:
|
|
166
|
-
- Verify the exact brand name spelling
|
|
167
|
-
- Try searching for the generic name instead
|
|
168
|
-
- Check if the drug is FDA-approved` : (s.status === 401 || s.status === 403) && (a += `
|
|
169
|
-
|
|
170
|
-
Please check the API key configuration.`);
|
|
171
|
-
break;
|
|
172
|
-
case "network":
|
|
173
|
-
a += `
|
|
174
|
-
|
|
175
|
-
Please check your internet connection and try again.`;
|
|
176
|
-
break;
|
|
177
|
-
case "timeout":
|
|
178
|
-
a += `
|
|
179
|
-
|
|
180
|
-
The request took too long. Please try again.`;
|
|
181
|
-
break;
|
|
182
|
-
}
|
|
183
|
-
return {
|
|
184
|
-
content: [{ type: "text", text: a }],
|
|
185
|
-
isError: !0
|
|
186
|
-
};
|
|
187
|
-
}
|
|
188
|
-
if (!(r != null && r.results) || r.results.length === 0)
|
|
189
|
-
return {
|
|
240
|
+
async handler({ drugName: e }) {
|
|
241
|
+
const t = await O(e, 1);
|
|
242
|
+
if (!t.found)
|
|
243
|
+
return t.error ? {
|
|
190
244
|
content: [
|
|
191
245
|
{
|
|
192
246
|
type: "text",
|
|
193
|
-
text: `
|
|
247
|
+
text: `Failed to retrieve drug data for "${e}": ${t.error.message}`
|
|
194
248
|
}
|
|
195
|
-
]
|
|
249
|
+
],
|
|
250
|
+
isError: !0
|
|
251
|
+
} : {
|
|
252
|
+
content: [{ type: "text", text: U(e) }]
|
|
196
253
|
};
|
|
197
|
-
const
|
|
198
|
-
brand_name:
|
|
199
|
-
generic_name:
|
|
200
|
-
manufacturer_name:
|
|
201
|
-
product_ndc:
|
|
202
|
-
product_type:
|
|
203
|
-
route:
|
|
204
|
-
substance_name:
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
do_not_use: e == null ? void 0 : e.do_not_use,
|
|
208
|
-
ask_doctor: e == null ? void 0 : e.ask_doctor,
|
|
209
|
-
ask_doctor_or_pharmacist: e == null ? void 0 : e.ask_doctor_or_pharmacist,
|
|
210
|
-
stop_use: e == null ? void 0 : e.stop_use,
|
|
211
|
-
pregnancy_or_breast_feeding: e == null ? void 0 : e.pregnancy_or_breast_feeding
|
|
254
|
+
const n = t.data.results[0], a = {
|
|
255
|
+
brand_name: n == null ? void 0 : n.openfda.brand_name,
|
|
256
|
+
generic_name: n == null ? void 0 : n.openfda.generic_name,
|
|
257
|
+
manufacturer_name: n == null ? void 0 : n.openfda.manufacturer_name,
|
|
258
|
+
product_ndc: n == null ? void 0 : n.openfda.product_ndc,
|
|
259
|
+
product_type: n == null ? void 0 : n.openfda.product_type,
|
|
260
|
+
route: n == null ? void 0 : n.openfda.route,
|
|
261
|
+
substance_name: n == null ? void 0 : n.openfda.substance_name,
|
|
262
|
+
matched_via: t.matched_via,
|
|
263
|
+
...V(n)
|
|
212
264
|
};
|
|
213
265
|
return {
|
|
214
266
|
content: [
|
|
@@ -216,12 +268,24 @@ The request took too long. Please try again.`;
|
|
|
216
268
|
type: "text",
|
|
217
269
|
text: `Drug information retrieved successfully:
|
|
218
270
|
|
|
219
|
-
${JSON.stringify(
|
|
271
|
+
${JSON.stringify(a, null, 2)}`
|
|
220
272
|
}
|
|
221
273
|
]
|
|
222
274
|
};
|
|
223
275
|
}
|
|
224
|
-
}
|
|
276
|
+
};
|
|
277
|
+
function N(e, t, n) {
|
|
278
|
+
return typeof t != "number" ? `Showing ${e} ${n} (total unknown)` : e >= t ? `Showing all ${t} ${n}` : `Showing ${e} of ${t} ${n}`;
|
|
279
|
+
}
|
|
280
|
+
function S(e, t, n) {
|
|
281
|
+
return {
|
|
282
|
+
total: typeof t == "number" ? t : null,
|
|
283
|
+
returned: e.length,
|
|
284
|
+
limit: n,
|
|
285
|
+
results: e
|
|
286
|
+
};
|
|
287
|
+
}
|
|
288
|
+
const Q = {
|
|
225
289
|
name: "get-drug-by-generic-name",
|
|
226
290
|
description: "Get drug information by generic (active ingredient) name. Useful when you know the generic name but not the brand name. Returns all brand versions of the generic drug.",
|
|
227
291
|
inputSchema: d.object({
|
|
@@ -229,51 +293,75 @@ ${JSON.stringify(o, null, 2)}`
|
|
|
229
293
|
limit: d.number().optional().default(5).describe("Maximum number of results to return")
|
|
230
294
|
}),
|
|
231
295
|
async handler({
|
|
232
|
-
genericName:
|
|
296
|
+
genericName: e,
|
|
233
297
|
limit: t
|
|
234
298
|
}) {
|
|
235
|
-
|
|
236
|
-
|
|
299
|
+
var s, p, c, f;
|
|
300
|
+
const n = new b().dataset("drug").context("label").search(`openfda.generic_name:"${e}"`).limit(t).build(), { data: a, error: i } = await k(n);
|
|
301
|
+
if (i)
|
|
237
302
|
return {
|
|
238
303
|
content: [
|
|
239
304
|
{
|
|
240
305
|
type: "text",
|
|
241
|
-
text: `Failed to retrieve drug data for generic name "${
|
|
306
|
+
text: `Failed to retrieve drug data for generic name "${e}": ${i.message}`
|
|
242
307
|
}
|
|
243
308
|
],
|
|
244
309
|
isError: !0
|
|
245
310
|
};
|
|
246
|
-
if (!(
|
|
311
|
+
if (!(a != null && a.results) || a.results.length === 0)
|
|
247
312
|
return {
|
|
248
313
|
content: [
|
|
249
314
|
{
|
|
250
315
|
type: "text",
|
|
251
|
-
text: `No drug information found for generic name "${
|
|
316
|
+
text: `No drug information found for generic name "${e}".`
|
|
252
317
|
}
|
|
253
318
|
]
|
|
254
319
|
};
|
|
255
|
-
const o =
|
|
256
|
-
var
|
|
320
|
+
const o = a.results.map((r) => {
|
|
321
|
+
var g, u, l, h;
|
|
257
322
|
return {
|
|
258
|
-
brand_name: ((
|
|
259
|
-
generic_name: ((
|
|
260
|
-
manufacturer_name: ((l =
|
|
261
|
-
product_type: ((
|
|
262
|
-
route: (
|
|
323
|
+
brand_name: ((g = r == null ? void 0 : r.openfda.brand_name) == null ? void 0 : g[0]) || "Unknown",
|
|
324
|
+
generic_name: ((u = r == null ? void 0 : r.openfda.generic_name) == null ? void 0 : u[0]) || "Unknown",
|
|
325
|
+
manufacturer_name: ((l = r == null ? void 0 : r.openfda.manufacturer_name) == null ? void 0 : l[0]) || "Unknown",
|
|
326
|
+
product_type: ((h = r == null ? void 0 : r.openfda.product_type) == null ? void 0 : h[0]) || "Unknown",
|
|
327
|
+
route: (r == null ? void 0 : r.openfda.route) || []
|
|
263
328
|
};
|
|
264
329
|
});
|
|
265
330
|
return {
|
|
266
331
|
content: [
|
|
267
332
|
{
|
|
268
333
|
type: "text",
|
|
269
|
-
text:
|
|
334
|
+
text: `${N(o.length, (p = (s = a.meta) == null ? void 0 : s.results) == null ? void 0 : p.total, `labels with generic name "${e}"`)}
|
|
270
335
|
|
|
271
|
-
${JSON.stringify(o, null, 2)}`
|
|
336
|
+
${JSON.stringify(S(o, (f = (c = a.meta) == null ? void 0 : c.results) == null ? void 0 : f.total, t ?? 5), null, 2)}`
|
|
272
337
|
}
|
|
273
338
|
]
|
|
274
339
|
};
|
|
275
340
|
}
|
|
276
|
-
},
|
|
341
|
+
}, Z = {
|
|
342
|
+
1: "Recovered/resolved",
|
|
343
|
+
2: "Recovering/resolving",
|
|
344
|
+
3: "Not recovered/not resolved",
|
|
345
|
+
4: "Recovered/resolved with sequelae",
|
|
346
|
+
5: "Fatal",
|
|
347
|
+
6: "Unknown"
|
|
348
|
+
};
|
|
349
|
+
function ee(e) {
|
|
350
|
+
if (e == null || e === "") return "Not reported";
|
|
351
|
+
const t = String(e);
|
|
352
|
+
return Z[t] ?? `Unrecognized outcome code "${t}"`;
|
|
353
|
+
}
|
|
354
|
+
function te(e) {
|
|
355
|
+
const t = /* @__PURE__ */ new Set(), n = [];
|
|
356
|
+
for (const a of e) {
|
|
357
|
+
const i = a == null ? void 0 : a.reactionmeddrapt;
|
|
358
|
+
if (!i) continue;
|
|
359
|
+
const o = `${i}\0${String(a == null ? void 0 : a.reactionoutcome)}`;
|
|
360
|
+
t.has(o) || (t.add(o), n.push({ reaction: i, outcome: a == null ? void 0 : a.reactionoutcome }));
|
|
361
|
+
}
|
|
362
|
+
return n.slice(0, 3);
|
|
363
|
+
}
|
|
364
|
+
const ne = {
|
|
277
365
|
name: "get-drug-adverse-events",
|
|
278
366
|
description: "Get adverse event reports for a drug. This provides safety information about reported side effects and reactions. Use brand name or generic name.",
|
|
279
367
|
inputSchema: d.object({
|
|
@@ -282,19 +370,20 @@ ${JSON.stringify(o, null, 2)}`
|
|
|
282
370
|
seriousness: d.enum(["serious", "non-serious", "all"]).optional().default("all").describe("Filter by event seriousness")
|
|
283
371
|
}),
|
|
284
372
|
async handler({
|
|
285
|
-
drugName:
|
|
373
|
+
drugName: e,
|
|
286
374
|
limit: t,
|
|
287
|
-
seriousness:
|
|
375
|
+
seriousness: n
|
|
288
376
|
}) {
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
377
|
+
var c, f, r, g;
|
|
378
|
+
let a = `patient.drug.medicinalproduct:"${e}"`;
|
|
379
|
+
n !== "all" && (a += ` AND serious:${n === "serious" ? "1" : "2"}`);
|
|
380
|
+
const i = new b().dataset("drug").context("event").search(a).limit(t).build(), { data: o, error: s } = await k(i);
|
|
381
|
+
if (s)
|
|
293
382
|
return {
|
|
294
383
|
content: [
|
|
295
384
|
{
|
|
296
385
|
type: "text",
|
|
297
|
-
text: `Failed to retrieve adverse events for "${
|
|
386
|
+
text: `Failed to retrieve adverse events for "${e}": ${s.message}`
|
|
298
387
|
}
|
|
299
388
|
],
|
|
300
389
|
isError: !0
|
|
@@ -304,34 +393,37 @@ ${JSON.stringify(o, null, 2)}`
|
|
|
304
393
|
content: [
|
|
305
394
|
{
|
|
306
395
|
type: "text",
|
|
307
|
-
text: `No adverse events found for "${
|
|
396
|
+
text: `No adverse events found for "${e}".`
|
|
308
397
|
}
|
|
309
398
|
]
|
|
310
399
|
};
|
|
311
|
-
const p = o.results.map((
|
|
312
|
-
var
|
|
400
|
+
const p = o.results.map((u) => {
|
|
401
|
+
var h, _, $, v;
|
|
402
|
+
const l = te(((h = u.patient) == null ? void 0 : h.reaction) ?? []);
|
|
313
403
|
return {
|
|
314
|
-
report_id:
|
|
315
|
-
serious:
|
|
316
|
-
patient_age: ((
|
|
317
|
-
patient_sex: ((
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
404
|
+
report_id: u.safetyreportid,
|
|
405
|
+
serious: u.serious === "1" ? "Yes" : "No",
|
|
406
|
+
patient_age: ((_ = u.patient) == null ? void 0 : _.patientonsetage) || "Unknown",
|
|
407
|
+
patient_sex: (($ = u.patient) == null ? void 0 : $.patientsex) === "1" ? "Male" : ((v = u.patient) == null ? void 0 : v.patientsex) === "2" ? "Female" : "Unknown",
|
|
408
|
+
// Derived from the same deduped pairs so the two arrays stay the
|
|
409
|
+
// same length and positionally aligned (see dedupeReactionPairs).
|
|
410
|
+
reactions: l.map((w) => w.reaction),
|
|
411
|
+
outcomes: l.map((w) => ee(w.outcome)),
|
|
412
|
+
report_date: u.receiptdate || "Unknown"
|
|
321
413
|
};
|
|
322
414
|
});
|
|
323
415
|
return {
|
|
324
416
|
content: [
|
|
325
417
|
{
|
|
326
418
|
type: "text",
|
|
327
|
-
text:
|
|
419
|
+
text: `${N(p.length, (f = (c = o.meta) == null ? void 0 : c.results) == null ? void 0 : f.total, `adverse event reports for "${e}"`)}
|
|
328
420
|
|
|
329
|
-
${JSON.stringify(p, null, 2)}`
|
|
421
|
+
${JSON.stringify(S(p, (g = (r = o.meta) == null ? void 0 : r.results) == null ? void 0 : g.total, t ?? 10), null, 2)}`
|
|
330
422
|
}
|
|
331
423
|
]
|
|
332
424
|
};
|
|
333
425
|
}
|
|
334
|
-
},
|
|
426
|
+
}, ae = {
|
|
335
427
|
name: "get-drugs-by-manufacturer",
|
|
336
428
|
description: "Get all drugs manufactured by a specific company. Useful for finding alternatives or checking manufacturer portfolios.",
|
|
337
429
|
inputSchema: d.object({
|
|
@@ -339,126 +431,115 @@ ${JSON.stringify(p, null, 2)}`
|
|
|
339
431
|
limit: d.number().optional().default(20).describe("Maximum number of drugs to return")
|
|
340
432
|
}),
|
|
341
433
|
async handler({
|
|
342
|
-
manufacturerName:
|
|
434
|
+
manufacturerName: e,
|
|
343
435
|
limit: t
|
|
344
436
|
}) {
|
|
345
|
-
|
|
346
|
-
|
|
437
|
+
var s, p, c, f;
|
|
438
|
+
const n = new b().dataset("drug").context("label").search(`openfda.manufacturer_name:"${e}"`).limit(t).build(), { data: a, error: i } = await k(n);
|
|
439
|
+
if (i)
|
|
347
440
|
return {
|
|
348
441
|
content: [
|
|
349
442
|
{
|
|
350
443
|
type: "text",
|
|
351
|
-
text:
|
|
352
|
-
Failed to retrieve drugs for manufacturer "${n}": ${e.message}`
|
|
444
|
+
text: `Failed to retrieve drugs for manufacturer "${e}": ${i.message}`
|
|
353
445
|
}
|
|
354
446
|
],
|
|
355
447
|
isError: !0
|
|
356
448
|
};
|
|
357
|
-
if (!(
|
|
449
|
+
if (!(a != null && a.results) || a.results.length === 0)
|
|
358
450
|
return {
|
|
359
451
|
content: [
|
|
360
452
|
{
|
|
361
453
|
type: "text",
|
|
362
|
-
text: `No drugs found for manufacturer "${
|
|
454
|
+
text: `No drugs found for manufacturer "${e}".`
|
|
363
455
|
}
|
|
364
456
|
]
|
|
365
457
|
};
|
|
366
|
-
const o =
|
|
367
|
-
var
|
|
458
|
+
const o = a.results.map((r) => {
|
|
459
|
+
var g, u, l, h;
|
|
368
460
|
return {
|
|
369
|
-
brand_name: ((
|
|
370
|
-
generic_name: ((
|
|
371
|
-
product_type: ((l =
|
|
372
|
-
route: (
|
|
373
|
-
ndc: ((
|
|
461
|
+
brand_name: ((g = r == null ? void 0 : r.openfda.brand_name) == null ? void 0 : g[0]) || "Unknown",
|
|
462
|
+
generic_name: ((u = r == null ? void 0 : r.openfda.generic_name) == null ? void 0 : u[0]) || "Unknown",
|
|
463
|
+
product_type: ((l = r == null ? void 0 : r.openfda.product_type) == null ? void 0 : l[0]) || "Unknown",
|
|
464
|
+
route: (r == null ? void 0 : r.openfda.route) || [],
|
|
465
|
+
ndc: ((h = r == null ? void 0 : r.openfda.product_ndc) == null ? void 0 : h[0]) || "Unknown"
|
|
374
466
|
};
|
|
375
467
|
});
|
|
376
468
|
return {
|
|
377
469
|
content: [
|
|
378
470
|
{
|
|
379
471
|
type: "text",
|
|
380
|
-
text:
|
|
472
|
+
text: `${N(o.length, (p = (s = a.meta) == null ? void 0 : s.results) == null ? void 0 : p.total, `labels from manufacturer "${e}"`)}
|
|
381
473
|
|
|
382
|
-
${JSON.stringify(o, null, 2)}`
|
|
474
|
+
${JSON.stringify(S(o, (f = (c = a.meta) == null ? void 0 : c.results) == null ? void 0 : f.total, t ?? 20), null, 2)}`
|
|
383
475
|
}
|
|
384
476
|
]
|
|
385
477
|
};
|
|
386
478
|
}
|
|
387
|
-
},
|
|
479
|
+
}, re = {
|
|
388
480
|
name: "get-drug-safety-info",
|
|
389
|
-
description: "Get comprehensive safety information for a drug including warnings, contraindications, drug interactions, and precautions.
|
|
481
|
+
description: "Get comprehensive safety information for a drug including the boxed warning, warnings and cautions, contraindications, drug interactions, and precautions. Accepts a brand name, generic name, or active substance; the response reports which field matched via matched_via.",
|
|
390
482
|
inputSchema: d.object({
|
|
391
483
|
drugName: d.string().describe("Drug brand name")
|
|
392
484
|
}),
|
|
393
|
-
async handler({ drugName:
|
|
394
|
-
var
|
|
395
|
-
const t =
|
|
396
|
-
if (
|
|
397
|
-
return {
|
|
485
|
+
async handler({ drugName: e }) {
|
|
486
|
+
var i, o;
|
|
487
|
+
const t = await O(e, 1);
|
|
488
|
+
if (!t.found)
|
|
489
|
+
return t.error ? {
|
|
398
490
|
content: [
|
|
399
491
|
{
|
|
400
492
|
type: "text",
|
|
401
|
-
text: `Failed to retrieve safety information for "${
|
|
493
|
+
text: `Failed to retrieve safety information for "${e}": ${t.error.message}`
|
|
402
494
|
}
|
|
403
495
|
],
|
|
404
496
|
isError: !0
|
|
497
|
+
} : {
|
|
498
|
+
content: [{ type: "text", text: U(e) }]
|
|
405
499
|
};
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
text: `No safety information found for "${n}".`
|
|
412
|
-
}
|
|
413
|
-
]
|
|
414
|
-
};
|
|
415
|
-
const e = r.results[0], o = {
|
|
416
|
-
drug_name: ((a = e == null ? void 0 : e.openfda.brand_name) == null ? void 0 : a[0]) || n,
|
|
417
|
-
generic_name: ((p = e == null ? void 0 : e.openfda.generic_name) == null ? void 0 : p[0]) || "Unknown",
|
|
418
|
-
warnings: (e == null ? void 0 : e.warnings) || [],
|
|
419
|
-
contraindications: (e == null ? void 0 : e.contraindications) || [],
|
|
420
|
-
drug_interactions: (e == null ? void 0 : e.drug_interactions) || [],
|
|
421
|
-
precautions: (e == null ? void 0 : e.precautions) || [],
|
|
422
|
-
adverse_reactions: (e == null ? void 0 : e.adverse_reactions) || [],
|
|
423
|
-
overdosage: (e == null ? void 0 : e.overdosage) || [],
|
|
424
|
-
do_not_use: (e == null ? void 0 : e.do_not_use) || [],
|
|
425
|
-
ask_doctor: (e == null ? void 0 : e.ask_doctor) || [],
|
|
426
|
-
stop_use: (e == null ? void 0 : e.stop_use) || [],
|
|
427
|
-
pregnancy_or_breast_feeding: (e == null ? void 0 : e.pregnancy_or_breast_feeding) || []
|
|
500
|
+
const n = t.data.results[0], a = {
|
|
501
|
+
drug_name: ((i = n == null ? void 0 : n.openfda.brand_name) == null ? void 0 : i[0]) || e,
|
|
502
|
+
generic_name: ((o = n == null ? void 0 : n.openfda.generic_name) == null ? void 0 : o[0]) || "Unknown",
|
|
503
|
+
matched_via: t.matched_via,
|
|
504
|
+
...W(n)
|
|
428
505
|
};
|
|
429
506
|
return {
|
|
430
507
|
content: [
|
|
431
508
|
{
|
|
432
509
|
type: "text",
|
|
433
|
-
text: `Safety information for "${
|
|
510
|
+
text: `Safety information for "${e}":
|
|
434
511
|
|
|
435
|
-
${JSON.stringify(
|
|
512
|
+
${JSON.stringify(a, null, 2)}`
|
|
436
513
|
}
|
|
437
514
|
]
|
|
438
515
|
};
|
|
439
516
|
}
|
|
440
|
-
}
|
|
441
|
-
function
|
|
442
|
-
const t =
|
|
443
|
-
let
|
|
517
|
+
}, se = /^(?:\d{4}-\d{4}|\d{5}-\d{3,4})$/, oe = /^\d{1,2}$/;
|
|
518
|
+
function I(e) {
|
|
519
|
+
const t = e.trim().toUpperCase(), n = { productNDC: t, packageNDC: null, isValid: !1 };
|
|
520
|
+
let a, i = null;
|
|
444
521
|
if (t.includes("-")) {
|
|
445
522
|
const o = t.split("-");
|
|
446
523
|
if (o.length === 2)
|
|
447
|
-
|
|
448
|
-
else if (o.length === 3)
|
|
449
|
-
|
|
524
|
+
a = t;
|
|
525
|
+
else if (o.length === 3) {
|
|
526
|
+
const s = o[2];
|
|
527
|
+
if (!s || !oe.test(s)) return n;
|
|
528
|
+
a = `${o[0]}-${o[1]}`, i = t;
|
|
529
|
+
} else
|
|
530
|
+
return n;
|
|
531
|
+
} else if (/^\d+$/.test(t))
|
|
532
|
+
if (t.length === 11)
|
|
533
|
+
a = `${t.slice(0, 5)}-${t.slice(5, 9)}`, i = `${a}-${t.slice(9, 11)}`;
|
|
534
|
+
else if (t.length === 9)
|
|
535
|
+
a = `${t.slice(0, 5)}-${t.slice(5, 9)}`;
|
|
450
536
|
else
|
|
451
|
-
return
|
|
452
|
-
} else if (t.length === 11)
|
|
453
|
-
r = `${t.substring(0, 5)}-${t.substring(5, 9)}`, s = `${t.substring(0, 5)}-${t.substring(5, 9)}-${t.substring(9, 11)}`;
|
|
454
|
-
else if (t.length === 9)
|
|
455
|
-
r = `${t.substring(0, 5)}-${t.substring(5, 9)}`, s = null;
|
|
537
|
+
return n;
|
|
456
538
|
else
|
|
457
|
-
return
|
|
458
|
-
|
|
459
|
-
return { productNDC: r, packageNDC: s, isValid: e };
|
|
539
|
+
return n;
|
|
540
|
+
return se.test(a) ? { productNDC: a, packageNDC: i, isValid: !0 } : n;
|
|
460
541
|
}
|
|
461
|
-
const
|
|
542
|
+
const ie = {
|
|
462
543
|
name: "get-drug-by-ndc",
|
|
463
544
|
description: "Get drug information by National Drug Code (NDC). Accepts both product NDC (XXXXX-XXXX) and package NDC (XXXXX-XXXX-XX) formats. Also accepts NDC codes without dashes.",
|
|
464
545
|
inputSchema: d.object({
|
|
@@ -466,14 +547,15 @@ const I = {
|
|
|
466
547
|
"National Drug Code (NDC) - accepts formats: XXXXX-XXXX, XXXXX-XXXX-XX, or without dashes"
|
|
467
548
|
)
|
|
468
549
|
}),
|
|
469
|
-
async handler({ ndcCode:
|
|
470
|
-
|
|
471
|
-
|
|
550
|
+
async handler({ ndcCode: e }) {
|
|
551
|
+
var g, u, l, h;
|
|
552
|
+
const { productNDC: t, packageNDC: n, isValid: a } = I(e);
|
|
553
|
+
if (!a)
|
|
472
554
|
return {
|
|
473
555
|
content: [
|
|
474
556
|
{
|
|
475
557
|
type: "text",
|
|
476
|
-
text: `Invalid NDC format: "${
|
|
558
|
+
text: `Invalid NDC format: "${e}"
|
|
477
559
|
|
|
478
560
|
✅ Accepted formats:
|
|
479
561
|
• Product NDC: 12345-1234
|
|
@@ -483,25 +565,25 @@ const I = {
|
|
|
483
565
|
],
|
|
484
566
|
isError: !0
|
|
485
567
|
};
|
|
486
|
-
let
|
|
487
|
-
|
|
488
|
-
const o = new
|
|
568
|
+
let i = `openfda.product_ndc:"${t}"`;
|
|
569
|
+
n && (i += ` OR openfda.package_ndc:"${n}"`);
|
|
570
|
+
const o = new b().dataset("drug").context("label").search(i).limit(10).build(), { data: s, error: p } = await k(o);
|
|
489
571
|
if (p)
|
|
490
572
|
return {
|
|
491
573
|
content: [
|
|
492
574
|
{
|
|
493
575
|
type: "text",
|
|
494
|
-
text: `Failed to retrieve drug data for NDC "${
|
|
576
|
+
text: `Failed to retrieve drug data for NDC "${e}": ${p.message}`
|
|
495
577
|
}
|
|
496
578
|
],
|
|
497
579
|
isError: !0
|
|
498
580
|
};
|
|
499
|
-
if (!(
|
|
581
|
+
if (!(s != null && s.results) || s.results.length === 0)
|
|
500
582
|
return {
|
|
501
583
|
content: [
|
|
502
584
|
{
|
|
503
585
|
type: "text",
|
|
504
|
-
text: `No drug found with NDC "${
|
|
586
|
+
text: `No drug found with NDC "${e}" (product: ${t}).
|
|
505
587
|
|
|
506
588
|
💡 Tips:
|
|
507
589
|
• Verify the NDC format
|
|
@@ -510,112 +592,122 @@ const I = {
|
|
|
510
592
|
}
|
|
511
593
|
]
|
|
512
594
|
};
|
|
513
|
-
const
|
|
514
|
-
var
|
|
515
|
-
const
|
|
516
|
-
(
|
|
595
|
+
const c = s.results.map((_) => {
|
|
596
|
+
var w, A;
|
|
597
|
+
const $ = ((w = _.openfda.product_ndc) == null ? void 0 : w.filter((x) => x === t)) || [], v = ((A = _.openfda.package_ndc) == null ? void 0 : A.filter(
|
|
598
|
+
(x) => n ? x === n : x.startsWith(t)
|
|
517
599
|
)) || [];
|
|
518
600
|
return {
|
|
519
|
-
brand_name:
|
|
520
|
-
generic_name:
|
|
521
|
-
manufacturer_name:
|
|
522
|
-
product_type:
|
|
523
|
-
route:
|
|
524
|
-
substance_name:
|
|
525
|
-
matching_product_ndc:
|
|
526
|
-
matching_package_ndc:
|
|
527
|
-
all_product_ndc:
|
|
528
|
-
all_package_ndc:
|
|
529
|
-
dosage_and_administration:
|
|
530
|
-
package_label_principal_display_panel:
|
|
531
|
-
active_ingredient:
|
|
532
|
-
purpose:
|
|
601
|
+
brand_name: _.openfda.brand_name || [],
|
|
602
|
+
generic_name: _.openfda.generic_name || [],
|
|
603
|
+
manufacturer_name: _.openfda.manufacturer_name || [],
|
|
604
|
+
product_type: _.openfda.product_type || [],
|
|
605
|
+
route: _.openfda.route || [],
|
|
606
|
+
substance_name: _.openfda.substance_name || [],
|
|
607
|
+
matching_product_ndc: $,
|
|
608
|
+
matching_package_ndc: v,
|
|
609
|
+
all_product_ndc: _.openfda.product_ndc || [],
|
|
610
|
+
all_package_ndc: _.openfda.package_ndc || [],
|
|
611
|
+
dosage_and_administration: _.dosage_and_administration || [],
|
|
612
|
+
package_label_principal_display_panel: _.package_label_principal_display_panel || [],
|
|
613
|
+
active_ingredient: _.active_ingredient || [],
|
|
614
|
+
purpose: _.purpose || []
|
|
533
615
|
};
|
|
534
|
-
}),
|
|
535
|
-
(
|
|
616
|
+
}), f = c.reduce(
|
|
617
|
+
(_, $) => _ + $.matching_package_ndc.length,
|
|
536
618
|
0
|
|
537
|
-
),
|
|
619
|
+
), r = n ? `Searched for specific package NDC: ${n}` : `Searched for product NDC: ${t} (all packages)`;
|
|
538
620
|
return {
|
|
539
621
|
content: [
|
|
540
622
|
{
|
|
541
623
|
type: "text",
|
|
542
|
-
text:
|
|
624
|
+
text: `${N(c.length, (u = (g = s.meta) == null ? void 0 : g.results) == null ? void 0 : u.total, `labels for NDC "${e}"`)} with ${f} package(s)
|
|
543
625
|
|
|
544
|
-
${
|
|
626
|
+
${r}
|
|
545
627
|
|
|
546
|
-
${JSON.stringify(
|
|
628
|
+
${JSON.stringify(S(c, (h = (l = s.meta) == null ? void 0 : l.results) == null ? void 0 : h.total, 10), null, 2)}`
|
|
547
629
|
}
|
|
548
630
|
]
|
|
549
631
|
};
|
|
550
632
|
}
|
|
551
|
-
},
|
|
633
|
+
}, ce = {
|
|
552
634
|
name: "get-drug-by-product-ndc",
|
|
553
|
-
description: "Get drug information by product NDC
|
|
635
|
+
description: "Get drug information by product NDC. Accepts the dashed forms 4-4 (0456-4020), 5-3 (58151-155) and 5-4 (12345-1234), plus undashed 9-digit (5-4) and 11-digit (5-4-2) input. Undashed 8- and 10-digit input is rejected as ambiguous — dash it. This ignores package variations and finds all packages for a product.",
|
|
554
636
|
inputSchema: d.object({
|
|
555
|
-
productNDC: d.string().describe(
|
|
637
|
+
productNDC: d.string().describe(
|
|
638
|
+
"Product NDC: dashed 4-4 (0456-4020), 5-3 (58151-155) or 5-4 (12345-1234); undashed 9-digit (123451234) or 11-digit (12345123401) also work"
|
|
639
|
+
)
|
|
556
640
|
}),
|
|
557
|
-
async handler({ productNDC:
|
|
558
|
-
var
|
|
559
|
-
|
|
641
|
+
async handler({ productNDC: e }) {
|
|
642
|
+
var f;
|
|
643
|
+
const { productNDC: t, isValid: n } = I(e);
|
|
644
|
+
if (!n)
|
|
560
645
|
return {
|
|
561
646
|
content: [
|
|
562
647
|
{
|
|
563
648
|
type: "text",
|
|
564
|
-
text: `Invalid product NDC format: "${
|
|
649
|
+
text: `Invalid product NDC format: "${e}"
|
|
565
650
|
|
|
566
|
-
✅
|
|
651
|
+
✅ Accepted formats:
|
|
652
|
+
• 4-4 product NDC: 0456-4020
|
|
653
|
+
• 5-3 product NDC: 58151-155
|
|
654
|
+
• 5-4 product NDC: 12345-1234
|
|
655
|
+
• Undashed 9 digits: 123451234 (read as 5-4)
|
|
656
|
+
• Undashed 11 digits: 12345123401 (read as 5-4-2)
|
|
657
|
+
|
|
658
|
+
Undashed 8- and 10-digit input is rejected because the split is ambiguous: 8 digits could be 5-3 or 4-4, and 10 could be 4-4-2, 5-3-2 or 5-4-1. Guessing could return a different drug, so add the dashes instead.`
|
|
567
659
|
}
|
|
568
660
|
],
|
|
569
661
|
isError: !0
|
|
570
662
|
};
|
|
571
|
-
const
|
|
572
|
-
if (
|
|
663
|
+
const a = new b().dataset("drug").context("label").search(`openfda.product_ndc:"${t}"`).limit(1).build(), { data: i, error: o } = await k(a);
|
|
664
|
+
if (o)
|
|
573
665
|
return {
|
|
574
666
|
content: [
|
|
575
667
|
{
|
|
576
668
|
type: "text",
|
|
577
|
-
text:
|
|
669
|
+
text: `Failed to retrieve drug data for product NDC "${e}": ${o.message}`
|
|
578
670
|
}
|
|
579
671
|
],
|
|
580
672
|
isError: !0
|
|
581
673
|
};
|
|
582
|
-
if (!(
|
|
674
|
+
if (!(i != null && i.results) || i.results.length === 0)
|
|
583
675
|
return {
|
|
584
676
|
content: [
|
|
585
677
|
{
|
|
586
678
|
type: "text",
|
|
587
|
-
text: `No drug found with product NDC "${
|
|
679
|
+
text: `No drug found with product NDC "${e}".`
|
|
588
680
|
}
|
|
589
681
|
],
|
|
590
682
|
structuredContent: null
|
|
591
683
|
};
|
|
592
|
-
const
|
|
593
|
-
(
|
|
594
|
-
)) || [],
|
|
595
|
-
product_ndc:
|
|
596
|
-
available_packages:
|
|
597
|
-
brand_name:
|
|
598
|
-
generic_name:
|
|
599
|
-
manufacturer_name:
|
|
600
|
-
product_type:
|
|
601
|
-
route:
|
|
602
|
-
substance_name:
|
|
603
|
-
active_ingredient:
|
|
604
|
-
purpose:
|
|
605
|
-
dosage_and_administration:
|
|
684
|
+
const s = i.results[0], p = ((f = s.openfda.package_ndc) == null ? void 0 : f.filter(
|
|
685
|
+
(r) => r.startsWith(t)
|
|
686
|
+
)) || [], c = {
|
|
687
|
+
product_ndc: t,
|
|
688
|
+
available_packages: p,
|
|
689
|
+
brand_name: s.openfda.brand_name || [],
|
|
690
|
+
generic_name: s.openfda.generic_name || [],
|
|
691
|
+
manufacturer_name: s.openfda.manufacturer_name || [],
|
|
692
|
+
product_type: s.openfda.product_type || [],
|
|
693
|
+
route: s.openfda.route || [],
|
|
694
|
+
substance_name: s.openfda.substance_name || [],
|
|
695
|
+
active_ingredient: s.active_ingredient || [],
|
|
696
|
+
purpose: s.purpose || [],
|
|
697
|
+
dosage_and_administration: s.dosage_and_administration || []
|
|
606
698
|
};
|
|
607
699
|
return {
|
|
608
700
|
content: [
|
|
609
701
|
{
|
|
610
702
|
type: "text",
|
|
611
|
-
text: `✅ Product NDC "${
|
|
703
|
+
text: `✅ Product NDC "${t}" found with ${p.length} package variation(s):
|
|
612
704
|
|
|
613
|
-
${JSON.stringify(
|
|
705
|
+
${JSON.stringify(c, null, 2)}`
|
|
614
706
|
}
|
|
615
707
|
]
|
|
616
708
|
};
|
|
617
709
|
}
|
|
618
|
-
},
|
|
710
|
+
}, ue = {
|
|
619
711
|
name: "get-drugsfda",
|
|
620
712
|
description: "Get drugsfda data by section and field. Search OpenFDA drugsfda endpoint by specifying a section (application, openfda, products, submissions, application_docs), a field name within that section, and a search value.",
|
|
621
713
|
inputSchema: d.object({
|
|
@@ -628,44 +720,44 @@ ${JSON.stringify(a, null, 2)}`
|
|
|
628
720
|
searchValue: d.string().describe("Value to search for in the specified field")
|
|
629
721
|
}),
|
|
630
722
|
async handler({
|
|
631
|
-
sectionName:
|
|
723
|
+
sectionName: e,
|
|
632
724
|
fieldName: t,
|
|
633
|
-
searchValue:
|
|
725
|
+
searchValue: n
|
|
634
726
|
}) {
|
|
635
|
-
const
|
|
727
|
+
const a = new b().dataset("drug").context("drugsfda").search(`${e}.${t}:"${n}"`).limit(1).build(), { data: i, error: o } = await k(a);
|
|
636
728
|
if (o) {
|
|
637
|
-
let
|
|
729
|
+
let s = `Failed to retrieve drugsfda data for "${n}" in ${e}.${t}: ${o.message}`;
|
|
638
730
|
switch (o.type) {
|
|
639
731
|
case "http":
|
|
640
|
-
o.status === 404 ?
|
|
732
|
+
o.status === 404 ? s += `
|
|
641
733
|
|
|
642
734
|
Suggestions:
|
|
643
735
|
- Verify the field name is correct for the section
|
|
644
|
-
- Check the search value spelling` : (o.status === 401 || o.status === 403) && (
|
|
736
|
+
- Check the search value spelling` : (o.status === 401 || o.status === 403) && (s += `
|
|
645
737
|
|
|
646
738
|
Please check the API key configuration.`);
|
|
647
739
|
break;
|
|
648
740
|
case "network":
|
|
649
|
-
|
|
741
|
+
s += `
|
|
650
742
|
|
|
651
743
|
Please check your internet connection and try again.`;
|
|
652
744
|
break;
|
|
653
745
|
case "timeout":
|
|
654
|
-
|
|
746
|
+
s += `
|
|
655
747
|
|
|
656
748
|
The request took too long. Please try again.`;
|
|
657
749
|
break;
|
|
658
750
|
}
|
|
659
751
|
return {
|
|
660
|
-
content: [{ type: "text", text:
|
|
752
|
+
content: [{ type: "text", text: s }],
|
|
661
753
|
isError: !0
|
|
662
754
|
};
|
|
663
755
|
}
|
|
664
|
-
return !(
|
|
756
|
+
return !(i != null && i.results) || i.results.length === 0 ? {
|
|
665
757
|
content: [
|
|
666
758
|
{
|
|
667
759
|
type: "text",
|
|
668
|
-
text: `No drugsfda data found for "${
|
|
760
|
+
text: `No drugsfda data found for "${n}" in ${e}.${t}. Please verify the search parameters.`
|
|
669
761
|
}
|
|
670
762
|
]
|
|
671
763
|
} : {
|
|
@@ -674,15 +766,15 @@ The request took too long. Please try again.`;
|
|
|
674
766
|
type: "text",
|
|
675
767
|
text: `drugsfda data retrieved successfully:
|
|
676
768
|
|
|
677
|
-
${JSON.stringify(
|
|
769
|
+
${JSON.stringify(i.results[0], null, 2)}`
|
|
678
770
|
}
|
|
679
771
|
]
|
|
680
772
|
};
|
|
681
773
|
}
|
|
682
|
-
},
|
|
774
|
+
}, de = "1.1.0", R = new K(
|
|
683
775
|
{
|
|
684
776
|
name: "openfda",
|
|
685
|
-
version:
|
|
777
|
+
version: de,
|
|
686
778
|
description: "OpenFDA Model Context Protocol"
|
|
687
779
|
},
|
|
688
780
|
{
|
|
@@ -691,19 +783,20 @@ ${JSON.stringify(e.results[0], null, 2)}`
|
|
|
691
783
|
tools: {}
|
|
692
784
|
}
|
|
693
785
|
}
|
|
694
|
-
), y = new
|
|
695
|
-
y.registerTool(
|
|
696
|
-
y.registerTool(
|
|
697
|
-
y.registerTool(
|
|
698
|
-
y.registerTool(
|
|
699
|
-
y.registerTool(
|
|
700
|
-
y.registerTool(
|
|
701
|
-
y.registerTool(
|
|
702
|
-
y.registerTool(
|
|
703
|
-
async function
|
|
704
|
-
|
|
705
|
-
|
|
786
|
+
), y = new J(R);
|
|
787
|
+
y.registerTool(H);
|
|
788
|
+
y.registerTool(Q);
|
|
789
|
+
y.registerTool(ne);
|
|
790
|
+
y.registerTool(ae);
|
|
791
|
+
y.registerTool(re);
|
|
792
|
+
y.registerTool(ie);
|
|
793
|
+
y.registerTool(ce);
|
|
794
|
+
y.registerTool(ue);
|
|
795
|
+
async function pe() {
|
|
796
|
+
B();
|
|
797
|
+
const e = new L();
|
|
798
|
+
await R.connect(e), console.error("OpenFDA MCP Server running on stdio");
|
|
706
799
|
}
|
|
707
|
-
|
|
708
|
-
console.error("Fatal error in main():",
|
|
800
|
+
pe().catch((e) => {
|
|
801
|
+
console.error("Fatal error in main():", e), process.exit(1);
|
|
709
802
|
});
|