openfda-mcp-server 1.1.0 → 2.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/README.md +6 -0
- package/dist/defineProperty-D36W847_.js +1 -0
- package/dist/handlers/bulk-data-handlers.d.ts +108 -0
- package/dist/handlers/bulk-data-handlers.js +2 -0
- package/dist/handlers/bulk-data-handlers.js.map +1 -0
- package/dist/handlers/device-handlers.d.ts +13 -14
- package/dist/handlers/device-handlers.js +2 -1
- package/dist/handlers/device-handlers.js.map +1 -0
- package/dist/handlers/drug-handlers.d.ts +17 -18
- package/dist/handlers/drug-handlers.js +2 -1
- package/dist/handlers/drug-handlers.js.map +1 -0
- package/dist/handlers/query.d.ts +21 -0
- package/dist/handlers/query.js +2 -0
- package/dist/handlers/query.js.map +1 -0
- package/dist/index.d.ts +1 -1
- package/dist/index.js +1 -1
- package/dist/index.js.map +1 -1
- package/dist/lib.d.ts +9 -3
- package/dist/lib.js +1 -1
- package/dist/server.d.ts +9 -10
- package/dist/server.js +1 -1
- package/dist/server.js.map +1 -1
- package/dist/tools/index.d.ts +231 -409
- package/dist/tools/index.js +2 -1
- package/dist/tools/index.js.map +1 -0
- package/dist/types/fda.d.ts +71 -18
- package/dist/utils/api-client.d.ts +27 -24
- package/dist/utils/api-client.js +2 -1
- package/dist/utils/api-client.js.map +1 -0
- package/dist/utils/bulk-data-client.d.ts +30 -0
- package/dist/utils/bulk-data-client.js +3 -0
- package/dist/utils/bulk-data-client.js.map +1 -0
- package/dist/utils/errors.d.ts +8 -0
- package/dist/utils/errors.js +2 -0
- package/dist/utils/errors.js.map +1 -0
- package/dist/utils/fields.d.ts +11 -0
- package/dist/utils/fields.js +2 -0
- package/dist/utils/fields.js.map +1 -0
- package/dist/utils/logger.d.ts +9 -9
- package/dist/utils/logger.js +1 -1
- package/dist/utils/logger.js.map +1 -1
- package/dist/version-BNWZ_jnk.d.ts +7 -0
- package/dist/version.d.ts +2 -0
- package/dist/version.js +2 -0
- package/dist/version.js.map +1 -0
- package/package.json +32 -24
- package/dist/api-client-BGnUXMwX.js +0 -2
- package/dist/api-client-BGnUXMwX.js.map +0 -1
- package/dist/device-handlers-D8AD5oIk.js +0 -2
- package/dist/device-handlers-D8AD5oIk.js.map +0 -1
- package/dist/drug-handlers-D5VxbBCL.js +0 -2
- package/dist/drug-handlers-D5VxbBCL.js.map +0 -1
- package/dist/tools-3M3lSxQv.js +0 -2
- package/dist/tools-3M3lSxQv.js.map +0 -1
package/README.md
CHANGED
|
@@ -72,6 +72,12 @@ Get a free API key at: https://open.fda.gov/apis/authentication/
|
|
|
72
72
|
| `search_device_adverse_events` | Search MDR adverse event reports. Retrieve specific report by `reportNumber`. |
|
|
73
73
|
| `search_device_recalls` | Search device recall enforcement reports |
|
|
74
74
|
|
|
75
|
+
### Built-in Tools
|
|
76
|
+
|
|
77
|
+
| Tool | Description |
|
|
78
|
+
| ------ | --------------------------------------------------------------------------------------- |
|
|
79
|
+
| `info` | Server name, version, build and capability counts. Registered automatically by SomaMCP. |
|
|
80
|
+
|
|
75
81
|
## Tool Parameters
|
|
76
82
|
|
|
77
83
|
### search_drug_labels
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
function e(t){"@babel/helpers - typeof";return e=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},e(t)}function t(t,n){if(e(t)!=`object`||!t)return t;var r;if(typeof Symbol<`u`&&(r=t[Symbol.toPrimitive])!==void 0){var i=r.call(t,n||`default`);if(e(i)!=`object`)return i;throw TypeError(`@@toPrimitive must return a primitive value.`)}return(n===`string`?String:Number)(t)}function n(n){var r=t(n,`string`);return e(r)==`symbol`?r:r+``}function r(e,t,r){return(t=n(t))in e?Object.defineProperty(e,t,{value:r,enumerable:!0,configurable:!0,writable:!0}):e[t]=r,e}export{r as t};
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Bulk Data Handlers
|
|
3
|
+
* Query handlers for FDA Orange Book and Purple Book data
|
|
4
|
+
*/
|
|
5
|
+
import { FDAToolResponse } from "../types/fda.js";
|
|
6
|
+
export type OrangeBookSearchParams = {
|
|
7
|
+
drugName?: string;
|
|
8
|
+
applicant?: string;
|
|
9
|
+
applNo?: string;
|
|
10
|
+
teCode?: string;
|
|
11
|
+
limit?: number;
|
|
12
|
+
skip?: number;
|
|
13
|
+
};
|
|
14
|
+
type FormattedOrangeBookProduct = {
|
|
15
|
+
tradeName: string;
|
|
16
|
+
ingredient: string;
|
|
17
|
+
applicant: string;
|
|
18
|
+
applicantFullName: string;
|
|
19
|
+
applType: string;
|
|
20
|
+
applNo: string;
|
|
21
|
+
productNo: string;
|
|
22
|
+
dfRoute: string;
|
|
23
|
+
teCode: string;
|
|
24
|
+
approvalDate: string;
|
|
25
|
+
rld: string;
|
|
26
|
+
type: string;
|
|
27
|
+
patentCount: number;
|
|
28
|
+
exclusivityCount: number;
|
|
29
|
+
};
|
|
30
|
+
export declare function handleSearchOrangeBook(params: OrangeBookSearchParams): Promise<FDAToolResponse<FormattedOrangeBookProduct[]>>;
|
|
31
|
+
export type OrangeBookPatentsParams = {
|
|
32
|
+
drugName?: string;
|
|
33
|
+
applNo?: string;
|
|
34
|
+
patentNo?: string;
|
|
35
|
+
limit?: number;
|
|
36
|
+
skip?: number;
|
|
37
|
+
};
|
|
38
|
+
type FormattedOrangeBookPatent = {
|
|
39
|
+
patentNo: string;
|
|
40
|
+
patentExpireDate: string;
|
|
41
|
+
drugSubstanceFlag: string;
|
|
42
|
+
drugProductFlag: string;
|
|
43
|
+
patentUseCode: string;
|
|
44
|
+
applType: string;
|
|
45
|
+
applNo: string;
|
|
46
|
+
productNo: string;
|
|
47
|
+
tradeName: string;
|
|
48
|
+
ingredient: string;
|
|
49
|
+
submissionDate: string;
|
|
50
|
+
exclusivities: Array<{
|
|
51
|
+
code: string;
|
|
52
|
+
date: string;
|
|
53
|
+
}>;
|
|
54
|
+
};
|
|
55
|
+
export declare function handleSearchOrangeBookPatents(params: OrangeBookPatentsParams): Promise<FDAToolResponse<FormattedOrangeBookPatent[]>>;
|
|
56
|
+
export type PurpleBookSearchParams = {
|
|
57
|
+
productName?: string;
|
|
58
|
+
applicant?: string;
|
|
59
|
+
blaNumber?: string;
|
|
60
|
+
licenseType?: "351(a)" | "351(k)";
|
|
61
|
+
biosimilar?: boolean;
|
|
62
|
+
interchangeable?: boolean;
|
|
63
|
+
limit?: number;
|
|
64
|
+
skip?: number;
|
|
65
|
+
};
|
|
66
|
+
type FormattedPurpleBookEntry = {
|
|
67
|
+
blaNumber: string;
|
|
68
|
+
proprietaryName: string;
|
|
69
|
+
properName: string;
|
|
70
|
+
applicant: string;
|
|
71
|
+
blaType: string;
|
|
72
|
+
strength: string;
|
|
73
|
+
dosageForm: string;
|
|
74
|
+
route: string;
|
|
75
|
+
status: string;
|
|
76
|
+
licensingStatus: string;
|
|
77
|
+
biosimilar: boolean;
|
|
78
|
+
interchangeable: boolean;
|
|
79
|
+
referenceProductBla: string;
|
|
80
|
+
referenceProductName: string;
|
|
81
|
+
approvalDate: string;
|
|
82
|
+
};
|
|
83
|
+
export declare function handleSearchPurpleBook(params: PurpleBookSearchParams): Promise<FDAToolResponse<FormattedPurpleBookEntry[]>>;
|
|
84
|
+
export type DrugPatentExpiryParams = {
|
|
85
|
+
drugName?: string;
|
|
86
|
+
applNo?: string;
|
|
87
|
+
includeExclusivity?: boolean;
|
|
88
|
+
includePurpleBook?: boolean;
|
|
89
|
+
limit?: number;
|
|
90
|
+
skip?: number;
|
|
91
|
+
};
|
|
92
|
+
type PatentExpiryEntry = {
|
|
93
|
+
source: string;
|
|
94
|
+
tradeName: string;
|
|
95
|
+
ingredient: string;
|
|
96
|
+
applNo: string;
|
|
97
|
+
patentNo: string;
|
|
98
|
+
patentExpireDate: string;
|
|
99
|
+
drugSubstanceFlag: string;
|
|
100
|
+
drugProductFlag: string;
|
|
101
|
+
patentUseCode: string;
|
|
102
|
+
exclusivities: Array<{
|
|
103
|
+
code: string;
|
|
104
|
+
date: string;
|
|
105
|
+
}>;
|
|
106
|
+
};
|
|
107
|
+
export declare function handleSearchDrugPatentExpiry(params: DrugPatentExpiryParams): Promise<FDAToolResponse<PatentExpiryEntry[]>>;
|
|
108
|
+
export {};
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{bulkDataClient as e}from"../utils/bulk-data-client.js";import{failure as t}from"../utils/errors.js";import{loggers as n}from"../utils/logger.js";import{Map as r}from"functype";function i(e,t){return!t||e.toLowerCase().includes(t.toLowerCase())}function a(e){return`${e.applType}-${e.applNo}-${e.productNo}`}function o(e,t){return Object.entries(Object.groupBy(e,t)).map(([e,t])=>[e,t??[]])}function s(e,t){return r(o(e,t).map(([e,t])=>[e,t.length]))}function c(e,t){return r(e.map(e=>[t(e),e]))}function l(e,t){return r(o(e,t))}function u(e,t,n){let r=e.length,i=n??0,a=Math.min(t??10,100);return{items:e.slice(i,i+a),total:r}}const d={name:`FDA Orange Book`,lastUpdated:`Monthly`,url:`https://www.fda.gov/drugs/drug-approvals-and-databases/approved-drug-products-therapeutic-equivalence-evaluations-orange-book`},f={name:`FDA Purple Book`,lastUpdated:`Monthly`,url:`https://purplebooksearch.fda.gov/`};async function p(r){return n.tools(`searchOrangeBook`,r),(await e.getOrangeBookData()).fold(t,({products:e,patents:t,exclusivities:n})=>{let o=s(t,a),c=s(n,a),{items:l,total:f}=u(e.filter(e=>!(!i(e.tradeName,r.drugName)&&!i(e.ingredient,r.drugName)||!i(e.applicant,r.applicant)&&!i(e.applicantFullName,r.applicant)||r.applNo&&e.applNo!==r.applNo||!i(e.teCode,r.teCode))),r.limit,r.skip);return{success:!0,data:l.map(e=>{let t=a(e);return{tradeName:e.tradeName,ingredient:e.ingredient,applicant:e.applicant,applicantFullName:e.applicantFullName,applType:e.applType,applNo:e.applNo,productNo:e.productNo,dfRoute:e.dfRoute,teCode:e.teCode,approvalDate:e.approvalDate,rld:e.rld,type:e.type,patentCount:o.getOrElse(t,0),exclusivityCount:c.getOrElse(t,0)}}),totalResults:f,displayedResults:l.length,dataSource:d}})}async function m(r){return n.tools(`searchOrangeBookPatents`,r),(await e.getOrangeBookData()).fold(t,({products:e,patents:t,exclusivities:n})=>{let o=c(e,a),s=l(n,a),{items:f,total:p}=u(t.filter(e=>{if(r.patentNo&&e.patentNo!==r.patentNo||r.applNo&&e.applNo!==r.applNo)return!1;if(r.drugName){let t=o.get(a(e)).orUndefined();if(!t||!i(t.tradeName,r.drugName)&&!i(t.ingredient,r.drugName))return!1}return!0}),r.limit,r.skip);return{success:!0,data:f.map(e=>{let t=a(e),n=o.get(t).orUndefined(),r=s.getOrElse(t,[]);return{patentNo:e.patentNo,patentExpireDate:e.patentExpireDate,drugSubstanceFlag:e.drugSubstanceFlag,drugProductFlag:e.drugProductFlag,patentUseCode:e.patentUseCode,applType:e.applType,applNo:e.applNo,productNo:e.productNo,tradeName:n?.tradeName??``,ingredient:n?.ingredient??``,submissionDate:e.submissionDate,exclusivities:r.map(e=>({code:e.exclusivityCode,date:e.exclusivityDate}))}}),totalResults:p,displayedResults:f.length,dataSource:d}})}async function h(r){return n.tools(`searchPurpleBook`,r),(await e.getPurpleBookData()).fold(t,e=>{let{items:t,total:n}=u(e.filter(e=>!(!i(e.proprietaryName,r.productName)&&!i(e.properName,r.productName)||!i(e.applicant,r.applicant)||r.blaNumber&&e.blaNumber!==r.blaNumber||r.licenseType&&e.blaType!==r.licenseType||r.biosimilar!==void 0&&e.biosimilar!==r.biosimilar||r.interchangeable!==void 0&&e.interchangeable!==r.interchangeable)),r.limit,r.skip);return{success:!0,data:t.map(e=>({blaNumber:e.blaNumber,proprietaryName:e.proprietaryName,properName:e.properName,applicant:e.applicant,blaType:e.blaType,strength:e.strength,dosageForm:e.dosageForm,route:e.route,status:e.status,licensingStatus:e.licensingStatus,biosimilar:e.biosimilar,interchangeable:e.interchangeable,referenceProductBla:e.referenceProductBla,referenceProductName:e.referenceProductProprietaryName,approvalDate:e.approvalDate})),totalResults:n,displayedResults:t.length,dataSource:f}})}async function g(o){n.tools(`searchDrugPatentExpiry`,o);let s=o.includeExclusivity!==!1,f=o.includePurpleBook===!0;return(await e.getOrangeBookData()).fold(e=>Promise.resolve(t(e)),async({products:e,patents:t,exclusivities:n})=>{let p=c(e,a),m=s?l(n,a):r.empty(),h=t.filter(e=>{if(o.applNo&&e.applNo!==o.applNo)return!1;if(o.drugName){let t=p.get(a(e)).orUndefined();if(!t||!i(t.tradeName,o.drugName)&&!i(t.ingredient,o.drugName))return!1}return!0}).map(e=>{let t=a(e),n=p.get(t).orUndefined(),r=m.getOrElse(t,[]);return{source:`Orange Book`,tradeName:n?.tradeName??``,ingredient:n?.ingredient??``,applNo:e.applNo,patentNo:e.patentNo,patentExpireDate:e.patentExpireDate,drugSubstanceFlag:e.drugSubstanceFlag,drugProductFlag:e.drugProductFlag,patentUseCode:e.patentUseCode,exclusivities:r.map(e=>({code:e.exclusivityCode,date:e.exclusivityDate}))}}),g=f?await _(o):[],{items:v,total:y}=u([...h,...g].sort((e,t)=>!e.patentExpireDate&&!t.patentExpireDate?0:e.patentExpireDate?t.patentExpireDate?e.patentExpireDate.localeCompare(t.patentExpireDate):-1:1),o.limit,o.skip);return{success:!0,data:v,totalResults:y,displayedResults:v.length,dataSource:d}})}async function _(t){return(await e.getPurpleBookData()).fold(e=>(n.bulk(`Purple Book data unavailable: ${e.message}`),[]),e=>e.filter(e=>!(t.drugName&&!i(e.proprietaryName,t.drugName)&&!i(e.properName,t.drugName)||t.applNo&&e.blaNumber!==t.applNo)).map(e=>({source:`Purple Book`,tradeName:e.proprietaryName,ingredient:e.properName,applNo:e.blaNumber,patentNo:``,patentExpireDate:``,drugSubstanceFlag:``,drugProductFlag:``,patentUseCode:``,exclusivities:[]})))}export{g as handleSearchDrugPatentExpiry,p as handleSearchOrangeBook,m as handleSearchOrangeBookPatents,h as handleSearchPurpleBook};
|
|
2
|
+
//# sourceMappingURL=bulk-data-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"bulk-data-handlers.js","names":["matchesFilter","filter","value","obJoinKey","record","groupEntries","items","keyFn","key","group","buildCountMap","Map","buildLookupMap","item","buildGroupMap","applyPagination","total","effectiveSkip","skip","effectiveLimit","limit","ORANGE_BOOK_SOURCE","PURPLE_BOOK_SOURCE","handleSearchOrangeBook","loggers","params","bulkDataClient","failure","patentCounts","patents","exclusivityCounts","exclusivities","products","p","handleSearchOrangeBookPatents","productMap","exclusivityMap","pat","product","excl","handleSearchPurpleBook","entries","handleSearchDrugPatentExpiry","includeExclusivity","includePurpleBook","error","orangeEntries","purpleEntries","getPurpleBookPatentEntries","a","b","pe"],"sources":["../../src/handlers/bulk-data-handlers.ts"],"sourcesContent":["/**\n * Bulk Data Handlers\n * Query handlers for FDA Orange Book and Purple Book data\n */\n\nimport { Map } from \"functype\"\n\nimport type { FDAToolResponse, OrangeBookExclusivity } from \"../types/fda.js\"\nimport { bulkDataClient } from \"../utils/bulk-data-client.js\"\nimport { failure } from \"../utils/errors.js\"\nimport { loggers } from \"../utils/logger.js\"\n\n// Helper for case-insensitive substring matching\nfunction matchesFilter(value: string, filter: string | undefined): boolean {\n if (!filter) return true\n return value.toLowerCase().includes(filter.toLowerCase())\n}\n\n// Join key for Orange Book records\nfunction obJoinKey(record: { applType: string; applNo: string; productNo: string }): string {\n return `${record.applType}-${record.applNo}-${record.productNo}`\n}\n\n// Group items by key in one linear pass (Object.groupBy). List.groupBy is much\n// slower on Orange Book-sized inputs, and these maps are rebuilt per query.\n// Object.entries lists integer-like keys first, in ascending order; obJoinKey\n// keys are never integer-like, and these maps are only used for lookups.\nfunction groupEntries<T>(items: readonly T[], keyFn: (item: T) => string): [string, T[]][] {\n return Object.entries(Object.groupBy(items, keyFn)).map(([key, group]): [string, T[]] => [key, group ?? []])\n}\n\n// Build a counting map from an array using a key function\nfunction buildCountMap<T>(items: readonly T[], keyFn: (item: T) => string): Map<string, number> {\n return Map(groupEntries(items, keyFn).map(([key, group]): [string, number] => [key, group.length]))\n}\n\n// Build a lookup map from an array using a key function (last item wins on duplicate keys)\nfunction buildLookupMap<T>(items: readonly T[], keyFn: (item: T) => string): Map<string, T> {\n return Map(items.map((item): [string, T] => [keyFn(item), item]))\n}\n\n// Build a grouped map from an array using a key function\nfunction buildGroupMap<T>(items: readonly T[], keyFn: (item: T) => string): Map<string, T[]> {\n return Map(groupEntries(items, keyFn))\n}\n\n// Default pagination values\nconst DEFAULT_LIMIT = 10\nconst MAX_LIMIT = 100\n\nfunction applyPagination<T>(items: T[], limit?: number, skip?: number): { items: T[]; total: number } {\n const total = items.length\n const effectiveSkip = skip ?? 0\n const effectiveLimit = Math.min(limit ?? DEFAULT_LIMIT, MAX_LIMIT)\n return {\n items: items.slice(effectiveSkip, effectiveSkip + effectiveLimit),\n total,\n }\n}\n\nconst ORANGE_BOOK_SOURCE = {\n name: \"FDA Orange Book\",\n lastUpdated: \"Monthly\",\n url: \"https://www.fda.gov/drugs/drug-approvals-and-databases/approved-drug-products-therapeutic-equivalence-evaluations-orange-book\",\n}\n\nconst PURPLE_BOOK_SOURCE = {\n name: \"FDA Purple Book\",\n lastUpdated: \"Monthly\",\n url: \"https://purplebooksearch.fda.gov/\",\n}\n\n// ============================================================\n// search_fda_orange_book\n// ============================================================\n\nexport type OrangeBookSearchParams = {\n drugName?: string\n applicant?: string\n applNo?: string\n teCode?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedOrangeBookProduct = {\n tradeName: string\n ingredient: string\n applicant: string\n applicantFullName: string\n applType: string\n applNo: string\n productNo: string\n dfRoute: string\n teCode: string\n approvalDate: string\n rld: string\n type: string\n patentCount: number\n exclusivityCount: number\n}\n\nexport async function handleSearchOrangeBook(\n params: OrangeBookSearchParams,\n): Promise<FDAToolResponse<FormattedOrangeBookProduct[]>> {\n loggers.tools(\"searchOrangeBook\", params)\n\n return (await bulkDataClient.getOrangeBookData()).fold(\n failure<FormattedOrangeBookProduct[]>,\n ({ products, patents, exclusivities }) => {\n const patentCounts = buildCountMap(patents, obJoinKey)\n const exclusivityCounts = buildCountMap(exclusivities, obJoinKey)\n\n const filtered = products.filter((p) => {\n if (!matchesFilter(p.tradeName, params.drugName) && !matchesFilter(p.ingredient, params.drugName)) return false\n if (!matchesFilter(p.applicant, params.applicant) && !matchesFilter(p.applicantFullName, params.applicant))\n return false\n if (params.applNo && p.applNo !== params.applNo) return false\n if (!matchesFilter(p.teCode, params.teCode)) return false\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedOrangeBookProduct[] = items.map((p) => {\n const key = obJoinKey(p)\n return {\n tradeName: p.tradeName,\n ingredient: p.ingredient,\n applicant: p.applicant,\n applicantFullName: p.applicantFullName,\n applType: p.applType,\n applNo: p.applNo,\n productNo: p.productNo,\n dfRoute: p.dfRoute,\n teCode: p.teCode,\n approvalDate: p.approvalDate,\n rld: p.rld,\n type: p.type,\n patentCount: patentCounts.getOrElse(key, 0),\n exclusivityCount: exclusivityCounts.getOrElse(key, 0),\n }\n })\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n },\n )\n}\n\n// ============================================================\n// search_fda_orange_book_patents\n// ============================================================\n\nexport type OrangeBookPatentsParams = {\n drugName?: string\n applNo?: string\n patentNo?: string\n limit?: number\n skip?: number\n}\n\ntype FormattedOrangeBookPatent = {\n patentNo: string\n patentExpireDate: string\n drugSubstanceFlag: string\n drugProductFlag: string\n patentUseCode: string\n applType: string\n applNo: string\n productNo: string\n tradeName: string\n ingredient: string\n submissionDate: string\n exclusivities: Array<{ code: string; date: string }>\n}\n\nexport async function handleSearchOrangeBookPatents(\n params: OrangeBookPatentsParams,\n): Promise<FDAToolResponse<FormattedOrangeBookPatent[]>> {\n loggers.tools(\"searchOrangeBookPatents\", params)\n\n return (await bulkDataClient.getOrangeBookData()).fold(\n failure<FormattedOrangeBookPatent[]>,\n ({ products, patents, exclusivities }) => {\n const productMap = buildLookupMap(products, obJoinKey)\n const exclusivityMap = buildGroupMap(exclusivities, obJoinKey)\n\n const filtered = patents.filter((pat) => {\n if (params.patentNo && pat.patentNo !== params.patentNo) return false\n if (params.applNo && pat.applNo !== params.applNo) return false\n\n if (params.drugName) {\n const product = productMap.get(obJoinKey(pat)).orUndefined()\n if (!product) return false\n if (!matchesFilter(product.tradeName, params.drugName) && !matchesFilter(product.ingredient, params.drugName))\n return false\n }\n\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedOrangeBookPatent[] = items.map((pat) => {\n const key = obJoinKey(pat)\n const product = productMap.get(key).orUndefined()\n const excl = exclusivityMap.getOrElse(key, [])\n\n return {\n patentNo: pat.patentNo,\n patentExpireDate: pat.patentExpireDate,\n drugSubstanceFlag: pat.drugSubstanceFlag,\n drugProductFlag: pat.drugProductFlag,\n patentUseCode: pat.patentUseCode,\n applType: pat.applType,\n applNo: pat.applNo,\n productNo: pat.productNo,\n tradeName: product?.tradeName ?? \"\",\n ingredient: product?.ingredient ?? \"\",\n submissionDate: pat.submissionDate,\n exclusivities: excl.map((e) => ({ code: e.exclusivityCode, date: e.exclusivityDate })),\n }\n })\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n },\n )\n}\n\n// ============================================================\n// search_fda_purple_book\n// ============================================================\n\nexport type PurpleBookSearchParams = {\n productName?: string\n applicant?: string\n blaNumber?: string\n licenseType?: \"351(a)\" | \"351(k)\"\n biosimilar?: boolean\n interchangeable?: boolean\n limit?: number\n skip?: number\n}\n\ntype FormattedPurpleBookEntry = {\n blaNumber: string\n proprietaryName: string\n properName: string\n applicant: string\n blaType: string\n strength: string\n dosageForm: string\n route: string\n status: string\n licensingStatus: string\n biosimilar: boolean\n interchangeable: boolean\n referenceProductBla: string\n referenceProductName: string\n approvalDate: string\n}\n\nexport async function handleSearchPurpleBook(\n params: PurpleBookSearchParams,\n): Promise<FDAToolResponse<FormattedPurpleBookEntry[]>> {\n loggers.tools(\"searchPurpleBook\", params)\n\n return (await bulkDataClient.getPurpleBookData()).fold(failure<FormattedPurpleBookEntry[]>, (entries) => {\n const filtered = entries.filter((e) => {\n if (!matchesFilter(e.proprietaryName, params.productName) && !matchesFilter(e.properName, params.productName))\n return false\n if (!matchesFilter(e.applicant, params.applicant)) return false\n if (params.blaNumber && e.blaNumber !== params.blaNumber) return false\n if (params.licenseType && e.blaType !== params.licenseType) return false\n if (params.biosimilar !== undefined && e.biosimilar !== params.biosimilar) return false\n if (params.interchangeable !== undefined && e.interchangeable !== params.interchangeable) return false\n return true\n })\n\n const { items, total } = applyPagination(filtered, params.limit, params.skip)\n\n const formatted: FormattedPurpleBookEntry[] = items.map((e) => ({\n blaNumber: e.blaNumber,\n proprietaryName: e.proprietaryName,\n properName: e.properName,\n applicant: e.applicant,\n blaType: e.blaType,\n strength: e.strength,\n dosageForm: e.dosageForm,\n route: e.route,\n status: e.status,\n licensingStatus: e.licensingStatus,\n biosimilar: e.biosimilar,\n interchangeable: e.interchangeable,\n referenceProductBla: e.referenceProductBla,\n referenceProductName: e.referenceProductProprietaryName,\n approvalDate: e.approvalDate,\n }))\n\n return {\n success: true,\n data: formatted,\n totalResults: total,\n displayedResults: items.length,\n dataSource: PURPLE_BOOK_SOURCE,\n }\n })\n}\n\n// ============================================================\n// search_fda_drug_patent_expiry\n// ============================================================\n\nexport type DrugPatentExpiryParams = {\n drugName?: string\n applNo?: string\n includeExclusivity?: boolean\n includePurpleBook?: boolean\n limit?: number\n skip?: number\n}\n\ntype PatentExpiryEntry = {\n source: string\n tradeName: string\n ingredient: string\n applNo: string\n patentNo: string\n patentExpireDate: string\n drugSubstanceFlag: string\n drugProductFlag: string\n patentUseCode: string\n exclusivities: Array<{ code: string; date: string }>\n}\n\nexport async function handleSearchDrugPatentExpiry(\n params: DrugPatentExpiryParams,\n): Promise<FDAToolResponse<PatentExpiryEntry[]>> {\n loggers.tools(\"searchDrugPatentExpiry\", params)\n\n const includeExclusivity = params.includeExclusivity !== false\n const includePurpleBook = params.includePurpleBook === true\n\n return (await bulkDataClient.getOrangeBookData()).fold(\n (error) => Promise.resolve(failure<PatentExpiryEntry[]>(error)),\n async ({ products, patents, exclusivities }) => {\n const productMap = buildLookupMap(products, obJoinKey)\n const exclusivityMap = includeExclusivity\n ? buildGroupMap(exclusivities, obJoinKey)\n : Map.empty<string, OrangeBookExclusivity[]>()\n\n // Filter patents by drug name / applNo\n const filteredPatents = patents.filter((pat) => {\n if (params.applNo && pat.applNo !== params.applNo) return false\n if (params.drugName) {\n const product = productMap.get(obJoinKey(pat)).orUndefined()\n if (!product) return false\n if (!matchesFilter(product.tradeName, params.drugName) && !matchesFilter(product.ingredient, params.drugName))\n return false\n }\n return true\n })\n\n const orangeEntries: PatentExpiryEntry[] = filteredPatents.map((pat) => {\n const key = obJoinKey(pat)\n const product = productMap.get(key).orUndefined()\n const excl = exclusivityMap.getOrElse(key, [])\n\n return {\n source: \"Orange Book\",\n tradeName: product?.tradeName ?? \"\",\n ingredient: product?.ingredient ?? \"\",\n applNo: pat.applNo,\n patentNo: pat.patentNo,\n patentExpireDate: pat.patentExpireDate,\n drugSubstanceFlag: pat.drugSubstanceFlag,\n drugProductFlag: pat.drugProductFlag,\n patentUseCode: pat.patentUseCode,\n exclusivities: excl.map((e) => ({ code: e.exclusivityCode, date: e.exclusivityDate })),\n }\n })\n\n // Optionally add Purple Book entries that match\n const purpleEntries: PatentExpiryEntry[] = includePurpleBook ? await getPurpleBookPatentEntries(params) : []\n\n const entries = [...orangeEntries, ...purpleEntries].sort((a, b) => {\n if (!a.patentExpireDate && !b.patentExpireDate) return 0\n if (!a.patentExpireDate) return 1\n if (!b.patentExpireDate) return -1\n return a.patentExpireDate.localeCompare(b.patentExpireDate)\n })\n\n const { items, total } = applyPagination(entries, params.limit, params.skip)\n\n return {\n success: true,\n data: items,\n totalResults: total,\n displayedResults: items.length,\n dataSource: ORANGE_BOOK_SOURCE,\n }\n },\n )\n}\n\nasync function getPurpleBookPatentEntries(params: DrugPatentExpiryParams): Promise<PatentExpiryEntry[]> {\n return (await bulkDataClient.getPurpleBookData()).fold(\n (error) => {\n loggers.bulk(`Purple Book data unavailable: ${error.message}`)\n return []\n },\n (purpleEntries) =>\n purpleEntries\n .filter((e) => {\n if (params.drugName) {\n if (!matchesFilter(e.proprietaryName, params.drugName) && !matchesFilter(e.properName, params.drugName))\n return false\n }\n if (params.applNo && e.blaNumber !== params.applNo) return false\n return true\n })\n .map((pe): PatentExpiryEntry => ({\n source: \"Purple Book\",\n tradeName: pe.proprietaryName,\n ingredient: pe.properName,\n applNo: pe.blaNumber,\n patentNo: \"\",\n patentExpireDate: \"\",\n drugSubstanceFlag: \"\",\n drugProductFlag: \"\",\n patentUseCode: \"\",\n exclusivities: [],\n })),\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Device Handlers
|
|
3
|
+
* Query handlers for FDA device-related endpoints
|
|
4
|
+
*/
|
|
1
5
|
import { FDAToolResponse } from "../types/fda.js";
|
|
2
|
-
|
|
3
|
-
//#region src/handlers/device-handlers.d.ts
|
|
4
|
-
|
|
5
|
-
type Device510KParams = {
|
|
6
|
+
export type Device510KParams = {
|
|
6
7
|
deviceName?: string;
|
|
7
8
|
applicant?: string;
|
|
8
9
|
productCode?: string;
|
|
@@ -26,8 +27,8 @@ type FormattedDevice510K = {
|
|
|
26
27
|
state: string | undefined;
|
|
27
28
|
country: string | undefined;
|
|
28
29
|
};
|
|
29
|
-
declare function handleSearchDevice510K(params: Device510KParams): Promise<FDAToolResponse<FormattedDevice510K[]>>;
|
|
30
|
-
type DeviceClassificationParams = {
|
|
30
|
+
export declare function handleSearchDevice510K(params: Device510KParams): Promise<FDAToolResponse<FormattedDevice510K[]>>;
|
|
31
|
+
export type DeviceClassificationParams = {
|
|
31
32
|
deviceName?: string;
|
|
32
33
|
deviceClass?: "1" | "2" | "3";
|
|
33
34
|
medicalSpecialty?: string;
|
|
@@ -48,8 +49,8 @@ type FormattedDeviceClassification = {
|
|
|
48
49
|
implant: boolean;
|
|
49
50
|
lifeSustaining: boolean;
|
|
50
51
|
};
|
|
51
|
-
declare function handleSearchDeviceClassifications(params: DeviceClassificationParams): Promise<FDAToolResponse<FormattedDeviceClassification[]>>;
|
|
52
|
-
type DeviceAdverseEventsParams = {
|
|
52
|
+
export declare function handleSearchDeviceClassifications(params: DeviceClassificationParams): Promise<FDAToolResponse<FormattedDeviceClassification[]>>;
|
|
53
|
+
export type DeviceAdverseEventsParams = {
|
|
53
54
|
reportNumber?: string;
|
|
54
55
|
deviceName?: string;
|
|
55
56
|
brandName?: string;
|
|
@@ -80,8 +81,8 @@ type FormattedDeviceAdverseEvent = {
|
|
|
80
81
|
text: string | undefined;
|
|
81
82
|
}>;
|
|
82
83
|
};
|
|
83
|
-
declare function handleSearchDeviceAdverseEvents(params: DeviceAdverseEventsParams): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>>;
|
|
84
|
-
type DeviceEnforcementParams = {
|
|
84
|
+
export declare function handleSearchDeviceAdverseEvents(params: DeviceAdverseEventsParams): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>>;
|
|
85
|
+
export type DeviceEnforcementParams = {
|
|
85
86
|
recallingFirm?: string;
|
|
86
87
|
productDescription?: string;
|
|
87
88
|
classification?: "Class I" | "Class II" | "Class III";
|
|
@@ -105,7 +106,5 @@ type FormattedDeviceEnforcement = {
|
|
|
105
106
|
deviceName: string | undefined;
|
|
106
107
|
deviceClass: string | undefined;
|
|
107
108
|
};
|
|
108
|
-
declare function handleSearchDeviceEnforcement(params: DeviceEnforcementParams): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>>;
|
|
109
|
-
|
|
110
|
-
export { Device510KParams, DeviceAdverseEventsParams, DeviceClassificationParams, DeviceEnforcementParams, handleSearchDevice510K, handleSearchDeviceAdverseEvents, handleSearchDeviceClassifications, handleSearchDeviceEnforcement };
|
|
111
|
-
//# sourceMappingURL=device-handlers.d.ts.map
|
|
109
|
+
export declare function handleSearchDeviceEnforcement(params: DeviceEnforcementParams): Promise<FDAToolResponse<FormattedDeviceEnforcement[]>>;
|
|
110
|
+
export {};
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import"../utils/
|
|
1
|
+
import{fdaAPIClient as e}from"../utils/api-client.js";import{loggers as t}from"../utils/logger.js";import{buildDateQuery as n,buildSearchQuery as r,escapeSearchTerm as i,joinQueries as a,toSearchResponse as o,truncateText as s}from"./query.js";function c(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 l(i){t.tools(`searchDevice510K`,i);let s=[{field:`device_name`,value:i.deviceName},{field:`applicant`,value:i.applicant},{field:`product_code`,value:i.productCode},{field:`clearance_type`,value:i.clearanceType}],l=r(s),u=n(`decision_date`,i.decisionDateFrom,i.decisionDateTo),d={search:a(l,u)||void 0,limit:i.limit,skip:i.skip};return o(await e.searchDevice510K(d),d,c)}function u(e){return{deviceName:e.device_name,deviceClass:e.device_class,definition:s(e.definition).orUndefined(),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 d(n){t.tools(`searchDeviceClassifications`,n);let 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}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDeviceClassifications(a),a,u)}function f(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:s(e.text,300).orUndefined()}))??[]}}async function p(s){if(t.tools(`searchDeviceAdverseEvents`,s),s.reportNumber){let t={search:`report_number:"${i(s.reportNumber)}"`,limit:1};return o(await e.searchDeviceAdverseEvents(t),t,f)}let c=[{field:`device.generic_name`,value:s.deviceName},{field:`device.brand_name`,value:s.brandName},{field:`device.manufacturer_d_name`,value:s.manufacturerName},{field:`event_type`,value:s.eventType}],l=r(c),u=n(`date_received`,s.dateFrom,s.dateTo),d={search:a(l,u)||void 0,limit:s.limit,skip:s.skip};return o(await e.searchDeviceAdverseEvents(d),d,f)}function m(e){return{recallNumber:e.recall_number,recallingFirm:e.recalling_firm,classification:e.classification,status:e.status,productDescription:s(e.product_description).orUndefined(),reasonForRecall:s(e.reason_for_recall).orUndefined(),recallInitiationDate:e.recall_initiation_date,distributionPattern:s(e.distribution_pattern).orUndefined(),city:e.city,state:e.state,deviceName:e.openfda?.device_name,deviceClass:e.openfda?.device_class}}async function h(i){t.tools(`searchDeviceEnforcement`,i);let s=[{field:`recalling_firm`,value:i.recallingFirm},{field:`product_description`,value:i.productDescription},{field:`classification`,value:i.classification},{field:`status`,value:i.status}],c=r(s),l=n(`recall_initiation_date`,i.dateFrom,i.dateTo),u={search:a(c,l)||void 0,limit:i.limit,skip:i.skip};return o(await e.searchDeviceEnforcement(u),u,m)}export{l as handleSearchDevice510K,p as handleSearchDeviceAdverseEvents,d as handleSearchDeviceClassifications,h as handleSearchDeviceEnforcement};
|
|
2
|
+
//# sourceMappingURL=device-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"device-handlers.js","names":["formatDevice510K","device","handleSearchDevice510K","loggers","params","searchTerms","searchQuery","buildSearchQuery","dateQuery","buildDateQuery","searchParams","joinQueries","toSearchResponse","fdaAPIClient","formatDeviceClassification","classification","truncateText","handleSearchDeviceClassifications","formatDeviceAdverseEvent","event","d","t","handleSearchDeviceAdverseEvents","escapeSearchTerm","formatDeviceEnforcement","recall","handleSearchDeviceEnforcement"],"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\"\nimport {\n buildDateQuery,\n buildSearchQuery,\n escapeSearchTerm,\n joinQueries,\n toSearchResponse,\n truncateText,\n} from \"./query.js\"\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 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 = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(await fdaAPIClient.searchDevice510K(searchParams), searchParams, formatDevice510K)\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).orUndefined(),\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 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 return toSearchResponse(\n await fdaAPIClient.searchDeviceClassifications(searchParams),\n searchParams,\n formatDeviceClassification,\n )\n}\n\n// Device Adverse Events (MDR) Handler\nexport type DeviceAdverseEventsParams = {\n reportNumber?: string\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).orUndefined(),\n })) ?? [],\n }\n}\n\nexport async function handleSearchDeviceAdverseEvents(\n params: DeviceAdverseEventsParams,\n): Promise<FDAToolResponse<FormattedDeviceAdverseEvent[]>> {\n loggers.tools(\"searchDeviceAdverseEvents\", params)\n\n // If reportNumber is provided, do a direct lookup\n if (params.reportNumber) {\n const searchParams: SearchParams = {\n search: `report_number:\"${escapeSearchTerm(params.reportNumber)}\"`,\n limit: 1,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDeviceAdverseEvents(searchParams),\n searchParams,\n formatDeviceAdverseEvent,\n )\n }\n\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 = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDeviceAdverseEvents(searchParams),\n searchParams,\n formatDeviceAdverseEvent,\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).orUndefined(),\n reasonForRecall: truncateText(recall.reason_for_recall).orUndefined(),\n recallInitiationDate: recall.recall_initiation_date,\n distributionPattern: truncateText(recall.distribution_pattern).orUndefined(),\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 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 = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDeviceEnforcement(searchParams),\n searchParams,\n formatDeviceEnforcement,\n )\n}\n"],"mappings":"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"}
|
|
@@ -1,8 +1,9 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Drug Handlers
|
|
3
|
+
* Query handlers for FDA drug-related endpoints
|
|
4
|
+
*/
|
|
1
5
|
import { FDAToolResponse } from "../types/fda.js";
|
|
2
|
-
|
|
3
|
-
//#region src/handlers/drug-handlers.d.ts
|
|
4
|
-
|
|
5
|
-
type DrugAdverseEventsParams = {
|
|
6
|
+
export type DrugAdverseEventsParams = {
|
|
6
7
|
safetyReportId?: string;
|
|
7
8
|
drugName?: string;
|
|
8
9
|
reaction?: string;
|
|
@@ -32,8 +33,8 @@ type FormattedDrugAdverseEvent = {
|
|
|
32
33
|
weight: string | undefined;
|
|
33
34
|
};
|
|
34
35
|
};
|
|
35
|
-
declare function handleSearchDrugAdverseEvents(params: DrugAdverseEventsParams): Promise<FDAToolResponse<FormattedDrugAdverseEvent[]>>;
|
|
36
|
-
type DrugLabelsParams = {
|
|
36
|
+
export declare function handleSearchDrugAdverseEvents(params: DrugAdverseEventsParams): Promise<FDAToolResponse<FormattedDrugAdverseEvent[]>>;
|
|
37
|
+
export type DrugLabelsParams = {
|
|
37
38
|
setId?: string;
|
|
38
39
|
drugName?: string;
|
|
39
40
|
indication?: string;
|
|
@@ -66,8 +67,8 @@ type FormattedDrugLabel = {
|
|
|
66
67
|
storageAndHandling: string | undefined;
|
|
67
68
|
route: string[];
|
|
68
69
|
};
|
|
69
|
-
declare function handleSearchDrugLabels(params: DrugLabelsParams): Promise<FDAToolResponse<FormattedDrugLabel[]>>;
|
|
70
|
-
type DrugNDCParams = {
|
|
70
|
+
export declare function handleSearchDrugLabels(params: DrugLabelsParams): Promise<FDAToolResponse<FormattedDrugLabel[]>>;
|
|
71
|
+
export type DrugNDCParams = {
|
|
71
72
|
productNdc?: string;
|
|
72
73
|
brandName?: string;
|
|
73
74
|
genericName?: string;
|
|
@@ -91,8 +92,8 @@ type FormattedDrugNDC = {
|
|
|
91
92
|
marketingStartDate: string | undefined;
|
|
92
93
|
productType: string | undefined;
|
|
93
94
|
};
|
|
94
|
-
declare function handleSearchDrugNDC(params: DrugNDCParams): Promise<FDAToolResponse<FormattedDrugNDC[]>>;
|
|
95
|
-
type DrugEnforcementParams = {
|
|
95
|
+
export declare function handleSearchDrugNDC(params: DrugNDCParams): Promise<FDAToolResponse<FormattedDrugNDC[]>>;
|
|
96
|
+
export type DrugEnforcementParams = {
|
|
96
97
|
recallingFirm?: string;
|
|
97
98
|
classification?: "Class I" | "Class II" | "Class III";
|
|
98
99
|
status?: "Ongoing" | "Completed" | "Terminated" | "Pending";
|
|
@@ -114,8 +115,8 @@ type FormattedDrugEnforcement = {
|
|
|
114
115
|
city: string | undefined;
|
|
115
116
|
state: string | undefined;
|
|
116
117
|
};
|
|
117
|
-
declare function handleSearchDrugEnforcement(params: DrugEnforcementParams): Promise<FDAToolResponse<FormattedDrugEnforcement[]>>;
|
|
118
|
-
type DrugsFDAParams = {
|
|
118
|
+
export declare function handleSearchDrugEnforcement(params: DrugEnforcementParams): Promise<FDAToolResponse<FormattedDrugEnforcement[]>>;
|
|
119
|
+
export type DrugsFDAParams = {
|
|
119
120
|
sponsorName?: string;
|
|
120
121
|
applicationNumber?: string;
|
|
121
122
|
brandName?: string;
|
|
@@ -142,8 +143,8 @@ type FormattedDrugsFDA = {
|
|
|
142
143
|
statusDate: string | undefined;
|
|
143
144
|
} | undefined;
|
|
144
145
|
};
|
|
145
|
-
declare function handleSearchDrugsFDA(params: DrugsFDAParams): Promise<FDAToolResponse<FormattedDrugsFDA[]>>;
|
|
146
|
-
type DrugShortagesParams = {
|
|
146
|
+
export declare function handleSearchDrugsFDA(params: DrugsFDAParams): Promise<FDAToolResponse<FormattedDrugsFDA[]>>;
|
|
147
|
+
export type DrugShortagesParams = {
|
|
147
148
|
genericName?: string;
|
|
148
149
|
status?: "Current" | "Resolved";
|
|
149
150
|
limit?: number;
|
|
@@ -157,7 +158,5 @@ type FormattedDrugShortage = {
|
|
|
157
158
|
initialPostingDate: string | undefined;
|
|
158
159
|
resolvedShortageDate: string | undefined;
|
|
159
160
|
};
|
|
160
|
-
declare function handleSearchDrugShortages(params: DrugShortagesParams): Promise<FDAToolResponse<FormattedDrugShortage[]>>;
|
|
161
|
-
|
|
162
|
-
export { DrugAdverseEventsParams, DrugEnforcementParams, DrugLabelsParams, DrugNDCParams, DrugShortagesParams, DrugsFDAParams, handleSearchDrugAdverseEvents, handleSearchDrugEnforcement, handleSearchDrugLabels, handleSearchDrugNDC, handleSearchDrugShortages, handleSearchDrugsFDA };
|
|
163
|
-
//# sourceMappingURL=drug-handlers.d.ts.map
|
|
161
|
+
export declare function handleSearchDrugShortages(params: DrugShortagesParams): Promise<FDAToolResponse<FormattedDrugShortage[]>>;
|
|
162
|
+
export {};
|
|
@@ -1 +1,2 @@
|
|
|
1
|
-
import"../utils/
|
|
1
|
+
import{fdaAPIClient as e}from"../utils/api-client.js";import{loggers as t}from"../utils/logger.js";import{buildDateQuery as n,buildSearchQuery as r,escapeSearchTerm as i,joinQueries as a,toSearchResponse as o,truncateText as s}from"./query.js";import{None as c,Some as l}from"functype";function u(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 d(s){if(t.tools(`searchDrugAdverseEvents`,s),s.safetyReportId){let t={search:`safetyreportid:"${i(s.safetyReportId)}"`,limit:1};return o(await e.searchDrugAdverseEvents(t),t,u)}let c=[{field:`patient.drug.medicinalproduct`,value:s.drugName},{field:`patient.reaction.reactionmeddrapt`,value:s.reaction},{field:`patient.drug.openfda.manufacturer_name`,value:s.manufacturer}];s.serious!==void 0&&c.push({field:`serious`,value:s.serious?`1`:`2`});let l=r(c),d=n(`receivedate`,s.dateFrom,s.dateTo),f={search:a(l,d)||void 0,limit:s.limit,skip:s.skip};return o(await e.searchDrugAdverseEvents(f),f,u)}function f(e,t){let n=e=>!t||t.includes(e);return{setId:e.set_id,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)??[],boxedWarning:n(`boxed_warning`)?s(e.boxed_warning?.[0],500).orUndefined():void 0,indications:n(`indications_and_usage`)?s(e.indications_and_usage?.[0]).orUndefined():void 0,warnings:n(`warnings`)?s(e.warnings?.[0]).orUndefined():void 0,contraindications:n(`contraindications`)?s(e.contraindications?.[0]).orUndefined():void 0,adverseReactions:n(`adverse_reactions`)?s(e.adverse_reactions?.[0]).orUndefined():void 0,drugInteractions:n(`drug_interactions`)?s(e.drug_interactions?.[0]).orUndefined():void 0,dosageAndAdministration:n(`dosage_and_administration`)?s(e.dosage_and_administration?.[0]).orUndefined():void 0,clinicalPharmacology:n(`clinical_pharmacology`)?s(e.clinical_pharmacology?.[0]).orUndefined():void 0,mechanismOfAction:n(`mechanism_of_action`)?s(e.mechanism_of_action?.[0]).orUndefined():void 0,pharmacokinetics:n(`pharmacokinetics`)?s(e.pharmacokinetics?.[0]).orUndefined():void 0,overdosage:n(`overdosage`)?s(e.overdosage?.[0]).orUndefined():void 0,description:n(`description`)?s(e.description?.[0]).orUndefined():void 0,howSupplied:n(`how_supplied`)?s(e.how_supplied?.[0]).orUndefined():void 0,storageAndHandling:n(`storage_and_handling`)?s(e.storage_and_handling?.[0]).orUndefined():void 0,route:e.openfda?.route?.slice(0,3)??[]}}async function p(n){if(t.tools(`searchDrugLabels`,n),n.setId){let t={search:`set_id:"${i(n.setId)}"`,limit:1};return o(await e.searchDrugLabels(t),t,e=>f(e,n.sections))}let s=[{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}],u=r(s),d=n.hasBoxedWarning?l(`_exists_:boxed_warning`):c(),p={search:a(u,d)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugLabels(p),p,e=>f(e,n.sections))}function m(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 h(n){t.tools(`searchDrugNDC`,n);let 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}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugNDC(a),a,m)}function g(e){return{recallNumber:e.recall_number,recallingFirm:e.recalling_firm,classification:e.classification,status:e.status,productDescription:s(e.product_description).orUndefined(),reasonForRecall:s(e.reason_for_recall).orUndefined(),recallInitiationDate:e.recall_initiation_date,distributionPattern:s(e.distribution_pattern).orUndefined(),city:e.city,state:e.state}}async function _(i){t.tools(`searchDrugEnforcement`,i);let s=[{field:`recalling_firm`,value:i.recallingFirm},{field:`classification`,value:i.classification},{field:`status`,value:i.status},{field:`state`,value:i.state}],c=r(s),l=n(`recall_initiation_date`,i.dateFrom,i.dateTo),u={search:a(c,l)||void 0,limit:i.limit,skip:i.skip};return o(await e.searchDrugEnforcement(u),u,g)}function v(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 y(n){t.tools(`searchDrugsFDA`,n);let 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}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugsFDA(a),a,v)}function b(e){return{genericName:e.generic_name,proprietaryName:e.proprietary_name,status:e.status,description:s(e.description).orUndefined(),initialPostingDate:e.initial_posting_date,resolvedShortageDate:e.resolved_shortage_date}}async function x(n){t.tools(`searchDrugShortages`,n);let i=[{field:`generic_name`,value:n.genericName},{field:`status`,value:n.status}],a={search:r(i)||void 0,limit:n.limit,skip:n.skip};return o(await e.searchDrugShortages(a),a,b)}export{d as handleSearchDrugAdverseEvents,_ as handleSearchDrugEnforcement,p as handleSearchDrugLabels,h as handleSearchDrugNDC,x as handleSearchDrugShortages,y as handleSearchDrugsFDA};
|
|
2
|
+
//# sourceMappingURL=drug-handlers.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"drug-handlers.js","names":["formatDrugAdverseEvent","event","d","r","handleSearchDrugAdverseEvents","loggers","params","searchParams","escapeSearchTerm","toSearchResponse","fdaAPIClient","searchTerms","searchQuery","buildSearchQuery","dateQuery","buildDateQuery","joinQueries","formatDrugLabel","includeSection","sections","sectionName","label","truncateText","handleSearchDrugLabels","boxedWarningQuery","Some","None","formatDrugNDC","ndc","ai","handleSearchDrugNDC","formatDrugEnforcement","recall","handleSearchDrugEnforcement","formatDrugsFDA","latestSubmission","drug","p","handleSearchDrugsFDA","formatDrugShortage","shortage","handleSearchDrugShortages"],"sources":["../../src/handlers/drug-handlers.ts"],"sourcesContent":["/**\n * Drug Handlers\n * Query handlers for FDA drug-related endpoints\n */\n\nimport { None, Some } from \"functype\"\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\"\nimport {\n buildDateQuery,\n buildSearchQuery,\n escapeSearchTerm,\n joinQueries,\n toSearchResponse,\n truncateText,\n} from \"./query.js\"\n\n// Drug Adverse Events Handler\nexport type DrugAdverseEventsParams = {\n safetyReportId?: string\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 // If safetyReportId is provided, do a direct lookup\n if (params.safetyReportId) {\n const searchParams: SearchParams = {\n search: `safetyreportid:\"${escapeSearchTerm(params.safetyReportId)}\"`,\n limit: 1,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDrugAdverseEvents(searchParams),\n searchParams,\n formatDrugAdverseEvent,\n )\n }\n\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 = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(\n await fdaAPIClient.searchDrugAdverseEvents(searchParams),\n searchParams,\n formatDrugAdverseEvent,\n )\n}\n\n// Drug Labels Handler\nexport type DrugLabelsParams = {\n setId?: string\n drugName?: string\n indication?: string\n activeIngredient?: string\n route?: string\n hasBoxedWarning?: boolean\n sections?: string[]\n limit?: number\n skip?: number\n}\n\ntype FormattedDrugLabel = {\n setId: string | undefined\n brandName: string | undefined\n genericName: string | undefined\n manufacturer: string | undefined\n activeIngredients: string[]\n boxedWarning: string | undefined\n indications: string | undefined\n warnings: string | undefined\n contraindications: string | undefined\n adverseReactions: string | undefined\n drugInteractions: string | undefined\n dosageAndAdministration: string | undefined\n clinicalPharmacology: string | undefined\n mechanismOfAction: string | undefined\n pharmacokinetics: string | undefined\n overdosage: string | undefined\n description: string | undefined\n howSupplied: string | undefined\n storageAndHandling: string | undefined\n route: string[]\n}\n\nfunction formatDrugLabel(label: DrugLabel, sections?: string[]): FormattedDrugLabel {\n // Helper to conditionally include section based on sections filter\n const includeSection = (sectionName: string) => !sections || sections.includes(sectionName)\n\n return {\n setId: label.set_id,\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 boxedWarning: includeSection(\"boxed_warning\")\n ? truncateText(label.boxed_warning?.[0], 500).orUndefined()\n : undefined,\n indications: includeSection(\"indications_and_usage\")\n ? truncateText(label.indications_and_usage?.[0]).orUndefined()\n : undefined,\n warnings: includeSection(\"warnings\") ? truncateText(label.warnings?.[0]).orUndefined() : undefined,\n contraindications: includeSection(\"contraindications\")\n ? truncateText(label.contraindications?.[0]).orUndefined()\n : undefined,\n adverseReactions: includeSection(\"adverse_reactions\")\n ? truncateText(label.adverse_reactions?.[0]).orUndefined()\n : undefined,\n drugInteractions: includeSection(\"drug_interactions\")\n ? truncateText(label.drug_interactions?.[0]).orUndefined()\n : undefined,\n dosageAndAdministration: includeSection(\"dosage_and_administration\")\n ? truncateText(label.dosage_and_administration?.[0]).orUndefined()\n : undefined,\n clinicalPharmacology: includeSection(\"clinical_pharmacology\")\n ? truncateText(label.clinical_pharmacology?.[0]).orUndefined()\n : undefined,\n mechanismOfAction: includeSection(\"mechanism_of_action\")\n ? truncateText(label.mechanism_of_action?.[0]).orUndefined()\n : undefined,\n pharmacokinetics: includeSection(\"pharmacokinetics\")\n ? truncateText(label.pharmacokinetics?.[0]).orUndefined()\n : undefined,\n overdosage: includeSection(\"overdosage\") ? truncateText(label.overdosage?.[0]).orUndefined() : undefined,\n description: includeSection(\"description\") ? truncateText(label.description?.[0]).orUndefined() : undefined,\n howSupplied: includeSection(\"how_supplied\") ? truncateText(label.how_supplied?.[0]).orUndefined() : undefined,\n storageAndHandling: includeSection(\"storage_and_handling\")\n ? truncateText(label.storage_and_handling?.[0]).orUndefined()\n : undefined,\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 // If setId is provided, do a direct lookup\n if (params.setId) {\n const searchParams: SearchParams = {\n search: `set_id:\"${escapeSearchTerm(params.setId)}\"`,\n limit: 1,\n }\n\n return toSearchResponse(await fdaAPIClient.searchDrugLabels(searchParams), searchParams, (label) =>\n formatDrugLabel(label, params.sections),\n )\n }\n\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 // Build boxed warning existence query\n const boxedWarningQuery = params.hasBoxedWarning ? Some(\"_exists_:boxed_warning\") : None<string>()\n\n const fullQuery = joinQueries(searchQuery, boxedWarningQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(await fdaAPIClient.searchDrugLabels(searchParams), searchParams, (label) =>\n formatDrugLabel(label, params.sections),\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 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 return toSearchResponse(await fdaAPIClient.searchDrugNDC(searchParams), searchParams, formatDrugNDC)\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).orUndefined(),\n reasonForRecall: truncateText(recall.reason_for_recall).orUndefined(),\n recallInitiationDate: recall.recall_initiation_date,\n distributionPattern: truncateText(recall.distribution_pattern).orUndefined(),\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 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 = joinQueries(searchQuery, dateQuery)\n\n const searchParams: SearchParams = {\n search: fullQuery || undefined,\n limit: params.limit,\n skip: params.skip,\n }\n\n return toSearchResponse(await fdaAPIClient.searchDrugEnforcement(searchParams), searchParams, formatDrugEnforcement)\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 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 return toSearchResponse(await fdaAPIClient.searchDrugsFDA(searchParams), searchParams, formatDrugsFDA)\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).orUndefined(),\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 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 return toSearchResponse(await fdaAPIClient.searchDrugShortages(searchParams), searchParams, formatDrugShortage)\n}\n"],"mappings":"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"}
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Query building and response shaping shared by the openFDA API handlers
|
|
3
|
+
*/
|
|
4
|
+
import { FDAResponse, FDAToolResponse, SearchParams } from "../types/fda.js";
|
|
5
|
+
import { Either, Option } from "functype";
|
|
6
|
+
export type SearchTerm = {
|
|
7
|
+
field: string;
|
|
8
|
+
value?: string;
|
|
9
|
+
};
|
|
10
|
+
/** Escape characters that have meaning in openFDA's search syntax */
|
|
11
|
+
export declare function escapeSearchTerm(term: string): string;
|
|
12
|
+
/** AND together the terms that have a non-blank value */
|
|
13
|
+
export declare function buildSearchQuery(terms: readonly SearchTerm[]): string;
|
|
14
|
+
/** A date range clause, or None when neither bound is given. openFDA dates are YYYYMMDD. */
|
|
15
|
+
export declare function buildDateQuery(field: string, dateFrom?: string, dateTo?: string): Option<string>;
|
|
16
|
+
/** Join a search query with optional extra clauses, skipping empty parts */
|
|
17
|
+
export declare function joinQueries(searchQuery: string, ...clauses: readonly Option<string>[]): string;
|
|
18
|
+
/** Shorten long text for display; None for missing or empty text */
|
|
19
|
+
export declare function truncateText(text: string | undefined, maxLength?: number): Option<string>;
|
|
20
|
+
/** Turn a client result into the tool response, formatting each record */
|
|
21
|
+
export declare function toSearchResponse<R, F>(result: Either<Error, FDAResponse<R>>, searchParams: SearchParams, format: (record: R) => F): FDAToolResponse<F[]>;
|
|
@@ -0,0 +1,2 @@
|
|
|
1
|
+
import{fdaAPIClient as e}from"../utils/api-client.js";import{failure as t}from"../utils/errors.js";import{None as n,Option as r,Some as i}from"functype";function a(e){return e.replace(/[+\-&|!(){}[\]^"~*?:\\]/g,`\\$&`).replace(/\s+/g,`+`)}function o(e){return e.flatMap(e=>r(e.value).filter(e=>e.trim()!==``).map(t=>`${e.field}:"${a(t)}"`).toArray()).join(`+AND+`)}function s(e,t,a){let o=e=>r(e).map(e=>e.replace(/-/g,``)).orElse(`*`);return[t,a].some(e=>(e??``)!==``)?i(`${e}:[${o(t)}+TO+${o(a)}]`):n()}function c(e,...t){return[e,...t.flatMap(e=>e.toArray())].filter(e=>e!==``).join(`+AND+`)}function l(e,t=200){return r(e).filter(e=>e!==``).map(e=>e.length>t?`${e.substring(0,t)}...`:e)}function u(n,r,i){return n.fold(t,t=>{let n=t.results??[];return{success:!0,data:n.map(i),totalResults:t.meta?.results?.total,displayedResults:n.length,searchParams:r,apiUsage:e.getRateLimitInfo()}})}export{s as buildDateQuery,o as buildSearchQuery,a as escapeSearchTerm,c as joinQueries,u as toSearchResponse,l as truncateText};
|
|
2
|
+
//# sourceMappingURL=query.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"query.js","names":["escapeSearchTerm","term","buildSearchQuery","terms","Option","value","buildDateQuery","bound","date","d","dateFrom","dateTo","Some","field","None","joinQueries","clauses","searchQuery","clause","part","truncateText","text","t","maxLength","toSearchResponse","result","failure","results","response","format","fdaAPIClient"],"sources":["../../src/handlers/query.ts"],"sourcesContent":["/**\n * Query building and response shaping shared by the openFDA API handlers\n */\n\nimport type { Either } from \"functype\"\nimport { None, Option, Some } from \"functype\"\n\nimport type { FDAResponse, FDAToolResponse, SearchParams } from \"../types/fda.js\"\nimport { fdaAPIClient } from \"../utils/api-client.js\"\nimport { failure } from \"../utils/errors.js\"\n\nexport type SearchTerm = { field: string; value?: string }\n\n/** Escape characters that have meaning in openFDA's search syntax */\nexport function escapeSearchTerm(term: string): string {\n return term.replace(/[+\\-&|!(){}[\\]^\"~*?:\\\\]/g, \"\\\\$&\").replace(/\\s+/g, \"+\")\n}\n\n/** AND together the terms that have a non-blank value */\nexport function buildSearchQuery(terms: readonly SearchTerm[]): string {\n return terms\n .flatMap((term) =>\n Option(term.value)\n .filter((value) => value.trim() !== \"\")\n .map((value) => `${term.field}:\"${escapeSearchTerm(value)}\"`)\n .toArray(),\n )\n .join(\"+AND+\")\n}\n\n/** A date range clause, or None when neither bound is given. openFDA dates are YYYYMMDD. */\nexport function buildDateQuery(field: string, dateFrom?: string, dateTo?: string): Option<string> {\n const bound = (date?: string): string =>\n Option(date)\n .map((d) => d.replace(/-/g, \"\"))\n .orElse(\"*\")\n\n const hasBound = [dateFrom, dateTo].some((date) => (date ?? \"\") !== \"\")\n\n return hasBound ? Some(`${field}:[${bound(dateFrom)}+TO+${bound(dateTo)}]`) : None()\n}\n\n/** Join a search query with optional extra clauses, skipping empty parts */\nexport function joinQueries(searchQuery: string, ...clauses: readonly Option<string>[]): string {\n return [searchQuery, ...clauses.flatMap((clause) => clause.toArray())].filter((part) => part !== \"\").join(\"+AND+\")\n}\n\n/** Shorten long text for display; None for missing or empty text */\nexport function truncateText(text: string | undefined, maxLength = 200): Option<string> {\n return Option(text)\n .filter((t) => t !== \"\")\n .map((t) => (t.length > maxLength ? `${t.substring(0, maxLength)}...` : t))\n}\n\n/** Turn a client result into the tool response, formatting each record */\nexport function toSearchResponse<R, F>(\n result: Either<Error, FDAResponse<R>>,\n searchParams: SearchParams,\n format: (record: R) => F,\n): FDAToolResponse<F[]> {\n return result.fold(failure<F[]>, (response) => {\n const results = response.results ?? []\n return {\n success: true,\n data: results.map(format),\n totalResults: response.meta?.results?.total,\n displayedResults: results.length,\n searchParams,\n apiUsage: fdaAPIClient.getRateLimitInfo(),\n }\n })\n}\n"],"mappings":"A,yJAcA,SAAgBA,EAAiB,EAAsB,CACrD,OAAOC,EAAK,QAAQ,2BAA4B,MAAM,CAAC,CAAC,QAAQ,OAAQ,GAAG,CAC7E,CAGA,SAAgBC,EAAiB,EAAsC,CACrE,OAAOC,EACJ,QAAS,GACRC,EAAOH,EAAK,KAAK,CAAC,CACf,OAAQ,GAAUI,EAAM,KAAK,IAAM,EAAE,CAAC,CACtC,IAAK,GAAU,GAAGJ,EAAK,MAAM,IAAID,EAAiBK,CAAK,EAAE,EAAE,CAAC,CAC5D,QAAQ,CACb,CAAC,CACA,KAAK,OAAO,CACjB,CAGA,SAAgBC,EAAe,EAAe,EAAmB,EAAiC,CAChG,IAAMC,EAAS,GACbH,EAAOI,CAAI,CAAC,CACT,IAAK,GAAMC,EAAE,QAAQ,KAAM,EAAE,CAAC,CAAC,CAC/B,OAAO,GAAG,EAIf,MAFiB,CAACC,EAAUC,CAAM,CAAC,CAAC,KAAM,IAAUH,GAAQ,MAAQ,EAEtD,EAAII,EAAK,GAAGC,EAAM,IAAIN,EAAMG,CAAQ,EAAE,MAAMH,EAAMI,CAAM,EAAE,EAAE,EAAIG,EAAK,CACrF,CAGA,SAAgBC,EAAY,EAAqB,GAAGC,EAA4C,CAC9F,MAAO,CAACC,EAAa,GAAGD,EAAQ,QAAS,GAAWE,EAAO,QAAQ,CAAC,CAAC,CAAC,CAAC,OAAQ,GAASC,IAAS,EAAE,CAAC,CAAC,KAAK,OAAO,CACnH,CAGA,SAAgBC,EAAa,EAA0B,EAAY,IAAqB,CACtF,OAAOhB,EAAOiB,CAAI,CAAC,CAChB,OAAQ,GAAMC,IAAM,EAAE,CAAC,CACvB,IAAK,GAAOA,EAAE,OAASC,EAAY,GAAGD,EAAE,UAAU,EAAGC,CAAS,EAAE,KAAOD,CAAE,CAC9E,CAGA,SAAgBE,EACd,EACA,EACA,EACsB,CACtB,OAAOC,EAAO,KAAKC,EAAe,GAAa,CAC7C,IAAMC,EAAUC,EAAS,SAAW,CAAC,EACrC,MAAO,CACL,QAAS,GACT,KAAMD,EAAQ,IAAIE,CAAM,EACxB,aAAcD,EAAS,MAAM,SAAS,MACtC,iBAAkBD,EAAQ,OAC1B,eACA,SAAUG,EAAa,iBAAiB,CAC1C,CACF,CAAC,CACH,C"}
|
package/dist/index.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export {
|
|
1
|
+
export {}
|
package/dist/index.js
CHANGED
|
@@ -1,3 +1,3 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
|
-
import{
|
|
2
|
+
import{createOpenFDAServer as e}from"./server.js";import{loggers as t}from"./utils/logger.js";import{VERSION as n}from"./version.js";import{Command as r}from"commander";import{Try as i}from"functype";const a=new r;a.name(`openfda-mcp-server`).description(`MCP server for querying U.S. FDA public datasets`).version(n).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 r=>{let{transport:a,port:o,host:s}=r;t.main(`Starting OpenFDA MCP server with transport: ${a}`);let c=e({name:`openfda-mcp-server`,version:n});(await i.async(()=>{if(a===`http`){let e=parseInt(o,10);return t.main(`Starting HTTP server on ${s}:${e}`),c.start({transportType:`httpStream`,httpStream:{endpoint:`/mcp`,port:e,host:s}}).then(()=>console.error(`OpenFDA MCP server running at http://${s}:${e}/mcp`))}return t.main(`Starting stdio server`),c.start({transportType:`stdio`})})).fold(e=>{console.error(`Failed to start server:`,e),process.exit(1)},()=>void 0)}),a.parse();export{};
|
|
3
3
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","names":[],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * OpenFDA MCP Server CLI\n * Command-line interface for starting the MCP server\n */\n\nimport { Command } from \"commander\"\n\nimport { createOpenFDAServer } from \"./server.js\"\nimport { loggers } from \"./utils/logger.js\"\n\nconst program = new Command()\n\nprogram\n .name(\"openfda-mcp-server\")\n .description(\"MCP server for querying U.S. FDA public datasets\")\n .version(
|
|
1
|
+
{"version":3,"file":"index.js","names":["program","Command","VERSION","options","loggers","transport","server","createOpenFDAServer","Try","portNum","port","host","error"],"sources":["../src/index.ts"],"sourcesContent":["#!/usr/bin/env node\n/**\n * OpenFDA MCP Server CLI\n * Command-line interface for starting the MCP server\n */\n\nimport { Command } from \"commander\"\nimport { Try } from \"functype\"\n\nimport { createOpenFDAServer } from \"./server.js\"\nimport { loggers } from \"./utils/logger.js\"\nimport { VERSION } from \"./version.js\"\n\nconst program = new Command()\n\nprogram\n .name(\"openfda-mcp-server\")\n .description(\"MCP server for querying U.S. FDA public datasets\")\n .version(VERSION)\n .option(\"-t, --transport <type>\", \"Transport type (stdio or http)\", \"stdio\")\n .option(\"-p, --port <number>\", \"Port for HTTP transport\", \"3000\")\n .option(\"--host <host>\", \"Host for HTTP transport\", \"localhost\")\n .action(async (options) => {\n const { transport, port, host } = options\n\n loggers.main(`Starting OpenFDA MCP server with transport: ${transport}`)\n\n const server = createOpenFDAServer({ name: \"openfda-mcp-server\", version: VERSION })\n\n const start = (): Promise<void> => {\n if (transport === \"http\") {\n const portNum = parseInt(port, 10)\n loggers.main(`Starting HTTP server on ${host}:${portNum}`)\n return server\n .start({\n transportType: \"httpStream\",\n httpStream: {\n endpoint: \"/mcp\" as `/${string}`,\n port: portNum,\n host,\n },\n })\n .then(() => console.error(`OpenFDA MCP server running at http://${host}:${portNum}/mcp`))\n }\n loggers.main(\"Starting stdio server\")\n return server.start({ transportType: \"stdio\" })\n }\n\n const started = await Try.async(start)\n started.fold(\n (error) => {\n console.error(\"Failed to start server:\", error)\n process.exit(1)\n },\n () => undefined,\n )\n })\n\nprogram.parse()\n"],"mappings":";A,wMAaA,MAAMA,EAAU,IAAIC,EAEpBD,EACG,KAAK,oBAAoB,CAAC,CAC1B,YAAY,kDAAkD,CAAC,CAC/D,QAAQE,CAAO,CAAC,CAChB,OAAO,yBAA0B,iCAAkC,OAAO,CAAC,CAC3E,OAAO,sBAAuB,0BAA2B,MAAM,CAAC,CAChE,OAAO,gBAAiB,0BAA2B,WAAW,CAAC,CAC/D,OAAO,KAAO,IAAY,CACzB,GAAM,CAAE,YAAW,OAAM,QAASC,EAElCC,EAAQ,KAAK,+CAA+CC,GAAW,EAEvE,IAAMC,EAASC,EAAoB,CAAE,KAAM,qBAAsB,QAASL,CAAQ,CAAC,GAsBnF,MADsBM,EAAI,UAnBS,CACjC,GAAIH,IAAc,OAAQ,CACxB,IAAMI,EAAU,SAASC,EAAM,EAAE,EAEjC,OADAN,EAAQ,KAAK,2BAA2BO,EAAK,GAAGF,GAAS,EAClDH,EACJ,MAAM,CACL,cAAe,aACf,WAAY,CACV,SAAU,OACV,KAAMG,EACN,MACF,CACF,CAAC,CAAC,CACD,SAAW,QAAQ,MAAM,wCAAwCE,EAAK,GAAGF,EAAQ,KAAK,CAAC,CAC5F,CAEA,OADAL,EAAQ,KAAK,uBAAuB,EAC7BE,EAAO,MAAM,CAAE,cAAe,OAAQ,CAAC,CAChD,CAEqC,EAAA,CAC7B,KACL,GAAU,CACT,QAAQ,MAAM,0BAA2BM,CAAK,EAC9C,QAAQ,KAAK,CAAC,CAChB,MACM,IAAA,EACR,CACF,CAAC,EAEHZ,EAAQ,MAAM,E"}
|
package/dist/lib.d.ts
CHANGED
|
@@ -1,8 +1,14 @@
|
|
|
1
|
-
|
|
1
|
+
/**
|
|
2
|
+
* OpenFDA MCP Server Library Exports
|
|
3
|
+
* For programmatic use and extension
|
|
4
|
+
*/
|
|
5
|
+
import { ServerOptions, createOpenFDAServer } from "./server.js";
|
|
6
|
+
import { DrugPatentExpiryParams, OrangeBookPatentsParams, OrangeBookSearchParams, PurpleBookSearchParams, handleSearchDrugPatentExpiry, handleSearchOrangeBook, handleSearchOrangeBookPatents, handleSearchPurpleBook } from "./handlers/bulk-data-handlers.js";
|
|
2
7
|
import { Device510KParams, DeviceAdverseEventsParams, DeviceClassificationParams, DeviceEnforcementParams, handleSearchDevice510K, handleSearchDeviceAdverseEvents, handleSearchDeviceClassifications, handleSearchDeviceEnforcement } from "./handlers/device-handlers.js";
|
|
3
8
|
import { DrugAdverseEventsParams, DrugEnforcementParams, DrugLabelsParams, DrugNDCParams, DrugShortagesParams, DrugsFDAParams, handleSearchDrugAdverseEvents, handleSearchDrugEnforcement, handleSearchDrugLabels, handleSearchDrugNDC, handleSearchDrugShortages, handleSearchDrugsFDA } from "./handlers/drug-handlers.js";
|
|
4
|
-
import { ServerOptions, createOpenFDAServer } from "./server.js";
|
|
5
9
|
import { toolSchemas } from "./tools/index.js";
|
|
6
10
|
import { FDAAPIClient, fdaAPIClient } from "./utils/api-client.js";
|
|
11
|
+
import { BulkDataClient, bulkDataClient } from "./utils/bulk-data-client.js";
|
|
12
|
+
import { Device510K, DeviceAdverseEvent, DeviceClassification, DeviceEnforcement, DrugAdverseEvent, DrugEnforcement, DrugLabel, DrugNDC, DrugShortage, DrugsFDA, FDAResponse, FDAToolResponse, OpenFDAFields, OrangeBookExclusivity, OrangeBookPatent, OrangeBookProduct, PurpleBookEntry, SearchParams } from "./types/fda.js";
|
|
7
13
|
import { loggers } from "./utils/logger.js";
|
|
8
|
-
export { type Device510K, type Device510KParams, type DeviceAdverseEvent, type DeviceAdverseEventsParams, type DeviceClassification, type DeviceClassificationParams, type DeviceEnforcement, type DeviceEnforcementParams, type DrugAdverseEvent, type DrugAdverseEventsParams, type DrugEnforcement, type DrugEnforcementParams, type DrugLabel, type DrugLabelsParams, type DrugNDC, type DrugNDCParams, type DrugShortage, type DrugShortagesParams, type DrugsFDA, type DrugsFDAParams, FDAAPIClient, type FDAResponse, type FDAToolResponse, type OpenFDAFields, type SearchParams, type ServerOptions, createOpenFDAServer, fdaAPIClient, handleSearchDevice510K, handleSearchDeviceAdverseEvents, handleSearchDeviceClassifications, handleSearchDeviceEnforcement, handleSearchDrugAdverseEvents, handleSearchDrugEnforcement, handleSearchDrugLabels, handleSearchDrugNDC, handleSearchDrugShortages, handleSearchDrugsFDA, loggers, toolSchemas };
|
|
14
|
+
export { BulkDataClient, type Device510K, type Device510KParams, type DeviceAdverseEvent, type DeviceAdverseEventsParams, type DeviceClassification, type DeviceClassificationParams, type DeviceEnforcement, type DeviceEnforcementParams, type DrugAdverseEvent, type DrugAdverseEventsParams, type DrugEnforcement, type DrugEnforcementParams, type DrugLabel, type DrugLabelsParams, type DrugNDC, type DrugNDCParams, type DrugPatentExpiryParams, type DrugShortage, type DrugShortagesParams, type DrugsFDA, type DrugsFDAParams, FDAAPIClient, type FDAResponse, type FDAToolResponse, type OpenFDAFields, type OrangeBookExclusivity, type OrangeBookPatent, type OrangeBookPatentsParams, type OrangeBookProduct, type OrangeBookSearchParams, type PurpleBookEntry, type PurpleBookSearchParams, type SearchParams, type ServerOptions, bulkDataClient, createOpenFDAServer, fdaAPIClient, handleSearchDevice510K, handleSearchDeviceAdverseEvents, handleSearchDeviceClassifications, handleSearchDeviceEnforcement, handleSearchDrugAdverseEvents, handleSearchDrugEnforcement, handleSearchDrugLabels, handleSearchDrugNDC, handleSearchDrugPatentExpiry, handleSearchDrugShortages, handleSearchDrugsFDA, handleSearchOrangeBook, handleSearchOrangeBookPatents, handleSearchPurpleBook, loggers, toolSchemas };
|
package/dist/lib.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
import{
|
|
1
|
+
import{createOpenFDAServer as e}from"./server.js";import{handleSearchDrugPatentExpiry as t,handleSearchOrangeBook as n,handleSearchOrangeBookPatents as r,handleSearchPurpleBook as i}from"./handlers/bulk-data-handlers.js";import{handleSearchDevice510K as a,handleSearchDeviceAdverseEvents as o,handleSearchDeviceClassifications as s,handleSearchDeviceEnforcement as c}from"./handlers/device-handlers.js";import{handleSearchDrugAdverseEvents as l,handleSearchDrugEnforcement as u,handleSearchDrugLabels as d,handleSearchDrugNDC as f,handleSearchDrugShortages as p,handleSearchDrugsFDA as m}from"./handlers/drug-handlers.js";import{toolSchemas as h}from"./tools/index.js";import{FDAAPIClient as g,fdaAPIClient as _}from"./utils/api-client.js";import{BulkDataClient as v,bulkDataClient as y}from"./utils/bulk-data-client.js";import{loggers as b}from"./utils/logger.js";export{v as BulkDataClient,g as FDAAPIClient,y as bulkDataClient,e as createOpenFDAServer,_ as fdaAPIClient,a as handleSearchDevice510K,o as handleSearchDeviceAdverseEvents,s as handleSearchDeviceClassifications,c as handleSearchDeviceEnforcement,l as handleSearchDrugAdverseEvents,u as handleSearchDrugEnforcement,d as handleSearchDrugLabels,f as handleSearchDrugNDC,t as handleSearchDrugPatentExpiry,p as handleSearchDrugShortages,m as handleSearchDrugsFDA,n as handleSearchOrangeBook,r as handleSearchOrangeBookPatents,i as handleSearchPurpleBook,b as loggers,h as toolSchemas};
|
package/dist/server.d.ts
CHANGED
|
@@ -1,15 +1,14 @@
|
|
|
1
|
-
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
1
|
+
/**
|
|
2
|
+
* OpenFDA MCP Server
|
|
3
|
+
* SomaMCP server implementation with FDA tools
|
|
4
|
+
*/
|
|
5
|
+
import { t as Semver } from "./version-BNWZ_jnk.js";
|
|
6
|
+
import { SomaServerInstance } from "somamcp/node";
|
|
7
|
+
export type ServerOptions = {
|
|
6
8
|
name?: string;
|
|
7
|
-
version?:
|
|
9
|
+
version?: Semver;
|
|
8
10
|
};
|
|
9
11
|
/**
|
|
10
12
|
* Create and configure the OpenFDA MCP server
|
|
11
13
|
*/
|
|
12
|
-
declare function createOpenFDAServer(options?: ServerOptions):
|
|
13
|
-
//#endregion
|
|
14
|
-
export { ServerOptions, createOpenFDAServer };
|
|
15
|
-
//# sourceMappingURL=server.d.ts.map
|
|
14
|
+
export declare function createOpenFDAServer(options?: ServerOptions): SomaServerInstance;
|