Fhir Mcp — independently scanned and version-tracked by SaferSkills.
SaferSkills independently audited Fhir Mcp (Agent Skill) and scored it 100/100 (green). The audit ran 55 deterministic rules across Security, Supply Chain, Maintenance, Transparency, and Community; it found 0 high-severity and 0 lower-severity findings. The full rule-by-rule trace and per-finding evidence are below. Free, methodology-open.
Findings & checks · 0 flagged
Every scanned point with the score it earned and what moved between them.
First recorded scan — no prior version to compare against.
The primary manifest — the file an agent reads to learn what this artifact does.
The trustworthy FHIR bridge for AI agents. Plug any MCP-compatible assistant into a real FHIR R4 server — with reproducible clinical evals, reversible keyed de-identification, CDS Hooks decision support, and per-call audit trails out of the box.
🚀 Live demo: `https://dhairya-fhir-mcp.fly.dev/` → JSON info · `/sse` → MCP endpoint Synthea-style synthetic patients (5 golden bundles, no real PHI). Point any MCP client at /sse and call search_patients, get_patient_summary, run_cds_hook, or any of the other 8 tools. Deploy your own copy: see DEPLOY.md.
<p> <a href="https://github.com/DhairyaShah981/fhir-mcp/actions"><img src="https://img.shields.io/github/actions/workflow/status/DhairyaShah981/fhir-mcp/ci.yml?branch=main&label=CI" alt="CI"></a> <a href="https://github.com/DhairyaShah981/fhir-mcp/blob/main/LICENSE"><img src="https://img.shields.io/badge/license-Apache--2.0-blue" alt="License"></a> <a href="https://pypi.org/project/fhir-mcp/"><img src="https://img.shields.io/pypi/v/fhir-mcp?label=pypi" alt="PyPI"></a> <img src="https://img.shields.io/badge/FHIR-R4-orange" alt="FHIR R4"> <img src="https://img.shields.io/badge/MCP-1.x-purple" alt="MCP"> </p>
LLM coding agents have been plumbed into databases, IDEs, GitHub, Slack, and the desktop. Healthcare is the holdout. When a clinician, founder, or back-office team tries to give an MCP-compatible assistant real EHR context, they hit the same wall: FHIR is a 2,000-page spec, the data is dense with PHI, and nobody trusts a freeform LLM to touch it.
Six open-source FHIR MCP servers already exist. None of them solve the trust problem. They expose FHIR resources as MCP tools, hope the LLM doesn't hallucinate codes, and mask some PHI with regex. There is no reproducible clinical-accuracy benchmark. There is no controlled re-identification path. There is no bridge to CDS Hooks — the production standard for clinical decision support. There is no link between LLM traces and audit logs.
fhir-mcp is the first FHIR MCP server engineered for trust, not just access — so you can drop it into your MCP client on Monday morning, demo it to a clinical-safety officer on Friday, and have a defensible answer to every question they ask.
| Capability | [Momentum][m] | [xSoVx][x] | [WSO2][w] | [AWS HealthLake][a] | [Flexpa][f] | [langcare][l] | fhir-mcp |
|---|---|---|---|---|---|---|---|
| MCP tools for FHIR resources | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ | ✅ |
| De-identification on egress | ❌ | ⚠️ irreversible mask | ❌ | ❌ | ❌ | ⚠️ log-scrub only | ✅ |
| Reversible, keyed, audited re-id | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| Reproducible clinical eval harness (in-repo, CI-gated) | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| CDS Hooks bridge | ❌ | ❌ | ❌ | ❌ | ❌ | ❌ | ✅ |
| MCP Resources + Prompts | ❌ | ⚠️ prompts only | ❌ | ⚠️ resources only | ❌ | ⚠️ via embedded apps | ✅ all three |
| Audit log linked to LLM trace id | ❌ | ⚠️ generic trace ids | ❌ | ❌ | ❌ | ⚠️ HIPAA audit, not LLM-trace | ✅ |
| Zero-config local mode | ⚠️ | ⚠️ | ❌ | ❌ | ✅ | ⚠️ | ✅ |
[m]: https://github.com/the-momentum/fhir-mcp-server [x]: https://github.com/xSoVx/fhir-mcp [w]: https://github.com/wso2/fhir-mcp-server [a]: https://awslabs.github.io/mcp/servers/healthlake-mcp-server [f]: https://github.com/flexpa/mcp-fhir [l]: https://github.com/langcare/langcare-mcp-fhir
<sub>Last manually verified 2026-05-23 against each linked repo. If you find a row that's out of date, open an issue — we'll update it the same day.</sub>
_Generated by CI on every push. Run uv run python evals/report.py locally._
| Eval | Score | Coverage |
|---|---|---|
| Clinical accuracy (structural) | 7/7 ✅ | 5 golden patients · ~40 resources · 7 scenarios |
| Clinical accuracy (judge LLM) | _skipped without ANTHROPIC_API_KEY_ | optional opt-in |
| De-id leakage | 0 leaks ✅ | all 5 bundles → ~40 resources → ≥30 known PHI tokens, scanned across every tool output |
| Code validation | 31/31 ✅ | LOINC · SNOMED · RxNorm · ICD-10 (offline table) |
| CDS Hooks correctness | 4/4 ✅ | drug-drug, drug-allergy, prefetch-de-id, unknown-hook safety |
| Cross-model judge spread | _opt-in_ | run python -m evals.cross_model with API keys |
Engineering health (v0.2.1): 206 tests passing · 96% line coverage (run uv run pytest tests evals --cov=src/fhir_mcp locally) · ruff + pyright clean · MCP stdio end-to-end verified · SMART OAuth2 (PKCE + backend-services) implemented · 5 golden Synthea bundles · STRIDE threat model.
The scoreboard above is reproducible:uv run python evals/report.py --out scoreboard.mdre-runs every suite and writes the table. Judge-LLM scoring is opt-in; without an API key it's reported asskipped, never as a fake pass.
fhir-mcp is a confidential SMART app. Two OAuth2 flows are wired up:
| Flow | Use case | CLI helper |
|---|---|---|
| Authorization code + PKCE | Interactive launch against SMART Health IT / Epic on FHIR sandboxes. | fhir-mcp smart-discover <iss> · fhir-mcp smart-authorize-url |
| Backend services (asymmetric JWT bearer) | Server-to-server access against EHRs that support SMART backend services. | fhir-mcp smart-jwt (mints a client assertion) |
The custom scope fhir-mcp/reid gates re-identification:
# Either a local key …
FHIR_MCP_ENABLE_REID=true FHIR_MCP_REID_KEY=… fhir-mcp reid PT_a1b2
# … or pass a SMART token carrying `fhir-mcp/reid` to reidentify() from code.Every re-id call writes an audit_events row carrying the requesting SMART subject (or Patient/... context), the Langfuse trace id, and the auth path used. See docs/THREAT_MODEL.md for the full STRIDE breakdown.
# 1. Run the server (no signups, no .env required — uses in-memory Synthea patients)
uvx fhir-mcp serve
# 2. Wire it into Claude Desktop
cat >> ~/Library/Application\ Support/Claude/claude_desktop_config.json <<'JSON'
{
"mcpServers": {
"fhir-mcp": {
"command": "uvx",
"args": ["fhir-mcp", "serve"]
}
}
}
JSON
# 3. Restart Claude Desktop and ask:
# "Find patients named Smith with diabetes and summarize the first one."Want real FHIR data? Switch the backend in one env var:
FHIR_MCP_BACKEND=hapi uvx fhir-mcp serveflowchart LR
subgraph CLI["AI Client"]
A[MCP-compatible client]
end
subgraph SRV["fhir-mcp server"]
B[MCP Server<br/>mcp-python-sdk]
C[Tool Router]
D[FHIR Client<br/>httpx, R4 JSON]
E[De-id Pipeline<br/>Structural walk + regex + Vault]
F[CDS Hooks Bridge]
G[Terminology<br/>LOINC/SNOMED/RxNorm]
H[Audit Logger]
I[Langfuse Tracer]
end
subgraph BE["Data backends"]
J[HAPI Public Test Server]
K[In-memory Synthea Bundles]
end
subgraph OPT["Cloud opt-in"]
L[(Supabase<br/>audit + phi_vault + pgvector)]
M[Langfuse Cloud]
end
subgraph EXT["External standards"]
N[cds-hooks.org sandbox]
O[tx.fhir.org]
end
A <-->|MCP stdio| B
B --> C
C --> D
C --> E
C --> F
C --> G
C --> H
C --> I
D --> J
D --> K
F --> N
G --> O
H --> L
I --> M| Tool | Purpose |
|---|---|
search_patients | Search by name, MRN, DOB, gender. Returns de-identified summaries with stable pseudonyms. |
get_patient_summary | Composite clinical summary: conditions, active meds, allergies, recent obs, primary Dx. |
search_observations | LOINC + date range + value-op queries, plus trend windows ("HbA1c slope, last 12 months"). |
search_conditions | SNOMED-coded or free-text search, filterable by clinical status. |
get_medications | Active med list with RxNorm + dosing + drug-drug interaction flags from CDS Hooks. |
validate_code | Resolve a LOINC / SNOMED / RxNorm / ICD-10 code. Offline-first; optional tx.fhir.org fallback. |
create_clinical_note | Structured SOAP / discharge / prior-auth note; emits a FHIR DocumentReference. |
run_cds_hook | Execute a CDS Hooks decision-support call. Offline mock by default; live sandbox opt-in. |
Resources (fhir://patient/{pseudonym}/…):
| Resource | URI | Format |
|---|---|---|
patient_summary | fhir://patient/{pseudonym}/summary | text/markdown |
lab_trends | fhir://patient/{pseudonym}/trends | application/json |
medications | fhir://patient/{pseudonym}/medications | text/markdown |
Prompts: soap_note · discharge_summary · prior_auth_letter — versioned templates that orchestrate the tool calls listed above and produce production-shaped documentation.
PT_a1b2c3d4e5f60718, etc., HMAC-SHA256 over a per-vault salt, 64-bit tail). The original ↔ pseudonym mapping lives in a vault (SQLite locally; Postgres / Supabase in cloud mode). Presidio is available as an optional deid-advanced extra. AES-GCM at-rest encryption for the vault is on the v0.3 roadmap — docs/THREAT_MODEL.md is explicit that the current vault stores plaintext at rest and operators must treat it accordingly.FHIR_MCP_ENABLE_REID=true plus a scoped key (or a SMART-on-FHIR token with the right scope). Every re-id call writes an audit row carrying the requester, the Langfuse trace ID, and the revealed pseudonyms.audit_events row per call: (timestamp, actor, tool, args_hash, resource_refs[], trace_id, outcome). Open the audit row → jump straight to the LLM trace.FHIR_MCP_REID_KEY or by a SMART token carrying the fhir-mcp/reid custom scope; the actor recorded in the audit row is the SMART Patient/... context when one is present.See evals/test_deid.py for the leakage test suite.
Most FHIR MCP projects ship without any clinical-accuracy benchmark. We ship four suites that run in CI on every push:
claude-sonnet-4-6, pinned) scores the output against a versioned rubric. Pass = ≥8/10.cards[].Reproducibility is non-negotiable: Synthea seeds, judge prompts, and judge model are all pinned in evals/conftest.py. The full scoreboard is auto-rendered to the top of this README on every CI run.
asyncio.Lock; @audited(phi_args=...) strips declared PHI fields even under FHIR_MCP_VERBOSE_AUDIT=true; HAPI date-range search fixed; $everything now caps at 200 resources.$export); FHIR Subscriptions; R5 dual support; nightly live-FHIR sandbox integration test (#3); cross-model bench wired into CI; MCP 2026-07-28 stateless transport migration (#2).PRs welcome. See CLAUDE.md for contributor context (mental model, conventions, do/don't). Every PR must keep the eval scoreboard green.
Apache-2.0. FHIR® is a registered trademark of HL7 and is used under license.
~30 seconds. Free. No account. Every finding cites a rule and a line of evidence.