@ythalorossy/openfda 1.0.18 → 1.0.19
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/dist/index.js +230 -162
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,7 +4,21 @@ var D = (n, t, r) => t in n ? v(n, t, { enumerable: !0, configurable: !0, writab
|
|
|
4
4
|
var k = (n, t, r) => D(n, typeof t != "symbol" ? t + "" : t, r);
|
|
5
5
|
import { McpServer as N } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
6
6
|
import { StdioServerTransport as C } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
|
-
import
|
|
7
|
+
import d from "zod";
|
|
8
|
+
class T {
|
|
9
|
+
constructor(t) {
|
|
10
|
+
k(this, "registerTool", (t) => this.server.registerTool(
|
|
11
|
+
t.name,
|
|
12
|
+
{
|
|
13
|
+
title: t.name,
|
|
14
|
+
description: t.description,
|
|
15
|
+
inputSchema: t.inputSchema
|
|
16
|
+
},
|
|
17
|
+
t.handler
|
|
18
|
+
));
|
|
19
|
+
this.server = t;
|
|
20
|
+
}
|
|
21
|
+
}
|
|
8
22
|
class g {
|
|
9
23
|
constructor() {
|
|
10
24
|
k(this, "urlBase", "https://api.fda.gov");
|
|
@@ -23,24 +37,10 @@ class g {
|
|
|
23
37
|
return this.params.set("limit", t), this;
|
|
24
38
|
}
|
|
25
39
|
build() {
|
|
26
|
-
const t = this.params.get("dataset"), r = this.params.get("context"), s = this.params.get("search"), e = this.params.get("limit") ?? 1,
|
|
40
|
+
const t = this.params.get("dataset"), r = this.params.get("context"), s = this.params.get("search"), e = this.params.get("limit") ?? 1, o = process.env.OPENFDA_API_KEY;
|
|
27
41
|
if (!t || !r || !s)
|
|
28
42
|
throw new Error("Missing required parameters: context or search");
|
|
29
|
-
return `${this.urlBase}/${t}/${r}.json?api_key=${
|
|
30
|
-
}
|
|
31
|
-
}
|
|
32
|
-
class T {
|
|
33
|
-
constructor(t) {
|
|
34
|
-
k(this, "registerTool", (t) => this.server.registerTool(
|
|
35
|
-
t.name,
|
|
36
|
-
{
|
|
37
|
-
title: t.name,
|
|
38
|
-
description: t.description,
|
|
39
|
-
inputSchema: t.inputSchema
|
|
40
|
-
},
|
|
41
|
-
t.handler
|
|
42
|
-
));
|
|
43
|
-
this.server = t;
|
|
43
|
+
return `${this.urlBase}/${t}/${r}.json?api_key=${o}&search=${s}&limit=${e}`;
|
|
44
44
|
}
|
|
45
45
|
}
|
|
46
46
|
const F = {
|
|
@@ -56,19 +56,19 @@ function x(n) {
|
|
|
56
56
|
function w(n) {
|
|
57
57
|
return new Promise((t) => setTimeout(t, n));
|
|
58
58
|
}
|
|
59
|
-
async function
|
|
60
|
-
const { maxRetries: r, retryDelay: s, timeout: e } = { ...F, ...t },
|
|
59
|
+
async function h(n, t = {}) {
|
|
60
|
+
const { maxRetries: r, retryDelay: s, timeout: e } = { ...F, ...t }, o = {
|
|
61
61
|
"User-Agent": "@ythalorossy/openfda",
|
|
62
62
|
Accept: "application/json"
|
|
63
63
|
};
|
|
64
64
|
let a = null;
|
|
65
65
|
for (let p = 0; p <= r; p++)
|
|
66
66
|
try {
|
|
67
|
-
const
|
|
68
|
-
headers:
|
|
69
|
-
signal:
|
|
67
|
+
const i = new AbortController(), l = setTimeout(() => i.abort(), e), c = await fetch(n, {
|
|
68
|
+
headers: o,
|
|
69
|
+
signal: i.signal
|
|
70
70
|
});
|
|
71
|
-
if (clearTimeout(
|
|
71
|
+
if (clearTimeout(l), !c.ok) {
|
|
72
72
|
const f = await c.text().catch(() => "Unable to read error response"), m = {
|
|
73
73
|
type: "http",
|
|
74
74
|
message: `HTTP ${c.status}: ${c.statusText}`,
|
|
@@ -100,8 +100,8 @@ async function y(n, t = {}) {
|
|
|
100
100
|
if (a = m, c.status >= 400 && c.status < 500 && c.status !== 429)
|
|
101
101
|
break;
|
|
102
102
|
if (p < r && x({ status: c.status })) {
|
|
103
|
-
const
|
|
104
|
-
await w(
|
|
103
|
+
const b = s * Math.pow(2, p);
|
|
104
|
+
await w(b);
|
|
105
105
|
continue;
|
|
106
106
|
}
|
|
107
107
|
break;
|
|
@@ -125,21 +125,21 @@ async function y(n, t = {}) {
|
|
|
125
125
|
break;
|
|
126
126
|
}
|
|
127
127
|
return { data: u, error: null };
|
|
128
|
-
} catch (
|
|
129
|
-
let
|
|
130
|
-
if (
|
|
128
|
+
} catch (i) {
|
|
129
|
+
let l;
|
|
130
|
+
if (i.name === "AbortError" ? l = {
|
|
131
131
|
type: "timeout",
|
|
132
132
|
message: `Request timeout after ${e}ms`,
|
|
133
|
-
details:
|
|
134
|
-
} :
|
|
133
|
+
details: i
|
|
134
|
+
} : i instanceof TypeError && i.message.includes("fetch") ? l = {
|
|
135
135
|
type: "network",
|
|
136
136
|
message: "Network error: Unable to connect to OpenFDA API",
|
|
137
|
-
details:
|
|
138
|
-
} :
|
|
137
|
+
details: i.message
|
|
138
|
+
} : l = {
|
|
139
139
|
type: "unknown",
|
|
140
|
-
message: `Unexpected error: ${
|
|
141
|
-
details:
|
|
142
|
-
}, a =
|
|
140
|
+
message: `Unexpected error: ${i.message || "Unknown error occurred"}`,
|
|
141
|
+
details: i
|
|
142
|
+
}, a = l, p < r && x(i)) {
|
|
143
143
|
const c = s * Math.pow(2, p);
|
|
144
144
|
await w(c);
|
|
145
145
|
continue;
|
|
@@ -148,47 +148,14 @@ async function y(n, t = {}) {
|
|
|
148
148
|
}
|
|
149
149
|
return { data: null, error: a };
|
|
150
150
|
}
|
|
151
|
-
const
|
|
152
|
-
{
|
|
153
|
-
name: "openfda",
|
|
154
|
-
version: "1.0.0",
|
|
155
|
-
description: "OpenFDA Model Context Protocol"
|
|
156
|
-
},
|
|
157
|
-
{
|
|
158
|
-
capabilities: {
|
|
159
|
-
resources: {},
|
|
160
|
-
tools: {}
|
|
161
|
-
}
|
|
162
|
-
}
|
|
163
|
-
), b = new T(X);
|
|
164
|
-
function E(n) {
|
|
165
|
-
const t = n.trim().toUpperCase();
|
|
166
|
-
let r, s = null;
|
|
167
|
-
if (t.includes("-")) {
|
|
168
|
-
const i = t.split("-");
|
|
169
|
-
if (i.length === 2)
|
|
170
|
-
r = t, s = null;
|
|
171
|
-
else if (i.length === 3)
|
|
172
|
-
r = `${i[0]}-${i[1]}`, s = t;
|
|
173
|
-
else
|
|
174
|
-
return { productNDC: t, packageNDC: null, isValid: !1 };
|
|
175
|
-
} else if (t.length === 11)
|
|
176
|
-
r = `${t.substring(0, 5)}-${t.substring(5, 9)}`, s = `${t.substring(0, 5)}-${t.substring(5, 9)}-${t.substring(9, 11)}`;
|
|
177
|
-
else if (t.length === 9)
|
|
178
|
-
r = `${t.substring(0, 5)}-${t.substring(5, 9)}`, s = null;
|
|
179
|
-
else
|
|
180
|
-
return { productNDC: t, packageNDC: null, isValid: !1 };
|
|
181
|
-
const e = /^\d{5}-\d{4}$/.test(r);
|
|
182
|
-
return { productNDC: r, packageNDC: s, isValid: e };
|
|
183
|
-
}
|
|
184
|
-
b.registerTool({
|
|
151
|
+
const S = {
|
|
185
152
|
name: "get-drug-by-name",
|
|
186
153
|
description: "Get drug by name. Use this tool to get the drug information by name. The drug name should be the brand name. It 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.",
|
|
187
|
-
inputSchema:
|
|
188
|
-
drugName:
|
|
154
|
+
inputSchema: d.object({
|
|
155
|
+
drugName: d.string().describe("Drug name")
|
|
189
156
|
}),
|
|
190
|
-
|
|
191
|
-
const t = new g().dataset("drug").context("label").search(`openfda.brand_name:"${n}"`).limit(1).build(), { data: r, error: s } = await
|
|
157
|
+
async handler({ drugName: n }) {
|
|
158
|
+
const t = new g().dataset("drug").context("label").search(`openfda.brand_name:"${n}"`).limit(1).build(), { data: r, error: s } = await h(t);
|
|
192
159
|
if (s) {
|
|
193
160
|
let a = `Failed to retrieve drug data for "${n}": ${s.message}`;
|
|
194
161
|
switch (s.type) {
|
|
@@ -214,12 +181,7 @@ The request took too long. Please try again.`;
|
|
|
214
181
|
break;
|
|
215
182
|
}
|
|
216
183
|
return {
|
|
217
|
-
content: [
|
|
218
|
-
{
|
|
219
|
-
type: "text",
|
|
220
|
-
text: a
|
|
221
|
-
}
|
|
222
|
-
],
|
|
184
|
+
content: [{ type: "text", text: a }],
|
|
223
185
|
isError: !0
|
|
224
186
|
};
|
|
225
187
|
}
|
|
@@ -232,7 +194,7 @@ The request took too long. Please try again.`;
|
|
|
232
194
|
}
|
|
233
195
|
]
|
|
234
196
|
};
|
|
235
|
-
const e = r.results[0],
|
|
197
|
+
const e = r.results[0], o = {
|
|
236
198
|
brand_name: e == null ? void 0 : e.openfda.brand_name,
|
|
237
199
|
generic_name: e == null ? void 0 : e.openfda.generic_name,
|
|
238
200
|
manufacturer_name: e == null ? void 0 : e.openfda.manufacturer_name,
|
|
@@ -254,21 +216,23 @@ The request took too long. Please try again.`;
|
|
|
254
216
|
type: "text",
|
|
255
217
|
text: `Drug information retrieved successfully:
|
|
256
218
|
|
|
257
|
-
${JSON.stringify(
|
|
219
|
+
${JSON.stringify(o, null, 2)}`
|
|
258
220
|
}
|
|
259
221
|
]
|
|
260
222
|
};
|
|
261
223
|
}
|
|
262
|
-
}
|
|
263
|
-
b.registerTool({
|
|
224
|
+
}, E = {
|
|
264
225
|
name: "get-drug-by-generic-name",
|
|
265
226
|
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.",
|
|
266
|
-
inputSchema:
|
|
267
|
-
genericName:
|
|
268
|
-
limit:
|
|
227
|
+
inputSchema: d.object({
|
|
228
|
+
genericName: d.string().describe("Generic drug name (active ingredient)"),
|
|
229
|
+
limit: d.number().optional().default(5).describe("Maximum number of results to return")
|
|
269
230
|
}),
|
|
270
|
-
|
|
271
|
-
|
|
231
|
+
async handler({
|
|
232
|
+
genericName: n,
|
|
233
|
+
limit: t
|
|
234
|
+
}) {
|
|
235
|
+
const r = new g().dataset("drug").context("label").search(`openfda.generic_name:"${n}"`).limit(t).build(), { data: s, error: e } = await h(r);
|
|
272
236
|
if (e)
|
|
273
237
|
return {
|
|
274
238
|
content: [
|
|
@@ -288,12 +252,12 @@ b.registerTool({
|
|
|
288
252
|
}
|
|
289
253
|
]
|
|
290
254
|
};
|
|
291
|
-
const
|
|
292
|
-
var p,
|
|
255
|
+
const o = s.results.map((a) => {
|
|
256
|
+
var p, i, l, c;
|
|
293
257
|
return {
|
|
294
258
|
brand_name: ((p = a == null ? void 0 : a.openfda.brand_name) == null ? void 0 : p[0]) || "Unknown",
|
|
295
|
-
generic_name: ((
|
|
296
|
-
manufacturer_name: ((
|
|
259
|
+
generic_name: ((i = a == null ? void 0 : a.openfda.generic_name) == null ? void 0 : i[0]) || "Unknown",
|
|
260
|
+
manufacturer_name: ((l = a == null ? void 0 : a.openfda.manufacturer_name) == null ? void 0 : l[0]) || "Unknown",
|
|
297
261
|
product_type: ((c = a == null ? void 0 : a.openfda.product_type) == null ? void 0 : c[0]) || "Unknown",
|
|
298
262
|
route: (a == null ? void 0 : a.openfda.route) || []
|
|
299
263
|
};
|
|
@@ -302,26 +266,29 @@ b.registerTool({
|
|
|
302
266
|
content: [
|
|
303
267
|
{
|
|
304
268
|
type: "text",
|
|
305
|
-
text: `Found ${
|
|
269
|
+
text: `Found ${o.length} drug(s) with generic name "${n}":
|
|
306
270
|
|
|
307
|
-
${JSON.stringify(
|
|
271
|
+
${JSON.stringify(o, null, 2)}`
|
|
308
272
|
}
|
|
309
273
|
]
|
|
310
274
|
};
|
|
311
275
|
}
|
|
312
|
-
}
|
|
313
|
-
b.registerTool({
|
|
276
|
+
}, P = {
|
|
314
277
|
name: "get-drug-adverse-events",
|
|
315
278
|
description: "Get adverse event reports for a drug. This provides safety information about reported side effects and reactions. Use brand name or generic name.",
|
|
316
|
-
inputSchema:
|
|
317
|
-
drugName:
|
|
318
|
-
limit:
|
|
319
|
-
seriousness:
|
|
279
|
+
inputSchema: d.object({
|
|
280
|
+
drugName: d.string().describe("Drug name (brand or generic)"),
|
|
281
|
+
limit: d.number().optional().default(10).describe("Maximum number of events to return"),
|
|
282
|
+
seriousness: d.enum(["serious", "non-serious", "all"]).optional().default("all").describe("Filter by event seriousness")
|
|
320
283
|
}),
|
|
321
|
-
|
|
284
|
+
async handler({
|
|
285
|
+
drugName: n,
|
|
286
|
+
limit: t,
|
|
287
|
+
seriousness: r
|
|
288
|
+
}) {
|
|
322
289
|
let s = `patient.drug.medicinalproduct:"${n}"`;
|
|
323
290
|
r !== "all" && (s += `+AND+serious:${r === "serious" ? "1" : "2"}`);
|
|
324
|
-
const e = new g().dataset("drug").context("event").search(s).limit(t).build(), { data:
|
|
291
|
+
const e = new g().dataset("drug").context("event").search(s).limit(t).build(), { data: o, error: a } = await h(e);
|
|
325
292
|
if (a)
|
|
326
293
|
return {
|
|
327
294
|
content: [
|
|
@@ -332,7 +299,7 @@ b.registerTool({
|
|
|
332
299
|
],
|
|
333
300
|
isError: !0
|
|
334
301
|
};
|
|
335
|
-
if (!(
|
|
302
|
+
if (!(o != null && o.results) || o.results.length === 0)
|
|
336
303
|
return {
|
|
337
304
|
content: [
|
|
338
305
|
{
|
|
@@ -341,16 +308,16 @@ b.registerTool({
|
|
|
341
308
|
}
|
|
342
309
|
]
|
|
343
310
|
};
|
|
344
|
-
const p =
|
|
345
|
-
var
|
|
311
|
+
const p = o.results.map((i) => {
|
|
312
|
+
var l, c, u, f, m, b, $;
|
|
346
313
|
return {
|
|
347
|
-
report_id:
|
|
348
|
-
serious:
|
|
349
|
-
patient_age: ((
|
|
350
|
-
patient_sex: ((c =
|
|
351
|
-
reactions: ((m = (f =
|
|
352
|
-
outcomes: (($ = (
|
|
353
|
-
report_date:
|
|
314
|
+
report_id: i.safetyreportid,
|
|
315
|
+
serious: i.serious === "1" ? "Yes" : "No",
|
|
316
|
+
patient_age: ((l = i.patient) == null ? void 0 : l.patientonsetage) || "Unknown",
|
|
317
|
+
patient_sex: ((c = i.patient) == null ? void 0 : c.patientsex) === "1" ? "Male" : ((u = i.patient) == null ? void 0 : u.patientsex) === "2" ? "Female" : "Unknown",
|
|
318
|
+
reactions: ((m = (f = i.patient) == null ? void 0 : f.reaction) == null ? void 0 : m.map((_) => _.reactionmeddrapt).slice(0, 3)) || [],
|
|
319
|
+
outcomes: (($ = (b = i.patient) == null ? void 0 : b.reaction) == null ? void 0 : $.map((_) => _.reactionoutcome).slice(0, 3)) || [],
|
|
320
|
+
report_date: i.receiptdate || "Unknown"
|
|
354
321
|
};
|
|
355
322
|
});
|
|
356
323
|
return {
|
|
@@ -364,16 +331,18 @@ ${JSON.stringify(p, null, 2)}`
|
|
|
364
331
|
]
|
|
365
332
|
};
|
|
366
333
|
}
|
|
367
|
-
}
|
|
368
|
-
b.registerTool({
|
|
334
|
+
}, A = {
|
|
369
335
|
name: "get-drugs-by-manufacturer",
|
|
370
336
|
description: "Get all drugs manufactured by a specific company. Useful for finding alternatives or checking manufacturer portfolios.",
|
|
371
|
-
inputSchema:
|
|
372
|
-
manufacturerName:
|
|
373
|
-
limit:
|
|
337
|
+
inputSchema: d.object({
|
|
338
|
+
manufacturerName: d.string().describe("Manufacturer/company name"),
|
|
339
|
+
limit: d.number().optional().default(20).describe("Maximum number of drugs to return")
|
|
374
340
|
}),
|
|
375
|
-
|
|
376
|
-
|
|
341
|
+
async handler({
|
|
342
|
+
manufacturerName: n,
|
|
343
|
+
limit: t
|
|
344
|
+
}) {
|
|
345
|
+
const r = new g().dataset("drug").context("label").search(`openfda.manufacturer_name:"${n}"`).limit(t).build(), { data: s, error: e } = await h(r);
|
|
377
346
|
if (e)
|
|
378
347
|
return {
|
|
379
348
|
content: [
|
|
@@ -394,12 +363,12 @@ Failed to retrieve drugs for manufacturer "${n}": ${e.message}`
|
|
|
394
363
|
}
|
|
395
364
|
]
|
|
396
365
|
};
|
|
397
|
-
const
|
|
398
|
-
var p,
|
|
366
|
+
const o = s.results.map((a) => {
|
|
367
|
+
var p, i, l, c;
|
|
399
368
|
return {
|
|
400
369
|
brand_name: ((p = a == null ? void 0 : a.openfda.brand_name) == null ? void 0 : p[0]) || "Unknown",
|
|
401
|
-
generic_name: ((
|
|
402
|
-
product_type: ((
|
|
370
|
+
generic_name: ((i = a == null ? void 0 : a.openfda.generic_name) == null ? void 0 : i[0]) || "Unknown",
|
|
371
|
+
product_type: ((l = a == null ? void 0 : a.openfda.product_type) == null ? void 0 : l[0]) || "Unknown",
|
|
403
372
|
route: (a == null ? void 0 : a.openfda.route) || [],
|
|
404
373
|
ndc: ((c = a == null ? void 0 : a.openfda.product_ndc) == null ? void 0 : c[0]) || "Unknown"
|
|
405
374
|
};
|
|
@@ -408,23 +377,22 @@ Failed to retrieve drugs for manufacturer "${n}": ${e.message}`
|
|
|
408
377
|
content: [
|
|
409
378
|
{
|
|
410
379
|
type: "text",
|
|
411
|
-
text: `Found ${
|
|
380
|
+
text: `Found ${o.length} drug(s) from manufacturer "${n}":
|
|
412
381
|
|
|
413
|
-
${JSON.stringify(
|
|
382
|
+
${JSON.stringify(o, null, 2)}`
|
|
414
383
|
}
|
|
415
384
|
]
|
|
416
385
|
};
|
|
417
386
|
}
|
|
418
|
-
}
|
|
419
|
-
b.registerTool({
|
|
387
|
+
}, U = {
|
|
420
388
|
name: "get-drug-safety-info",
|
|
421
389
|
description: "Get comprehensive safety information for a drug including warnings, contraindications, drug interactions, and precautions. Use brand name.",
|
|
422
|
-
inputSchema:
|
|
423
|
-
drugName:
|
|
390
|
+
inputSchema: d.object({
|
|
391
|
+
drugName: d.string().describe("Drug brand name")
|
|
424
392
|
}),
|
|
425
|
-
|
|
393
|
+
async handler({ drugName: n }) {
|
|
426
394
|
var a, p;
|
|
427
|
-
const t = new g().dataset("drug").context("label").search(`openfda.brand_name:"${n}"`).limit(1).build(), { data: r, error: s } = await
|
|
395
|
+
const t = new g().dataset("drug").context("label").search(`openfda.brand_name:"${n}"`).limit(1).build(), { data: r, error: s } = await h(t);
|
|
428
396
|
if (s)
|
|
429
397
|
return {
|
|
430
398
|
content: [
|
|
@@ -444,7 +412,7 @@ b.registerTool({
|
|
|
444
412
|
}
|
|
445
413
|
]
|
|
446
414
|
};
|
|
447
|
-
const e = r.results[0],
|
|
415
|
+
const e = r.results[0], o = {
|
|
448
416
|
drug_name: ((a = e == null ? void 0 : e.openfda.brand_name) == null ? void 0 : a[0]) || n,
|
|
449
417
|
generic_name: ((p = e == null ? void 0 : e.openfda.generic_name) == null ? void 0 : p[0]) || "Unknown",
|
|
450
418
|
warnings: (e == null ? void 0 : e.warnings) || [],
|
|
@@ -464,22 +432,42 @@ b.registerTool({
|
|
|
464
432
|
type: "text",
|
|
465
433
|
text: `Safety information for "${n}":
|
|
466
434
|
|
|
467
|
-
${JSON.stringify(
|
|
435
|
+
${JSON.stringify(o, null, 2)}`
|
|
468
436
|
}
|
|
469
437
|
]
|
|
470
438
|
};
|
|
471
439
|
}
|
|
472
|
-
}
|
|
473
|
-
|
|
440
|
+
};
|
|
441
|
+
function O(n) {
|
|
442
|
+
const t = n.trim().toUpperCase();
|
|
443
|
+
let r, s = null;
|
|
444
|
+
if (t.includes("-")) {
|
|
445
|
+
const o = t.split("-");
|
|
446
|
+
if (o.length === 2)
|
|
447
|
+
r = t, s = null;
|
|
448
|
+
else if (o.length === 3)
|
|
449
|
+
r = `${o[0]}-${o[1]}`, s = t;
|
|
450
|
+
else
|
|
451
|
+
return { productNDC: t, packageNDC: null, isValid: !1 };
|
|
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;
|
|
456
|
+
else
|
|
457
|
+
return { productNDC: t, packageNDC: null, isValid: !1 };
|
|
458
|
+
const e = /^\d{5}-\d{4}$/.test(r);
|
|
459
|
+
return { productNDC: r, packageNDC: s, isValid: e };
|
|
460
|
+
}
|
|
461
|
+
const I = {
|
|
474
462
|
name: "get-drug-by-ndc",
|
|
475
463
|
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.",
|
|
476
|
-
inputSchema:
|
|
477
|
-
ndcCode:
|
|
464
|
+
inputSchema: d.object({
|
|
465
|
+
ndcCode: d.string().describe(
|
|
478
466
|
"National Drug Code (NDC) - accepts formats: XXXXX-XXXX, XXXXX-XXXX-XX, or without dashes"
|
|
479
467
|
)
|
|
480
468
|
}),
|
|
481
|
-
|
|
482
|
-
const { productNDC: t, packageNDC: r, isValid: s } =
|
|
469
|
+
async handler({ ndcCode: n }) {
|
|
470
|
+
const { productNDC: t, packageNDC: r, isValid: s } = O(n);
|
|
483
471
|
if (!s)
|
|
484
472
|
return {
|
|
485
473
|
content: [
|
|
@@ -497,7 +485,7 @@ b.registerTool({
|
|
|
497
485
|
};
|
|
498
486
|
let e = `openfda.product_ndc:"${t}"`;
|
|
499
487
|
r && (e += `+OR+openfda.package_ndc:"${r}"`);
|
|
500
|
-
const
|
|
488
|
+
const o = new g().dataset("drug").context("label").search(e).limit(10).build(), { data: a, error: p } = await h(o);
|
|
501
489
|
if (p)
|
|
502
490
|
return {
|
|
503
491
|
content: [
|
|
@@ -522,31 +510,28 @@ b.registerTool({
|
|
|
522
510
|
}
|
|
523
511
|
]
|
|
524
512
|
};
|
|
525
|
-
const
|
|
526
|
-
var
|
|
527
|
-
const f = ((
|
|
513
|
+
const i = a.results.map((u) => {
|
|
514
|
+
var b, $;
|
|
515
|
+
const f = ((b = u.openfda.product_ndc) == null ? void 0 : b.filter((_) => _ === t)) || [], m = (($ = u.openfda.package_ndc) == null ? void 0 : $.filter(
|
|
528
516
|
(_) => r ? _ === r : _.startsWith(t)
|
|
529
517
|
)) || [];
|
|
530
518
|
return {
|
|
531
|
-
// Basic drug information
|
|
532
519
|
brand_name: u.openfda.brand_name || [],
|
|
533
520
|
generic_name: u.openfda.generic_name || [],
|
|
534
521
|
manufacturer_name: u.openfda.manufacturer_name || [],
|
|
535
522
|
product_type: u.openfda.product_type || [],
|
|
536
523
|
route: u.openfda.route || [],
|
|
537
524
|
substance_name: u.openfda.substance_name || [],
|
|
538
|
-
// NDC information
|
|
539
525
|
matching_product_ndc: f,
|
|
540
526
|
matching_package_ndc: m,
|
|
541
527
|
all_product_ndc: u.openfda.product_ndc || [],
|
|
542
528
|
all_package_ndc: u.openfda.package_ndc || [],
|
|
543
|
-
// Additional product details
|
|
544
529
|
dosage_and_administration: u.dosage_and_administration || [],
|
|
545
530
|
package_label_principal_display_panel: u.package_label_principal_display_panel || [],
|
|
546
531
|
active_ingredient: u.active_ingredient || [],
|
|
547
532
|
purpose: u.purpose || []
|
|
548
533
|
};
|
|
549
|
-
}),
|
|
534
|
+
}), l = i.reduce(
|
|
550
535
|
(u, f) => u + f.matching_package_ndc.length,
|
|
551
536
|
0
|
|
552
537
|
), c = r ? `Searched for specific package NDC: ${r}` : `Searched for product NDC: ${t} (all packages)`;
|
|
@@ -554,23 +539,22 @@ b.registerTool({
|
|
|
554
539
|
content: [
|
|
555
540
|
{
|
|
556
541
|
type: "text",
|
|
557
|
-
text: `✅ Found ${
|
|
542
|
+
text: `✅ Found ${i.length} drug(s) with ${l} package(s) for NDC "${n}"
|
|
558
543
|
|
|
559
544
|
${c}
|
|
560
545
|
|
|
561
|
-
${JSON.stringify(
|
|
546
|
+
${JSON.stringify(i, null, 2)}`
|
|
562
547
|
}
|
|
563
548
|
]
|
|
564
549
|
};
|
|
565
550
|
}
|
|
566
|
-
}
|
|
567
|
-
b.registerTool({
|
|
551
|
+
}, M = {
|
|
568
552
|
name: "get-drug-by-product-ndc",
|
|
569
553
|
description: "Get drug information by product NDC only (XXXXX-XXXX format). This ignores package variations and finds all packages for a product.",
|
|
570
|
-
inputSchema:
|
|
571
|
-
productNDC:
|
|
554
|
+
inputSchema: d.object({
|
|
555
|
+
productNDC: d.string().describe("Product NDC in format XXXXX-XXXX")
|
|
572
556
|
}),
|
|
573
|
-
|
|
557
|
+
async handler({ productNDC: n }) {
|
|
574
558
|
var p;
|
|
575
559
|
if (!/^\d{5}-\d{4}$/.test(n.trim()))
|
|
576
560
|
return {
|
|
@@ -584,7 +568,7 @@ b.registerTool({
|
|
|
584
568
|
],
|
|
585
569
|
isError: !0
|
|
586
570
|
};
|
|
587
|
-
const t = new g().dataset("drug").context("label").search(`openfda.product_ndc:"${n.trim()}"`).limit(1).build(), { data: r, error: s } = await
|
|
571
|
+
const t = new g().dataset("drug").context("label").search(`openfda.product_ndc:"${n.trim()}"`).limit(1).build(), { data: r, error: s } = await h(t);
|
|
588
572
|
if (s)
|
|
589
573
|
return {
|
|
590
574
|
content: [
|
|
@@ -605,11 +589,11 @@ b.registerTool({
|
|
|
605
589
|
],
|
|
606
590
|
structuredContent: null
|
|
607
591
|
};
|
|
608
|
-
const e = r.results[0],
|
|
609
|
-
(
|
|
592
|
+
const e = r.results[0], o = ((p = e.openfda.package_ndc) == null ? void 0 : p.filter(
|
|
593
|
+
(i) => i.startsWith(n.trim())
|
|
610
594
|
)) || [], a = {
|
|
611
595
|
product_ndc: n,
|
|
612
|
-
available_packages:
|
|
596
|
+
available_packages: o,
|
|
613
597
|
brand_name: e.openfda.brand_name || [],
|
|
614
598
|
generic_name: e.openfda.generic_name || [],
|
|
615
599
|
manufacturer_name: e.openfda.manufacturer_name || [],
|
|
@@ -624,18 +608,102 @@ b.registerTool({
|
|
|
624
608
|
content: [
|
|
625
609
|
{
|
|
626
610
|
type: "text",
|
|
627
|
-
text: `✅ Product NDC "${n}" found with ${
|
|
611
|
+
text: `✅ Product NDC "${n}" found with ${o.length} package variation(s):
|
|
628
612
|
|
|
629
613
|
${JSON.stringify(a, null, 2)}`
|
|
630
614
|
}
|
|
631
615
|
]
|
|
632
616
|
};
|
|
633
617
|
}
|
|
634
|
-
}
|
|
635
|
-
|
|
618
|
+
}, R = {
|
|
619
|
+
name: "get-drugsfda",
|
|
620
|
+
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
|
+
inputSchema: d.object({
|
|
622
|
+
sectionName: d.string().describe(
|
|
623
|
+
"Section within drugsfda. Valid values: application, openfda, products, submissions, application_docs"
|
|
624
|
+
),
|
|
625
|
+
fieldName: d.string().describe(
|
|
626
|
+
"Field name within the selected section. application: application_number. openfda: application_number, brand_name, generic_name, manufacturer_name, nui, package_ndc, pharm_class_cs, pharm_class_epc, pharm_class_pe, pharm_class_moa, product_ndc, route, rxcui, spl_id, spl_set_id, substance_name, unii. products: active_ingredients.name, active_ingredients.strength, dosage_form, marketing_status, product_number, reference_drug, reference_standard, route, te_code. submissions: application_docs, review_priority, submission_class_code, submission_class_code_description, submission_number, submission_property_type.code, submission_public_notes, submission_status, submission_status_date, submission_type. application_docs: applications_doc_id, applications_doc_date, application_docs_title, applications_doc_type, applications_doc_url"
|
|
627
|
+
),
|
|
628
|
+
searchValue: d.string().describe("Value to search for in the specified field")
|
|
629
|
+
}),
|
|
630
|
+
async handler({
|
|
631
|
+
sectionName: n,
|
|
632
|
+
fieldName: t,
|
|
633
|
+
searchValue: r
|
|
634
|
+
}) {
|
|
635
|
+
const s = new g().dataset("drug").context("drugsfda").search(`${n}.${t}:"${r}"`).limit(1).build(), { data: e, error: o } = await h(s);
|
|
636
|
+
if (o) {
|
|
637
|
+
let a = `${s} Failed to retrieve drugsfda data for "${r}" in ${n}.${t}: ${o.message}`;
|
|
638
|
+
switch (o.type) {
|
|
639
|
+
case "http":
|
|
640
|
+
o.status === 404 ? a += `
|
|
641
|
+
|
|
642
|
+
Suggestions:
|
|
643
|
+
- Verify the field name is correct for the section
|
|
644
|
+
- Check the search value spelling` : (o.status === 401 || o.status === 403) && (a += `
|
|
645
|
+
|
|
646
|
+
Please check the API key configuration.`);
|
|
647
|
+
break;
|
|
648
|
+
case "network":
|
|
649
|
+
a += `
|
|
650
|
+
|
|
651
|
+
Please check your internet connection and try again.`;
|
|
652
|
+
break;
|
|
653
|
+
case "timeout":
|
|
654
|
+
a += `
|
|
655
|
+
|
|
656
|
+
The request took too long. Please try again.`;
|
|
657
|
+
break;
|
|
658
|
+
}
|
|
659
|
+
return {
|
|
660
|
+
content: [{ type: "text", text: a }],
|
|
661
|
+
isError: !0
|
|
662
|
+
};
|
|
663
|
+
}
|
|
664
|
+
return !(e != null && e.results) || e.results.length === 0 ? {
|
|
665
|
+
content: [
|
|
666
|
+
{
|
|
667
|
+
type: "text",
|
|
668
|
+
text: `No drugsfda data found for "${r}" in ${n}.${t}. Please verify the search parameters.`
|
|
669
|
+
}
|
|
670
|
+
]
|
|
671
|
+
} : {
|
|
672
|
+
content: [
|
|
673
|
+
{
|
|
674
|
+
type: "text",
|
|
675
|
+
text: `drugsfda data retrieved successfully:
|
|
676
|
+
|
|
677
|
+
${JSON.stringify(e.results[0], null, 2)}`
|
|
678
|
+
}
|
|
679
|
+
]
|
|
680
|
+
};
|
|
681
|
+
}
|
|
682
|
+
}, X = new N(
|
|
683
|
+
{
|
|
684
|
+
name: "openfda",
|
|
685
|
+
version: "1.0.0",
|
|
686
|
+
description: "OpenFDA Model Context Protocol"
|
|
687
|
+
},
|
|
688
|
+
{
|
|
689
|
+
capabilities: {
|
|
690
|
+
resources: {},
|
|
691
|
+
tools: {}
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
), y = new T(X);
|
|
695
|
+
y.registerTool(S);
|
|
696
|
+
y.registerTool(E);
|
|
697
|
+
y.registerTool(P);
|
|
698
|
+
y.registerTool(A);
|
|
699
|
+
y.registerTool(U);
|
|
700
|
+
y.registerTool(I);
|
|
701
|
+
y.registerTool(M);
|
|
702
|
+
y.registerTool(R);
|
|
703
|
+
async function j() {
|
|
636
704
|
const n = new C();
|
|
637
705
|
await X.connect(n), console.error("OpenFDA MCP Server running on stdio");
|
|
638
706
|
}
|
|
639
|
-
|
|
707
|
+
j().catch((n) => {
|
|
640
708
|
console.error("Fatal error in main():", n), process.exit(1);
|
|
641
709
|
});
|