Connect Epic to Claude: Analyze Labs, Vitals, and Care Plans
A technical guide to generating a managed MCP server for Epic, enabling Claude to securely query FHIR data, analyze vitals, and review patient care plans.
If your team uses ChatGPT, check out our guide on connecting Epic to ChatGPT or explore our broader architectural overview on connecting Epic to AI Agents.
If you need to connect Epic to Claude to automate clinical summaries, analyze patient vitals, or extract structured data from care plans, you need a Model Context Protocol (MCP) server. This server acts as the translation layer between Claude's LLM function calls and Epic's sprawling Fast Healthcare Interoperability Resources (FHIR) APIs. You can either build and maintain this integration infrastructure yourself, or use a managed integration platform like Truto to dynamically generate a secure, authenticated MCP server URL.
Giving a Large Language Model (LLM) read and write access to a specialized, highly regulated electronic health record (EHR) system like Epic is a significant engineering challenge. You have to handle OAuth 2.0 token lifecycles, map massive nested FHIR schemas to flat MCP tool definitions, and deal with strict patient search parameters. Every time Epic updates an endpoint or changes its conformance statement, you have to update your server code, redeploy, and test the integration.
This guide breaks down exactly how to use Truto to generate a secure, managed MCP server for Epic, connect it natively to Claude, and execute complex clinical workflows using natural language.
The Engineering Reality of the Epic API
A custom MCP server is a self-hosted integration layer. While the open MCP standard provides a predictable way for models to discover tools, the reality of implementing it against Epic's FHIR APIs is painful. You are not just integrating a simple REST API - you are navigating complex clinical models, asynchronous operations, and strict querying constraints.
If you decide to build a custom MCP server for Epic, you own the entire API lifecycle. Here are the specific challenges you will face:
FHIR Search Context Constraints
Epic does not allow broad, unconstrained queries for clinical data. You cannot simply execute a GET /Observation to fetch all labs in the system. The API enforces strict search parameters based on clinical context. Almost every clinical endpoint (such as Conditions, Procedures, or Observations) strictly requires a patient or subject query parameter, and often a category code. Your MCP tools must be specifically designed to force the LLM to provide these required parameters, or Epic will return a 400 Bad Request error. Truto automatically extracts required fields from Epic's documentation schemas and injects them as required properties in the MCP tool definitions, ensuring Claude formats the request correctly.
Asynchronous Bulk Data Exports
Extracting large cohorts of patient data requires interacting with Epic's Bulk Data Export API (Group/$export). This is not a synchronous CRUD operation. When you trigger an export, Epic responds with an HTTP 202 Accepted and provides a status URL in the Content-Location header. A custom MCP server would need to manage the polling of this endpoint, parse the X-Progress headers, and eventually retrieve the output IDs to download massive newline-delimited JSON (NDJSON) files. Presenting this async multi-step flow to an LLM requires carefully scoped proxy tools.
Transparent Rate Limit Passthrough
When connecting AI agents to Epic, they can easily trigger API quotas by aggressively paginating through large FHIR Bundles. It is critical to understand how rate limits are handled in this architecture. When the Epic API returns an HTTP 429 Too Many Requests error, Truto does not retry, throttle, or apply backoff automatically. Instead, Truto passes that error directly to the caller. Truto normalizes the upstream rate limit information into standardized headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset) per the IETF specification. The caller (your application or the orchestrating agent framework) is fully responsible for parsing these headers and executing retry and backoff logic.
How to Generate an MCP Server for Epic
Truto dynamically generates MCP tools from Epic's API documentation and endpoint definitions. Tools are derived dynamically at runtime, ensuring that your agent always has the most up-to-date representation of the Epic instance without caching schemas.
There are two ways to create a secure MCP server for your connected Epic account: via the Truto UI or programmatically via the API.
Method 1: Via the Truto UI
If you want to manually provision a server for testing in Claude Desktop, the Truto dashboard provides a point-and-click interface.
- Log into your Truto environment and navigate to the integrated account page for your connected Epic instance.
- Click the MCP Servers tab.
- Click Create MCP Server.
- Configure your server by giving it a name and selecting any necessary method or tag filters (e.g., restricting the server to
readmethods only). - Click Save and copy the generated MCP server URL. This URL contains a cryptographic token that securely identifies the Epic instance.
Method 2: Via the Truto API
For production use cases where you need to provision MCP servers dynamically for your users, you can call the Truto API. The API validates the configuration, generates a hashed token backed by distributed edge storage, and returns a ready-to-use endpoint.
Make an authenticated POST request to the /integrated-account/:id/mcp endpoint:
curl -X POST https://api.truto.one/integrated-account/<epic_account_id>/mcp \
-H "Authorization: Bearer <your_truto_api_token>" \
-H "Content-Type: application/json" \
-d '{
"name": "Epic Clinical Read-Only",
"config": {
"methods": ["read"]
},
"expires_at": "2026-12-31T23:59:59Z"
}'The response contains the secure URL you will provide to your LLM client:
{
"id": "mcp_abc123",
"name": "Epic Clinical Read-Only",
"config": { "methods": ["read"] },
"expires_at": "2026-12-31T23:59:59.000Z",
"url": "https://api.truto.one/mcp/a1b2c3d4e5f6g7h8..."
}How to Connect the Epic MCP Server to Claude
Once you have the Truto MCP URL, you need to register it with your AI environment. You can connect it directly through standard application UIs or via configuration files for local agent development.
Method A: Via the Claude UI
If you are using an AI chat interface that natively supports MCP connectors (like Claude Enterprise, ChatGPT, or custom LangGraph interfaces):
- In your AI platform, navigate to Settings -> Integrations -> Add MCP Server (or Settings -> Connectors -> Add in ChatGPT).
- Name the integration (e.g., "Epic FHIR Data").
- Paste the
https://api.truto.one/mcp/...URL generated in the previous step. - Save the configuration. The AI agent will immediately perform the MCP handshake, call the
tools/listJSON-RPC method, and load the Epic clinical operations into its context window.
Method B: Via Manual Config File (Claude Desktop)
If you are building locally with Claude Desktop, you can add the Truto MCP server using the claude_desktop_config.json file. Because Truto provides a remote HTTP endpoint and Claude Desktop currently expects local stdio processes, you use the standard @modelcontextprotocol/server-sse npx bridge to connect them.
Open your claude_desktop_config.json (located at ~/Library/Application Support/Claude/claude_desktop_config.json on macOS or %APPDATA%\Claude\claude_desktop_config.json on Windows) and add:
{
"mcpServers": {
"epic-truto": {
"command": "npx",
"args": [
"-y",
"@modelcontextprotocol/server-sse",
"https://api.truto.one/mcp/<your_secure_token>"
]
}
}
}Restart Claude Desktop. The application will execute the bridge command and pull all authorized Epic tools into your active workspace.
Security and Access Control
Exposing clinical EHR data to an LLM requires strict boundary setting. Truto provides four critical controls to secure your Epic MCP servers:
- Method Filtering (
config.methods): Restrict the LLM's capabilities by explicitly allowing only specific operation types. Settingmethods: ["read"]prevents Claude from attempting to create or update patient records, limiting it togetandlistoperations. - Tag Filtering (
config.tags): Scope the server to specific functional domains. If you only want the agent to access financial data, you can passtags: ["billing"], which filters out clinical tools like Observations and Care Plans. - Secondary Authentication (
require_api_token_auth): When enabled, possessing the MCP URL is not enough. The client must also pass a valid Truto API token via theAuthorizationheader, enforcing identity validation at the request level. - Time-to-Live (
expires_at): Schedule automatic teardown of the MCP server. Truto uses distributed scheduling primitives to immediately invalidate the token and delete the server infrastructure at the exact timestamp provided.
Hero Tools for Epic
Truto maps Epic's complex FHIR resources into discrete, documented tools. During execution, the MCP router handles the flat input namespace from the LLM, intelligently splitting arguments into query and body parameters based on the schemas. Here are the highest-leverage tools available for Epic workflows.
1. list_all_epic_patients
Search for Patients in Epic. Because Epic requires high-confidence demographic searches, you must provide specific query parameters such as an identifier, or a combination like family name plus birthdate. The tool handles pagination via the standard limit argument mapped to the FHIR _count parameter.
"Find the patient record for John Doe, born on 1980-05-15, and return their primary FHIR ID and active status."
2. list_all_epic_observations
Retrieve clinical observations such as vitals, lab results, and social history. Epic enforces strict search constraints here: you must provide a patient or subject ID, and typically a category or code to filter the results.
"Fetch all recent laboratory observations for patient ID 12345. Focus on the category 'laboratory' and summarize any abnormal metabolic panels."
3. list_all_epic_care_plans
Retrieve the documented Care Plans for a specific patient. This returns the FHIR R4 CarePlan resources, including the implicit rules, descriptions, and current status of the patient's ongoing treatment objectives.
"Retrieve the active Care Plans for patient ID 12345, specifically looking for plans categorized under chronic disease management."
4. get_single_epic_diagnostic_report_by_id
Fetch a detailed Diagnostic Report using its specific FHIR ID. This is critical for pulling down the full conclusion codes, linked observation references, and categorical data for a specific imaging or lab event.
"Get the full details for the diagnostic report ID 98765. Extract the physician's conclusion code and summarize the primary findings."
5. list_all_epic_medication_requests
Search for active or historical MedicationRequests (prescriptions) in Epic. This requires the patient ID and returns the authored dates, categories, and dispense request details.
"List all active medication requests for patient ID 12345. Create a table showing the medication name, authored date, and the requested dispense amount."
6. epic_bulk_exports_start
Trigger a massive asynchronous FHIR Bulk Data export for a specific group of patients. This interacts with the Group/$export endpoint and starts a long-running job. The tool will return a status URL that you must poll using the associated export retrieval tools.
"Start a bulk data export for the patient group ID 555. Let me know when the request is accepted and provide the bulk request ID for polling."
For the complete inventory of Epic MCP tools, supported FHIR parameters, and nested JSON schemas, visit the Epic integration page.
Workflows in Action
By chaining these proxy tools together, Claude can execute complex, multi-step clinical intelligence workflows that would normally require manual chart review or custom Python scripting.
Workflow 1: Pre-Visit Clinical Summary
Physicians spend significant time reviewing patient history before an appointment. You can instruct Claude to act as a clinical assistant, aggregating demographic, vital, and treatment data into a unified briefing.
"Generate a pre-visit summary for patient John Smith, born 1975-08-22. Find his patient ID, retrieve his active care plans, and pull his latest vitals and laboratory observations. Summarize his current treatment goals and highlight any out-of-range lab results."
Execution Steps:
- Claude calls
list_all_epic_patientswith the demographics to resolve the patient's FHIR ID. - Claude uses the ID to call
list_all_epic_care_plans, identifying active treatment objectives. - Claude calls
list_all_epic_observationsfiltered by the patient ID andcategory=vital-signsto get recent physical metrics. - Claude calls
list_all_epic_observationsagain, this time withcategory=laboratory, to check recent blood work.
Result: The agent aggregates the FHIR responses, structures the data into a readable clinical brief, flags anomalous lab values, and presents a complete pre-visit summary without the physician ever opening the EHR interface.
sequenceDiagram
participant Claude as Claude Desktop
participant Truto as Truto MCP Server
participant Epic as Epic API
Claude->>Truto: call list_all_epic_patients
Truto->>Epic: GET /Patient?family=Smith&birthdate=1975-08-22
Epic-->>Truto: Bundle (Patient ID: 123)
Truto-->>Claude: JSON response
Claude->>Truto: call list_all_epic_care_plans
Truto->>Epic: GET /CarePlan?patient=123
Epic-->>Truto: Bundle (Care Plans)
Truto-->>Claude: JSON response
Claude->>Truto: call list_all_epic_observations
Truto->>Epic: GET /Observation?patient=123&category=laboratory
Epic-->>Truto: Bundle (Lab Results)
Truto-->>Claude: JSON responseWorkflow 2: Asynchronous Population Health Export
Healthcare analytics teams frequently need to export large datasets for population health studies. Handling Epic's asynchronous bulk export flow manually is tedious. Claude can orchestrate the initiation and status checking automatically.
"I need to run a population health study on patient cohort Group ID 888. Please trigger a bulk export for this group. Then, check the status of the export using the returned request ID. If it is finished, list the output file URLs we need to download."
Execution Steps:
- Claude calls
epic_bulk_exports_startpassing thegroup_id=888. Epic returns a 202 Accepted and the tool parses theContent-Locationheader to return the bulk request ID. - Claude takes the request ID and calls
get_single_epic_bulk_export_by_idto poll the job status. - If Epic responds that the job is still processing (via
X-Progress), Claude can pause and retry. Once complete, the tool returns the array of NDJSON output files.
Result: The user is insulated from the asynchronous polling complexity of the FHIR specification. Claude successfully navigates the 202 Accepted architecture and delivers the final secure URLs for downloading the massive data payloads.
Wrapping Up
Connecting Claude to Epic via a custom-built integration requires deep expertise in FHIR standards, complex token lifecycles, and asynchronous polling architectures. By utilizing Truto's dynamically generated MCP servers, you eliminate the need to write and maintain boilerplate code.
You can provision strictly scoped, secure endpoints that map directly to Epic's clinical resources, complete with schema validation and standardized rate limit passthrough. Whether you are building clinical assistant copilots, population health data extractors, or automated triage agents, managed MCP architecture allows your engineering team to focus on AI logic rather than EHR integration debt.
FAQ
- How does the Epic MCP server handle API rate limits?
- Truto does not automatically retry, throttle, or apply backoff. When Epic returns a 429 Too Many Requests error, Truto passes the error to the caller and standardizes the rate limit information into headers (ratelimit-limit, ratelimit-remaining, ratelimit-reset). The caller is responsible for implementing retry logic.
- Can I restrict what clinical data Claude can access in Epic?
- Yes. You can use Method Filtering to restrict tools to read-only operations, or use Tag Filtering to limit the server to specific domains, ensuring the LLM cannot accidentally modify sensitive patient records.
- How does the MCP server handle Epic's FHIR search constraints?
- Epic requires strict clinical contexts for searches (e.g., passing a patient ID when querying observations). Truto reads Epic's API schemas and enforces these requirements as mandatory properties in the generated MCP tool definitions, ensuring Claude formats the payload correctly.
- Does Truto store patient data while proxying these requests?
- No. Truto dynamically generates the MCP tools from documentation without caching the API response payloads. Requests pass directly through the proxy handlers to Epic, ensuring zero data retention of the underlying clinical records.