@cyanheads/openfda-mcp-server 0.1.6

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 (54) hide show
  1. package/CLAUDE.md +248 -0
  2. package/Dockerfile +99 -0
  3. package/LICENSE +190 -0
  4. package/README.md +281 -0
  5. package/dist/config/server-config.d.ts +14 -0
  6. package/dist/config/server-config.d.ts.map +1 -0
  7. package/dist/config/server-config.js +22 -0
  8. package/dist/config/server-config.js.map +1 -0
  9. package/dist/index.d.ts +7 -0
  10. package/dist/index.d.ts.map +1 -0
  11. package/dist/index.js +15 -0
  12. package/dist/index.js.map +1 -0
  13. package/dist/mcp-server/tools/definitions/count.tool.d.ts +42 -0
  14. package/dist/mcp-server/tools/definitions/count.tool.d.ts.map +1 -0
  15. package/dist/mcp-server/tools/definitions/count.tool.js +106 -0
  16. package/dist/mcp-server/tools/definitions/count.tool.js.map +1 -0
  17. package/dist/mcp-server/tools/definitions/get-drug-label.tool.d.ts +21 -0
  18. package/dist/mcp-server/tools/definitions/get-drug-label.tool.d.ts.map +1 -0
  19. package/dist/mcp-server/tools/definitions/get-drug-label.tool.js +102 -0
  20. package/dist/mcp-server/tools/definitions/get-drug-label.tool.js.map +1 -0
  21. package/dist/mcp-server/tools/definitions/index.d.ts +68 -0
  22. package/dist/mcp-server/tools/definitions/index.d.ts.map +1 -0
  23. package/dist/mcp-server/tools/definitions/index.js +21 -0
  24. package/dist/mcp-server/tools/definitions/index.js.map +1 -0
  25. package/dist/mcp-server/tools/definitions/lookup-ndc.tool.d.ts +21 -0
  26. package/dist/mcp-server/tools/definitions/lookup-ndc.tool.d.ts.map +1 -0
  27. package/dist/mcp-server/tools/definitions/lookup-ndc.tool.js +101 -0
  28. package/dist/mcp-server/tools/definitions/lookup-ndc.tool.js.map +1 -0
  29. package/dist/mcp-server/tools/definitions/search-adverse-events.tool.d.ts +26 -0
  30. package/dist/mcp-server/tools/definitions/search-adverse-events.tool.d.ts.map +1 -0
  31. package/dist/mcp-server/tools/definitions/search-adverse-events.tool.js +147 -0
  32. package/dist/mcp-server/tools/definitions/search-adverse-events.tool.js.map +1 -0
  33. package/dist/mcp-server/tools/definitions/search-device-clearances.tool.d.ts +25 -0
  34. package/dist/mcp-server/tools/definitions/search-device-clearances.tool.d.ts.map +1 -0
  35. package/dist/mcp-server/tools/definitions/search-device-clearances.tool.js +108 -0
  36. package/dist/mcp-server/tools/definitions/search-device-clearances.tool.js.map +1 -0
  37. package/dist/mcp-server/tools/definitions/search-drug-approvals.tool.d.ts +23 -0
  38. package/dist/mcp-server/tools/definitions/search-drug-approvals.tool.d.ts.map +1 -0
  39. package/dist/mcp-server/tools/definitions/search-drug-approvals.tool.js +136 -0
  40. package/dist/mcp-server/tools/definitions/search-drug-approvals.tool.js.map +1 -0
  41. package/dist/mcp-server/tools/definitions/search-recalls.tool.d.ts +30 -0
  42. package/dist/mcp-server/tools/definitions/search-recalls.tool.d.ts.map +1 -0
  43. package/dist/mcp-server/tools/definitions/search-recalls.tool.js +105 -0
  44. package/dist/mcp-server/tools/definitions/search-recalls.tool.js.map +1 -0
  45. package/dist/services/openfda/openfda-service.d.ts +25 -0
  46. package/dist/services/openfda/openfda-service.d.ts.map +1 -0
  47. package/dist/services/openfda/openfda-service.js +111 -0
  48. package/dist/services/openfda/openfda-service.js.map +1 -0
  49. package/dist/services/openfda/types.d.ts +30 -0
  50. package/dist/services/openfda/types.d.ts.map +1 -0
  51. package/dist/services/openfda/types.js +6 -0
  52. package/dist/services/openfda/types.js.map +1 -0
  53. package/package.json +88 -0
  54. package/server.json +98 -0
