What an MCP server looks like when a retired pharmacist decides what it returns — not just how it's coded.
Connect Claude to this server in about a minute, with no coding and nothing to install, then ask it about any drug label.
Try it in 60 seconds →Most AI tools that touch drug information get there by scraping a label page or summarizing a search result. That works until it doesn't — until the model paraphrases a boxed warning into something softer than it should be, or can't tell a discontinued NDC from an active one.
The OpenFDA MCP server gives any MCP-compatible AI client — Claude Desktop, Claude.ai through a remote connector, or another agent — structured access to two FDA data sets: drug labeling and adverse event reports. It's a personal build, not a client engagement, and it's live now at openfda.datacompounded.com.
Pulls the FDA-approved label for a drug and returns the sections a pharmacist reaches for first — boxed warning, indications, dosing, contraindications, warnings and precautions — each capped in length so the answer fits an AI client's limits. Ask for others (drug interactions, renal impairment, pregnancy and more) with sections. Every answer says which product it matched, whose label it is, and whether it's immediate or modified release.
Returns recent FAERS (FDA Adverse Event Reporting System) safety reports naming that drug — reported reactions, other drugs on the report, and outcome.
Both are read-only queries against OpenFDA's public API, wrapped in FastMCP and served over HTTP so any MCP client can reach them without a local install.
A drug name doesn't map to one label. Querying for something as common as lisinopril can return a combination product — lisinopril/hydrochlorothiazide — ahead of, or instead of, the monotherapy label, depending on what OpenFDA indexed most recently. A generic wrapper would return whatever came back first and let the model sort it out. The right behavior is for the tool to surface which product it matched — brand name, generic name, NDC — so the agent, and the person behind it, can tell a combination product from monotherapy before anything downstream treats them as the same drug.
FAERS data is a report, not a fact. Adverse event reports name every drug the patient was on, not just the one searched for, and the same event is often filed multiple times by different sources. Returned raw, a query against a common drug produces a wall of co-reported medications, vitamins, and duplicate submissions. The tool's job is to hand back structured reports faithfully — not to imply causation OpenFDA doesn't claim, and not to quietly clean out the noise in a way that hides how messy real-world safety data actually is.
What the server won't do. It doesn't diagnose, dose, or recommend — it returns FDA source data. Structured access to public drug data is a different thing from clinical decision support, and this server is scoped to the former.
The deployment reuses the pipeline built for an earlier server — droplet, reverse proxy, HTTPS, process supervision — and reused a lesson learned there too: a common MCP transport bridge some clients use has a known bug with listing tools, which can make a correctly running server look broken from the client side. Verifying through Claude Desktop's native remote connector instead of that bridge ruled it out.
The first real query, “metformin,” failed twice. The answer was far too big: the full label, raw HTML tables included, overflowed Claude’s 25,000-token tool-output limit and was cut off. And it was the wrong product. It returned Zituvimet, a sitagliptin/metformin combination, because the search matched any label that mentioned metformin and took whichever came first.
Return what a pharmacist needs, for the product they meant. The response now carries only the key sections, each capped, with the HTML tables dropped; on a full-size test label that took it from about 366,000 characters to under 10,000. Matching asks OpenFDA for the exact product names first and prefers the single-ingredient generic used on the most labels.
Two kinds of test. Fast offline tests check the logic. Live tests run against the real OpenFDA API for common drugs — metformin, lisinopril, warfarin, Eliquis and others. The live tests caught what no offline test would have: stray one-off names like “METFORMIN ER 500 MG” beating the standard METFORMIN HYDROCHLORIDE; the newest label often being a repackager’s copy rather than the manufacturer’s; and the default answer coming back as extended release when a pharmacist asking for “metformin” means immediate release.
Every label tagged ELIQUIS was a repackager’s. Bristol-Myers Squibb’s own label was in the data but carried no tags at all. The label text couldn’t be trusted to name the company either: repackagers copy “Marketed by: Bristol-Myers Squibb” word for word. What they can’t copy is the NDC on their own carton. The server now finds the original company’s labeler code in the FDA NDC directory — 0003 for Bristol-Myers Squibb — and picks the label with that code on its carton.
| Before | Now |
|---|---|
| “metformin” returned a combination product | Plain metformin HCl |
| Answer overflowed the client’s limit | Key sections, capped; more on request |
| Newest label, often a repackager’s | Original manufacturer; repackager only as a fallback |
| Extended release by default | Immediate release; “metformin ER” for ER |
| Eliquis only from repackagers | Bristol-Myers Squibb, matched by carton NDC |
Domain knowledge decided what “correct” meant: the right product, from the original maker, in the right release form. Live tests against real data showed where the code fell short of it. Every fix now has a test built from the real case that exposed it, and 29 offline and 13 live tests run before every deploy.
openfda.datacompounded.com/mcp (source on GitHub).
A working example of a domain expert's judgment showing up in tool design, not just in a system prompt. Where a generic integration would return raw API output, this one returns output shaped by knowing what the data can and can't tell you.