@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
package/README.md ADDED
@@ -0,0 +1,281 @@
1
+ <div align="center">
2
+ <h1>@cyanheads/openfda-mcp-server</h1>
3
+ <p><b>Query FDA data on drugs, food, devices, and recalls via openFDA. STDIO or Streamable HTTP.</b>
4
+ <div>7 Tools</div>
5
+ </p>
6
+ </div>
7
+
8
+ <div align="center">
9
+
10
+ [![npm](https://img.shields.io/npm/v/@cyanheads/openfda-mcp-server?style=flat-square&logo=npm&logoColor=white)](https://www.npmjs.com/package/@cyanheads/openfda-mcp-server) [![Version](https://img.shields.io/badge/Version-0.1.6-blue.svg?style=flat-square)](./CHANGELOG.md) [![Framework](https://img.shields.io/badge/Built%20on-@cyanheads/mcp--ts--core-259?style=flat-square)](https://www.npmjs.com/package/@cyanheads/mcp-ts-core) [![MCP SDK](https://img.shields.io/badge/MCP%20SDK-^1.29.0-green.svg?style=flat-square)](https://modelcontextprotocol.io/)
11
+
12
+ [![License](https://img.shields.io/badge/License-Apache%202.0-orange.svg?style=flat-square)](./LICENSE) [![TypeScript](https://img.shields.io/badge/TypeScript-^5.9.3-3178C6.svg?style=flat-square)](https://www.typescriptlang.org/) [![Bun](https://img.shields.io/badge/Bun-v1.3.11-blueviolet.svg?style=flat-square)](https://bun.sh/)
13
+
14
+ </div>
15
+
16
+ ---
17
+
18
+ ## Tools
19
+
20
+ Seven tools for querying FDA data across drugs, food, devices, and recalls:
21
+
22
+ | Tool | Description |
23
+ |:---|:---|
24
+ | `openfda_search_adverse_events` | Search adverse event reports across drugs, food, and devices |
25
+ | `openfda_search_recalls` | Search enforcement reports and recall actions across drugs, food, and devices |
26
+ | `openfda_count` | Aggregate and tally unique values for any field across any openFDA endpoint |
27
+ | `openfda_get_drug_label` | Look up FDA drug labeling (package inserts / SPL documents) |
28
+ | `openfda_search_drug_approvals` | Search the Drugs@FDA database for NDA/ANDA application approvals |
29
+ | `openfda_search_device_clearances` | Search FDA device premarket notifications — 510(k) clearances and PMA approvals |
30
+ | `openfda_lookup_ndc` | Look up drugs in the NDC (National Drug Code) Directory |
31
+
32
+ ### `openfda_search_adverse_events`
33
+
34
+ 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.
35
+
36
+ - Category selection: `drug`, `food`, or `device` — each returns different field schemas
37
+ - Elasticsearch query syntax for filtering by product, reaction, seriousness, date range
38
+ - Pagination via `limit` (up to 1000) and `skip` (up to 25000)
39
+ - Formatted output includes report ID, seriousness, patient demographics, reactions, and drugs with characterization
40
+
41
+ ---
42
+
43
+ ### `openfda_count`
44
+
45
+ Aggregate and tally unique values for any field across any openFDA endpoint. Returns ranked term-count pairs sorted by count descending.
46
+
47
+ - Works across all 19 openFDA endpoints (drugs, food, devices, animal/veterinary, other)
48
+ - Use `.exact` suffix on field names for whole-phrase counting
49
+ - Optional `search` filter to scope the aggregation
50
+ - Returns up to 1000 terms per query
51
+
52
+ ---
53
+
54
+ ### `openfda_search_recalls`
55
+
56
+ Search enforcement reports and recall actions across drugs, food, and devices.
57
+
58
+ - Supports `enforcement` (all categories) and `recall` (devices only) endpoints
59
+ - Filter by classification (Class I/II/III), recalling firm, reason, status
60
+ - Formatted output includes recall number, classification, product description, reason, distribution pattern
61
+
62
+ ---
63
+
64
+ ### `openfda_search_device_clearances`
65
+
66
+ Search FDA device premarket notifications — 510(k) clearances and PMA approvals.
67
+
68
+ - Two pathways: `510k` (174K+ records, most common) and `pma` (higher-risk devices)
69
+ - Filter by applicant, product code, advisory committee, device name
70
+ - Formatted output adapts to pathway: 510(k) shows K-number/clearance type, PMA shows supplement info
71
+
72
+ ---
73
+
74
+ ### `openfda_get_drug_label`
75
+
76
+ Look up FDA drug labeling (package inserts / SPL documents). Check indications, warnings, dosage, contraindications, active ingredients, or any structured label section.
77
+
78
+ - Search by brand name, generic name, manufacturer, or set ID
79
+ - Formatted output includes key label sections: boxed warning, indications, dosage, warnings, contraindications, adverse reactions, drug interactions, active ingredients
80
+ - Large sections are automatically truncated to keep output readable
81
+ - Default limit of 5 — labels are large documents
82
+
83
+ ---
84
+
85
+ ### `openfda_search_drug_approvals`
86
+
87
+ Search the Drugs@FDA database for drug application approvals (NDAs and ANDAs). Returns application details, sponsor info, and full submission history.
88
+
89
+ - Filter by brand name, sponsor, submission type, review priority
90
+ - Formatted output includes products with active ingredients, dosage forms, routes, and marketing status
91
+ - Full submission history with type, status, date, and review priority
92
+ - Pagination via `limit` (up to 1000) and `skip` (up to 25000)
93
+
94
+ ---
95
+
96
+ ### `openfda_lookup_ndc`
97
+
98
+ Look up drugs in the NDC (National Drug Code) Directory. Identify drug products by NDC code, find active ingredients, packaging details, or manufacturer info.
99
+
100
+ - Search by product NDC, brand name, generic name, manufacturer, or active ingredient
101
+ - Returns product details, active ingredients with strengths, and packaging information
102
+ - Sortable by listing expiration date or other fields
103
+
104
+ ## Features
105
+
106
+ Built on [`@cyanheads/mcp-ts-core`](https://www.npmjs.com/package/@cyanheads/mcp-ts-core):
107
+
108
+ - Declarative tool definitions — single file per tool, framework handles registration and validation
109
+ - Unified error handling across all tools
110
+ - Pluggable auth (`none`, `jwt`, `oauth`)
111
+ - Swappable storage backends: `in-memory`, `filesystem`, `Supabase`, `Cloudflare KV/R2/D1`
112
+ - Structured logging with optional OpenTelemetry tracing
113
+ - Runs locally (stdio/HTTP) or on Cloudflare Workers from the same codebase
114
+
115
+ openFDA-specific:
116
+
117
+ - Generic API client for all openFDA endpoints with retry (exponential backoff) and rate-limit awareness
118
+ - Automatic error normalization — 404 returns empty results, 429/5xx retries, 400 provides actionable messages
119
+ - Optional API key support — works without a key (1K requests/day), increases to 120K/day with a free key
120
+
121
+ ## Getting Started
122
+
123
+ ### Via bunx (no install)
124
+
125
+ Add to your MCP client config:
126
+
127
+ ```json
128
+ {
129
+ "mcpServers": {
130
+ "openfda": {
131
+ "type": "stdio",
132
+ "command": "bunx",
133
+ "args": ["@cyanheads/openfda-mcp-server@latest"],
134
+ "env": {
135
+ "MCP_TRANSPORT_TYPE": "stdio",
136
+ "MCP_LOG_LEVEL": "info",
137
+ "OPENFDA_API_KEY": "your-key-here"
138
+ }
139
+ }
140
+ }
141
+ }
142
+ ```
143
+
144
+ Or with npx (no Bun required):
145
+
146
+ ```json
147
+ {
148
+ "mcpServers": {
149
+ "openfda": {
150
+ "type": "stdio",
151
+ "command": "npx",
152
+ "args": ["-y", "@cyanheads/openfda-mcp-server@latest"],
153
+ "env": {
154
+ "MCP_TRANSPORT_TYPE": "stdio",
155
+ "MCP_LOG_LEVEL": "info",
156
+ "OPENFDA_API_KEY": "your-key-here"
157
+ }
158
+ }
159
+ }
160
+ }
161
+ ```
162
+
163
+ Or with Docker:
164
+
165
+ ```json
166
+ {
167
+ "mcpServers": {
168
+ "openfda": {
169
+ "type": "stdio",
170
+ "command": "docker",
171
+ "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/openfda-mcp-server:latest"]
172
+ }
173
+ }
174
+ }
175
+ ```
176
+
177
+ For Streamable HTTP, set the transport and start the server:
178
+
179
+ ```sh
180
+ MCP_TRANSPORT_TYPE=http MCP_HTTP_PORT=3010 bun run start:http
181
+ # Server listens at http://localhost:3010/mcp
182
+ ```
183
+
184
+ ### Prerequisites
185
+
186
+ - [Bun v1.2.0](https://bun.sh/) or higher.
187
+ - Optional: [openFDA API key](https://open.fda.gov/apis/authentication/) for higher rate limits (120K requests/day vs 1K/day).
188
+
189
+ ### Installation
190
+
191
+ 1. **Clone the repository:**
192
+
193
+ ```sh
194
+ git clone https://github.com/cyanheads/openfda-mcp-server.git
195
+ ```
196
+
197
+ 2. **Navigate into the directory:**
198
+
199
+ ```sh
200
+ cd openfda-mcp-server
201
+ ```
202
+
203
+ 3. **Install dependencies:**
204
+
205
+ ```sh
206
+ bun install
207
+ ```
208
+
209
+ ## Configuration
210
+
211
+ All configuration is validated at startup via Zod schemas in `src/config/server-config.ts`. Key environment variables:
212
+
213
+ | Variable | Description | Default |
214
+ |:---|:---|:---|
215
+ | `MCP_TRANSPORT_TYPE` | Transport: `stdio` or `http` | `stdio` |
216
+ | `MCP_HTTP_PORT` | HTTP server port | `3010` |
217
+ | `MCP_AUTH_MODE` | Authentication: `none`, `jwt`, or `oauth` | `none` |
218
+ | `MCP_LOG_LEVEL` | Log level (`debug`, `info`, `warning`, `error`, etc.) | `info` |
219
+ | `LOGS_DIR` | Directory for log files (Node.js only). | `<project-root>/logs` |
220
+ | `STORAGE_PROVIDER_TYPE` | Storage backend: `in-memory`, `filesystem`, `supabase`, `cloudflare-kv/r2/d1` | `in-memory` |
221
+ | `OPENFDA_API_KEY` | Free API key from [open.fda.gov](https://open.fda.gov/apis/authentication/). Increases daily limit from 1K to 120K requests. | none |
222
+ | `OPENFDA_BASE_URL` | Base URL override for testing against a proxy or mock. | `https://api.fda.gov` |
223
+ | `OTEL_ENABLED` | Enable OpenTelemetry | `false` |
224
+
225
+ ## Running the Server
226
+
227
+ ### Local Development
228
+
229
+ - **Build and run the production version:**
230
+
231
+ ```sh
232
+ # One-time build
233
+ bun run rebuild
234
+
235
+ # Run the built server
236
+ bun run start:http
237
+ # or
238
+ bun run start:stdio
239
+ ```
240
+
241
+ - **Dev mode with watch:**
242
+
243
+ ```sh
244
+ bun run dev:stdio # or dev:http
245
+ ```
246
+
247
+ - **Run checks and tests:**
248
+ ```sh
249
+ bun run devcheck # Lints, formats, type-checks, and more
250
+ bun run test # Runs the test suite
251
+ ```
252
+
253
+ ## Project Structure
254
+
255
+ | Directory | Purpose |
256
+ |:---|:---|
257
+ | `src/index.ts` | Entry point — `createApp()` with tool registration and service setup. |
258
+ | `src/config/` | Server-specific env var parsing and validation with Zod. |
259
+ | `src/services/openfda/` | openFDA API client with retry, rate-limit handling, and error normalization. |
260
+ | `src/mcp-server/tools/definitions/` | Tool definitions (`*.tool.ts`). Seven openFDA tools. |
261
+
262
+ ## Development Guide
263
+
264
+ See [`CLAUDE.md`](./CLAUDE.md) for development guidelines and architectural rules. The short version:
265
+
266
+ - Handlers throw, framework catches — no `try/catch` in tool logic
267
+ - Use `ctx.log` for request-scoped logging
268
+ - Register new tools in `src/mcp-server/tools/definitions/index.ts`
269
+
270
+ ## Contributing
271
+
272
+ Issues and pull requests are welcome. Run checks and tests before submitting:
273
+
274
+ ```sh
275
+ bun run devcheck
276
+ bun run test
277
+ ```
278
+
279
+ ## License
280
+
281
+ This project is licensed under the Apache 2.0 License. See the [LICENSE](./LICENSE) file for details.
@@ -0,0 +1,14 @@
1
+ /**
2
+ * @fileoverview Server-specific configuration for the openFDA MCP server.
3
+ * @module config/server-config
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ declare const ServerConfigSchema: z.ZodObject<{
7
+ apiKey: z.ZodOptional<z.ZodString>;
8
+ baseUrl: z.ZodDefault<z.ZodString>;
9
+ }, z.core.$strip>;
10
+ export type ServerConfig = z.infer<typeof ServerConfigSchema>;
11
+ /** Lazy-parsed server config from environment variables. */
12
+ export declare function getServerConfig(): ServerConfig;
13
+ export {};
14
+ //# sourceMappingURL=server-config.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-config.d.ts","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C,QAAA,MAAM,kBAAkB;;;iBAMtB,CAAC;AAEH,MAAM,MAAM,YAAY,GAAG,CAAC,CAAC,KAAK,CAAC,OAAO,kBAAkB,CAAC,CAAC;AAI9D,4DAA4D;AAC5D,wBAAgB,eAAe,IAAI,YAAY,CAM9C"}
@@ -0,0 +1,22 @@
1
+ /**
2
+ * @fileoverview Server-specific configuration for the openFDA MCP server.
3
+ * @module config/server-config
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ const ServerConfigSchema = z.object({
7
+ apiKey: z
8
+ .string()
9
+ .optional()
10
+ .describe('openFDA API key — increases daily request limit from 1K to 120K'),
11
+ baseUrl: z.string().default('https://api.fda.gov').describe('openFDA API base URL'),
12
+ });
13
+ let _config;
14
+ /** Lazy-parsed server config from environment variables. */
15
+ export function getServerConfig() {
16
+ _config ??= ServerConfigSchema.parse({
17
+ apiKey: process.env.OPENFDA_API_KEY,
18
+ baseUrl: process.env.OPENFDA_BASE_URL,
19
+ });
20
+ return _config;
21
+ }
22
+ //# sourceMappingURL=server-config.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"server-config.js","sourceRoot":"","sources":["../../src/config/server-config.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAE,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAE3C,MAAM,kBAAkB,GAAG,CAAC,CAAC,MAAM,CAAC;IAClC,MAAM,EAAE,CAAC;SACN,MAAM,EAAE;SACR,QAAQ,EAAE;SACV,QAAQ,CAAC,iEAAiE,CAAC;IAC9E,OAAO,EAAE,CAAC,CAAC,MAAM,EAAE,CAAC,OAAO,CAAC,qBAAqB,CAAC,CAAC,QAAQ,CAAC,sBAAsB,CAAC;CACpF,CAAC,CAAC;AAIH,IAAI,OAAiC,CAAC;AAEtC,4DAA4D;AAC5D,MAAM,UAAU,eAAe;IAC7B,OAAO,KAAK,kBAAkB,CAAC,KAAK,CAAC;QACnC,MAAM,EAAE,OAAO,CAAC,GAAG,CAAC,eAAe;QACnC,OAAO,EAAE,OAAO,CAAC,GAAG,CAAC,gBAAgB;KACtC,CAAC,CAAC;IACH,OAAO,OAAO,CAAC;AACjB,CAAC"}
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview openfda-mcp-server MCP server entry point.
4
+ * @module index
5
+ */
6
+ export {};
7
+ //# sourceMappingURL=index.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG"}
package/dist/index.js ADDED
@@ -0,0 +1,15 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * @fileoverview openfda-mcp-server MCP server entry point.
4
+ * @module index
5
+ */
6
+ import { createApp } from '@cyanheads/mcp-ts-core';
7
+ import { allToolDefinitions } from './mcp-server/tools/definitions/index.js';
8
+ import { initOpenFdaService } from './services/openfda/openfda-service.js';
9
+ await createApp({
10
+ tools: allToolDefinitions,
11
+ setup() {
12
+ initOpenFdaService();
13
+ },
14
+ });
15
+ //# sourceMappingURL=index.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AACA;;;GAGG;AAEH,OAAO,EAAE,SAAS,EAAE,MAAM,wBAAwB,CAAC;AACnD,OAAO,EAAE,kBAAkB,EAAE,MAAM,yCAAyC,CAAC;AAC7E,OAAO,EAAE,kBAAkB,EAAE,MAAM,uCAAuC,CAAC;AAE3E,MAAM,SAAS,CAAC;IACd,KAAK,EAAE,kBAAkB;IACzB,KAAK;QACH,kBAAkB,EAAE,CAAC;IACvB,CAAC;CACF,CAAC,CAAC"}
@@ -0,0 +1,42 @@
1
+ /**
2
+ * @fileoverview MCP tool for openFDA count/aggregation queries. Tallies unique
3
+ * values for any field across any endpoint, returning ranked term-count pairs.
4
+ * @module mcp-server/tools/definitions/count.tool
5
+ */
6
+ import { z } from '@cyanheads/mcp-ts-core';
7
+ export declare const countTool: import("@cyanheads/mcp-ts-core").ToolDefinition<z.ZodObject<{
8
+ endpoint: z.ZodEnum<{
9
+ "drug/event": "drug/event";
10
+ "drug/label": "drug/label";
11
+ "drug/enforcement": "drug/enforcement";
12
+ "drug/ndc": "drug/ndc";
13
+ "drug/drugsfda": "drug/drugsfda";
14
+ "drug/shortages": "drug/shortages";
15
+ "food/event": "food/event";
16
+ "food/enforcement": "food/enforcement";
17
+ "device/event": "device/event";
18
+ "device/510k": "device/510k";
19
+ "device/pma": "device/pma";
20
+ "device/recall": "device/recall";
21
+ "device/enforcement": "device/enforcement";
22
+ "device/classification": "device/classification";
23
+ "device/registrationlisting": "device/registrationlisting";
24
+ "device/udi": "device/udi";
25
+ "device/covid19serology": "device/covid19serology";
26
+ "animalandveterinary/event": "animalandveterinary/event";
27
+ "other/substance": "other/substance";
28
+ }>;
29
+ count: z.ZodString;
30
+ search: z.ZodOptional<z.ZodString>;
31
+ limit: z.ZodDefault<z.ZodNumber>;
32
+ }, z.core.$strip>, z.ZodObject<{
33
+ meta: z.ZodObject<{
34
+ lastUpdated: z.ZodString;
35
+ }, z.core.$strip>;
36
+ results: z.ZodArray<z.ZodObject<{
37
+ term: z.ZodString;
38
+ count: z.ZodNumber;
39
+ }, z.core.$strip>>;
40
+ message: z.ZodOptional<z.ZodString>;
41
+ }, z.core.$strip>>;
42
+ //# sourceMappingURL=count.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/count.tool.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AA0BjD,eAAO,MAAM,SAAS;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;;kBA+FpB,CAAC"}
@@ -0,0 +1,106 @@
1
+ /**
2
+ * @fileoverview MCP tool for openFDA count/aggregation queries. Tallies unique
3
+ * values for any field across any endpoint, returning ranked term-count pairs.
4
+ * @module mcp-server/tools/definitions/count.tool
5
+ */
6
+ import { tool, z } from '@cyanheads/mcp-ts-core';
7
+ import { getOpenFdaService } from '../../../services/openfda/openfda-service.js';
8
+ /** All valid openFDA endpoint paths. */
9
+ const ENDPOINTS = [
10
+ 'drug/event',
11
+ 'drug/label',
12
+ 'drug/enforcement',
13
+ 'drug/ndc',
14
+ 'drug/drugsfda',
15
+ 'drug/shortages',
16
+ 'food/event',
17
+ 'food/enforcement',
18
+ 'device/event',
19
+ 'device/510k',
20
+ 'device/pma',
21
+ 'device/recall',
22
+ 'device/enforcement',
23
+ 'device/classification',
24
+ 'device/registrationlisting',
25
+ 'device/udi',
26
+ 'device/covid19serology',
27
+ 'animalandveterinary/event',
28
+ 'other/substance',
29
+ ];
30
+ export const countTool = tool('openfda_count', {
31
+ description: 'Aggregate and tally unique values for any field across any openFDA endpoint. Returns ranked term-count pairs sorted by count descending.',
32
+ annotations: { readOnlyHint: true },
33
+ input: z.object({
34
+ endpoint: z
35
+ .enum(ENDPOINTS)
36
+ .describe('Full openFDA endpoint path (e.g. "drug/event", "device/classification")'),
37
+ count: z
38
+ .string()
39
+ .describe('Field to count. Append .exact for whole-phrase counting (e.g. "patient.reaction.reactionmeddrapt.exact", "openfda.brand_name.exact")'),
40
+ search: z
41
+ .string()
42
+ .optional()
43
+ .describe('Filter query to scope the count (e.g. patient.drug.medicinalproduct:"metformin")'),
44
+ limit: z
45
+ .number()
46
+ .min(1)
47
+ .max(1000)
48
+ .default(100)
49
+ .describe('Number of top terms to return (default 100, max 1000)'),
50
+ }),
51
+ output: z.object({
52
+ meta: z
53
+ .object({
54
+ lastUpdated: z.string().describe('Dataset last updated date'),
55
+ })
56
+ .describe('Response metadata'),
57
+ results: z
58
+ .array(z.object({
59
+ term: z.string().describe('Field value'),
60
+ count: z.number().describe('Number of occurrences'),
61
+ }))
62
+ .describe('Term-count pairs sorted by count descending'),
63
+ message: z.string().optional().describe('Guidance when results are empty'),
64
+ }),
65
+ async handler(input, ctx) {
66
+ const svc = getOpenFdaService();
67
+ const response = await svc.query(input.endpoint, {
68
+ search: input.search,
69
+ count: input.count,
70
+ limit: input.limit,
71
+ }, ctx);
72
+ ctx.log.info('Count query completed', {
73
+ endpoint: input.endpoint,
74
+ count: input.count,
75
+ terms: response.results.length,
76
+ });
77
+ const results = response.results.map((r) => ({
78
+ term: String(r.term),
79
+ count: r.count,
80
+ }));
81
+ if (results.length === 0) {
82
+ return {
83
+ meta: { lastUpdated: response.meta.lastUpdated },
84
+ results,
85
+ message: `No count results for ${input.count} on ${input.endpoint}${input.search ? ` with search: ${input.search}` : ''}. Verify the field name exists for this endpoint and check .exact suffix usage.`,
86
+ };
87
+ }
88
+ return { meta: { lastUpdated: response.meta.lastUpdated }, results };
89
+ },
90
+ format: (result) => {
91
+ if (result.results.length === 0) {
92
+ return [{ type: 'text', text: result.message ?? 'No count results.' }];
93
+ }
94
+ const totalCount = result.results.reduce((sum, r) => sum + r.count, 0);
95
+ const lines = [
96
+ `**${result.results.length} terms** (total occurrences: ${totalCount.toLocaleString()}) | Data updated: ${result.meta.lastUpdated}\n`,
97
+ '| # | Term | Count |',
98
+ '|---|------|-------|',
99
+ ];
100
+ for (const [i, r] of result.results.entries()) {
101
+ lines.push(`| ${i + 1} | ${r.term} | ${r.count.toLocaleString()} |`);
102
+ }
103
+ return [{ type: 'text', text: lines.join('\n') }];
104
+ },
105
+ });
106
+ //# sourceMappingURL=count.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"count.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/count.tool.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,21 @@
1
+ /**
2
+ * @fileoverview Tool definition for looking up FDA drug labeling (package inserts / SPL documents).
3
+ * @module mcp-server/tools/definitions/get-drug-label
4
+ */
5
+ import { z } from '@cyanheads/mcp-ts-core';
6
+ export declare const getDrugLabelTool: 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=get-drug-label.tool.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-drug-label.tool.d.ts","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-drug-label.tool.ts"],"names":[],"mappings":"AAAA;;;GAGG;AAEH,OAAO,EAAQ,CAAC,EAAE,MAAM,wBAAwB,CAAC;AAKjD,eAAO,MAAM,gBAAgB;;;;;;;;;;;;;;kBA6G3B,CAAC"}
@@ -0,0 +1,102 @@
1
+ /**
2
+ * @fileoverview Tool definition for looking up FDA drug labeling (package inserts / SPL documents).
3
+ * @module mcp-server/tools/definitions/get-drug-label
4
+ */
5
+ import { tool, z } from '@cyanheads/mcp-ts-core';
6
+ import { getOpenFdaService } from '../../../services/openfda/openfda-service.js';
7
+ const ENDPOINT = 'drug/label';
8
+ export const getDrugLabelTool = tool('openfda_get_drug_label', {
9
+ description: 'Look up FDA drug labeling (package inserts / SPL documents). Check indications, warnings, dosage, contraindications, active ingredients, or any structured label section.',
10
+ annotations: { readOnlyHint: true },
11
+ input: z.object({
12
+ search: z
13
+ .string()
14
+ .describe('Query targeting label fields. Examples: openfda.brand_name:"aspirin", openfda.generic_name:"metformin", openfda.manufacturer_name:"pfizer", set_id:"uuid".'),
15
+ sort: z.string().optional().describe('Sort order for results. Example: effective_time:desc.'),
16
+ limit: z
17
+ .number()
18
+ .min(1)
19
+ .max(1000)
20
+ .default(5)
21
+ .describe('Maximum number of results to return (1-1000). Default 5. Labels are large.'),
22
+ skip: z
23
+ .number()
24
+ .min(0)
25
+ .max(25000)
26
+ .default(0)
27
+ .describe('Number of results to skip for pagination (0-25000). Default 0.'),
28
+ }),
29
+ output: z.object({
30
+ meta: z
31
+ .object({
32
+ total: z.number().describe('Total matching results in the dataset.'),
33
+ skip: z.number().describe('Number of results skipped.'),
34
+ limit: z.number().describe('Maximum results returned per request.'),
35
+ lastUpdated: z.string().describe('Date the dataset was last updated.'),
36
+ })
37
+ .describe('Pagination and freshness metadata.'),
38
+ results: z.array(z.record(z.string(), z.any())).describe('Array of drug label records.'),
39
+ message: z.string().optional().describe('Human-readable note when the result set is empty.'),
40
+ }),
41
+ async handler(input, ctx) {
42
+ const service = getOpenFdaService();
43
+ const response = await service.query(ENDPOINT, {
44
+ search: input.search,
45
+ sort: input.sort,
46
+ limit: input.limit,
47
+ skip: input.skip,
48
+ }, ctx);
49
+ ctx.log.info('Drug label lookup completed', {
50
+ search: input.search,
51
+ total: response.meta.total,
52
+ returned: response.results.length,
53
+ });
54
+ return {
55
+ meta: response.meta,
56
+ results: response.results,
57
+ ...(response.results.length === 0 && {
58
+ message: `No labels matched${input.search ? ` search: ${input.search}` : ''}. Try broader terms or check field names (e.g. openfda.brand_name, openfda.generic_name, openfda.manufacturer_name).`,
59
+ }),
60
+ };
61
+ },
62
+ format: (result) => {
63
+ if (result.results.length === 0) {
64
+ return [{ type: 'text', text: result.message ?? 'No labels found.' }];
65
+ }
66
+ const lines = [
67
+ `**${result.meta.total.toLocaleString()} total labels** (showing ${result.results.length}, skip: ${result.meta.skip}) | Data updated: ${result.meta.lastUpdated}\n`,
68
+ ];
69
+ const sections = [
70
+ ['Boxed Warning', 'boxed_warning'],
71
+ ['Indications & Usage', 'indications_and_usage'],
72
+ ['Dosage & Administration', 'dosage_and_administration'],
73
+ ['Warnings', 'warnings'],
74
+ ['Contraindications', 'contraindications'],
75
+ ['Adverse Reactions', 'adverse_reactions'],
76
+ ['Drug Interactions', 'drug_interactions'],
77
+ ['Active Ingredient', 'active_ingredient'],
78
+ ];
79
+ for (const r of result.results) {
80
+ const openfda = r.openfda ?? {};
81
+ const brandName = (openfda.brand_name ?? [])[0] ?? 'Unknown';
82
+ const genericName = (openfda.generic_name ?? [])[0];
83
+ const manufacturer = (openfda.manufacturer_name ?? [])[0];
84
+ lines.push(`### ${brandName}${genericName ? ` (${genericName})` : ''}`);
85
+ if (manufacturer)
86
+ lines.push(`**Manufacturer:** ${manufacturer}`);
87
+ if (openfda.route)
88
+ lines.push(`**Route:** ${openfda.route.join(', ')}`);
89
+ for (const [label, key] of sections) {
90
+ const val = r[key];
91
+ if (!val)
92
+ continue;
93
+ const text = Array.isArray(val) ? val.join('\n') : String(val);
94
+ const truncated = text.length > 1000 ? `${text.slice(0, 1000)}... (truncated)` : text;
95
+ lines.push(`\n**${label}:**\n${truncated}`);
96
+ }
97
+ lines.push('\n---\n');
98
+ }
99
+ return [{ type: 'text', text: lines.join('\n') }];
100
+ },
101
+ });
102
+ //# sourceMappingURL=get-drug-label.tool.js.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"get-drug-label.tool.js","sourceRoot":"","sources":["../../../../src/mcp-server/tools/definitions/get-drug-label.tool.ts"],"names":[],"mappings":"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"}