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.
This repository is a developer infrastructure library and integration protocol — not a cleared medical device, not legal advice, and not a guarantee of regulatory compliance. It is not intended for use as a clinical decision support tool or patient safety system without independent validation, security review, and regulatory clearance appropriate to your jurisdiction and intended use. PHI handling, HIPAA alignment, and production deployment require independent assessment. See DISCLAIMER.md for full terms.
MIT — See LICENSE.
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.