fda-mcp-server 0.0.1

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 ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Jasen Carroll
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,145 @@
1
+ # fda-mcp-server
2
+
3
+ Query FDA Warning Letters, Form 483 Inspections, and Recalls directly from Claude. Built for quality engineers, regulatory professionals, and compliance teams working in FDA-regulated industries.
4
+
5
+ No API key required for core tools. Just install and start asking questions.
6
+
7
+ ## What You Can Do
8
+
9
+ **Ask Claude things like:**
10
+
11
+ - "Show me recent warning letters from CDRH mentioning software validation"
12
+ - "Find Class I device recalls from the last 6 months"
13
+ - "Pull up the enforcement profile for Siemens Healthcare"
14
+ - "What 483 inspections happened in New Jersey for medical devices?"
15
+
16
+ **With the enriched dataset (API key required):**
17
+
18
+ - "Run a CSA risk assessment on Fresenius Kabi"
19
+ - "Generate a supplier qualification report for Baxter International"
20
+ - "Analyze citation patterns for CFR Part 820"
21
+
22
+ ## Tools
23
+
24
+ ### Free (live FDA data)
25
+
26
+ | Tool | What it does |
27
+ |------|-------------|
28
+ | `search_warning_letters` | Search warning letters by company, keyword, issuing office, or date range |
29
+ | `search_483_inspections` | Search Form 483 observations by firm, state, FDA center, or project area |
30
+ | `search_recalls` | Search recalls by product type, classification, firm, or date |
31
+ | `company_enforcement_profile` | Warning letters + 483s + recalls for a single company in one view |
32
+
33
+ ### Enriched (curated CSA dataset, API key required)
34
+
35
+ | Tool | What it does |
36
+ |------|-------------|
37
+ | `csa_risk_assessment` | Risk score with 4.2x escalation multiplier, failure modes, citation breakdown |
38
+ | `citation_analysis` | Citation patterns by CFR part, section, and category with CSA-relevant percentages |
39
+ | `supplier_qualification_report` | Audit-ready qualification brief with risk level and go/no-go recommendation |
40
+
41
+ The enriched tools are powered by a curated dataset of 1,047 warning letters, 1,800 Form 483s, 3,400 extracted citations, and 157 failure modes — all classified for CSA relevance. This is especially valuable for CDRH-regulated firms navigating the QMSR transition.
42
+
43
+ ## Setup
44
+
45
+ ### Prerequisites
46
+
47
+ Install [Node.js](https://nodejs.org/) (v18 or later). Download the LTS version from [nodejs.org](https://nodejs.org/) and run the installer.
48
+
49
+ To check if it's already installed, open Terminal (Mac) or Command Prompt (Windows) and type:
50
+
51
+ ```
52
+ node --version
53
+ ```
54
+
55
+ ### Claude Desktop
56
+
57
+ 1. Open Claude Desktop
58
+ 2. Click the **gear icon** (Settings) → **Developer** → **Edit Config**
59
+ 3. Paste this into the file that opens:
60
+
61
+ ```json
62
+ {
63
+ "mcpServers": {
64
+ "fda": {
65
+ "command": "npx",
66
+ "args": ["-y", "fda-mcp-server"]
67
+ }
68
+ }
69
+ }
70
+ ```
71
+
72
+ 4. Save the file and **restart Claude Desktop**
73
+ 5. Start a new conversation — you should see a hammer icon indicating FDA tools are available
74
+
75
+ That's it. You can now ask Claude about FDA warning letters, 483s, and recalls.
76
+
77
+ ### Enabling Enriched Tools
78
+
79
+ To unlock CSA risk assessments, citation analysis, and supplier qualification reports, add your API key:
80
+
81
+ ```json
82
+ {
83
+ "mcpServers": {
84
+ "fda": {
85
+ "command": "npx",
86
+ "args": ["-y", "fda-mcp-server"],
87
+ "env": {
88
+ "FDA_API_KEY": "your-api-key-here"
89
+ }
90
+ }
91
+ }
92
+ }
93
+ ```
94
+
95
+ Get an API key at [jasenc.dev/fda-mcp](https://jasenc.dev/fda-mcp).
96
+
97
+ ### Claude Code
98
+
99
+ ```bash
100
+ claude mcp add fda -- npx -y fda-mcp-server
101
+ ```
102
+
103
+ With enriched tools:
104
+
105
+ ```bash
106
+ claude mcp add fda -e FDA_API_KEY=your-api-key-here -- npx -y fda-mcp-server
107
+ ```
108
+
109
+ ## Data Sources
110
+
111
+ All core tool data comes from public FDA sources — no authentication required:
112
+
113
+ - **Warning Letters** — FDA.gov (~3,300+ records, updated continuously)
114
+ - **Form 483 Inspections** — FDA.gov FOIA Reading Room
115
+ - **Recalls** — openFDA API (drugs, devices, food)
116
+
117
+ Enriched tool data comes from a curated PostgreSQL database maintained at [fda-csa-data](https://github.com/jasencarroll/fda-csa-data). The dataset classifies each warning letter and 483 observation for CSA relevance based on CFR citations (21 CFR 211.68, 820.70(i), 820.30(g), Part 11) and narrative keywords (software validation, data integrity, electronic records, audit trail, etc.).
118
+
119
+ ## Self-Hosting
120
+
121
+ If you want to run the enriched tools against your own database instead of using an API key:
122
+
123
+ ```json
124
+ {
125
+ "mcpServers": {
126
+ "fda": {
127
+ "command": "npx",
128
+ "args": ["-y", "fda-mcp-server"],
129
+ "env": {
130
+ "DATABASE_URL": "postgresql://localhost:5432/fda-csa-data"
131
+ }
132
+ }
133
+ }
134
+ }
135
+ ```
136
+
137
+ See [fda-csa-data](https://github.com/jasencarroll/fda-csa-data) for the database dump and restore instructions.
138
+
139
+ ## License
140
+
141
+ MIT
142
+
143
+ ## Author
144
+
145
+ [Jasen Carroll](https://jasenc.dev) — Quality engineering + software, FDA-regulated industries.
package/dist/db.d.ts ADDED
@@ -0,0 +1,105 @@
1
+ /**
2
+ * Optional PostgreSQL connection for enriched CSA dataset.
3
+ *
4
+ * When DATABASE_URL is set, enables premium tools:
5
+ * - csa_risk_assessment
6
+ * - citation_analysis
7
+ * - supplier_qualification_report
8
+ *
9
+ * API key gating:
10
+ * - FDA_MCP_API_KEY env var on the server side sets the required key
11
+ * - FDA_API_KEY env var on the client side must match
12
+ * - If FDA_MCP_API_KEY is not set, enriched tools are open (local/dev use)
13
+ *
14
+ * Schema: github.com/jasencarroll/fda-csa-data
15
+ */
16
+ import pg from "pg";
17
+ export declare function getPool(): pg.Pool | null;
18
+ export declare function isEnrichedAvailable(): boolean;
19
+ /**
20
+ * Validate API key for enriched tool access.
21
+ * If FDA_MCP_API_KEY is set on the server, FDA_API_KEY must match.
22
+ * If FDA_MCP_API_KEY is not set, access is open (local dev mode).
23
+ */
24
+ export declare function validateApiKey(): {
25
+ valid: boolean;
26
+ message?: string;
27
+ };
28
+ export declare function testConnection(): Promise<boolean>;
29
+ export interface CSARiskResult {
30
+ company_name: string;
31
+ warning_letter_count: number;
32
+ csa_warning_letter_count: number;
33
+ total_citations: number;
34
+ csa_citations: number;
35
+ inspection_count: number;
36
+ csa_inspection_count: number;
37
+ failure_modes: string[];
38
+ top_cfr_citations: {
39
+ cfr_full: string;
40
+ category: string;
41
+ count: number;
42
+ }[];
43
+ risk_score: number;
44
+ risk_level: "LOW" | "MODERATE" | "HIGH" | "CRITICAL";
45
+ escalation_multiplier: number;
46
+ }
47
+ export declare function csaRiskAssessment(companyName: string): Promise<CSARiskResult | null>;
48
+ export interface CitationAnalysisResult {
49
+ total_citations: number;
50
+ by_type: {
51
+ type: string;
52
+ count: number;
53
+ }[];
54
+ by_cfr_part: {
55
+ part: string;
56
+ count: number;
57
+ }[];
58
+ by_category: {
59
+ category: string;
60
+ count: number;
61
+ csa_relevant: boolean;
62
+ }[];
63
+ top_sections: {
64
+ cfr_full: string;
65
+ description: string;
66
+ count: number;
67
+ }[];
68
+ csa_percentage: number;
69
+ }
70
+ export declare function citationAnalysis(params: {
71
+ cfr_part?: string;
72
+ category?: string;
73
+ center?: string;
74
+ fiscal_year?: number;
75
+ }): Promise<CitationAnalysisResult | null>;
76
+ export interface SupplierQualResult {
77
+ company_name: string;
78
+ fei_numbers: string[];
79
+ warning_letters: {
80
+ issue_date: string;
81
+ center: string;
82
+ regime: string;
83
+ csa_relevant: boolean;
84
+ url: string;
85
+ }[];
86
+ inspections: {
87
+ inspection_end_date: string;
88
+ center: string;
89
+ product_area: string;
90
+ csa_relevant: boolean;
91
+ }[];
92
+ citation_summary: {
93
+ cfr_full: string;
94
+ category: string;
95
+ count: number;
96
+ }[];
97
+ failure_modes: {
98
+ mode: string;
99
+ confidence: string;
100
+ count: number;
101
+ }[];
102
+ csa_risk: CSARiskResult | null;
103
+ }
104
+ export declare function supplierQualification(companyName: string): Promise<SupplierQualResult | null>;
105
+ //# sourceMappingURL=db.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.d.ts","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
package/dist/db.js ADDED
@@ -0,0 +1,288 @@
1
+ /**
2
+ * Optional PostgreSQL connection for enriched CSA dataset.
3
+ *
4
+ * When DATABASE_URL is set, enables premium tools:
5
+ * - csa_risk_assessment
6
+ * - citation_analysis
7
+ * - supplier_qualification_report
8
+ *
9
+ * API key gating:
10
+ * - FDA_MCP_API_KEY env var on the server side sets the required key
11
+ * - FDA_API_KEY env var on the client side must match
12
+ * - If FDA_MCP_API_KEY is not set, enriched tools are open (local/dev use)
13
+ *
14
+ * Schema: github.com/jasencarroll/fda-csa-data
15
+ */
16
+ import pg from "pg";
17
+ const { Pool } = pg;
18
+ let pool = null;
19
+ export function getPool() {
20
+ if (pool)
21
+ return pool;
22
+ const connectionString = process.env.DATABASE_URL;
23
+ if (!connectionString)
24
+ return null;
25
+ pool = new Pool({
26
+ connectionString,
27
+ max: 5,
28
+ idleTimeoutMillis: 30000,
29
+ connectionTimeoutMillis: 5000,
30
+ });
31
+ pool.on("error", (err) => {
32
+ console.error("PostgreSQL pool error:", err.message);
33
+ });
34
+ return pool;
35
+ }
36
+ export function isEnrichedAvailable() {
37
+ return !!process.env.DATABASE_URL;
38
+ }
39
+ /**
40
+ * Validate API key for enriched tool access.
41
+ * If FDA_MCP_API_KEY is set on the server, FDA_API_KEY must match.
42
+ * If FDA_MCP_API_KEY is not set, access is open (local dev mode).
43
+ */
44
+ export function validateApiKey() {
45
+ const serverKey = process.env.FDA_MCP_API_KEY;
46
+ if (!serverKey)
47
+ return { valid: true }; // no key required (local mode)
48
+ const clientKey = process.env.FDA_API_KEY;
49
+ if (!clientKey) {
50
+ return {
51
+ valid: false,
52
+ message: "Enriched tools require an API key. Set FDA_API_KEY in your MCP client config.\n\nGet a key at: https://jasenc.dev/fda-mcp",
53
+ };
54
+ }
55
+ if (clientKey !== serverKey) {
56
+ return {
57
+ valid: false,
58
+ message: "Invalid API key. Check your FDA_API_KEY value.",
59
+ };
60
+ }
61
+ return { valid: true };
62
+ }
63
+ export async function testConnection() {
64
+ const p = getPool();
65
+ if (!p)
66
+ return false;
67
+ try {
68
+ await p.query("SELECT 1");
69
+ return true;
70
+ }
71
+ catch {
72
+ return false;
73
+ }
74
+ }
75
+ export async function csaRiskAssessment(companyName) {
76
+ const p = getPool();
77
+ if (!p)
78
+ return null;
79
+ // Warning letter counts
80
+ const wlResult = await p.query(`SELECT
81
+ COUNT(*) AS total,
82
+ COUNT(*) FILTER (WHERE csa_relevant = true) AS csa_count
83
+ FROM warning_letter
84
+ WHERE LOWER(company_name) LIKE LOWER($1)`, [`%${companyName}%`]);
85
+ // Citation breakdown
86
+ const citResult = await p.query(`SELECT
87
+ COUNT(*) AS total,
88
+ COUNT(*) FILTER (WHERE c.normalized_category IN (
89
+ 'software_validation', 'data_integrity', 'electronic_records',
90
+ 'computer_systems', 'audit_trail', 'part_11'
91
+ )) AS csa_count
92
+ FROM citation c
93
+ JOIN warning_letter wl ON c.letter_id = wl.id
94
+ WHERE LOWER(wl.company_name) LIKE LOWER($1)`, [`%${companyName}%`]);
95
+ // Top CFR citations
96
+ const topCfr = await p.query(`SELECT c.cfr_full, c.normalized_category, COUNT(*) AS cnt
97
+ FROM citation c
98
+ JOIN warning_letter wl ON c.letter_id = wl.id
99
+ WHERE LOWER(wl.company_name) LIKE LOWER($1)
100
+ GROUP BY c.cfr_full, c.normalized_category
101
+ ORDER BY cnt DESC
102
+ LIMIT 10`, [`%${companyName}%`]);
103
+ // 483 inspection counts
104
+ const inspResult = await p.query(`SELECT
105
+ COUNT(*) AS total,
106
+ COUNT(*) FILTER (WHERE csa_relevant = true) AS csa_count
107
+ FROM observation_483
108
+ WHERE LOWER(company_name) LIKE LOWER($1)`, [`%${companyName}%`]);
109
+ // Failure modes
110
+ const fmResult = await p.query(`SELECT DISTINCT fm.mode
111
+ FROM failure_mode fm
112
+ JOIN warning_letter wl ON fm.letter_id = wl.id
113
+ WHERE LOWER(wl.company_name) LIKE LOWER($1)
114
+ ORDER BY fm.mode`, [`%${companyName}%`]);
115
+ const wlTotal = parseInt(wlResult.rows[0]?.total ?? "0");
116
+ const wlCsa = parseInt(wlResult.rows[0]?.csa_count ?? "0");
117
+ const citTotal = parseInt(citResult.rows[0]?.total ?? "0");
118
+ const citCsa = parseInt(citResult.rows[0]?.csa_count ?? "0");
119
+ const inspTotal = parseInt(inspResult.rows[0]?.total ?? "0");
120
+ const inspCsa = parseInt(inspResult.rows[0]?.csa_count ?? "0");
121
+ // Risk scoring: weighted composite
122
+ // CSA warning letters are 4.2x more significant (per dataset analysis)
123
+ const ESCALATION_MULTIPLIER = 4.2;
124
+ const score = wlCsa * 30 * ESCALATION_MULTIPLIER +
125
+ wlTotal * 10 +
126
+ citCsa * 5 +
127
+ inspCsa * 15 +
128
+ inspTotal * 3;
129
+ let riskLevel = "LOW";
130
+ if (score >= 500)
131
+ riskLevel = "CRITICAL";
132
+ else if (score >= 200)
133
+ riskLevel = "HIGH";
134
+ else if (score >= 50)
135
+ riskLevel = "MODERATE";
136
+ return {
137
+ company_name: companyName,
138
+ warning_letter_count: wlTotal,
139
+ csa_warning_letter_count: wlCsa,
140
+ total_citations: citTotal,
141
+ csa_citations: citCsa,
142
+ inspection_count: inspTotal,
143
+ csa_inspection_count: inspCsa,
144
+ failure_modes: fmResult.rows.map((r) => r.mode),
145
+ top_cfr_citations: topCfr.rows.map((r) => ({
146
+ cfr_full: r.cfr_full,
147
+ category: r.normalized_category,
148
+ count: parseInt(r.cnt),
149
+ })),
150
+ risk_score: Math.round(score),
151
+ risk_level: riskLevel,
152
+ escalation_multiplier: ESCALATION_MULTIPLIER,
153
+ };
154
+ }
155
+ export async function citationAnalysis(params) {
156
+ const p = getPool();
157
+ if (!p)
158
+ return null;
159
+ const conditions = [];
160
+ const values = [];
161
+ let idx = 1;
162
+ if (params.cfr_part) {
163
+ conditions.push(`c.cfr_part = $${idx++}`);
164
+ values.push(params.cfr_part);
165
+ }
166
+ if (params.category) {
167
+ conditions.push(`c.normalized_category = $${idx++}`);
168
+ values.push(params.category);
169
+ }
170
+ if (params.center) {
171
+ conditions.push(`wl.center = $${idx++}`);
172
+ values.push(params.center);
173
+ }
174
+ if (params.fiscal_year) {
175
+ conditions.push(`wl.fiscal_year = $${idx++}`);
176
+ values.push(params.fiscal_year);
177
+ }
178
+ const where = conditions.length > 0 ? `WHERE ${conditions.join(" AND ")}` : "";
179
+ const totalResult = await p.query(`SELECT COUNT(*) AS total FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}`, values);
180
+ const byType = await p.query(`SELECT c.citation_type AS type, COUNT(*) AS cnt
181
+ FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
182
+ GROUP BY c.citation_type ORDER BY cnt DESC`, values);
183
+ const byPart = await p.query(`SELECT c.cfr_part AS part, COUNT(*) AS cnt
184
+ FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
185
+ GROUP BY c.cfr_part ORDER BY cnt DESC LIMIT 15`, values);
186
+ const byCategory = await p.query(`SELECT c.normalized_category AS category, COUNT(*) AS cnt,
187
+ CASE WHEN c.normalized_category IN (
188
+ 'software_validation', 'data_integrity', 'electronic_records',
189
+ 'computer_systems', 'audit_trail', 'part_11'
190
+ ) THEN true ELSE false END AS csa_relevant
191
+ FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
192
+ GROUP BY c.normalized_category ORDER BY cnt DESC`, values);
193
+ const topSections = await p.query(`SELECT c.cfr_full, c.normalized_category AS description, COUNT(*) AS cnt
194
+ FROM citation c JOIN warning_letter wl ON c.letter_id = wl.id ${where}
195
+ GROUP BY c.cfr_full, c.normalized_category ORDER BY cnt DESC LIMIT 20`, values);
196
+ const total = parseInt(totalResult.rows[0]?.total ?? "0");
197
+ const csaCount = byCategory.rows
198
+ .filter((r) => r.csa_relevant)
199
+ .reduce((sum, r) => sum + parseInt(r.cnt), 0);
200
+ return {
201
+ total_citations: total,
202
+ by_type: byType.rows.map((r) => ({ type: r.type, count: parseInt(r.cnt) })),
203
+ by_cfr_part: byPart.rows.map((r) => ({ part: r.part, count: parseInt(r.cnt) })),
204
+ by_category: byCategory.rows.map((r) => ({
205
+ category: r.category,
206
+ count: parseInt(r.cnt),
207
+ csa_relevant: r.csa_relevant,
208
+ })),
209
+ top_sections: topSections.rows.map((r) => ({
210
+ cfr_full: r.cfr_full,
211
+ description: r.description,
212
+ count: parseInt(r.cnt),
213
+ })),
214
+ csa_percentage: total > 0 ? Math.round((csaCount / total) * 100 * 10) / 10 : 0,
215
+ };
216
+ }
217
+ export async function supplierQualification(companyName) {
218
+ const p = getPool();
219
+ if (!p)
220
+ return null;
221
+ // FEI numbers
222
+ const feiResult = await p.query(`SELECT DISTINCT fei_number FROM (
223
+ SELECT fei_number FROM warning_letter WHERE LOWER(company_name) LIKE LOWER($1) AND fei_number IS NOT NULL
224
+ UNION
225
+ SELECT fei_number FROM observation_483 WHERE LOWER(company_name) LIKE LOWER($1) AND fei_number IS NOT NULL
226
+ ) sub WHERE fei_number != ''`, [`%${companyName}%`]);
227
+ // Warning letters
228
+ const wlResult = await p.query(`SELECT
229
+ to_timestamp(issue_date) AS issue_ts,
230
+ center, regime, csa_relevant, url
231
+ FROM warning_letter
232
+ WHERE LOWER(company_name) LIKE LOWER($1)
233
+ ORDER BY issue_date DESC
234
+ LIMIT 20`, [`%${companyName}%`]);
235
+ // 483 inspections
236
+ const inspResult = await p.query(`SELECT inspection_end_date, center, product_area, csa_relevant
237
+ FROM observation_483
238
+ WHERE LOWER(company_name) LIKE LOWER($1)
239
+ ORDER BY inspection_end_date DESC
240
+ LIMIT 20`, [`%${companyName}%`]);
241
+ // Citation summary
242
+ const citResult = await p.query(`SELECT c.cfr_full, c.normalized_category, COUNT(*) AS cnt
243
+ FROM citation c
244
+ JOIN warning_letter wl ON c.letter_id = wl.id
245
+ WHERE LOWER(wl.company_name) LIKE LOWER($1)
246
+ GROUP BY c.cfr_full, c.normalized_category
247
+ ORDER BY cnt DESC
248
+ LIMIT 15`, [`%${companyName}%`]);
249
+ // Failure modes
250
+ const fmResult = await p.query(`SELECT fm.mode, fm.confidence, COUNT(*) AS cnt
251
+ FROM failure_mode fm
252
+ JOIN warning_letter wl ON fm.letter_id = wl.id
253
+ WHERE LOWER(wl.company_name) LIKE LOWER($1)
254
+ GROUP BY fm.mode, fm.confidence
255
+ ORDER BY cnt DESC`, [`%${companyName}%`]);
256
+ const risk = await csaRiskAssessment(companyName);
257
+ return {
258
+ company_name: companyName,
259
+ fei_numbers: feiResult.rows.map((r) => r.fei_number),
260
+ warning_letters: wlResult.rows.map((r) => ({
261
+ issue_date: r.issue_ts
262
+ ? new Date(r.issue_ts).toISOString().split("T")[0]
263
+ : "",
264
+ center: r.center ?? "",
265
+ regime: r.regime ?? "",
266
+ csa_relevant: r.csa_relevant ?? false,
267
+ url: r.url ?? "",
268
+ })),
269
+ inspections: inspResult.rows.map((r) => ({
270
+ inspection_end_date: r.inspection_end_date ?? "",
271
+ center: r.center ?? "",
272
+ product_area: r.product_area ?? "",
273
+ csa_relevant: r.csa_relevant ?? false,
274
+ })),
275
+ citation_summary: citResult.rows.map((r) => ({
276
+ cfr_full: r.cfr_full,
277
+ category: r.normalized_category,
278
+ count: parseInt(r.cnt),
279
+ })),
280
+ failure_modes: fmResult.rows.map((r) => ({
281
+ mode: r.mode,
282
+ confidence: r.confidence,
283
+ count: parseInt(r.cnt),
284
+ })),
285
+ csa_risk: risk,
286
+ };
287
+ }
288
+ //# sourceMappingURL=db.js.map
package/dist/db.js.map ADDED
@@ -0,0 +1 @@
1
+ {"version":3,"file":"db.js","sourceRoot":"","sources":["../src/db.ts"],"names":[],"mappings":"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"}
@@ -0,0 +1,92 @@
1
+ /**
2
+ * FDA API Client
3
+ *
4
+ * Wraps FDA public data sources:
5
+ * 1. FDA.gov Warning Letters (Drupal DataTables / Solr)
6
+ * 2. FDA.gov 483 Inspection Observations (FOIA Reading Room)
7
+ * 3. openFDA Enforcement/Recalls API
8
+ */
9
+ export interface WarningLetter {
10
+ posted_date: string;
11
+ issue_date: string;
12
+ company_name: string;
13
+ issuing_office: string;
14
+ subject: string;
15
+ response_letter: string;
16
+ closeout_letter: string;
17
+ excerpt: string;
18
+ url: string;
19
+ }
20
+ export interface EnforcementRecord {
21
+ recall_number: string;
22
+ status: string;
23
+ classification: string;
24
+ product_type: string;
25
+ recalling_firm: string;
26
+ city: string;
27
+ state: string;
28
+ country: string;
29
+ reason_for_recall: string;
30
+ product_description: string;
31
+ product_quantity: string;
32
+ distribution_pattern: string;
33
+ voluntary_mandated: string;
34
+ recall_initiation_date: string;
35
+ report_date: string;
36
+ center_classification_date: string;
37
+ termination_date: string;
38
+ event_id: string;
39
+ }
40
+ export interface InspectionRecord {
41
+ firm_name: string;
42
+ fei_number: string;
43
+ city: string;
44
+ state: string;
45
+ country: string;
46
+ zip_code: string;
47
+ inspection_end_date: string;
48
+ classification: string;
49
+ center: string;
50
+ project_area: string;
51
+ posted_citations: string;
52
+ url?: string;
53
+ }
54
+ export declare function searchWarningLetters(params: {
55
+ search?: string;
56
+ issuing_office?: string;
57
+ limit?: number;
58
+ offset?: number;
59
+ date_from?: string;
60
+ date_to?: string;
61
+ }): Promise<{
62
+ total: number;
63
+ letters: WarningLetter[];
64
+ }>;
65
+ export declare function searchInspections(params: {
66
+ firm_name?: string;
67
+ state?: string;
68
+ center?: string;
69
+ project_area?: string;
70
+ limit?: number;
71
+ offset?: number;
72
+ date_from?: string;
73
+ date_to?: string;
74
+ }): Promise<{
75
+ total: number;
76
+ inspections: InspectionRecord[];
77
+ }>;
78
+ export declare function searchEnforcement(params: {
79
+ search?: string;
80
+ product_type?: "drug" | "device" | "food";
81
+ classification?: "Class I" | "Class II" | "Class III";
82
+ state?: string;
83
+ recalling_firm?: string;
84
+ date_from?: string;
85
+ date_to?: string;
86
+ limit?: number;
87
+ skip?: number;
88
+ }): Promise<{
89
+ total: number;
90
+ records: EnforcementRecord[];
91
+ }>;
92
+ //# sourceMappingURL=fda-client.d.ts.map
@@ -0,0 +1 @@
1
+ {"version":3,"file":"fda-client.d.ts","sourceRoot":"","sources":["../src/fda-client.ts"],"names":[],"mappings":"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"}