Skip to main content
Glama

drug-safety-mcp

A Model Context Protocol (MCP) server that gives any AI assistant grounded access to FDA drug-safety data, plus a LangGraph agent that finds and uses its tools at runtime.

Plug it into Claude Desktop, Claude Code, Cursor, or your own agent and ask questions like "Is it safe to take ibuprofen with warfarin?" The model answers from the FDA label and FAERS report data and cites where each fact came from, rather than answering from memory.

flowchart LR
    U[User] --> A
    subgraph Agent [LangGraph agent]
      A[agent<br/>LLM + bound tools] -- tool calls --> T[ToolNode] --> C[count_round<br/>budget guard] --> A
      A -- done / budget hit --> F[finalize<br/>tools used + disclaimer]
    end
    T <-- MCP stdio / streamable HTTP --> S[drug-safety MCP server]
    D[Claude Desktop · Cursor · Claude Code] <-- MCP --> S
    S --> O[(openFDA<br/>labels · FAERS · recalls)]

Tools

tool

what it returns

openFDA endpoint

get_drug_label(drug, sections?)

brand/generic names, manufacturer, boxed-warning flag, selected label sections (boxed warning, indications, contraindications, warnings, adverse reactions, drug interactions)

/drug/label

top_adverse_events(drug, limit, serious_only)

most-reported MedDRA reactions, report counts, total reports, and a causation caveat

/drug/event (FAERS)

recent_recalls(drug, limit)

recall number, class, status, reason, product

/drug/enforcement

check_interaction(drug_a, drug_b)

whether either label mentions the other drug (by generic or brand name), with section-tagged excerpts

/drug/label ×2, in parallel

The server also exposes a resource (drug-safety://about, a data-provenance disclaimer) and a prompt (safety_brief(drug), a cited one-page brief template).

Engineering details

  • Structured output. Every tool returns a pydantic model, so MCP clients receive an outputSchema and structuredContent, not just free text.

  • Query-injection safe. Drug names are sanitised before they go into openFDA's Lucene query syntax, so input like metformin" OR x:"* can't change the query.

  • Resilient client. Async httpx with retry and exponential backoff on 429/5xx, and a TTL cache. openFDA's "404 = no matches" is mapped to found: false instead of an error. The API key is sent but kept out of cache keys.

  • Agent guardrails. A tool-round budget stops runaway loops, a finalize node appends the tools used and the disclaimer, and tool errors go back to the model instead of crashing the graph.

  • Two transports. stdio for desktop clients, and streamable HTTP (/mcp) for remote or containerised deployment.

Related MCP server: OpenFDA FastMCP Server

Use it from Claude Desktop

Add to claude_desktop_config.json (see examples/):

{
  "mcpServers": {
    "drug-safety": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/gopipamulapati/drug-safety-mcp", "drug-safety-mcp"]
    }
  }
}

Run the agent

pip install -e ".[agent]"
export OPENAI_API_KEY=...            # any OpenAI-compatible endpoint; see .env.example
drug-safety-agent --trace "Can I take ibuprofen with warfarin?"

Example trace (the exact calls depend on the model):

-> check_interaction({'drug_a': 'ibuprofen', 'drug_b': 'warfarin'})
-> get_drug_label({'drug': 'warfarin', 'sections': ['boxed_warning', 'drug_interactions']})
...

No network? --offline (or DRUG_SAFETY_OFFLINE=1) serves a small illustrative bundled sample for warfarin, ibuprofen, and metformin. Its text is paraphrased and its counts are made up, so it's only for demos and tests.

Serve over HTTP / Docker

docker build -t drug-safety-mcp . && docker run -p 8000:8000 drug-safety-mcp
# MCP endpoint: http://localhost:8000/mcp

Tests

pip install -e ".[dev]"
pytest -q        # 17 tests, ~5 s, no network
  • Client: sanitisation, retry on 429, 404 → not found, cache TTL/eviction, API key kept out of cache keys.

  • Server: every tool, the resource, and the prompt are called through a real MCP client session (in-memory transport), including brand-name resolution, tool errors, and structured output schemas.

  • Agent: the LangGraph graph talks to the MCP server as a real stdio subprocess, driven by a scripted chat model. This checks that data flows back from the tools, that the final answer cites the tools used, and that the tool budget stops a model that keeps calling tools.

Layout

src/drug_safety_mcp/
  server.py     FastMCP server: 4 tools, 1 resource, 1 prompt, stdio/HTTP entrypoint
  openfda.py    async client: sanitising, retries, TTL cache
  agent.py      LangGraph StateGraph agent using langchain-mcp-adapters
  offline.py    httpx MockTransport serving bundled sample data
examples/       Claude Desktop config

Data comes from openFDA. FAERS counts are spontaneous reports, not incidence rates, and don't establish causation. This is not medical advice.

License

MIT

Related MCP Connectors

Related MCP Servers