@@ -0,0 +1,68 @@
1
+ /**
2
+ * @fileoverview Barrel export for all openFDA tool definitions.
3
+ * @module mcp-server/tools/definitions
4
+ */
5
+ export declare const allToolDefinitions: (import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
6
+ endpoint: import("zod").ZodEnum<{
7
+ "drug/event": "drug/event";
8
+ "drug/label": "drug/label";
9
+ "drug/enforcement": "drug/enforcement";
10
+ "drug/ndc": "drug/ndc";
11
+ "drug/drugsfda": "drug/drugsfda";
12
+ "drug/shortages": "drug/shortages";
13
+ "food/event": "food/event";
14
+ "food/enforcement": "food/enforcement";
15
+ "device/event": "device/event";
16
+ "device/510k": "device/510k";
17
+ "device/pma": "device/pma";
18
+ "device/recall": "device/recall";
19
+ "device/enforcement": "device/enforcement";
20
+ "device/classification": "device/classification";
21
+ "device/registrationlisting": "device/registrationlisting";
22
+ "device/udi": "device/udi";
23
+ "device/covid19serology": "device/covid19serology";
24
+ "animalandveterinary/event": "animalandveterinary/event";
25
+ "other/substance": "other/substance";
26
+ }>;
27
+ count: import("zod").ZodString;
28
+ search: import("zod").ZodOptional<import("zod").ZodString>;
29
+ limit: import("zod").ZodDefault<import("zod").ZodNumber>;
30
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
31
+ meta: import("zod").ZodObject<{
32
+ lastUpdated: import("zod").ZodString;
33
+ }, import("zod/v4/core").$strip>;
34
+ results: import("zod").ZodArray<import("zod").ZodObject<{
35
+ term: import("zod").ZodString;
36
+ count: import("zod").ZodNumber;
37
+ }, import("zod/v4/core").$strip>>;
38
+ message: import("zod").ZodOptional<import("zod").ZodString>;
39
+ }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
40
+ search: import("zod").ZodString;
41
+ sort: import("zod").ZodOptional<import("zod").ZodString>;
42
+ limit: import("zod").ZodDefault<import("zod").ZodNumber>;
43
+ skip: import("zod").ZodDefault<import("zod").ZodNumber>;
44
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
45
+ meta: import("zod").ZodObject<{
46
+ total: import("zod").ZodNumber;
47
+ skip: import("zod").ZodNumber;
48
+ limit: import("zod").ZodNumber;
49
+ lastUpdated: import("zod").ZodString;
50
+ }, import("zod/v4/core").$strip>;
51
+ results: import("zod").ZodArray<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
52
+ message: import("zod").ZodOptional<import("zod").ZodString>;
53
+ }, import("zod/v4/core").$strip>> | import("@cyanheads/mcp-ts-core").ToolDefinition<import("zod").ZodObject<{
54
+ search: import("zod").ZodOptional<import("zod").ZodString>;
55
+ sort: import("zod").ZodOptional<import("zod").ZodString>;
56
+ limit: import("zod").ZodDefault<import("zod").ZodNumber>;
57
+ skip: import("zod").ZodDefault<import("zod").ZodNumber>;
58
+ }, import("zod/v4/core").$strip>, import("zod").ZodObject<{
59
+ meta: import("zod").ZodObject<{
60
+ total: import("zod").ZodNumber;
61
+ skip: import("zod").ZodNumber;
62
+ limit: import("zod").ZodNumber;
63
+ lastUpdated: import("zod").ZodString;
64
+ }, import("zod/v4/core").$strip>;
65
+ results: import("zod").ZodArray<import("zod").ZodRecord<import("zod").ZodString, import("zod").ZodAny>>;
66
+ message: import("zod").ZodOptional<import("zod").ZodString>;
67
+ }, import("zod/v4/core").$strip>>)[];
68
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAUH,eAAO,MAAM,kBAAkB;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;oCAQ9B,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Barrel export for all openFDA tool definitions.
3
+ * @module mcp-server/tools/definitions
4
+ */
5
+ import { countTool } from './count.tool.js';
6
+ import { getDrugLabelTool } from './get-drug-label.tool.js';
7
+ import { lookupNdcTool } from './lookup-ndc.tool.js';
8
+ import { searchAdverseEventsTool } from './search-adverse-events.tool.js';
9
+ import { searchDeviceClearancesTool } from './search-device-clearances.tool.js';
10
+ import { searchDrugApprovalsTool } from './search-drug-approvals.tool.js';
11
+ import { searchRecallsTool } from './search-recalls.tool.js';
12
+ export const allToolDefinitions = [
13
+ searchAdverseEventsTool,
14
+ searchRecallsTool,
15
+ countTool,
16
+ getDrugLabelTool,
17
+ searchDrugApprovalsTool,
18
+ searchDeviceClearancesTool,
19
+ lookupNdcTool,
20
+ ];
21
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/index.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,iBAAiB,CAAC;AAC5C,OAAO,EAAE,gBAAgB,EAAE,MAAM,0BAA0B,CAAC;AAC5D,OAAO,EAAE,aAAa,EAAE,MAAM,sBAAsB,CAAC;AACrD,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,0BAA0B,EAAE,MAAM,oCAAoC,CAAC;AAChF,OAAO,EAAE,uBAAuB,EAAE,MAAM,iCAAiC,CAAC;AAC1E,OAAO,EAAE,iBAAiB,EAAE,MAAM,0BAA0B,CAAC;AAE7D,MAAM,CAAC,MAAM,kBAAkB,GAAG;IAChC,uBAAuB;IACvB,iBAAiB;IACjB,SAAS;IACT,gBAAgB;IAChB,uBAAuB;IACvB,0BAA0B;IAC1B,aAAa;CACd,CAAC"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview MCP tool for looking up drugs in the openFDA NDC (National Drug Code) Directory.
3
+ * @module mcp-server/tools/definitions/lookup-ndc
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ export declare const lookupNdcTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
7
+ search: z.ZodString;
8
+ sort: z.ZodOptional<z.ZodString>;
9
+ limit: z.ZodDefault<z.ZodNumber>;
10
+ skip: z.ZodDefault<z.ZodNumber>;
11
+ }, z.core.$strip>, z.ZodObject<{
12
+ meta: z.ZodObject<{
13
+ total: z.ZodNumber;
14
+ skip: z.ZodNumber;
15
+ limit: z.ZodNumber;
16
+ lastUpdated: z.ZodString;
17
+ }, z.core.$strip>;
18
+ results: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodAny>>;
19
+ message: z.ZodOptional<z.ZodString>;
20
+ }, z.core.$strip>>;
21
+ //# sourceMappingURL=lookup-ndc.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-ndc.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/lookup-ndc.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGjD,eAAO,MAAM,aAAa;;;;;;;;;;;;;;kBA8GxB,CAAC"}
@@ -0,0 +1,101 @@
1
+ /**
2
+ * @fileoverview MCP tool for looking up drugs in the openFDA NDC (National Drug Code) Directory.
3
+ * @module mcp-server/tools/definitions/lookup-ndc
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { getOpenFdaService } from '../../../services/openfda/openfda-service.js';
7
+ export const lookupNdcTool = tool('openfda_lookup_ndc', {
8
+ description: 'Look up drugs in the NDC (National Drug Code) Directory. Identify drug products by NDC code, find active ingredients, packaging details, or manufacturer info.',
9
+ annotations: { readOnlyHint: true },
10
+ input: z.object({
11
+ search: z
12
+ .string()
13
+ .describe('openFDA search query. Examples: product_ndc:"0363-0218", brand_name:"aspirin", generic_name:"metformin", openfda.manufacturer_name:"walgreen", active_ingredients.name:"ASPIRIN"'),
14
+ sort: z
15
+ .string()
16
+ .optional()
17
+ .describe('Sort field and direction. Example: listing_expiration_date:desc'),
18
+ limit: z
19
+ .number()
20
+ .min(1)
21
+ .max(1000)
22
+ .default(10)
23
+ .describe('Maximum number of records to return (1-1000, default 10)'),
24
+ skip: z
25
+ .number()
26
+ .min(0)
27
+ .max(25000)
28
+ .default(0)
29
+ .describe('Number of records to skip for pagination (0-25000, default 0)'),
30
+ }),
31
+ output: z.object({
32
+ meta: z
33
+ .object({
34
+ total: z.number().describe('Total matching records'),
35
+ skip: z.number().describe('Pagination offset'),
36
+ limit: z.number().describe('Records returned'),
37
+ lastUpdated: z.string().describe('Dataset last updated date'),
38
+ })
39
+ .describe('Response metadata'),
40
+ results: z
41
+ .array(z.record(z.string(), z.any()))
42
+ .describe('NDC directory records with product and packaging details'),
43
+ message: z
44
+ .string()
45
+ .optional()
46
+ .describe('Guidance when results are empty or search can be refined'),
47
+ }),
48
+ async handler(input, ctx) {
49
+ const service = getOpenFdaService();
50
+ const response = await service.query('drug/ndc', input, ctx);
51
+ ctx.log.info('NDC lookup completed', {
52
+ search: input.search,
53
+ total: response.meta.total,
54
+ returned: response.results.length,
55
+ });
56
+ return {
57
+ meta: response.meta,
58
+ results: response.results,
59
+ message: response.results.length === 0
60
+ ? 'No NDC records matched the query. Try broadening the search — use brand_name, generic_name, or active_ingredients.name fields.'
61
+ : undefined,
62
+ };
63
+ },
64
+ format: (result) => {
65
+ if (result.results.length === 0) {
66
+ return [{ type: 'text', text: result.message ?? 'No NDC records found.' }];
67
+ }
68
+ const lines = [
69
+ `**${result.meta.total.toLocaleString()} total results** (showing ${result.results.length}, skip: ${result.meta.skip}) | Data updated: ${result.meta.lastUpdated}\n`,
70
+ ];
71
+ for (const r of result.results) {
72
+ const title = r.brand_name ?? r.generic_name ?? r.product_ndc ?? 'Unknown';
73
+ lines.push(`### ${title}`);
74
+ lines.push(`**NDC:** ${r.product_ndc ?? 'N/A'} | **Labeler:** ${r.labeler_name ?? 'N/A'}`);
75
+ if (r.generic_name && r.brand_name)
76
+ lines.push(`**Generic:** ${r.generic_name}`);
77
+ if (r.dosage_form)
78
+ lines.push(`**Form:** ${r.dosage_form}${r.route ? ` | **Route:** ${(Array.isArray(r.route) ? r.route : [r.route]).join(', ')}` : ''}`);
79
+ if (r.marketing_category)
80
+ lines.push(`**Category:** ${r.marketing_category}`);
81
+ const ingredients = r.active_ingredients ?? [];
82
+ if (ingredients.length > 0) {
83
+ lines.push(`**Active ingredients:** ${ingredients.map((i) => `${i.name}${i.strength ? ` (${i.strength})` : ''}`).join(', ')}`);
84
+ }
85
+ const packaging = r.packaging ?? [];
86
+ if (packaging.length > 0) {
87
+ lines.push(`**Packaging:**`);
88
+ for (const p of packaging.slice(0, 5)) {
89
+ lines.push(`- ${p.package_ndc ?? ''}: ${p.description ?? 'N/A'}`);
90
+ }
91
+ if (packaging.length > 5)
92
+ lines.push(`- ... and ${packaging.length - 5} more`);
93
+ }
94
+ if (r.listing_expiration_date)
95
+ lines.push(`**Listing expires:** ${r.listing_expiration_date}`);
96
+ lines.push('');
97
+ }
98
+ return [{ type: 'text', text: lines.join('\n') }];
99
+ },
100
+ });
101
+ //# sourceMappingURL=lookup-ndc.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"lookup-ndc.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/lookup-ndc.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,26 @@
1
+ /**
2
+ * @fileoverview Tool definition for searching openFDA adverse event reports across drugs, food, and devices.
3
+ * @module mcp-server/tools/definitions/search-adverse-events
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ export declare const searchAdverseEventsTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
7
+ category: z.ZodEnum<{
8
+ drug: "drug";
9
+ food: "food";
10
+ device: "device";
11
+ }>;
12
+ search: z.ZodOptional<z.ZodString>;
13
+ sort: z.ZodOptional<z.ZodString>;
14
+ limit: z.ZodDefault<z.ZodNumber>;
15
+ skip: z.ZodDefault<z.ZodNumber>;
16
+ }, z.core.$strip>, z.ZodObject<{
17
+ meta: z.ZodObject<{
18
+ total: z.ZodNumber;
19
+ skip: z.ZodNumber;
20
+ limit: z.ZodNumber;
21
+ lastUpdated: z.ZodString;
22
+ }, z.core.$strip>;
23
+ results: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodAny>>;
24
+ message: z.ZodOptional<z.ZodString>;
25
+ }, z.core.$strip>>;
26
+ //# sourceMappingURL=search-adverse-events.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-adverse-events.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-adverse-events.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGjD,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;;;;;;kBAuKlC,CAAC"}
@@ -0,0 +1,147 @@
1
+ /**
2
+ * @fileoverview Tool definition for searching openFDA adverse event reports across drugs, food, and devices.
3
+ * @module mcp-server/tools/definitions/search-adverse-events
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { getOpenFdaService } from '../../../services/openfda/openfda-service.js';
7
+ export const searchAdverseEventsTool = tool('openfda_search_adverse_events', {
8
+ description: 'Search adverse event reports across drugs, food, and devices. Use to investigate safety signals, find reports for a specific product, or explore reactions by demographics.',
9
+ annotations: { readOnlyHint: true },
10
+ input: z.object({
11
+ category: z
12
+ .enum(['drug', 'food', 'device'])
13
+ .describe('Product category — each has different field schemas in the response'),
14
+ search: z
15
+ .string()
16
+ .optional()
17
+ .describe('Elasticsearch query string. Examples: patient.drug.medicinalproduct:"aspirin", patient.reaction.reactionmeddrapt:"nausea" AND serious:"1". Omit to browse recent.'),
18
+ sort: z.string().optional().describe('Sort field and direction. Example: receivedate:desc'),
19
+ limit: z
20
+ .number()
21
+ .min(1)
22
+ .max(1000)
23
+ .default(10)
24
+ .describe('Maximum number of records to return (1-1000, default 10)'),
25
+ skip: z
26
+ .number()
27
+ .min(0)
28
+ .max(25000)
29
+ .default(0)
30
+ .describe('Number of records to skip for pagination (0-25000, default 0)'),
31
+ }),
32
+ output: z.object({
33
+ meta: z
34
+ .object({
35
+ total: z.number().describe('Total matching records in the database'),
36
+ skip: z.number().describe('Pagination offset'),
37
+ limit: z.number().describe('Records returned in this response'),
38
+ lastUpdated: z.string().describe('Dataset last updated date'),
39
+ })
40
+ .describe('Response metadata'),
41
+ results: z
42
+ .array(z.record(z.string(), z.any()))
43
+ .describe('Adverse event records — fields vary by category (drug: patient/reactions/drugs, device: device details/event type, food: products/outcomes)'),
44
+ message: z
45
+ .string()
46
+ .optional()
47
+ .describe('Guidance when results are empty or search can be refined'),
48
+ }),
49
+ async handler(input, ctx) {
50
+ const svc = getOpenFdaService();
51
+ const endpoint = `${input.category}/event`;
52
+ const response = await svc.query(endpoint, {
53
+ search: input.search,
54
+ sort: input.sort,
55
+ limit: input.limit,
56
+ skip: input.skip,
57
+ }, ctx);
58
+ ctx.log.info('Adverse event search completed', {
59
+ category: input.category,
60
+ total: response.meta.total,
61
+ returned: response.results.length,
62
+ });
63
+ if (response.results.length === 0) {
64
+ return {
65
+ ...response,
66
+ message: `No adverse event reports matched${input.search ? ` search: ${input.search}` : ''} in ${input.category}/event. Try broadening filters, checking field names (use openfda.brand_name for product searches), or removing date constraints.`,
67
+ };
68
+ }
69
+ return response;
70
+ },
71
+ format: (result) => {
72
+ if (result.results.length === 0) {
73
+ return [{ type: 'text', text: result.message ?? 'No results found.' }];
74
+ }
75
+ const lines = [
76
+ `**${result.meta.total.toLocaleString()} total results** (showing ${result.results.length}, skip: ${result.meta.skip}) | Data updated: ${result.meta.lastUpdated}\n`,
77
+ ];
78
+ for (const r of result.results) {
79
+ // Drug adverse events
80
+ if (r.patient) {
81
+ const patient = r.patient;
82
+ const reactions = (patient.reaction ?? [])
83
+ .map((rx) => rx.reactionmeddrapt)
84
+ .filter(Boolean)
85
+ .join(', ');
86
+ const drugs = (patient.drug ?? [])
87
+ .map((d) => {
88
+ const char = d.drugcharacterization === '1'
89
+ ? 'Suspect'
90
+ : d.drugcharacterization === '2'
91
+ ? 'Concomitant'
92
+ : d.drugcharacterization === '3'
93
+ ? 'Interacting'
94
+ : '';
95
+ return `${d.medicinalproduct ?? 'Unknown'}${char ? ` (${char})` : ''}`;
96
+ })
97
+ .join(', ');
98
+ lines.push(`### Report ${r.safetyreportid ?? 'N/A'}`);
99
+ lines.push(`**Date:** ${r.receivedate ?? 'N/A'} | **Serious:** ${r.serious === '1' ? 'Yes' : r.serious === '2' ? 'No' : (r.serious ?? 'N/A')}`);
100
+ if (patient.patientsex)
101
+ lines.push(`**Patient:** Sex ${patient.patientsex === '1' ? 'Male' : patient.patientsex === '2' ? 'Female' : patient.patientsex}`);
102
+ if (reactions)
103
+ lines.push(`**Reactions:** ${reactions}`);
104
+ if (drugs)
105
+ lines.push(`**Drugs:** ${drugs}`);
106
+ }
107
+ // Device adverse events
108
+ else if (r.device) {
109
+ lines.push(`### Report ${r.report_number ?? r.mdr_report_key ?? 'N/A'}`);
110
+ if (r.event_type)
111
+ lines.push(`**Event type:** ${r.event_type}`);
112
+ for (const d of Array.isArray(r.device) ? r.device : []) {
113
+ lines.push(`**Device:** ${d.brand_name ?? d.generic_name ?? 'Unknown'}${d.manufacturer_d_name ? ` by ${d.manufacturer_d_name}` : ''}`);
114
+ }
115
+ const texts = (r.mdr_text ?? [])
116
+ .slice(0, 2)
117
+ .map((t) => t.text)
118
+ .filter(Boolean);
119
+ if (texts.length) {
120
+ const narrative = texts.join(' ');
121
+ lines.push(`**Narrative:** ${narrative.length > 500 ? `${narrative.slice(0, 500)}...` : narrative}`);
122
+ }
123
+ }
124
+ // Food adverse events
125
+ else if (r.products || r.reactions) {
126
+ lines.push(`### Report ${r.report_number ?? 'N/A'}`);
127
+ if (r.reactions)
128
+ lines.push(`**Reactions:** ${(Array.isArray(r.reactions) ? r.reactions : [r.reactions]).join(', ')}`);
129
+ if (r.outcomes)
130
+ lines.push(`**Outcomes:** ${(Array.isArray(r.outcomes) ? r.outcomes : [r.outcomes]).join(', ')}`);
131
+ const products = (r.products ?? [])
132
+ .map((p) => p.name_brand ?? p.industry_name ?? 'Unknown')
133
+ .join(', ');
134
+ if (products)
135
+ lines.push(`**Products:** ${products}`);
136
+ }
137
+ // Fallback
138
+ else {
139
+ lines.push(`### Record`);
140
+ lines.push(`\`\`\`json\n${JSON.stringify(r, null, 2).slice(0, 500)}\n\`\`\``);
141
+ }
142
+ lines.push('');
143
+ }
144
+ return [{ type: 'text', text: lines.join('\n') }];
145
+ },
146
+ });
147
+ //# sourceMappingURL=search-adverse-events.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-adverse-events.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-adverse-events.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,25 @@
1
+ /**
2
+ * @fileoverview Tool for searching FDA device premarket notifications (510(k) clearances and PMA approvals).
3
+ * @module mcp-server/tools/definitions/search-device-clearances
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ export declare const searchDeviceClearancesTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
7
+ pathway: z.ZodEnum<{
8
+ "510k": "510k";
9
+ pma: "pma";
10
+ }>;
11
+ search: z.ZodOptional<z.ZodString>;
12
+ sort: z.ZodOptional<z.ZodString>;
13
+ limit: z.ZodDefault<z.ZodNumber>;
14
+ skip: z.ZodDefault<z.ZodNumber>;
15
+ }, z.core.$strip>, z.ZodObject<{
16
+ meta: z.ZodObject<{
17
+ total: z.ZodNumber;
18
+ skip: z.ZodNumber;
19
+ limit: z.ZodNumber;
20
+ lastUpdated: z.ZodString;
21
+ }, z.core.$strip>;
22
+ results: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodAny>>;
23
+ message: z.ZodOptional<z.ZodString>;
24
+ }, z.core.$strip>>;
25
+ //# sourceMappingURL=search-device-clearances.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-device-clearances.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-device-clearances.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGjD,eAAO,MAAM,0BAA0B;;;;;;;;;;;;;;;;;;kBA4HrC,CAAC"}
@@ -0,0 +1,108 @@
1
+ /**
2
+ * @fileoverview Tool for searching FDA device premarket notifications (510(k) clearances and PMA approvals).
3
+ * @module mcp-server/tools/definitions/search-device-clearances
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { getOpenFdaService } from '../../../services/openfda/openfda-service.js';
7
+ export const searchDeviceClearancesTool = tool('openfda_search_device_clearances', {
8
+ description: 'Search FDA device premarket notifications — 510(k) clearances and PMA approvals.',
9
+ annotations: { readOnlyHint: true },
10
+ input: z.object({
11
+ pathway: z
12
+ .enum(['510k', 'pma'])
13
+ .describe('Premarket pathway. 510(k) is most common (174K+ records). PMA is for higher-risk devices.'),
14
+ search: z
15
+ .string()
16
+ .optional()
17
+ .describe('openFDA search query. Examples: applicant:"medtronic", advisory_committee_description:"cardiovascular", product_code:"DXN", openfda.device_name:"catheter". Omit to browse recent.'),
18
+ sort: z.string().optional().describe('Sort expression. Example: decision_date:desc.'),
19
+ limit: z
20
+ .number()
21
+ .min(1)
22
+ .max(1000)
23
+ .default(10)
24
+ .describe('Maximum number of records to return (1-1000).'),
25
+ skip: z.number().min(0).max(25000).default(0).describe('Pagination offset (0-25000).'),
26
+ }),
27
+ output: z.object({
28
+ meta: z
29
+ .object({
30
+ total: z.number().describe('Total matching records'),
31
+ skip: z.number().describe('Pagination offset'),
32
+ limit: z.number().describe('Records returned'),
33
+ lastUpdated: z.string().describe('Dataset last updated date'),
34
+ })
35
+ .describe('Response metadata'),
36
+ results: z
37
+ .array(z.record(z.string(), z.any()))
38
+ .describe('510(k) clearance or PMA approval records'),
39
+ message: z
40
+ .string()
41
+ .optional()
42
+ .describe('Guidance when results are empty or search can be refined'),
43
+ }),
44
+ async handler(input, ctx) {
45
+ const service = getOpenFdaService();
46
+ const response = await service.query(`device/${input.pathway}`, {
47
+ search: input.search,
48
+ sort: input.sort,
49
+ limit: input.limit,
50
+ skip: input.skip,
51
+ }, ctx);
52
+ ctx.log.info('Device clearance search completed', {
53
+ pathway: input.pathway,
54
+ total: response.meta.total,
55
+ returned: response.results.length,
56
+ });
57
+ return {
58
+ meta: response.meta,
59
+ results: response.results,
60
+ message: response.results.length === 0
61
+ ? 'No matching device clearances found. Try broadening the search — use applicant, product_code, advisory_committee_description, or openfda.device_name fields.'
62
+ : undefined,
63
+ };
64
+ },
65
+ format: (result) => {
66
+ if (result.results.length === 0) {
67
+ return [{ type: 'text', text: result.message ?? 'No device clearances found.' }];
68
+ }
69
+ const lines = [
70
+ `**${result.meta.total.toLocaleString()} total results** (showing ${result.results.length}, skip: ${result.meta.skip}) | Data updated: ${result.meta.lastUpdated}\n`,
71
+ ];
72
+ for (const r of result.results) {
73
+ // 510(k)
74
+ if (r.k_number) {
75
+ lines.push(`### ${r.k_number}: ${r.device_name ?? 'Unknown device'}`);
76
+ lines.push(`**Applicant:** ${r.applicant ?? 'N/A'} | **Product code:** ${r.product_code ?? 'N/A'}`);
77
+ lines.push(`**Decision:** ${r.decision_description ?? 'N/A'} (${r.decision_date ?? 'N/A'})`);
78
+ if (r.advisory_committee_description)
79
+ lines.push(`**Advisory committee:** ${r.advisory_committee_description}`);
80
+ if (r.clearance_type)
81
+ lines.push(`**Clearance type:** ${r.clearance_type}`);
82
+ if (r.statement_or_summary) {
83
+ const text = String(r.statement_or_summary);
84
+ lines.push(`**Summary:** ${text.length > 500 ? `${text.slice(0, 500)}...` : text}`);
85
+ }
86
+ }
87
+ // PMA
88
+ else if (r.pma_number) {
89
+ const deviceLabel = r.trade_name ?? r.generic_name ?? '';
90
+ lines.push(`### ${r.pma_number}${deviceLabel ? `: ${deviceLabel}` : ''}`);
91
+ lines.push(`**Applicant:** ${r.applicant ?? 'N/A'} | **Product code:** ${r.product_code ?? 'N/A'}`);
92
+ lines.push(`**Decision:** ${r.decision_description ?? r.decision_code ?? 'N/A'} (${r.decision_date ?? 'N/A'})`);
93
+ if (r.advisory_committee_description)
94
+ lines.push(`**Advisory committee:** ${r.advisory_committee_description}`);
95
+ if (r.supplement_number)
96
+ lines.push(`**Supplement:** ${r.supplement_number}`);
97
+ }
98
+ // Fallback
99
+ else {
100
+ lines.push(`### Record`);
101
+ lines.push(`\`\`\`json\n${JSON.stringify(r, null, 2).slice(0, 500)}\n\`\`\``);
102
+ }
103
+ lines.push('');
104
+ }
105
+ return [{ type: 'text', text: lines.join('\n') }];
106
+ },
107
+ });
108
+ //# sourceMappingURL=search-device-clearances.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-device-clearances.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-device-clearances.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,23 @@
1
+ /**
2
+ * @fileoverview Tool for searching FDA drug application approvals (NDAs and ANDAs)
3
+ * via the Drugs@FDA endpoint.
4
+ * @module mcp-server/tools/definitions/search-drug-approvals
5
+ */
6
+ import { z } from '@cyanheads/mcp-ts-core';
7
+ /** Exported tool definition for searching drug approvals. */
8
+ export declare const searchDrugApprovalsTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
9
+ search: z.ZodOptional<z.ZodString>;
10
+ sort: z.ZodOptional<z.ZodString>;
11
+ limit: z.ZodDefault<z.ZodNumber>;
12
+ skip: z.ZodDefault<z.ZodNumber>;
13
+ }, z.core.$strip>, z.ZodObject<{
14
+ meta: z.ZodObject<{
15
+ total: z.ZodNumber;
16
+ skip: z.ZodNumber;
17
+ limit: z.ZodNumber;
18
+ lastUpdated: z.ZodString;
19
+ }, z.core.$strip>;
20
+ results: z.ZodArray<z.ZodRecord<z.ZodString, z.ZodAny>>;
21
+ message: z.ZodOptional<z.ZodString>;
22
+ }, z.core.$strip>>;
23
+ //# sourceMappingURL=search-drug-approvals.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"search-drug-approvals.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/search-drug-approvals.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAGjD,6DAA6D;AAC7D,eAAO,MAAM,uBAAuB;;;;;;;;;;;;;;kBAoJlC,CAAC"}