Every healthcare AI team builds the same EHR integration layer from scratch.
SMART-on-FHIR auth. FHIR R4 client. Vendor normalization. Data contract.
Every. Single. Team.
EHR-MCP is that layer — built once, reusable across every agent in your stack.
One protocol. Any EHR. Any agent framework.
MCP Tool Reference · ClinicalContextBundle · Quick Start · Agent Integration · Roadmap
I've watched this happen across 12 enterprise Epic health system deployments. A team decides to build a clinical AI agent. They have an LLM. They have a workflow. Then they hit the EHR integration wall:
SMART-on-FHIR app registration. RS384 JWT key pair generation. Token exchange implementation. FHIR R4 resource fetching. Vendor-specific quirk normalization. A typed data contract that downstream agents can actually work with.
That's 2–4 weeks of infrastructure work before a single agent prompt is written. And every team at every health system in every agentic AI product company is doing it from scratch.
EHR-MCP is the protocol layer that eliminates that work. Your agent calls get_patient_context(patient_id). It gets back a typed ClinicalContextBundle. The auth, the FHIR parsing, the vendor normalization — handled.
Agent (LangGraph / CrewAI / LangChain / AutoGen)
│
│ MCP Tool Call: get_patient_context(patient_id)
▼
┌─────────────────────────────────────────┐
│ EHR-MCP Server │
│ │
│ ┌─ SMART-on-FHIR Auth (RS384 JWT) ──┐ │
│ ├─ FHIR R4 Resource Fetch ──────────┤ │
│ └─ ClinicalContextBundle Assembly ──┘ │
└─────────────────────────────────────────┘
│
▼
Structured ClinicalContextBundle → returned to agent
flowchart TD
A["Agent\nLangGraph · CrewAI · LangChain · AutoGen"] -->|MCP Tool Call| B["EHR-MCP Server\nserver.py"]
B --> C{Tool Router}
C -->|get_patient_context| D["ClinicalContextPackager\ncontext_packager.py"]
C -->|"get_patient / get_conditions\nget_medications / get_observations\nget_allergies / get_encounters\nget_diagnostic_reports"| E["FHIRClient\nfhir_client.py"]
C -->|search_fhir| E
D --> E
E --> F["SMART-on-FHIR Auth\nauth.py\nRS384 JWT → Bearer Token"]
F --> G[("FHIR R4 Server\nEpic · Cerner · Any")]
G -->|FHIR Resources| E
E -->|Assembled Bundle| D
D -->|ClinicalContextBundle| B
B -->|TextContent| A
| Tool | FHIR Resource(s) | Description |
|---|---|---|
get_patient_context |
Bundle (all) | Full clinical context bundle — the primary tool for any workflow agent |
get_patient |
Patient |
Demographics, identifiers, contact info |
get_conditions |
Condition |
Active diagnoses with ICD-10 codes |
get_medications |
MedicationRequest |
Active prescriptions with dosage and prescriber |
get_observations |
Observation |
Labs and vitals with LOINC codes and values |
get_allergies |
AllergyIntolerance |
Allergy list with reaction severity |
get_encounters |
Encounter |
Visit history with type, class, and dates |
get_diagnostic_reports |
DiagnosticReport |
Imaging, pathology, and procedure reports |
search_fhir |
Any FHIRResourceType |
Raw FHIR search for advanced agent use cases |
Every get_patient_context call returns a typed ClinicalContextBundle — a Pydantic v2 model that gives downstream agents a consistent, predictable data contract regardless of which EHR vendor is on the other end.
class ClinicalContextBundle(BaseModel):
patient_id: str
patient: Optional[dict] # FHIR Patient resource
conditions: List[dict] # Active Condition resources
medications: List[dict] # Active MedicationRequest resources
allergies: List[dict] # AllergyIntolerance resources
observations: List[dict] # Observation resources (labs + vitals)
encounters: List[dict] # Encounter history
diagnostic_reports: List[dict] # DiagnosticReport resources
vendor: Optional[str] # EHR vendor detected from FHIR metadata
fhir_version: str # Default: "R4"Agents receive a normalized bundle, not raw FHIR JSON. Vendor normalization happens inside EHR-MCP — not inside every agent.
EHR-MCP implements SMART on FHIR Backend Services — the standard for system-to-system EHR access used by Epic, Cerner, and most FHIR R4-compliant platforms.
- Auth flow: RS384 JWT assertion → token exchange → Bearer token on all FHIR requests
- No user login required — designed for backend agent workflows
- Epic-compatible — aligns with Epic's Non-Patient-Facing App registration requirements
| EHR Platform | FHIR R4 | SMART Backend Services | Status |
|---|---|---|---|
| Epic | ✅ | ✅ | Sandbox tested |
| Cerner (Oracle Health) | ✅ | ✅ | Planned |
| Meditech Expanse | ✅ | ✅ | Planned |
| Any FHIR R4 Server | ✅ | ✅ | Via FHIR_BASE_URL |
Choose your install method:
🐍 Python (pip)
git clone https://github.com/jsfaulkner86/ehr-mcp
cd ehr-mcp
python -m venv venv
source venv/bin/activate # Windows: venv\Scripts\activate
pip install -r requirements.txt
cp .env.example .env
python main.py🐳 Docker
docker build -t ehr-mcp .
docker run --env-file .env ehr-mcpOr with Docker Compose:
# docker-compose.yml
services:
ehr-mcp:
build: .
env_file: .env
stdin_open: true
tty: truedocker compose up🤖 Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"ehr-mcp": {
"command": "python",
"args": ["-m", "ehr_mcp.server"],
"cwd": "/path/to/ehr-mcp",
"env": {
"FHIR_BASE_URL": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4",
"SMART_TOKEN_URL": "https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token",
"SMART_CLIENT_ID": "your_client_id",
"SMART_PRIVATE_KEY_PATH": "/path/to/private_key.pem"
}
}
}
}Restart Claude Desktop. You'll see EHR tools available in the tool picker.
🖱️ Cursor / Windsurf
Add to your MCP config (.cursor/mcp.json or .codeium/windsurf/mcp_config.json):
{
"mcpServers": {
"ehr-mcp": {
"command": "python",
"args": ["-m", "ehr_mcp.server"],
"cwd": "/path/to/ehr-mcp",
"env": {
"FHIR_BASE_URL": "your_fhir_base_url",
"SMART_TOKEN_URL": "your_token_url",
"SMART_CLIENT_ID": "your_client_id",
"SMART_PRIVATE_KEY_PATH": "/path/to/private_key.pem"
}
}
}
}Reload your editor. EHR-MCP tools will be available to your AI coding assistant.
FHIR_BASE_URL=https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4
SMART_TOKEN_URL=https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token
SMART_CLIENT_ID=your_client_id
SMART_PRIVATE_KEY_PATH=./keys/private_key.pem
MCP_SERVER_NAME=ehr-mcp
MCP_SERVER_VERSION=0.1.0
OPENAI_API_KEY=your_key_here # Only required for summary generationEpic FHIR Sandbox: fhir.epic.com — free registration, full R4 resource access for development.
# LangGraph + langchain-mcp-adapters
from langchain_mcp_adapters.client import MultiServerMCPClient
async with MultiServerMCPClient({
"ehr": {
"command": "python",
"args": ["-m", "ehr_mcp.server"],
"transport": "stdio",
}
}) as client:
tools = client.get_tools()
# tools now includes get_patient_context, get_conditions, etc.
# Works identically with CrewAI, LangChain, AutoGenAny MCP-compatible agent framework connects the same way — no framework-specific integration code required.
EHR-MCP is the shared data layer for the healthcare agent portfolio. Any agent that needs patient context calls in through here:
| Agent | Integration Point |
|---|---|
clinical-triage-agent |
Patient context for acuity classification |
pph-risk-scoring-agent |
Live vitals + labs for risk scoring |
prior-auth-research-agent |
Diagnosis + medication context for auth criteria |
healthcare-compliance-guardrail |
PHI-safe patient context delivery |
| Your custom agent | Any MCP-compatible framework |
This implementation was inspired by peer-reviewed research validating LLM + EHR-MCP in a live hospital environment:
EHR-MCP: Real-world Evaluation of Clinical Information Retrieval by Large Language Models via Model Context Protocol
Masayoshi et al. — arXiv:2509.15957 — https://doi.org/10.48550/arXiv.2509.15957
Their study demonstrated near-perfect MCP tool selection accuracy using GPT-4.1 + LangGraph ReAct in a live hospital EHR. This repository extends that concept with vendor-agnostic FHIR abstraction, SMART Backend Services auth, multi-framework compatibility, and a fully open-source implementation.
Production healthcare AI needs an honest failure mode table. Here's mine.
| Failure Mode | Impact | Mitigation |
|---|---|---|
| Epic FHIR rate limiting | Bundle assembly delays under load | Exponential backoff + per-resource timeout handling |
| RS384 token expiry during long agent session | Silent FHIR auth failure | Pre-expiry token refresh with 5-min rotation buffer |
| Vendor FHIR quirks (non-standard resource shapes) | Normalization failures | Vendor-specific adapters in roadmap; current Epic validation tested |
PHI in raw search_fhir output |
Unmasked PHI reaching agent | Route through healthcare-compliance-guardrail for PHI-safe delivery |
- Epic FHIR sandbox end-to-end integration test suite
- Bidirectional write support (
Task,Communication,ServiceRequest) -
Coverage+Claimtools for prior auth workflows - Cerner (Oracle Health) validation
- OpenAPI spec for REST-based agent integration
If this protocol is useful to you, a ⭐ helps others find it.
If you're a health system or women's health tech company building multi-agent clinical AI and need the interoperability layer designed properly — this is the kind of infrastructure I architect at The Faulkner Group.
The connective tissue for multi-agent healthcare AI.
Part of The Faulkner Group's healthcare agentic AI portfolio → github.com/jsfaulkner86
Built from 14 years and 12 Epic enterprise health system deployments.