关于 openfda-mcp-server
openfda-mcp-server 将 openFDA 公共 API 的药品、食品、器械及召回数据提供给 MCP 客户端,面向需要查询 FDA 记录的场景。它还支持字段统计和对已暂存结果执行只读 SQL 查询,并提供 STDIO 与 Streamable HTTP。
主要功能
- 01搜索不良事件、召回及短缺
- 02查询药品标签、批准和 NDC
- 03检索器械许可与烟草报告
- 04统计字段值并查看字段说明
- 05暂存结果并执行只读 SQL
适用场景
- 01查看某药品的标签、不良事件与召回
- 02查询药品短缺或 FDA 批准记录
- 03分析食品、器械或药品不良事件
- 04对暂存的查询结果运行 SQL
MCP 相关
通过 15 个 MCP 工具连接 openFDA 数据,涵盖查询、查找、统计和暂存结果分析。支持 STDIO 和 Streamable HTTP。
配置
远程服务无需本地安装。选择你使用的客户端,复制配置或使用一键安装。
https://openfda.caseyjhand.com/mcpclaude mcp add --transport http cyanheads-openfda https://openfda.caseyjhand.com/mcp{"mcpServers":{"cyanheads-openfda":{"type":"http","url":"https://openfda.caseyjhand.com/mcp"}}}在 Cursor 中打开{"servers":{"cyanheads-openfda":{"type":"http","url":"https://openfda.caseyjhand.com/mcp"}}}在 VS Code 中打开{"mcpServers":{"cyanheads-openfda":{"type":"http","url":"https://openfda.caseyjhand.com/mcp"}}}{"mcpServers":{"cyanheads-openfda":{"serverUrl":"https://openfda.caseyjhand.com/mcp"}}}命令在终端中运行;JSON 粘贴到对应客户端的 MCP 配置后重启。需要授权的服务会在首次连接时提示登录。
工具
14| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| skip | number | 否 | 分页时要跳过的记录数(默认 0)。openFDA 的分页上限为 25000 条记录;更高的值会返回 pagination_limit_reached 错误。 |
| sort | string | 否 | 排序表达式——字段路径后可选添加 :asc 或 :desc(大小写不限;也接受 :ascending 和 :descending,所有方向均会以 asc 或 desc 发送);多个字段用逗号分隔。字段路径只能包含字母、数字、下划线和句点;任何其他字符或方向都会在请求前被拒绝。可排序的日期字段因类别而异:drug → receivedate:desc(或 receiptdate),food → date_created:desc(或 date_started),device → date_received:desc(或 date_of_event)。使用其他类别的字段(例如在 food 或 device 中使用 receivedate)会导致查询错误——请使用当前类别对应的字段。 |
| limit | number | 否 | 要返回的记录最大数量(1–1000,默认值为 10)。在不同 openFDA 端点中,序列化记录的大小可能相差三个数量级,因此页面还受 24000 字节的序列化预算限制:如果页面超出预算,则返回的记录数会少于请求数量,并在 page_omitted 中报告截断情况。只要匹配到记录,就至少返回一条,无论其大小如何;page_bytes 会报告其大小,page_largest_fields 会列出最大的字段,这些字段可以通过投影移除。药品报告远大于其他报告——单条药品/事件报告平均有数万字节,而食品/事件报告只有几百字节。 |
| stage | boolean | 否 | 将匹配结果集暂存到 DataCanvas,以便进行 SQL 分析——openfda_dataframe_describe 用于列出暂存列,openfda_dataframe_query 用于执行 SQL。默认为 false——调用仅针对一个上游请求返回一页。设为 true 时,也会将记录持续写入画布表,直到达到大小预算(staged_rows 报告写入的行数)。暂存适用于对有界切片进行记录级 SQL;若要统计所有匹配结果的分布,请使用 openfda_count_values 在服务器端通过一次请求聚合。需要 CANVAS_PROVIDER_TYPE=duckdb。 |
| fields | array | 否 | 要在每条返回记录中保留的点分隔记录路径,例如 ["receivedate", "patient.drug.medicinalproduct"]——返回记录中的任何键路径均可使用,openfda_describe_fields 会列出可搜索的字段。在测量内联字节预算之前,每条记录都会投影为这些路径及其标识键(safetyreportid、report_number、mdr_report_key),两个响应界面的处理方式相同;经过数组的路径会应用于每个元素。记录较大时使用此参数:如果 page_bytes 超出预算,响应会附带超大记录中最大的字段。通知中会列出任何返回记录均不包含的路径。省略则返回完整记录。路径只能包含字母、数字、下划线和点;其他字符会在请求前被拒绝。stage=true 时,仅投影内联页面——暂存表保留其列。 |
| search | string | 否 | openFDA 搜索查询。示例:patient.drug.medicinalproduct:"aspirin"、patient.reaction.reactionmeddrapt:"nausea" AND serious:"1"。省略则浏览最新记录。双引号、括号和范围方括号必须成对,查询末尾不得为反斜杠;否则会在请求前被拒绝。 |
| category | enum | 是 | 产品类别——每种类别在响应中使用不同的字段架构drug | food | device |
| canvas_id | string | 否 | 由先前对此工具或其他 openFDA 搜索工具(openfda_search_* 或 openfda_lookup_ndc)调用 stage=true 返回的 Canvas ID。传入此 ID 会将本次搜索暂存到该画布(效果等同于 stage=true),以便累积结果集并进行跨表联接。省略则暂存到新画布。 |
详细介绍
@cyanheads/openfda-mcp-server
通过 openFDA 查询药品、食品、器械和召回数据。支持 STDIO 或 Streamable HTTP。
概述
通过 openFDA 公共 API 获取药品、食品、器械和召回数据。搜索不良事件、召回、药品批准和器械许可;查询 NDC 代码和药品标签;聚合任意端点的字段计数。
工具
| 工具 | 描述 |
|---|---|
openfda_drug_profile | 输入一个药品名称 → 汇总的 FDA 资料:身份信息、标签、不良事件、召回、批准和短缺 |
openfda_search_adverse_events | 搜索药品、食品和器械的不良事件报告 |
openfda_search_animal_events | 搜索兽用药品和器械的不良事件报告 |
openfda_search_drug_shortages | 按状态、可获得性、治疗类别和制造商搜索 FDA 药品短缺记录 |
openfda_search_tobacco_reports | 搜索烟草制品、电子烟和雾化设备的问题报告 |
openfda_search_recalls | 搜索药品、食品和器械的执法报告及召回行动 |
openfda_count_values | 汇总并统计任意 openFDA 端点中任意字段的唯一值 |
openfda_describe_fields | 返回 openFDA 端点的可搜索字段路径,并按类别分组 |
openfda_get_drug_label | 查询 FDA 药品标签(包装说明书 / SPL 文档) |
openfda_search_drug_approvals | 搜索 Drugs@FDA 数据库中的 NDA/ANDA 申请批准信息 |
openfda_search_device_clearances | 搜索 FDA 器械上市前通知:510(k) 许可和 PMA 批准 |
openfda_lookup_ndc | 在 NDC(国家药品代码)目录中查询药品 |
openfda_dataframe_describe | 列出已暂存到 DataCanvas 的表和列架构 |
openfda_dataframe_query | 对暂存到 DataCanvas 的结果集运行只读 SQL |
openfda_dataframe_drop | 删除一个暂存到 DataCanvas 的表(需主动启用,默认关闭) |
功能参考
openfda_drug_profile 工具
- 输入一个
drug名称(品牌名或通用名),解析一次以确定其身份信息(通用名、NDC、RxCUI、SPL 集 ID);召回、批准和短缺信息以meta.fanOutKey为键——当输入名称是已解析通用名的某种成分时使用该名称,否则使用通用名;meta.resolvedVia(label、ndc、none)表示信息来源,并优先选择单一成分产品而非复方产品 - 召回和批准查询也会搜索 openFDA 从未统一过的记录(
product_description、products.active_ingredients.name);recalls_total会在最新 5 条召回记录旁给出匹配数 label、adverse_events、recalls、approval和shortage部分尽力返回结果,未找到时返回null(或空的recalls);degraded[]会列出上游子查询失败的部分,因此列出的部分表示未知,而不是不存在- 身份验证、配置和取消错误会导致整个调用失败,而不会仅使某个部分降级
openfda_search_adverse_events 工具
category(drug、food、device)用于选择端点及其记录架构;每页最多 1000 条记录- 可排序的日期字段因类别而异:
receivedate(药品)、date_created(食品)、date_received(器械);使用其他类别的字段会导致query_error - 文本输出会在原始值旁解码药品给药途径、反应结局和患者年龄组代码(
via oral (048)、fatal (5)、elderly (6));结构化输出保留这些代码
openfda_search_animal_events 工具
- 可选地使用
search搜索兽医报告,例如animal.species、drug.active_ingredients.name、reaction.veddra_term_name、serious_ae;每页最多 1000 条记录 - 每条记录中的品牌名和制造商名称都会被屏蔽为
MSK,因此请按活性成分搜索;drug.manufacturer.registration_number可将结果缩小到某个批准 - 记录包含动物种类、品种、年龄和体重、药品及给药途径、VeDDRA 反应术语和结局
openfda_search_drug_shortages 工具
- 可选地按
status(Current/To Be Discontinued/Resolved)、therapeutic_category、generic_name或company_name进行search;每页最多 1000 条记录 - 每条记录的
openfda块包含brand_name、product_ndc和rxcui,可用于接续调用openfda_get_drug_label或openfda_lookup_ndc
openfda_search_tobacco_reports 工具
- 可选地按
tobacco_products、reported_health_problems、reported_product_problems或nonuser_affected进行search;每页最多 1000 条记录 - 报告包含问题数组以及
number_tobacco_products、number_health_problems和number_product_problems计数
openfda_search_recalls 工具
category(drug、food、device)以及endpoint:enforcement(默认,适用于所有类别)或recall(仅适用于设备;其他类别会以recall_endpoint_non_device失败);每页最多 1000 条记录- Enforcement 记录包含
recall_number、classification(Class I/II/III)和status;设备召回记录则包含product_res_number和recall_status,不含危害分类
openfda_count_values 工具
endpoint(20 个 openFDA endpoint 中的任意一个)、count字段表达式以及可选的search;返回最多 1000 个热门词项(默认 100 个),按计数排序;日期字段会为每个不同的YYYYMMDD日期返回一个词项,排序方式相同- 如果存在超出
limit的不同词项,truncated会标记这一情况;但达到 1000 个词项的上限时,会显示无法确定数据是否完整的提示(日期计数在任何limit下都是精确的,因为 openFDA 会发送每个日期);如果 openFDA 无法聚合某个表达式,则会以not_aggregatable失败(请从openfda_describe_fields获取countAs格式) - 即使启用了 bulk 镜像,也始终通过实时 API 执行
openfda_describe_fields 工具
endpoint:与openfda_count_values接受的 20 个 endpoint 相同- 按类别分组的字段路径;每个字段都包含
type、描述和countAs(已验证的计数表达式;字段无法聚合时为null),以及关于引号、AND/OR、.exact和日期范围的queryTips
openfda_get_drug_label 工具
- 必须在标签字段(
openfda.brand_name、openfda.generic_name、openfda.manufacturer_name、set_id)上执行search;limit默认值为 5,最大值为 1000 - 如果某页超过 24,000 字节的内联预算,则会返回
kind: "outline"(按大小从大到小排列的章节名称和大小),而不是标签文本;再次调用并传入sections: [...],即可获取这些章节以及身份元数据(openfda、set_id、id、effective_time、version),即使超出预算也会完整返回 *_table章节在文本输出中会渲染为 Markdown 表格;结构化结果则保留原始 SPL 标记
openfda_search_drug_approvals 工具
- 可选地按
openfda.brand_name、sponsor_name(以大写形式存储,因此带引号的小写值不会匹配任何内容)、submissions.submission_type或submissions.review_priority进行search;每页最多 1000 条记录 - 记录包含
application_number、sponsor_name、products[]以及该申请的完整submissions[]历史记录
openfda_search_device_clearances 工具
pathway(510k或pma,每次调用只能指定一个)以及可选的search,可按applicant、product_code、advisory_committee_description或openfda.device_name搜索;每页最多 1000 条记录- 510(k) 记录包含
k_number、device_name和decision_date;PMA 记录包含pma_number、trade_name和supplement_number
openfda_lookup_ndc 工具
- 必须按
product_ndc、brand_name、generic_name、openfda.manufacturer_name或active_ingredients.name进行search;每页最多 1000 条记录 - 记录包含
product_ndc、labeler_name、dosage_form、route、marketing_category、active_ingredients[]和packaging[]
openfda_dataframe_describe 工具
- 使用暂存搜索中的
canvas_id;列出每个表的name、kind、完整暂存row_count,以及包含 DuckDB 类型和是否可为 null 的列 - 当
CANVAS_PROVIDER_TYPE=none时失败并返回canvas_disabled;当canvas_id已过期或从未存在时失败并返回canvas_not_found
openfda_dataframe_query 工具
- 使用
canvas_id和一条只读SELECT;不允许 DDL、DML、COPY 和文件读取函数。标量以文本形式存储(数学运算需使用CAST),嵌套的 openFDA 数据块以 JSON 列存储 - 行数上限为 canvas 行数限制;
truncated: true表示需要使用ORDER BY和LIMIT/OFFSET继续分页;失败时返回canvas_disabled、canvas_not_found、missing_table或invalid_query
openfda_dataframe_drop 工具
- 默认关闭:设置
OPENFDA_DATAFRAME_DROP_ENABLED=true后即可调用;否则该工具会在着陆页上标记为已禁用,且不会出现在tools/list中 - 使用
canvas_id和openfda_dataframe_describe返回的table名称;删除该表或视图,并返回remaining_tables,canvas 和其中其他表会保留 - 失败时返回
canvas_disabled、canvas_not_found或missing_table(已删除、已过期或名称拼写错误)
功能
基于 @cyanheads/mcp-ts-core 构建:支持 stdio 和 Streamable HTTP 传输、可插拔身份验证(none / jwt / oauth)、可切换存储(in-memory、filesystem、Supabase、Cloudflare KV/R2/D1),并提供结构化日志记录和可选的 OpenTelemetry 跟踪。
openFDA 专属功能:
- 一个客户端支持全部 20 个 openFDA 端点,遇到 429 和 5xx 时会重试。无需 API 密钥(每天 1K 次请求);免费的
OPENFDA_API_KEY可将限额提升至每天 120K 次 - 查询使用点分路径,并以 AND/OR 连接
field:value条件(子句之间 URL 风格的+会被视为空格);openfda_describe_fields会列出路径,格式错误的查询会在发起请求前以malformed_search失败 - 分页:
limit最高为 1000,skip最高可达 openFDA 的 25,000 条记录上限(超过后返回pagination_limit_reached);每页大小限制为 24,000 字节 - 在任何
openfda_search_*工具或openfda_lookup_ndc中使用fields,可按指定的点分路径裁剪每条记录,使大型记录也能以内联形式返回 - DataCanvas 暂存(默认开启;设置
CANVAS_PROVIDER_TYPE=none可关闭),可使用stage: true或canvas_id;还可选配本地批量镜像(OPENFDA_MIRROR_ENABLED=true),对精确键查找进行响应而不消耗 API 配额
便于 Agent 使用的输出:
- 明确披露边界:
page_omitted、page_bytes、page_largest_fields、projected_fields、标签outline和staged_rows会报告所有截断情况 - 带类型的失败:调用方可根据
error.data.reason(rate_limited、query_error、not_aggregatable等)进行分支处理,而无需解析消息 - 区分未知与不存在:对于没有 FDA 记录的部分,
openfda_drug_profile会返回null,并在degraded[]中列出失败的部分 - 空结果指引:搜索无匹配结果时,会提示查看
openfda_describe_fields并尝试更宽泛的搜索词
开始使用
连接到托管实例,无需安装:
claude mcp add --transport http openfda-mcp-server https://openfda.caseyjhand.com/mcp
或在本地运行:
claude mcp add openfda-mcp-server -- npx -y @cyanheads/openfda-mcp-server
对于其他客户端,请将以下内容添加到 MCP 配置文件:
{
"mcpServers": {
"openfda-mcp-server": {
"command": "npx",
"args": ["-y", "@cyanheads/openfda-mcp-server"]
}
}
}
无需 API 密钥。将免费的 openFDA API 密钥 设置为 OPENFDA_API_KEY,可将限额从每天 1K 次请求提升至 120K 次。Docker 或源码构建方式请参阅运行服务。
配置
| 变量 | 说明 | 默认值 |
|---|---|---|
OPENFDA_API_KEY | 从 open.fda.gov 获取的免费 API 密钥;每日限额从 1K 次请求提高至 120K 次。 | none |
OPENFDA_BASE_URL | API 基础 URL 覆盖值,用于代理或模拟服务。 | https://api.fda.gov |
OPENFDA_MIRROR_ENABLED | 从本地批量镜像响应精确键查找。 | false |
OPENFDA_MIRROR_PATH | 存放每个镜像数据集对应 SQLite 文件的目录。 | ./data/openfda-mirror |
OPENFDA_MIRROR_REFRESH_CRON | 进程内镜像刷新的 Cron 表达式(仅限 HTTP 传输)。 | none |
OPENFDA_MIRROR_FALLBACK_LIVE | 镜像尚未就绪、缺少记录或发生故障时,回退到实时 API。 | true |
OPENFDA_MIRROR_REFRESH_TIMEOUT_MS | 单次刷新被中止前的墙钟时间预算,单位为 ms。 | 21600000 (6h) |
OPENFDA_MIRROR_BASE_URL | 提供批量下载清单(download.json)的主机。 | https://api.fda.gov |
CANVAS_PROVIDER_TYPE | DataCanvas 暂存和 SQL。none 会禁用这些功能;数据框工具仍会列出,但会返回 canvas_disabled。 | duckdb |
OPENFDA_DATAFRAME_DROP_ENABLED | 启用 openfda_dataframe_drop,用于删除暂存表。需要 CANVAS_PROVIDER_TYPE=duckdb。 | false |
MCP_TRANSPORT_TYPE | 传输方式:stdio 或 http。 | stdio |
MCP_HTTP_PORT | HTTP 服务端口。 | 3010 |
MCP_SESSION_MODE | HTTP 会话模式:stateless、stateful 或 auto。 | stateless |
MCP_AUTH_MODE | 身份验证:none、jwt 或 oauth。 | none |
MCP_LOG_LEVEL | 日志级别(debug、info、warning、error 等)。 | info |
LOGS_DIR | 日志文件目录(仅 Node.js)。 | <app-root>/logs |
STORAGE_PROVIDER_TYPE | 存储后端:in-memory、filesystem、supabase、cloudflare-kv/r2/d1。 | in-memory |
OTEL_ENABLED | 启用 OpenTelemetry。 | false |
完整的可选覆盖项列表请参见 .env.example。
Claude Desktop MCPB 包会禁用 DataCanvas,因为其中不包含特定于平台的 DuckDB 绑定。要使用暂存和 SQL,请使用 npm 或 Docker。
本地批量镜像
设置 OPENFDA_MIRROR_ENABLED=true 后,服务会在本地保留四个 openFDA 批量数据转储(drug/label、drug/ndc、drug/enforcement、drug/drugsfda)的 SQLite 副本。只有精确查找会在本地响应:单个带引号的 field:"value" 条件,字段为 id、set_id、product_id、product_ndc、recall_number、event_id 或 application_number;不带 count 或 sort,skip 为 0,且只有一条匹配记录。其他所有请求都会转发到 API。
初始数据抓取会单独运行,不会在启动时执行:
bun run mirror:init # all four datasets
bun run mirror:init drug/enforcement # one dataset
bun run mirror:status # sync state per dataset
bun run mirror:verify # integrity check + row counts
bun run mirror:refresh # re-harvest datasets whose dump has advanced
刷新会重新读取整个数据转储。设置 OPENFDA_MIRROR_REFRESH_CRON,即可通过 HTTP 传输在进程内运行刷新;使用 stdio 时,请在宿主机上运行 bun run mirror:refresh。镜像响应中的 meta.lastUpdated 是所提供数据转储的时间戳,可能与实时 API 中的时间戳不同。
在 Node 上,请安装可选的 better-sqlite3 对等依赖(Bun 使用内置的 bun:sqlite)。OPENFDA_MIRROR_REFRESH_CRON 还需要可选的 node-cron 对等依赖;未安装时,服务会在启动时失败。
运行服务
本地开发
需要 Bun v1.4.0 或更高版本(或 Node.js v24+)。
-
克隆并安装:
git clone https://github.com/cyanheads/openfda-mcp-server.git cd openfda-mcp-server && bun install cp .env.example .env # optionally set OPENFDA_API_KEY and the mirror or canvas options -
构建并运行:
# One-time build bun run rebuild # Run the built server bun run start:stdio # or bun run start:http -
运行检查和测试:
bun run devcheck # Lint, format, typecheck, security bun run test # Vitest test suite bun run lint:mcp # Validate MCP definitions against spec
Docker
docker run --rm -p 3010:3010 ghcr.io/cyanheads/openfda-mcp-server:latest
该镜像默认使用 HTTP 传输、无状态会话模式,并将日志写入 /var/log/openfda-mcp-server。若要通过 MCP 客户端使用 stdio 运行,请使用 "command": "docker" 和 "args": ["run", "-i", "--rm", "-e", "MCP_TRANSPORT_TYPE=stdio", "ghcr.io/cyanheads/openfda-mcp-server:latest"]。若要在本地构建,运行 docker build -t openfda-mcp-server .;默认会安装 OpenTelemetry 对等依赖,若要省略这些依赖,请使用 --build-arg OTEL_ENABLED=false 构建。若要在容器更换后保留镜像采集的数据,请在 /usr/src/app/data/openfda-mirror 上挂载卷。
项目结构
| 目录 | 用途 |
|---|---|
src/index.ts | createApp() 入口:工具注册、服务设置、镜像刷新配置。 |
src/config | 使用 Zod 解析并验证特定于服务的环境变量。 |
src/mcp-server/tools/definitions | 工具定义(*.tool.ts),共 15 个。 |
src/mcp-server/tools | 共享工具辅助模块:各端点的字段目录、输入模式和校验逻辑、格式化程序、SPL 表格渲染。 |
src/services/openfda | openFDA API 客户端(重试、速率限制、错误规范化)、内联页面预算、fields 记录投影、canvas 暂存。 |
src/services/openfda/mirror | 可选的本地批量镜像:数据集注册表、转储读取器、采集器、镜像与实时查询的选择逻辑、刷新计划。 |
src/services/canvas | 用于暂存和 SQL 的 DataCanvas 访问器。 |
scripts/openfda-mirror.ts | 由 mirror:* 脚本调用的镜像生命周期 CLI。 |
tests/ | 单元测试和集成测试,结构与 src/ 对应。 |
开发指南
开发指南和架构规则请参阅 CLAUDE.md。简要说明:
- 处理程序抛出错误,由框架捕获;工具逻辑中不要使用
try/catch - 使用
ctx.log记录请求范围内的日志 - 在
src/mcp-server/tools/definitions/index.ts的汇总入口中注册新工具 - 封装外部 API 调用:校验原始数据 → 规范化为领域类型 → 返回输出模式;绝不编造 openFDA 未返回的字段
数据归属说明
数据来自 openFDA,这是美国食品药品监督管理局的一项服务。根据 openFDA 许可,这些数据依据 CC0 1.0 许可奉献给公有领域,但有一个例外:GMDN® 设备分类内容(Term Code、Term Name 和 Term Definition)由 The GMDN Agency 授权;重新分发这些内容或将其用于训练 AI,均需获得该机构的单独许可。
因此,本地镜像仅涵盖药品数据集。device/classification 和所有其他设备端点均不包含在镜像中;摄取程序也会拒绝任何带有 GMDN 相关字段的记录,不会将其写入磁盘。
FDA 不为本项目背书。请勿依赖 openFDA 来做出医疗护理相关决定。
贡献
欢迎提交问题。提交前请运行检查和测试:
bun run devcheck
bun run test
许可证
本项目采用 Apache 2.0 许可证。详情请参阅 LICENSE 文件。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。