openfda-mcp-server 1.0.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 +21 -0
- package/README.md +198 -0
- package/dist/api-client-BGnUXMwX.js +2 -0
- package/dist/api-client-BGnUXMwX.js.map +1 -0
- package/dist/device-handlers-D01LfIJk.js +2 -0
- package/dist/device-handlers-D01LfIJk.js.map +1 -0
- package/dist/drug-handlers-Fz_6NLl5.js +2 -0
- package/dist/drug-handlers-Fz_6NLl5.js.map +1 -0
- package/dist/handlers/device-handlers.d.ts +110 -0
- package/dist/handlers/device-handlers.js +1 -0
- package/dist/handlers/drug-handlers.d.ts +147 -0
- package/dist/handlers/drug-handlers.js +1 -0
- package/dist/index.d.ts +1 -0
- package/dist/index.js +3 -0
- package/dist/index.js.map +1 -0
- package/dist/lib.d.ts +8 -0
- package/dist/lib.js +1 -0
- package/dist/server.d.ts +15 -0
- package/dist/server.js +2 -0
- package/dist/server.js.map +1 -0
- package/dist/tools/index.d.ts +525 -0
- package/dist/tools/index.js +1 -0
- package/dist/tools-Bh3wjIRH.js +2 -0
- package/dist/tools-Bh3wjIRH.js.map +1 -0
- package/dist/types/fda.d.ts +422 -0
- package/dist/types/fda.js +1 -0
- package/dist/utils/api-client.d.ts +38 -0
- package/dist/utils/api-client.js +1 -0
- package/dist/utils/logger.d.ts +14 -0
- package/dist/utils/logger.js +2 -0
- package/dist/utils/logger.js.map +1 -0
- package/package.json +71 -0
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2023 Jordan
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
package/README.md
ADDED
|
@@ -0,0 +1,198 @@
|
|
|
1
|
+
# OpenFDA MCP Server
|
|
2
|
+
|
|
3
|
+
An MCP (Model Context Protocol) server that provides access to U.S. FDA public datasets including drugs, medical devices, adverse events, recalls, and more.
|
|
4
|
+
|
|
5
|
+
## Features
|
|
6
|
+
|
|
7
|
+
- **10 FDA Tools**: Query drug and device databases through a standardized MCP interface
|
|
8
|
+
- **Drug Tools**: Adverse events (FAERS), labels, NDC directory, recalls, Drugs@FDA, shortages
|
|
9
|
+
- **Device Tools**: 510(k) clearances, classifications, adverse events (MDR), recalls
|
|
10
|
+
- **Rate Limit Aware**: Supports authenticated requests for higher rate limits (120k/hour vs 1k/hour)
|
|
11
|
+
- **TypeScript**: Fully typed with Zod schema validation
|
|
12
|
+
|
|
13
|
+
## Installation
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
# Install globally
|
|
17
|
+
npm install -g openfda-mcp-server
|
|
18
|
+
|
|
19
|
+
# Or use with npx
|
|
20
|
+
npx openfda-mcp-server
|
|
21
|
+
```
|
|
22
|
+
|
|
23
|
+
## Configuration
|
|
24
|
+
|
|
25
|
+
### Claude Desktop
|
|
26
|
+
|
|
27
|
+
Add to your Claude Desktop configuration (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS):
|
|
28
|
+
|
|
29
|
+
```json
|
|
30
|
+
{
|
|
31
|
+
"mcpServers": {
|
|
32
|
+
"openfda": {
|
|
33
|
+
"command": "npx",
|
|
34
|
+
"args": ["-y", "openfda-mcp-server"],
|
|
35
|
+
"env": {
|
|
36
|
+
"OPENFDA_API_KEY": "your-api-key-optional"
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
}
|
|
41
|
+
```
|
|
42
|
+
|
|
43
|
+
### Environment Variables
|
|
44
|
+
|
|
45
|
+
| Variable | Description | Default |
|
|
46
|
+
| ----------------- | ---------------------------------- | ------------------------- |
|
|
47
|
+
| `OPENFDA_API_KEY` | FDA API key for higher rate limits | None (uses public limits) |
|
|
48
|
+
|
|
49
|
+
Get a free API key at: https://open.fda.gov/apis/authentication/
|
|
50
|
+
|
|
51
|
+
## Available Tools
|
|
52
|
+
|
|
53
|
+
### Drug Tools
|
|
54
|
+
|
|
55
|
+
| Tool | Description |
|
|
56
|
+
| ---------------------------- | -------------------------------------------- |
|
|
57
|
+
| `search_drug_adverse_events` | Search FAERS for drug safety reports |
|
|
58
|
+
| `search_drug_labels` | Search drug labeling/prescribing information |
|
|
59
|
+
| `search_drug_ndc` | Search the National Drug Code directory |
|
|
60
|
+
| `search_drug_recalls` | Search drug recall enforcement reports |
|
|
61
|
+
| `search_drugs_at_fda` | Search approved drug applications |
|
|
62
|
+
| `search_drug_shortages` | Search current and resolved drug shortages |
|
|
63
|
+
|
|
64
|
+
### Device Tools
|
|
65
|
+
|
|
66
|
+
| Tool | Description |
|
|
67
|
+
| ------------------------------- | ---------------------------------------- |
|
|
68
|
+
| `search_device_510k` | Search 510(k) premarket notifications |
|
|
69
|
+
| `search_device_classifications` | Search device classification database |
|
|
70
|
+
| `search_device_adverse_events` | Search MDR adverse event reports |
|
|
71
|
+
| `search_device_recalls` | Search device recall enforcement reports |
|
|
72
|
+
|
|
73
|
+
## Usage Examples
|
|
74
|
+
|
|
75
|
+
### Search for drug adverse events
|
|
76
|
+
|
|
77
|
+
```
|
|
78
|
+
Find adverse events for aspirin in the last year that were serious
|
|
79
|
+
```
|
|
80
|
+
|
|
81
|
+
### Search for drug recalls
|
|
82
|
+
|
|
83
|
+
```
|
|
84
|
+
Search for Class I drug recalls from Pfizer
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
### Search for device clearances
|
|
88
|
+
|
|
89
|
+
```
|
|
90
|
+
Find 510(k) clearances for cardiac pacemakers
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Search for device adverse events
|
|
94
|
+
|
|
95
|
+
```
|
|
96
|
+
Search device adverse events for insulin pumps that resulted in injury
|
|
97
|
+
```
|
|
98
|
+
|
|
99
|
+
## CLI Usage
|
|
100
|
+
|
|
101
|
+
```bash
|
|
102
|
+
# Start with stdio transport (default)
|
|
103
|
+
openfda-mcp-server
|
|
104
|
+
|
|
105
|
+
# Start with HTTP transport
|
|
106
|
+
openfda-mcp-server --transport http --port 3000
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
## Docker
|
|
110
|
+
|
|
111
|
+
### Quick Start
|
|
112
|
+
|
|
113
|
+
```bash
|
|
114
|
+
# Build and run with docker-compose
|
|
115
|
+
docker-compose up -d
|
|
116
|
+
|
|
117
|
+
# Or build manually
|
|
118
|
+
docker build -t openfda-mcp-server .
|
|
119
|
+
docker run -p 3000:3000 -e OPENFDA_API_KEY=your-key openfda-mcp-server
|
|
120
|
+
```
|
|
121
|
+
|
|
122
|
+
### Docker Compose
|
|
123
|
+
|
|
124
|
+
```yaml
|
|
125
|
+
services:
|
|
126
|
+
openfda-mcp:
|
|
127
|
+
image: openfda-mcp-server:latest
|
|
128
|
+
ports:
|
|
129
|
+
- "3000:3000"
|
|
130
|
+
environment:
|
|
131
|
+
- OPENFDA_API_KEY=${OPENFDA_API_KEY:-}
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
### Environment Variables
|
|
135
|
+
|
|
136
|
+
Pass environment variables to the container:
|
|
137
|
+
|
|
138
|
+
```bash
|
|
139
|
+
docker run -p 3000:3000 \
|
|
140
|
+
-e OPENFDA_API_KEY=your-api-key \
|
|
141
|
+
-e DEBUG=openfda-mcp:* \
|
|
142
|
+
openfda-mcp-server
|
|
143
|
+
```
|
|
144
|
+
|
|
145
|
+
## Development
|
|
146
|
+
|
|
147
|
+
```bash
|
|
148
|
+
# Install dependencies
|
|
149
|
+
pnpm install
|
|
150
|
+
|
|
151
|
+
# Run in development mode
|
|
152
|
+
pnpm dev
|
|
153
|
+
|
|
154
|
+
# Test locally
|
|
155
|
+
pnpm serve:test
|
|
156
|
+
|
|
157
|
+
# Run full validation (format, lint, test, build)
|
|
158
|
+
pnpm validate
|
|
159
|
+
|
|
160
|
+
# Build for production
|
|
161
|
+
pnpm build
|
|
162
|
+
```
|
|
163
|
+
|
|
164
|
+
## Programmatic Usage
|
|
165
|
+
|
|
166
|
+
```typescript
|
|
167
|
+
import { createOpenFDAServer, handleSearchDrugAdverseEvents } from "openfda-mcp-server/lib"
|
|
168
|
+
|
|
169
|
+
// Create a custom server
|
|
170
|
+
const server = createOpenFDAServer({
|
|
171
|
+
name: "my-fda-server",
|
|
172
|
+
version: "1.0.0",
|
|
173
|
+
})
|
|
174
|
+
|
|
175
|
+
// Or use handlers directly
|
|
176
|
+
const result = await handleSearchDrugAdverseEvents({
|
|
177
|
+
drugName: "aspirin",
|
|
178
|
+
serious: true,
|
|
179
|
+
limit: 10,
|
|
180
|
+
})
|
|
181
|
+
```
|
|
182
|
+
|
|
183
|
+
## API Rate Limits
|
|
184
|
+
|
|
185
|
+
| Authentication | Per Minute | Per Hour |
|
|
186
|
+
| --------------- | ---------- | -------- |
|
|
187
|
+
| Without API Key | 40 | 1,000 |
|
|
188
|
+
| With API Key | 240 | 120,000 |
|
|
189
|
+
|
|
190
|
+
## License
|
|
191
|
+
|
|
192
|
+
MIT
|
|
193
|
+
|
|
194
|
+
## Acknowledgments
|
|
195
|
+
|
|
196
|
+
- [OpenFDA](https://open.fda.gov/) - FDA open data API
|
|
197
|
+
- [FastMCP](https://github.com/jordanburke/fastmcp) - MCP server framework
|
|
198
|
+
- Based on [OpenFDA-MCP-Server](https://github.com/Augmented-Nature/OpenFDA-MCP-Server) concept
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{loggers as e}from"./utils/logger.js";function t(e){"@babel/helpers - typeof";return t=typeof Symbol==`function`&&typeof Symbol.iterator==`symbol`?function(e){return typeof e}:function(e){return e&&typeof Symbol==`function`&&e.constructor===Symbol&&e!==Symbol.prototype?`symbol`:typeof e},t(e)}function n(e,n){if(t(e)!=`object`||!e)return e;var r=e[Symbol.toPrimitive];if(r!==void 0){var i=r.call(e,n||`default`);if(t(i)!=`object`)return i;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(n===`string`?String:Number)(e)}function r(e){var r=n(e,`string`);return t(r)==`symbol`?r:r+``}function i(e,t,n){return(t=r(t))in e?Object.defineProperty(e,t,{value:n,enumerable:!0,configurable:!0,writable:!0}):e[t]=n,e}var a=class{constructor(){i(this,`apiKey`,void 0),i(this,`baseUrl`,void 0),this.apiKey=process.env.OPENFDA_API_KEY,this.baseUrl=`https://api.fda.gov`}buildSearchParams(e){let t=new URLSearchParams;e.search&&t.set(`search`,e.search),e.count&&t.set(`count`,e.count);let n=Math.min(e.limit??10,100);return t.set(`limit`,String(n)),e.skip!==void 0&&e.skip>0&&t.set(`skip`,String(e.skip)),this.apiKey&&t.set(`api_key`,this.apiKey),t}async request(t,n){let r=this.buildSearchParams(n),i=`${this.baseUrl}${t}?${r.toString()}`;e.api(`Request: ${i.replace(this.apiKey??``,`[REDACTED]`)}`);try{let t=await fetch(i);if(!t.ok){let e=await t.json().catch(()=>({}));if(t.status===429)throw Error(`Rate limit exceeded. Consider using an API key for higher limits.`);if(t.status===404)return{results:[]};throw t.status===400?Error(`Invalid request: ${e.error?.message??`Bad request parameters`}`):Error(`FDA API error: ${t.status} ${t.statusText}`)}let n=await t.json();return e.api(`Response: ${n.meta?.results?.total??0} total results`),n}catch(e){throw e instanceof Error?e:Error(`Failed to fetch from FDA API: ${String(e)}`)}}getRateLimitInfo(){return{authenticated:!!this.apiKey,rateLimit:this.apiKey?`240/minute, 120,000/hour`:`40/minute, 1,000/hour`}}async searchDrugAdverseEvents(e){return this.request(`/drug/event.json`,e)}async searchDrugLabels(e){return this.request(`/drug/label.json`,e)}async searchDrugNDC(e){return this.request(`/drug/ndc.json`,e)}async searchDrugEnforcement(e){return this.request(`/drug/enforcement.json`,e)}async searchDrugsFDA(e){return this.request(`/drug/drugsfda.json`,e)}async searchDrugShortages(e){return this.request(`/drug/shortages.json`,e)}async searchDevice510K(e){return this.request(`/device/510k.json`,e)}async searchDeviceClassifications(e){return this.request(`/device/classification.json`,e)}async searchDeviceAdverseEvents(e){return this.request(`/device/event.json`,e)}async searchDeviceEnforcement(e){return this.request(`/device/enforcement.json`,e)}};const o=new a;export{o as n,a as t};
|
|
2
|
+
//# sourceMappingURL=api-client-BGnUXMwX.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"api-client-BGnUXMwX.js","names":[],"sources":["../src/utils/api-client.ts"],"sourcesContent":["/**\n * FDA API Client\n * HTTP client for interacting with FDA OpenAPI endpoints\n */\n\nimport type {\n Device510K,\n DeviceAdverseEvent,\n DeviceClassification,\n DeviceEnforcement,\n DrugAdverseEvent,\n DrugEnforcement,\n DrugLabel,\n DrugNDC,\n DrugsFDA,\n DrugShortage,\n FDAResponse,\n SearchParams,\n} from \"../types/fda.js\"\nimport { loggers } from \"./logger.js\"\n\nconst FDA_BASE_URL = \"https://api.fda.gov\"\nconst DEFAULT_LIMIT = 10\nconst MAX_LIMIT = 100\n\ntype FDAEndpoint =\n | \"/drug/event.json\"\n | \"/drug/label.json\"\n | \"/drug/ndc.json\"\n | \"/drug/enforcement.json\"\n | \"/drug/drugsfda.json\"\n | \"/drug/shortages.json\"\n | \"/device/510k.json\"\n | \"/device/classification.json\"\n | \"/device/event.json\"\n | \"/device/enforcement.json\"\n\nexport class FDAAPIClient {\n private apiKey: string | undefined\n private baseUrl: string\n\n constructor() {\n this.apiKey = process.env.OPENFDA_API_KEY\n this.baseUrl = FDA_BASE_URL\n }\n\n /**\n * Build URL search parameters for the FDA API\n */\n private buildSearchParams(params: SearchParams): URLSearchParams {\n const urlParams = new URLSearchParams()\n\n if (params.search) {\n urlParams.set(\"search\", params.search)\n }\n\n if (params.count) {\n urlParams.set(\"count\", params.count)\n }\n\n const limit = Math.min(params.limit ?? DEFAULT_LIMIT, MAX_LIMIT)\n urlParams.set(\"limit\", String(limit))\n\n if (params.skip !== undefined && params.skip > 0) {\n urlParams.set(\"skip\", String(params.skip))\n }\n\n if (this.apiKey) {\n urlParams.set(\"api_key\", this.apiKey)\n }\n\n return urlParams\n }\n\n /**\n * Make a request to an FDA API endpoint\n */\n private async request<T>(endpoint: FDAEndpoint, params: SearchParams): Promise<FDAResponse<T>> {\n const urlParams = this.buildSearchParams(params)\n const url = `${this.baseUrl}${endpoint}?${urlParams.toString()}`\n\n loggers.api(`Request: ${url.replace(this.apiKey ?? \"\", \"[REDACTED]\")}`)\n\n try {\n const response = await fetch(url)\n\n if (!response.ok) {\n const errorData = await response.json().catch(() => ({}))\n\n if (response.status === 429) {\n throw new Error(\"Rate limit exceeded. Consider using an API key for higher limits.\")\n }\n if (response.status === 404) {\n return { results: [] } // No results found\n }\n if (response.status === 400) {\n throw new Error(\n `Invalid request: ${(errorData as { error?: { message?: string } }).error?.message ?? \"Bad request parameters\"}`,\n )\n }\n\n throw new Error(`FDA API error: ${response.status} ${response.statusText}`)\n }\n\n const data = (await response.json()) as FDAResponse<T>\n loggers.api(`Response: ${data.meta?.results?.total ?? 0} total results`)\n return data\n } catch (error) {\n if (error instanceof Error) {\n throw error\n }\n throw new Error(`Failed to fetch from FDA API: ${String(error)}`)\n }\n }\n\n /**\n * Get rate limit info based on authentication status\n */\n getRateLimitInfo(): { authenticated: boolean; rateLimit: string } {\n return {\n authenticated: !!this.apiKey,\n rateLimit: this.apiKey ? \"240/minute, 120,000/hour\" : \"40/minute, 1,000/hour\",\n }\n }\n\n // Drug endpoints\n\n async searchDrugAdverseEvents(params: SearchParams): Promise<FDAResponse<DrugAdverseEvent>> {\n return this.request<DrugAdverseEvent>(\"/drug/event.json\", params)\n }\n\n async searchDrugLabels(params: SearchParams): Promise<FDAResponse<DrugLabel>> {\n return this.request<DrugLabel>(\"/drug/label.json\", params)\n }\n\n async searchDrugNDC(params: SearchParams): Promise<FDAResponse<DrugNDC>> {\n return this.request<DrugNDC>(\"/drug/ndc.json\", params)\n }\n\n async searchDrugEnforcement(params: SearchParams): Promise<FDAResponse<DrugEnforcement>> {\n return this.request<DrugEnforcement>(\"/drug/enforcement.json\", params)\n }\n\n async searchDrugsFDA(params: SearchParams): Promise<FDAResponse<DrugsFDA>> {\n return this.request<DrugsFDA>(\"/drug/drugsfda.json\", params)\n }\n\n async searchDrugShortages(params: SearchParams): Promise<FDAResponse<DrugShortage>> {\n return this.request<DrugShortage>(\"/drug/shortages.json\", params)\n }\n\n // Device endpoints\n\n async searchDevice510K(params: SearchParams): Promise<FDAResponse<Device510K>> {\n return this.request<Device510K>(\"/device/510k.json\", params)\n }\n\n async searchDeviceClassifications(params: SearchParams): Promise<FDAResponse<DeviceClassification>> {\n return this.request<DeviceClassification>(\"/device/classification.json\", params)\n }\n\n async searchDeviceAdverseEvents(params: SearchParams): Promise<FDAResponse<DeviceAdverseEvent>> {\n return this.request<DeviceAdverseEvent>(\"/device/event.json\", params)\n }\n\n async searchDeviceEnforcement(params: SearchParams): Promise<FDAResponse<DeviceEnforcement>> {\n return this.request<DeviceEnforcement>(\"/device/enforcement.json\", params)\n }\n}\n\n// Singleton instance\nexport const fdaAPIClient = new FDAAPIClient()\n"],"mappings":"guBAqCA,IAAa,EAAb,KAA0B,CAIxB,aAAc,QAHN,SAAA,IAAA,GAAA,QACA,UAAA,IAAA,GAAA,CAGN,KAAK,OAAS,QAAQ,IAAI,gBAC1B,KAAK,QAAU,sBAMjB,kBAA0B,EAAuC,CAC/D,IAAM,EAAY,IAAI,gBAElB,EAAO,QACT,EAAU,IAAI,SAAU,EAAO,OAAO,CAGpC,EAAO,OACT,EAAU,IAAI,QAAS,EAAO,MAAM,CAGtC,IAAM,EAAQ,KAAK,IAAI,EAAO,OAAS,GAAe,IAAU,CAWhE,OAVA,EAAU,IAAI,QAAS,OAAO,EAAM,CAAC,CAEjC,EAAO,OAAS,IAAA,IAAa,EAAO,KAAO,GAC7C,EAAU,IAAI,OAAQ,OAAO,EAAO,KAAK,CAAC,CAGxC,KAAK,QACP,EAAU,IAAI,UAAW,KAAK,OAAO,CAGhC,EAMT,MAAc,QAAW,EAAuB,EAA+C,CAC7F,IAAM,EAAY,KAAK,kBAAkB,EAAO,CAC1C,EAAM,GAAG,KAAK,UAAU,EAAS,GAAG,EAAU,UAAU,GAE9D,EAAQ,IAAI,YAAY,EAAI,QAAQ,KAAK,QAAU,GAAI,aAAa,GAAG,CAEvE,GAAI,CACF,IAAM,EAAW,MAAM,MAAM,EAAI,CAEjC,GAAI,CAAC,EAAS,GAAI,CAChB,IAAM,EAAY,MAAM,EAAS,MAAM,CAAC,WAAa,EAAE,EAAE,CAEzD,GAAI,EAAS,SAAW,IACtB,MAAU,MAAM,oEAAoE,CAEtF,GAAI,EAAS,SAAW,IACtB,MAAO,CAAE,QAAS,EAAE,CAAE,CAQxB,MANI,EAAS,SAAW,IACZ,MACR,oBAAqB,EAA+C,OAAO,SAAW,2BACvF,CAGO,MAAM,kBAAkB,EAAS,OAAO,GAAG,EAAS,aAAa,CAG7E,IAAM,EAAQ,MAAM,EAAS,MAAM,CAEnC,OADA,EAAQ,IAAI,aAAa,EAAK,MAAM,SAAS,OAAS,EAAE,gBAAgB,CACjE,QACA,EAAO,CAId,MAHI,aAAiB,MACb,EAEE,MAAM,iCAAiC,OAAO,EAAM,GAAG,EAOrE,kBAAkE,CAChE,MAAO,CACL,cAAe,CAAC,CAAC,KAAK,OACtB,UAAW,KAAK,OAAS,2BAA6B,wBACvD,CAKH,MAAM,wBAAwB,EAA8D,CAC1F,OAAO,KAAK,QAA0B,mBAAoB,EAAO,CAGnE,MAAM,iBAAiB,EAAuD,CAC5E,OAAO,KAAK,QAAmB,mBAAoB,EAAO,CAG5D,MAAM,cAAc,EAAqD,CACvE,OAAO,KAAK,QAAiB,iBAAkB,EAAO,CAGxD,MAAM,sBAAsB,EAA6D,CACvF,OAAO,KAAK,QAAyB,yBAA0B,EAAO,CAGxE,MAAM,eAAe,EAAsD,CACzE,OAAO,KAAK,QAAkB,sBAAuB,EAAO,CAG9D,MAAM,oBAAoB,EAA0D,CAClF,OAAO,KAAK,QAAsB,uBAAwB,EAAO,CAKnE,MAAM,iBAAiB,EAAwD,CAC7E,OAAO,KAAK,QAAoB,oBAAqB,EAAO,CAG9D,MAAM,4BAA4B,EAAkE,CAClG,OAAO,KAAK,QAA8B,8BAA+B,EAAO,CAGlF,MAAM,0BAA0B,EAAgE,CAC9F,OAAO,KAAK,QAA4B,qBAAsB,EAAO,CAGvE,MAAM,wBAAwB,EAA+D,CAC3F,OAAO,KAAK,QAA2B,2BAA4B,EAAO,GAK9E,MAAa,EAAe,IAAI"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{loggers as e}from"./utils/logger.js";import{n as t}from"./api-client-BGnUXMwX.js";function n(e,t,n){if(!(!t&&!n))return`${e}:[${t?.replace(/-/g,``)??`*`}+TO+${n?.replace(/-/g,``)??`*`}]`}function r(e){return e.replace(/[+\-&|!(){}[\]^"~*?:\\]/g,`\\$&`).replace(/\s+/g,`+`)}function i(e){let t=e.filter(e=>e.value!==void 0&&e.value.trim()!==``);return t.length===0?``:t.map(e=>`${e.field}:"${r(e.value)}"`).join(`+AND+`)}function a(e,t=200){if(e)return e.length>t?e.substring(0,t)+`...`:e}function o(e){return{kNumber:e.k_number,deviceName:e.device_name??e.openfda?.device_name,applicant:e.applicant,productCode:e.product_code,clearanceType:e.clearance_type,decisionCode:e.decision_code,decisionDescription:e.decision_description,decisionDate:e.decision_date,dateReceived:e.date_received,city:e.city,state:e.state,country:e.country_code}}async function s(r){e.tools(`searchDevice510K`,r);try{let e={search:[i([{field:`device_name`,value:r.deviceName},{field:`applicant`,value:r.applicant},{field:`product_code`,value:r.productCode},{field:`clearance_type`,value:r.clearanceType}]),n(`decision_date`,r.decisionDateFrom,r.decisionDateTo)].filter(Boolean).join(`+AND+`)||void 0,limit:r.limit,skip:r.skip},a=await t.searchDevice510K(e),s=a.results??[];return{success:!0,data:s.map(o),totalResults:a.meta?.results?.total,displayedResults:s.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function c(e){return{deviceName:e.device_name,deviceClass:e.device_class,definition:a(e.definition),medicalSpecialty:e.medical_specialty,medicalSpecialtyDescription:e.medical_specialty_description,productCode:e.product_code,regulationNumber:e.regulation_number,gmpExempt:e.gmp_exempt_flag===`Y`,implant:e.implant_flag===`Y`,lifeSustaining:e.life_sustain_support_flag===`Y`}}async function l(n){e.tools(`searchDeviceClassifications`,n);try{let e={search:i([{field:`device_name`,value:n.deviceName},{field:`device_class`,value:n.deviceClass},{field:`medical_specialty`,value:n.medicalSpecialty},{field:`product_code`,value:n.productCode},{field:`regulation_number`,value:n.regulationNumber}])||void 0,limit:n.limit,skip:n.skip},r=await t.searchDeviceClassifications(e),a=r.results??[];return{success:!0,data:a.map(c),totalResults:r.meta?.results?.total,displayedResults:a.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function u(e){return{reportNumber:e.report_number,dateOfEvent:e.date_of_event,dateReceived:e.date_received,eventType:e.event_type,adverseEventFlag:e.adverse_event_flag===`Y`,productProblemFlag:e.product_problem_flag===`Y`,devices:e.device?.slice(0,3).map(e=>({brandName:e.brand_name,genericName:e.generic_name,manufacturerName:e.manufacturer_d_name,modelNumber:e.model_number,productCode:e.device_report_product_code,deviceClass:e.openfda?.device_class}))??[],mdrText:e.mdr_text?.slice(0,2).map(e=>({textType:e.text_type_code,text:a(e.text,300)}))??[]}}async function d(r){e.tools(`searchDeviceAdverseEvents`,r);try{let e={search:[i([{field:`device.generic_name`,value:r.deviceName},{field:`device.brand_name`,value:r.brandName},{field:`device.manufacturer_d_name`,value:r.manufacturerName},{field:`event_type`,value:r.eventType}]),n(`date_received`,r.dateFrom,r.dateTo)].filter(Boolean).join(`+AND+`)||void 0,limit:r.limit,skip:r.skip},a=await t.searchDeviceAdverseEvents(e),o=a.results??[];return{success:!0,data:o.map(u),totalResults:a.meta?.results?.total,displayedResults:o.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function f(e){return{recallNumber:e.recall_number,recallingFirm:e.recalling_firm,classification:e.classification,status:e.status,productDescription:a(e.product_description),reasonForRecall:a(e.reason_for_recall),recallInitiationDate:e.recall_initiation_date,distributionPattern:a(e.distribution_pattern),city:e.city,state:e.state,deviceName:e.openfda?.device_name,deviceClass:e.openfda?.device_class}}async function p(r){e.tools(`searchDeviceEnforcement`,r);try{let e={search:[i([{field:`recalling_firm`,value:r.recallingFirm},{field:`product_description`,value:r.productDescription},{field:`classification`,value:r.classification},{field:`status`,value:r.status}]),n(`recall_initiation_date`,r.dateFrom,r.dateTo)].filter(Boolean).join(`+AND+`)||void 0,limit:r.limit,skip:r.skip},a=await t.searchDeviceEnforcement(e),o=a.results??[];return{success:!0,data:o.map(f),totalResults:a.meta?.results?.total,displayedResults:o.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}export{p as i,d as n,l as r,s as t};
|
|
2
|
+
//# sourceMappingURL=device-handlers-D01LfIJk.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-handlers-D01LfIJk.js","names":["searchParams: SearchParams"],"sources":["../src/handlers/device-handlers.ts"],"sourcesContent":["/**\n * Device Handlers\n * Query handlers for FDA device-related endpoints\n */\n\nimport type {\n Device510K,\n DeviceAdverseEvent,\n DeviceClassification,\n DeviceEnforcement,\n FDAToolResponse,\n SearchParams,\n} from \"../types/fda.js\"\nimport { fdaAPIClient } from \"../utils/api-client.js\"\nimport { loggers } from \"../utils/logger.js\"\n\n// Helper to build date range query\nfunction buildDateQuery(field: string, dateFrom?: string, dateTo?: string): string | undefined {\n if (!dateFrom && !dateTo) return undefined\n\n // FDA date format is YYYYMMDD\n const from = dateFrom?.replace(/-/g, \"\") ?? \"*\"\n const to = dateTo?.replace(/-/g, \"\") ?? \"*\"\n\n return `${field}:[${from}+TO+${to}]`\n}\n\n// Helper to escape special characters in search terms\nfunction escapeSearchTerm(term: string): string {\n return term.replace(/[+\\-&|!(){}[\\]^\"~*?:\\\\]/g, \"\\\\$&\").replace(/\\s+/g, \"+\")\n}\n\n// Helper to build a search query from multiple terms\nfunction buildSearchQuery(terms: Array<{ field: string; value?: string }>): string {\n const validTerms = terms.filter((t) => t.value !== undefined && t.value.trim() !== \"\")\n if (validTerms.length === 0) return \"\"\n\n return validTerms.map((t) => `${t.field}:\"${escapeSearchTerm(t.value!)}\"`).join(\"+AND+\")\n}\n\n// Format truncated text\nfunction truncateText(text: string | undefined, maxLength = 200): string | undefined {\n if (!text) return undefined\n return text.length > maxLength ? text.substring(0, maxLength) + \"...\" : text\n}\n\n// Device 510(k) Handler\nexport type Device510KParams = {\n deviceName?: string\n applicant?: string\n productCode?: string\n clearanceType?: string\n decisionDateFrom?: string\n decisionDateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDevice510K = {\n kNumber: string | undefined\n deviceName: string | undefined\n applicant: string | undefined\n productCode: string | undefined\n clearanceType: string | undefined\n decisionCode: string | undefined\n decisionDescription: string | undefined\n decisionDate: string | undefined\n dateReceived: string | undefined\n city: string | undefined\n state: string | undefined\n country: string | undefined\n}\n\nfunction formatDevice510K(device: Device510K): FormattedDevice510K {\n return {\n kNumber: device.k_number,\n deviceName: device.device_name ?? device.openfda?.device_name,\n applicant: device.applicant,\n productCode: device.product_code,\n clearanceType: device.clearance_type,\n decisionCode: device.decision_code,\n decisionDescription: device.decision_description,\n decisionDate: device.decision_date,\n dateReceived: device.date_received,\n city: device.city,\n state: device.state,\n country: device.country_code,\n }\n}\n\nexport async function handleSearchDevice510K(\n params: Device510KParams,\n): Promise<FDAToolResponse<FormattedDevice510K[]>> {\n loggers.tools(\"searchDevice510K\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device_name\", value: params.deviceName },\n { field: \"applicant\", value: params.applicant },\n { field: \"product_code\", value: params.productCode },\n { field: \"clearance_type\", value: params.clearanceType },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"decision_date\", params.decisionDateFrom, params.decisionDateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDevice510K(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDevice510K),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Device Classification Handler\nexport type DeviceClassificationParams = {\n deviceName?: string\n deviceClass?: \"1\" | \"2\" | \"3\"\n medicalSpecialty?: string\n productCode?: string\n regulationNumber?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceClassification = {\n deviceName: string | undefined\n deviceClass: string | undefined\n definition: string | undefined\n medicalSpecialty: string | undefined\n medicalSpecialtyDescription: string | undefined\n productCode: string | undefined\n regulationNumber: string | undefined\n gmpExempt: boolean\n implant: boolean\n lifeSustaining: boolean\n}\n\nfunction formatDeviceClassification(classification: DeviceClassification): FormattedDeviceClassification {\n return {\n deviceName: classification.device_name,\n deviceClass: classification.device_class,\n definition: truncateText(classification.definition),\n medicalSpecialty: classification.medical_specialty,\n medicalSpecialtyDescription: classification.medical_specialty_description,\n productCode: classification.product_code,\n regulationNumber: classification.regulation_number,\n gmpExempt: classification.gmp_exempt_flag === \"Y\",\n implant: classification.implant_flag === \"Y\",\n lifeSustaining: classification.life_sustain_support_flag === \"Y\",\n }\n}\n\nexport async function handleSearchDeviceClassifications(\n params: DeviceClassificationParams,\n): Promise<FDAToolResponse<FormattedDeviceClassification[]>> {\n loggers.tools(\"searchDeviceClassifications\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device_name\", value: params.deviceName },\n { field: \"device_class\", value: params.deviceClass },\n { field: \"medical_specialty\", value: params.medicalSpecialty },\n { field: \"product_code\", value: params.productCode },\n { field: \"regulation_number\", value: params.regulationNumber },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n\n const searchParams: SearchParams = {\n search: searchQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDeviceClassifications(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDeviceClassification),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Device Adverse Events (MDR) Handler\nexport type DeviceAdverseEventsParams = {\n deviceName?: string\n brandName?: string\n manufacturerName?: string\n eventType?: \"Injury\" | \"Malfunction\" | \"Death\" | \"Other\"\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceAdverseEvent = {\n reportNumber: string | undefined\n dateOfEvent: string | undefined\n dateReceived: string | undefined\n eventType: string | undefined\n adverseEventFlag: boolean\n productProblemFlag: boolean\n devices: Array<{\n brandName: string | undefined\n genericName: string | undefined\n manufacturerName: string | undefined\n modelNumber: string | undefined\n productCode: string | undefined\n deviceClass: string | undefined\n }>\n mdrText: Array<{\n textType: string | undefined\n text: string | undefined\n }>\n}\n\nfunction formatDeviceAdverseEvent(event: DeviceAdverseEvent): FormattedDeviceAdverseEvent {\n return {\n reportNumber: event.report_number,\n dateOfEvent: event.date_of_event,\n dateReceived: event.date_received,\n eventType: event.event_type,\n adverseEventFlag: event.adverse_event_flag === \"Y\",\n productProblemFlag: event.product_problem_flag === \"Y\",\n devices:\n event.device?.slice(0, 3).map((d) => ({\n brandName: d.brand_name,\n genericName: d.generic_name,\n manufacturerName: d.manufacturer_d_name,\n modelNumber: d.model_number,\n productCode: d.device_report_product_code,\n deviceClass: d.openfda?.device_class,\n })) ?? [],\n mdrText:\n event.mdr_text?.slice(0, 2).map((t) => ({\n textType: t.text_type_code,\n text: truncateText(t.text, 300),\n })) ?? [],\n }\n}\n\nexport async function handleSearchDeviceAdverseEvents(\n params: DeviceAdverseEventsParams,\n): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>> {\n loggers.tools(\"searchDeviceAdverseEvents\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"device.generic_name\", value: params.deviceName },\n { field: \"device.brand_name\", value: params.brandName },\n { field: \"device.manufacturer_d_name\", value: params.manufacturerName },\n { field: \"event_type\", value: params.eventType },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"date_received\", params.dateFrom, params.dateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDeviceAdverseEvents(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDeviceAdverseEvent),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Device Enforcement (Recalls) Handler\nexport type DeviceEnforcementParams = {\n recallingFirm?: string\n productDescription?: string\n classification?: \"Class I\" | \"Class II\" | \"Class III\"\n status?: \"Ongoing\" | \"Completed\" | \"Terminated\" | \"Pending\"\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDeviceEnforcement = {\n recallNumber: string | undefined\n recallingFirm: string | undefined\n classification: string | undefined\n status: string | undefined\n productDescription: string | undefined\n reasonForRecall: string | undefined\n recallInitiationDate: string | undefined\n distributionPattern: string | undefined\n city: string | undefined\n state: string | undefined\n deviceName: string | undefined\n deviceClass: string | undefined\n}\n\nfunction formatDeviceEnforcement(recall: DeviceEnforcement): FormattedDeviceEnforcement {\n return {\n recallNumber: recall.recall_number,\n recallingFirm: recall.recalling_firm,\n classification: recall.classification,\n status: recall.status,\n productDescription: truncateText(recall.product_description),\n reasonForRecall: truncateText(recall.reason_for_recall),\n recallInitiationDate: recall.recall_initiation_date,\n distributionPattern: truncateText(recall.distribution_pattern),\n city: recall.city,\n state: recall.state,\n deviceName: recall.openfda?.device_name,\n deviceClass: recall.openfda?.device_class,\n }\n}\n\nexport async function handleSearchDeviceEnforcement(\n params: DeviceEnforcementParams,\n): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>> {\n loggers.tools(\"searchDeviceEnforcement\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"recalling_firm\", value: params.recallingFirm },\n { field: \"product_description\", value: params.productDescription },\n { field: \"classification\", value: params.classification },\n { field: \"status\", value: params.status },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"recall_initiation_date\", params.dateFrom, params.dateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDeviceEnforcement(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDeviceEnforcement),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{loggers as e}from"./utils/logger.js";import{n as t}from"./api-client-BGnUXMwX.js";function n(e,t,n){if(!(!t&&!n))return`${e}:[${t?.replace(/-/g,``)??`*`}+TO+${n?.replace(/-/g,``)??`*`}]`}function r(e){return e.replace(/[+\-&|!(){}[\]^"~*?:\\]/g,`\\$&`).replace(/\s+/g,`+`)}function i(e){let t=e.filter(e=>e.value!==void 0&&e.value.trim()!==``);return t.length===0?``:t.map(e=>`${e.field}:"${r(e.value)}"`).join(`+AND+`)}function a(e,t=200){if(e)return e.length>t?e.substring(0,t)+`...`:e}function o(e){return{reportId:e.safetyreportid,receiveDate:e.receivedate,serious:e.serious===`1`,drugs:e.patient?.drug?.slice(0,3).map(e=>({name:e.medicinalproduct??e.activesubstance?.activesubstancename,indication:e.drugindication,route:e.drugadministrationroute}))??[],reactions:e.patient?.reaction?.slice(0,3).map(e=>({reaction:e.reactionmeddrapt,outcome:e.reactionoutcome}))??[],patient:{age:e.patient?.patientonsetage?`${e.patient.patientonsetage} ${e.patient.patientonsetageunit??``}`:void 0,sex:e.patient?.patientsex===`1`?`Male`:e.patient?.patientsex===`2`?`Female`:void 0,weight:e.patient?.patientweight?`${e.patient.patientweight} kg`:void 0}}}async function s(r){e.tools(`searchDrugAdverseEvents`,r);try{let e=[{field:`patient.drug.medicinalproduct`,value:r.drugName},{field:`patient.reaction.reactionmeddrapt`,value:r.reaction},{field:`patient.drug.openfda.manufacturer_name`,value:r.manufacturer}];r.serious!==void 0&&e.push({field:`serious`,value:r.serious?`1`:`2`});let a={search:[i(e),n(`receivedate`,r.dateFrom,r.dateTo)].filter(Boolean).join(`+AND+`)||void 0,limit:r.limit,skip:r.skip},s=await t.searchDrugAdverseEvents(a),c=s.results??[];return{success:!0,data:c.map(o),totalResults:s.meta?.results?.total,displayedResults:c.length,searchParams:a,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function c(e){return{brandName:e.openfda?.brand_name?.[0],genericName:e.openfda?.generic_name?.[0],manufacturer:e.openfda?.manufacturer_name?.[0],activeIngredients:e.openfda?.substance_name?.slice(0,5)??[],indications:a(e.indications_and_usage?.[0]),warnings:a(e.warnings?.[0]),dosageAndAdministration:a(e.dosage_and_administration?.[0]),route:e.openfda?.route?.slice(0,3)??[]}}async function l(n){e.tools(`searchDrugLabels`,n);try{let e={search:i([{field:`openfda.brand_name`,value:n.drugName},{field:`indications_and_usage`,value:n.indication},{field:`openfda.substance_name`,value:n.activeIngredient},{field:`openfda.route`,value:n.route}])||void 0,limit:n.limit,skip:n.skip},r=await t.searchDrugLabels(e),a=r.results??[];return{success:!0,data:a.map(c),totalResults:r.meta?.results?.total,displayedResults:a.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function u(e){return{productNdc:e.product_ndc,brandName:e.brand_name,genericName:e.generic_name,labelerName:e.labeler_name,dosageForm:e.dosage_form,route:e.route??[],activeIngredients:e.active_ingredients?.slice(0,3).map(e=>({name:e.name,strength:e.strength}))??[],marketingStartDate:e.marketing_start_date,productType:e.product_type}}async function d(n){e.tools(`searchDrugNDC`,n);try{let e={search:i([{field:`product_ndc`,value:n.productNdc},{field:`brand_name`,value:n.brandName},{field:`generic_name`,value:n.genericName},{field:`labeler_name`,value:n.labelerName},{field:`dosage_form`,value:n.dosageForm},{field:`route`,value:n.route}])||void 0,limit:n.limit,skip:n.skip},r=await t.searchDrugNDC(e),a=r.results??[];return{success:!0,data:a.map(u),totalResults:r.meta?.results?.total,displayedResults:a.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function f(e){return{recallNumber:e.recall_number,recallingFirm:e.recalling_firm,classification:e.classification,status:e.status,productDescription:a(e.product_description),reasonForRecall:a(e.reason_for_recall),recallInitiationDate:e.recall_initiation_date,distributionPattern:a(e.distribution_pattern),city:e.city,state:e.state}}async function p(r){e.tools(`searchDrugEnforcement`,r);try{let e={search:[i([{field:`recalling_firm`,value:r.recallingFirm},{field:`classification`,value:r.classification},{field:`status`,value:r.status},{field:`state`,value:r.state}]),n(`recall_initiation_date`,r.dateFrom,r.dateTo)].filter(Boolean).join(`+AND+`)||void 0,limit:r.limit,skip:r.skip},a=await t.searchDrugEnforcement(e),o=a.results??[];return{success:!0,data:o.map(f),totalResults:a.meta?.results?.total,displayedResults:o.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function m(e){let t=e.submissions?.[0];return{applicationNumber:e.application_number,sponsorName:e.sponsor_name,products:e.products?.slice(0,3).map(e=>({brandName:e.brand_name,dosageForm:e.dosage_form,route:e.route,marketingStatus:e.marketing_status,activeIngredients:e.active_ingredients?.slice(0,3).map(e=>({name:e.name,strength:e.strength}))??[]}))??[],latestSubmission:t?{type:t.submission_type,status:t.submission_status,statusDate:t.submission_status_date}:void 0}}async function h(n){e.tools(`searchDrugsFDA`,n);try{let e={search:i([{field:`sponsor_name`,value:n.sponsorName},{field:`application_number`,value:n.applicationNumber},{field:`products.brand_name`,value:n.brandName},{field:`products.marketing_status`,value:n.marketingStatus}])||void 0,limit:n.limit,skip:n.skip},r=await t.searchDrugsFDA(e),a=r.results??[];return{success:!0,data:a.map(m),totalResults:r.meta?.results?.total,displayedResults:a.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}function g(e){return{genericName:e.generic_name,proprietaryName:e.proprietary_name,status:e.status,description:a(e.description),initialPostingDate:e.initial_posting_date,resolvedShortageDate:e.resolved_shortage_date}}async function _(n){e.tools(`searchDrugShortages`,n);try{let e={search:i([{field:`generic_name`,value:n.genericName},{field:`status`,value:n.status}])||void 0,limit:n.limit,skip:n.skip},r=await t.searchDrugShortages(e),a=r.results??[];return{success:!0,data:a.map(g),totalResults:r.meta?.results?.total,displayedResults:a.length,searchParams:e,apiUsage:t.getRateLimitInfo()}}catch(e){return{success:!1,error:e instanceof Error?e.message:String(e)}}}export{_ as a,d as i,p as n,h as o,l as r,s as t};
|
|
2
|
+
//# sourceMappingURL=drug-handlers-Fz_6NLl5.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drug-handlers-Fz_6NLl5.js","names":["searchTerms: Array<{ field: string; value?: string }>","searchParams: SearchParams"],"sources":["../src/handlers/drug-handlers.ts"],"sourcesContent":["/**\n * Drug Handlers\n * Query handlers for FDA drug-related endpoints\n */\n\nimport type {\n DrugAdverseEvent,\n DrugEnforcement,\n DrugLabel,\n DrugNDC,\n DrugsFDA,\n DrugShortage,\n FDAToolResponse,\n SearchParams,\n} from \"../types/fda.js\"\nimport { fdaAPIClient } from \"../utils/api-client.js\"\nimport { loggers } from \"../utils/logger.js\"\n\n// Helper to build date range query\nfunction buildDateQuery(field: string, dateFrom?: string, dateTo?: string): string | undefined {\n if (!dateFrom && !dateTo) return undefined\n\n // FDA date format is YYYYMMDD\n const from = dateFrom?.replace(/-/g, \"\") ?? \"*\"\n const to = dateTo?.replace(/-/g, \"\") ?? \"*\"\n\n return `${field}:[${from}+TO+${to}]`\n}\n\n// Helper to escape special characters in search terms\nfunction escapeSearchTerm(term: string): string {\n // Escape special characters that have meaning in FDA's search syntax\n return term.replace(/[+\\-&|!(){}[\\]^\"~*?:\\\\]/g, \"\\\\$&\").replace(/\\s+/g, \"+\")\n}\n\n// Helper to build a search query from multiple terms\nfunction buildSearchQuery(terms: Array<{ field: string; value?: string }>): string {\n const validTerms = terms.filter((t) => t.value !== undefined && t.value.trim() !== \"\")\n if (validTerms.length === 0) return \"\"\n\n return validTerms.map((t) => `${t.field}:\"${escapeSearchTerm(t.value!)}\"`).join(\"+AND+\")\n}\n\n// Format truncated text\nfunction truncateText(text: string | undefined, maxLength = 200): string | undefined {\n if (!text) return undefined\n return text.length > maxLength ? text.substring(0, maxLength) + \"...\" : text\n}\n\n// Drug Adverse Events Handler\nexport type DrugAdverseEventsParams = {\n drugName?: string\n reaction?: string\n manufacturer?: string\n serious?: boolean\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDrugAdverseEvent = {\n reportId: string | undefined\n receiveDate: string | undefined\n serious: boolean\n drugs: Array<{\n name: string | undefined\n indication: string | undefined\n route: string | undefined\n }>\n reactions: Array<{\n reaction: string | undefined\n outcome: string | undefined\n }>\n patient: {\n age: string | undefined\n sex: string | undefined\n weight: string | undefined\n }\n}\n\nfunction formatDrugAdverseEvent(event: DrugAdverseEvent): FormattedDrugAdverseEvent {\n return {\n reportId: event.safetyreportid,\n receiveDate: event.receivedate,\n serious: event.serious === \"1\",\n drugs:\n event.patient?.drug?.slice(0, 3).map((d) => ({\n name: d.medicinalproduct ?? d.activesubstance?.activesubstancename,\n indication: d.drugindication,\n route: d.drugadministrationroute,\n })) ?? [],\n reactions:\n event.patient?.reaction?.slice(0, 3).map((r) => ({\n reaction: r.reactionmeddrapt,\n outcome: r.reactionoutcome,\n })) ?? [],\n patient: {\n age: event.patient?.patientonsetage\n ? `${event.patient.patientonsetage} ${event.patient.patientonsetageunit ?? \"\"}`\n : undefined,\n sex: event.patient?.patientsex === \"1\" ? \"Male\" : event.patient?.patientsex === \"2\" ? \"Female\" : undefined,\n weight: event.patient?.patientweight ? `${event.patient.patientweight} kg` : undefined,\n },\n }\n}\n\nexport async function handleSearchDrugAdverseEvents(\n params: DrugAdverseEventsParams,\n): Promise<FDAToolResponse<FormattedDrugAdverseEvent[]>> {\n loggers.tools(\"searchDrugAdverseEvents\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"patient.drug.medicinalproduct\", value: params.drugName },\n { field: \"patient.reaction.reactionmeddrapt\", value: params.reaction },\n { field: \"patient.drug.openfda.manufacturer_name\", value: params.manufacturer },\n ]\n\n if (params.serious !== undefined) {\n searchTerms.push({ field: \"serious\", value: params.serious ? \"1\" : \"2\" })\n }\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"receivedate\", params.dateFrom, params.dateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDrugAdverseEvents(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDrugAdverseEvent),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Drug Labels Handler\nexport type DrugLabelsParams = {\n drugName?: string\n indication?: string\n activeIngredient?: string\n route?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDrugLabel = {\n brandName: string | undefined\n genericName: string | undefined\n manufacturer: string | undefined\n activeIngredients: string[]\n indications: string | undefined\n warnings: string | undefined\n dosageAndAdministration: string | undefined\n route: string[]\n}\n\nfunction formatDrugLabel(label: DrugLabel): FormattedDrugLabel {\n return {\n brandName: label.openfda?.brand_name?.[0],\n genericName: label.openfda?.generic_name?.[0],\n manufacturer: label.openfda?.manufacturer_name?.[0],\n activeIngredients: label.openfda?.substance_name?.slice(0, 5) ?? [],\n indications: truncateText(label.indications_and_usage?.[0]),\n warnings: truncateText(label.warnings?.[0]),\n dosageAndAdministration: truncateText(label.dosage_and_administration?.[0]),\n route: label.openfda?.route?.slice(0, 3) ?? [],\n }\n}\n\nexport async function handleSearchDrugLabels(params: DrugLabelsParams): Promise<FDAToolResponse<FormattedDrugLabel[]>> {\n loggers.tools(\"searchDrugLabels\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"openfda.brand_name\", value: params.drugName },\n { field: \"indications_and_usage\", value: params.indication },\n { field: \"openfda.substance_name\", value: params.activeIngredient },\n { field: \"openfda.route\", value: params.route },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n\n const searchParams: SearchParams = {\n search: searchQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDrugLabels(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDrugLabel),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Drug NDC Handler\nexport type DrugNDCParams = {\n productNdc?: string\n brandName?: string\n genericName?: string\n labelerName?: string\n dosageForm?: string\n route?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDrugNDC = {\n productNdc: string | undefined\n brandName: string | undefined\n genericName: string | undefined\n labelerName: string | undefined\n dosageForm: string | undefined\n route: string[]\n activeIngredients: Array<{ name: string | undefined; strength: string | undefined }>\n marketingStartDate: string | undefined\n productType: string | undefined\n}\n\nfunction formatDrugNDC(ndc: DrugNDC): FormattedDrugNDC {\n return {\n productNdc: ndc.product_ndc,\n brandName: ndc.brand_name,\n genericName: ndc.generic_name,\n labelerName: ndc.labeler_name,\n dosageForm: ndc.dosage_form,\n route: ndc.route ?? [],\n activeIngredients:\n ndc.active_ingredients?.slice(0, 3).map((ai) => ({\n name: ai.name,\n strength: ai.strength,\n })) ?? [],\n marketingStartDate: ndc.marketing_start_date,\n productType: ndc.product_type,\n }\n}\n\nexport async function handleSearchDrugNDC(params: DrugNDCParams): Promise<FDAToolResponse<FormattedDrugNDC[]>> {\n loggers.tools(\"searchDrugNDC\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"product_ndc\", value: params.productNdc },\n { field: \"brand_name\", value: params.brandName },\n { field: \"generic_name\", value: params.genericName },\n { field: \"labeler_name\", value: params.labelerName },\n { field: \"dosage_form\", value: params.dosageForm },\n { field: \"route\", value: params.route },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n\n const searchParams: SearchParams = {\n search: searchQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDrugNDC(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDrugNDC),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Drug Enforcement (Recalls) Handler\nexport type DrugEnforcementParams = {\n recallingFirm?: string\n classification?: \"Class I\" | \"Class II\" | \"Class III\"\n status?: \"Ongoing\" | \"Completed\" | \"Terminated\" | \"Pending\"\n state?: string\n dateFrom?: string\n dateTo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDrugEnforcement = {\n recallNumber: string | undefined\n recallingFirm: string | undefined\n classification: string | undefined\n status: string | undefined\n productDescription: string | undefined\n reasonForRecall: string | undefined\n recallInitiationDate: string | undefined\n distributionPattern: string | undefined\n city: string | undefined\n state: string | undefined\n}\n\nfunction formatDrugEnforcement(recall: DrugEnforcement): FormattedDrugEnforcement {\n return {\n recallNumber: recall.recall_number,\n recallingFirm: recall.recalling_firm,\n classification: recall.classification,\n status: recall.status,\n productDescription: truncateText(recall.product_description),\n reasonForRecall: truncateText(recall.reason_for_recall),\n recallInitiationDate: recall.recall_initiation_date,\n distributionPattern: truncateText(recall.distribution_pattern),\n city: recall.city,\n state: recall.state,\n }\n}\n\nexport async function handleSearchDrugEnforcement(\n params: DrugEnforcementParams,\n): Promise<FDAToolResponse<FormattedDrugEnforcement[]>> {\n loggers.tools(\"searchDrugEnforcement\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"recalling_firm\", value: params.recallingFirm },\n { field: \"classification\", value: params.classification },\n { field: \"status\", value: params.status },\n { field: \"state\", value: params.state },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n const dateQuery = buildDateQuery(\"recall_initiation_date\", params.dateFrom, params.dateTo)\n const fullQuery = [searchQuery, dateQuery].filter(Boolean).join(\"+AND+\")\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDrugEnforcement(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDrugEnforcement),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Drugs@FDA Handler\nexport type DrugsFDAParams = {\n sponsorName?: string\n applicationNumber?: string\n brandName?: string\n marketingStatus?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedDrugsFDA = {\n applicationNumber: string | undefined\n sponsorName: string | undefined\n products: Array<{\n brandName: string | undefined\n dosageForm: string | undefined\n route: string | undefined\n marketingStatus: string | undefined\n activeIngredients: Array<{ name: string | undefined; strength: string | undefined }>\n }>\n latestSubmission:\n | {\n type: string | undefined\n status: string | undefined\n statusDate: string | undefined\n }\n | undefined\n}\n\nfunction formatDrugsFDA(drug: DrugsFDA): FormattedDrugsFDA {\n const latestSubmission = drug.submissions?.[0]\n\n return {\n applicationNumber: drug.application_number,\n sponsorName: drug.sponsor_name,\n products:\n drug.products?.slice(0, 3).map((p) => ({\n brandName: p.brand_name,\n dosageForm: p.dosage_form,\n route: p.route,\n marketingStatus: p.marketing_status,\n activeIngredients:\n p.active_ingredients?.slice(0, 3).map((ai) => ({\n name: ai.name,\n strength: ai.strength,\n })) ?? [],\n })) ?? [],\n latestSubmission: latestSubmission\n ? {\n type: latestSubmission.submission_type,\n status: latestSubmission.submission_status,\n statusDate: latestSubmission.submission_status_date,\n }\n : undefined,\n }\n}\n\nexport async function handleSearchDrugsFDA(params: DrugsFDAParams): Promise<FDAToolResponse<FormattedDrugsFDA[]>> {\n loggers.tools(\"searchDrugsFDA\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"sponsor_name\", value: params.sponsorName },\n { field: \"application_number\", value: params.applicationNumber },\n { field: \"products.brand_name\", value: params.brandName },\n { field: \"products.marketing_status\", value: params.marketingStatus },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n\n const searchParams: SearchParams = {\n search: searchQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDrugsFDA(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDrugsFDA),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n\n// Drug Shortages Handler\nexport type DrugShortagesParams = {\n genericName?: string\n status?: \"Current\" | \"Resolved\"\n limit?: number\n skip?: number\n}\n\ntype FormattedDrugShortage = {\n genericName: string | undefined\n proprietaryName: string | undefined\n status: string | undefined\n description: string | undefined\n initialPostingDate: string | undefined\n resolvedShortageDate: string | undefined\n}\n\nfunction formatDrugShortage(shortage: DrugShortage): FormattedDrugShortage {\n return {\n genericName: shortage.generic_name,\n proprietaryName: shortage.proprietary_name,\n status: shortage.status,\n description: truncateText(shortage.description),\n initialPostingDate: shortage.initial_posting_date,\n resolvedShortageDate: shortage.resolved_shortage_date,\n }\n}\n\nexport async function handleSearchDrugShortages(\n params: DrugShortagesParams,\n): Promise<FDAToolResponse<FormattedDrugShortage[]>> {\n loggers.tools(\"searchDrugShortages\", params)\n\n try {\n const searchTerms: Array<{ field: string; value?: string }> = [\n { field: \"generic_name\", value: params.genericName },\n { field: \"status\", value: params.status },\n ]\n\n const searchQuery = buildSearchQuery(searchTerms)\n\n const searchParams: SearchParams = {\n search: searchQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n const response = await fdaAPIClient.searchDrugShortages(searchParams)\n const results = response.results ?? []\n\n return {\n success: true,\n data: results.map(formatDrugShortage),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n } catch (error) {\n return {\n success: false,\n error: error instanceof Error ? error.message : String(error),\n }\n }\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,110 @@
|
|
|
1
|
+
import { FDAToolResponse } from "../types/fda.js";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/device-handlers.d.ts
|
|
4
|
+
|
|
5
|
+
type Device510KParams = {
|
|
6
|
+
deviceName?: string;
|
|
7
|
+
applicant?: string;
|
|
8
|
+
productCode?: string;
|
|
9
|
+
clearanceType?: string;
|
|
10
|
+
decisionDateFrom?: string;
|
|
11
|
+
decisionDateTo?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
skip?: number;
|
|
14
|
+
};
|
|
15
|
+
type FormattedDevice510K = {
|
|
16
|
+
kNumber: string | undefined;
|
|
17
|
+
deviceName: string | undefined;
|
|
18
|
+
applicant: string | undefined;
|
|
19
|
+
productCode: string | undefined;
|
|
20
|
+
clearanceType: string | undefined;
|
|
21
|
+
decisionCode: string | undefined;
|
|
22
|
+
decisionDescription: string | undefined;
|
|
23
|
+
decisionDate: string | undefined;
|
|
24
|
+
dateReceived: string | undefined;
|
|
25
|
+
city: string | undefined;
|
|
26
|
+
state: string | undefined;
|
|
27
|
+
country: string | undefined;
|
|
28
|
+
};
|
|
29
|
+
declare function handleSearchDevice510K(params: Device510KParams): Promise<FDAToolResponse<FormattedDevice510K[]>>;
|
|
30
|
+
type DeviceClassificationParams = {
|
|
31
|
+
deviceName?: string;
|
|
32
|
+
deviceClass?: "1" | "2" | "3";
|
|
33
|
+
medicalSpecialty?: string;
|
|
34
|
+
productCode?: string;
|
|
35
|
+
regulationNumber?: string;
|
|
36
|
+
limit?: number;
|
|
37
|
+
skip?: number;
|
|
38
|
+
};
|
|
39
|
+
type FormattedDeviceClassification = {
|
|
40
|
+
deviceName: string | undefined;
|
|
41
|
+
deviceClass: string | undefined;
|
|
42
|
+
definition: string | undefined;
|
|
43
|
+
medicalSpecialty: string | undefined;
|
|
44
|
+
medicalSpecialtyDescription: string | undefined;
|
|
45
|
+
productCode: string | undefined;
|
|
46
|
+
regulationNumber: string | undefined;
|
|
47
|
+
gmpExempt: boolean;
|
|
48
|
+
implant: boolean;
|
|
49
|
+
lifeSustaining: boolean;
|
|
50
|
+
};
|
|
51
|
+
declare function handleSearchDeviceClassifications(params: DeviceClassificationParams): Promise<FDAToolResponse<FormattedDeviceClassification[]>>;
|
|
52
|
+
type DeviceAdverseEventsParams = {
|
|
53
|
+
deviceName?: string;
|
|
54
|
+
brandName?: string;
|
|
55
|
+
manufacturerName?: string;
|
|
56
|
+
eventType?: "Injury" | "Malfunction" | "Death" | "Other";
|
|
57
|
+
dateFrom?: string;
|
|
58
|
+
dateTo?: string;
|
|
59
|
+
limit?: number;
|
|
60
|
+
skip?: number;
|
|
61
|
+
};
|
|
62
|
+
type FormattedDeviceAdverseEvent = {
|
|
63
|
+
reportNumber: string | undefined;
|
|
64
|
+
dateOfEvent: string | undefined;
|
|
65
|
+
dateReceived: string | undefined;
|
|
66
|
+
eventType: string | undefined;
|
|
67
|
+
adverseEventFlag: boolean;
|
|
68
|
+
productProblemFlag: boolean;
|
|
69
|
+
devices: Array<{
|
|
70
|
+
brandName: string | undefined;
|
|
71
|
+
genericName: string | undefined;
|
|
72
|
+
manufacturerName: string | undefined;
|
|
73
|
+
modelNumber: string | undefined;
|
|
74
|
+
productCode: string | undefined;
|
|
75
|
+
deviceClass: string | undefined;
|
|
76
|
+
}>;
|
|
77
|
+
mdrText: Array<{
|
|
78
|
+
textType: string | undefined;
|
|
79
|
+
text: string | undefined;
|
|
80
|
+
}>;
|
|
81
|
+
};
|
|
82
|
+
declare function handleSearchDeviceAdverseEvents(params: DeviceAdverseEventsParams): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>>;
|
|
83
|
+
type DeviceEnforcementParams = {
|
|
84
|
+
recallingFirm?: string;
|
|
85
|
+
productDescription?: string;
|
|
86
|
+
classification?: "Class I" | "Class II" | "Class III";
|
|
87
|
+
status?: "Ongoing" | "Completed" | "Terminated" | "Pending";
|
|
88
|
+
dateFrom?: string;
|
|
89
|
+
dateTo?: string;
|
|
90
|
+
limit?: number;
|
|
91
|
+
skip?: number;
|
|
92
|
+
};
|
|
93
|
+
type FormattedDeviceEnforcement = {
|
|
94
|
+
recallNumber: string | undefined;
|
|
95
|
+
recallingFirm: string | undefined;
|
|
96
|
+
classification: string | undefined;
|
|
97
|
+
status: string | undefined;
|
|
98
|
+
productDescription: string | undefined;
|
|
99
|
+
reasonForRecall: string | undefined;
|
|
100
|
+
recallInitiationDate: string | undefined;
|
|
101
|
+
distributionPattern: string | undefined;
|
|
102
|
+
city: string | undefined;
|
|
103
|
+
state: string | undefined;
|
|
104
|
+
deviceName: string | undefined;
|
|
105
|
+
deviceClass: string | undefined;
|
|
106
|
+
};
|
|
107
|
+
declare function handleSearchDeviceEnforcement(params: DeviceEnforcementParams): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>>;
|
|
108
|
+
//#endregion
|
|
109
|
+
export { Device510KParams, DeviceAdverseEventsParams, DeviceClassificationParams, DeviceEnforcementParams, handleSearchDevice510K, handleSearchDeviceAdverseEvents, handleSearchDeviceClassifications, handleSearchDeviceEnforcement };
|
|
110
|
+
//# sourceMappingURL=device-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"../utils/logger.js";import"../api-client-BGnUXMwX.js";import{i as e,n as t,r as n,t as r}from"../device-handlers-D01LfIJk.js";export{r as handleSearchDevice510K,t as handleSearchDeviceAdverseEvents,n as handleSearchDeviceClassifications,e as handleSearchDeviceEnforcement};
|
|
@@ -0,0 +1,147 @@
|
|
|
1
|
+
import { FDAToolResponse } from "../types/fda.js";
|
|
2
|
+
|
|
3
|
+
//#region src/handlers/drug-handlers.d.ts
|
|
4
|
+
|
|
5
|
+
type DrugAdverseEventsParams = {
|
|
6
|
+
drugName?: string;
|
|
7
|
+
reaction?: string;
|
|
8
|
+
manufacturer?: string;
|
|
9
|
+
serious?: boolean;
|
|
10
|
+
dateFrom?: string;
|
|
11
|
+
dateTo?: string;
|
|
12
|
+
limit?: number;
|
|
13
|
+
skip?: number;
|
|
14
|
+
};
|
|
15
|
+
type FormattedDrugAdverseEvent = {
|
|
16
|
+
reportId: string | undefined;
|
|
17
|
+
receiveDate: string | undefined;
|
|
18
|
+
serious: boolean;
|
|
19
|
+
drugs: Array<{
|
|
20
|
+
name: string | undefined;
|
|
21
|
+
indication: string | undefined;
|
|
22
|
+
route: string | undefined;
|
|
23
|
+
}>;
|
|
24
|
+
reactions: Array<{
|
|
25
|
+
reaction: string | undefined;
|
|
26
|
+
outcome: string | undefined;
|
|
27
|
+
}>;
|
|
28
|
+
patient: {
|
|
29
|
+
age: string | undefined;
|
|
30
|
+
sex: string | undefined;
|
|
31
|
+
weight: string | undefined;
|
|
32
|
+
};
|
|
33
|
+
};
|
|
34
|
+
declare function handleSearchDrugAdverseEvents(params: DrugAdverseEventsParams): Promise<FDAToolResponse<FormattedDrugAdverseEvent[]>>;
|
|
35
|
+
type DrugLabelsParams = {
|
|
36
|
+
drugName?: string;
|
|
37
|
+
indication?: string;
|
|
38
|
+
activeIngredient?: string;
|
|
39
|
+
route?: string;
|
|
40
|
+
limit?: number;
|
|
41
|
+
skip?: number;
|
|
42
|
+
};
|
|
43
|
+
type FormattedDrugLabel = {
|
|
44
|
+
brandName: string | undefined;
|
|
45
|
+
genericName: string | undefined;
|
|
46
|
+
manufacturer: string | undefined;
|
|
47
|
+
activeIngredients: string[];
|
|
48
|
+
indications: string | undefined;
|
|
49
|
+
warnings: string | undefined;
|
|
50
|
+
dosageAndAdministration: string | undefined;
|
|
51
|
+
route: string[];
|
|
52
|
+
};
|
|
53
|
+
declare function handleSearchDrugLabels(params: DrugLabelsParams): Promise<FDAToolResponse<FormattedDrugLabel[]>>;
|
|
54
|
+
type DrugNDCParams = {
|
|
55
|
+
productNdc?: string;
|
|
56
|
+
brandName?: string;
|
|
57
|
+
genericName?: string;
|
|
58
|
+
labelerName?: string;
|
|
59
|
+
dosageForm?: string;
|
|
60
|
+
route?: string;
|
|
61
|
+
limit?: number;
|
|
62
|
+
skip?: number;
|
|
63
|
+
};
|
|
64
|
+
type FormattedDrugNDC = {
|
|
65
|
+
productNdc: string | undefined;
|
|
66
|
+
brandName: string | undefined;
|
|
67
|
+
genericName: string | undefined;
|
|
68
|
+
labelerName: string | undefined;
|
|
69
|
+
dosageForm: string | undefined;
|
|
70
|
+
route: string[];
|
|
71
|
+
activeIngredients: Array<{
|
|
72
|
+
name: string | undefined;
|
|
73
|
+
strength: string | undefined;
|
|
74
|
+
}>;
|
|
75
|
+
marketingStartDate: string | undefined;
|
|
76
|
+
productType: string | undefined;
|
|
77
|
+
};
|
|
78
|
+
declare function handleSearchDrugNDC(params: DrugNDCParams): Promise<FDAToolResponse<FormattedDrugNDC[]>>;
|
|
79
|
+
type DrugEnforcementParams = {
|
|
80
|
+
recallingFirm?: string;
|
|
81
|
+
classification?: "Class I" | "Class II" | "Class III";
|
|
82
|
+
status?: "Ongoing" | "Completed" | "Terminated" | "Pending";
|
|
83
|
+
state?: string;
|
|
84
|
+
dateFrom?: string;
|
|
85
|
+
dateTo?: string;
|
|
86
|
+
limit?: number;
|
|
87
|
+
skip?: number;
|
|
88
|
+
};
|
|
89
|
+
type FormattedDrugEnforcement = {
|
|
90
|
+
recallNumber: string | undefined;
|
|
91
|
+
recallingFirm: string | undefined;
|
|
92
|
+
classification: string | undefined;
|
|
93
|
+
status: string | undefined;
|
|
94
|
+
productDescription: string | undefined;
|
|
95
|
+
reasonForRecall: string | undefined;
|
|
96
|
+
recallInitiationDate: string | undefined;
|
|
97
|
+
distributionPattern: string | undefined;
|
|
98
|
+
city: string | undefined;
|
|
99
|
+
state: string | undefined;
|
|
100
|
+
};
|
|
101
|
+
declare function handleSearchDrugEnforcement(params: DrugEnforcementParams): Promise<FDAToolResponse<FormattedDrugEnforcement[]>>;
|
|
102
|
+
type DrugsFDAParams = {
|
|
103
|
+
sponsorName?: string;
|
|
104
|
+
applicationNumber?: string;
|
|
105
|
+
brandName?: string;
|
|
106
|
+
marketingStatus?: string;
|
|
107
|
+
limit?: number;
|
|
108
|
+
skip?: number;
|
|
109
|
+
};
|
|
110
|
+
type FormattedDrugsFDA = {
|
|
111
|
+
applicationNumber: string | undefined;
|
|
112
|
+
sponsorName: string | undefined;
|
|
113
|
+
products: Array<{
|
|
114
|
+
brandName: string | undefined;
|
|
115
|
+
dosageForm: string | undefined;
|
|
116
|
+
route: string | undefined;
|
|
117
|
+
marketingStatus: string | undefined;
|
|
118
|
+
activeIngredients: Array<{
|
|
119
|
+
name: string | undefined;
|
|
120
|
+
strength: string | undefined;
|
|
121
|
+
}>;
|
|
122
|
+
}>;
|
|
123
|
+
latestSubmission: {
|
|
124
|
+
type: string | undefined;
|
|
125
|
+
status: string | undefined;
|
|
126
|
+
statusDate: string | undefined;
|
|
127
|
+
} | undefined;
|
|
128
|
+
};
|
|
129
|
+
declare function handleSearchDrugsFDA(params: DrugsFDAParams): Promise<FDAToolResponse<FormattedDrugsFDA[]>>;
|
|
130
|
+
type DrugShortagesParams = {
|
|
131
|
+
genericName?: string;
|
|
132
|
+
status?: "Current" | "Resolved";
|
|
133
|
+
limit?: number;
|
|
134
|
+
skip?: number;
|
|
135
|
+
};
|
|
136
|
+
type FormattedDrugShortage = {
|
|
137
|
+
genericName: string | undefined;
|
|
138
|
+
proprietaryName: string | undefined;
|
|
139
|
+
status: string | undefined;
|
|
140
|
+
description: string | undefined;
|
|
141
|
+
initialPostingDate: string | undefined;
|
|
142
|
+
resolvedShortageDate: string | undefined;
|
|
143
|
+
};
|
|
144
|
+
declare function handleSearchDrugShortages(params: DrugShortagesParams): Promise<FDAToolResponse<FormattedDrugShortage[]>>;
|
|
145
|
+
//#endregion
|
|
146
|
+
export { DrugAdverseEventsParams, DrugEnforcementParams, DrugLabelsParams, DrugNDCParams, DrugShortagesParams, DrugsFDAParams, handleSearchDrugAdverseEvents, handleSearchDrugEnforcement, handleSearchDrugLabels, handleSearchDrugNDC, handleSearchDrugShortages, handleSearchDrugsFDA };
|
|
147
|
+
//# sourceMappingURL=drug-handlers.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
import"../utils/logger.js";import"../api-client-BGnUXMwX.js";import{a as e,i as t,n,o as r,r as i,t as a}from"../drug-handlers-Fz_6NLl5.js";export{a as handleSearchDrugAdverseEvents,n as handleSearchDrugEnforcement,i as handleSearchDrugLabels,t as handleSearchDrugNDC,e as handleSearchDrugShortages,r as handleSearchDrugsFDA};
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export { };
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,3 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
import{loggers as e}from"./utils/logger.js";import"./api-client-BGnUXMwX.js";import"./device-handlers-D01LfIJk.js";import"./drug-handlers-Fz_6NLl5.js";import"./tools-Bh3wjIRH.js";import{createOpenFDAServer as t}from"./server.js";import{Command as n}from"commander";const r=new n;r.name(`openfda-mcp-server`).description(`MCP server for querying U.S. FDA public datasets`).version(`0.1.0`).option(`-t, --transport <type>`,`Transport type (stdio or http)`,`stdio`).option(`-p, --port <number>`,`Port for HTTP transport`,`3000`).option(`--host <host>`,`Host for HTTP transport`,`localhost`).action(async n=>{let{transport:r,port:i,host:a}=n;e.main(`Starting OpenFDA MCP server with transport: ${r}`);let o=t({name:`openfda-mcp-server`,version:`0.1.0`});try{if(r===`http`){let t=parseInt(i,10);e.main(`Starting HTTP server on ${a}:${t}`),await o.start({transportType:`httpStream`,httpStream:{endpoint:`/mcp`,port:t}}),console.error(`OpenFDA MCP server running at http://${a}:${t}/mcp`)}else e.main(`Starting stdio server`),await o.start({transportType:`stdio`})}catch(e){console.error(`Failed to start server:`,e),process.exit(1)}}),r.parse();export{};
|
|
3
|
+
//# sourceMappingURL=index.js.map
|