Skip to content

A unified API gateway for healthcare data across 60+ payers and EHR systems. REST API with MCP support for AI agents.

License

Notifications You must be signed in to change notification settings

donutdaniel/fhir-gateway

Repository files navigation

FHIR Gateway

A unified API gateway for healthcare data across 60+ payers and EHR systems. REST API with MCP support for AI agents.

Why FHIR Gateway?

The Problem: Healthcare data is fragmented. Your medical records, claims, and coverage information are scattered across dozens of different systems—each payer (Aetna, Cigna, UnitedHealthcare) and EHR (Epic, Cerner) has its own FHIR API with different endpoints, authentication flows, and quirks.

The Solution: FHIR Gateway provides a single REST API that handles all of this complexity. Connect to any supported platform through one unified interface. Users authorize access via OAuth, and the gateway handles routing, token management, and refresh automatically.

For AI applications, the gateway also exposes an MCP (Model Context Protocol) interface so LLMs can access healthcare data directly.

Demos

Epic MyChart - Patient OAuth flow and FHIR data retrieval

epic-patient-demo.mp4

SmartHealthIT Clinician - Clinician access with SMART on FHIR

smarthealthit-clinician-demo.mp4

How It Works

┌─────────────┐                               ┌─────────────────┐
│  Your App   │ ─── REST ───┐                 │  Aetna, Cigna,  │
└─────────────┘             │                 │  Epic, Cerner,  │
                            ▼                 │  UHC, Humana,   │
                   ┌──────────────────┐       │  60+ systems    │
                   │   FHIR Gateway   │ ───── └─────────────────┘
                   └──────────────────┘
                            ▲
┌─────────────┐             │                 ┌─────────────────┐
│  AI Agent   │ ─── MCP ────┘                 │      User       │
│  (Claude)   │                               │  (authorizes    │
└─────────────┘                               │   via OAuth)    │
                                              └─────────────────┘

Multi-Platform Routing — Every request includes a platform_id (e.g., epic, aetna, cerner) that tells the gateway which FHIR server to route to. The gateway loads platform configurations from JSON files, each defining the FHIR base URL, OAuth endpoints, supported scopes, and any platform-specific quirks.

OAuth with PKCE — When a user needs to authenticate, the gateway initiates a SMART on FHIR OAuth flow with PKCE (Proof Key for Code Exchange). The user authorizes in their browser, the callback is handled automatically, and tokens are stored in the user's session. No tokens are ever exposed to the client application.

Session-Scoped Tokens — Each browser session (tracked via cookies) maintains its own set of OAuth tokens. Tokens are stored in-memory for development or Redis for production, with optional encryption at rest. The gateway automatically refreshes expired tokens using refresh tokens when available.

Dual Interface — The same server exposes both a REST API and an MCP endpoint. REST clients use standard HTTP, while AI agents connect via MCP's streamable-http transport. Both interfaces share the same session and token management, so an OAuth flow initiated via MCP works seamlessly with subsequent REST calls.

Quick Start

# Clone and install
git clone git@github.com:donutdaniel/fhir-gateway.git
cd fhir-gateway
uv sync

# Run the server
fhir-gateway

The server provides both REST API and MCP on the same port:

Docker

Run the full stack with Redis, HAPI FHIR, and Keycloak:

docker compose up -d

This starts:

Test credentials: testuser / password

Configuration

Environment variables (prefix: FHIR_GATEWAY_):

Variable Description Default
HOST Server host 0.0.0.0
PORT Server port 8000
REDIS_URL Redis URL for token storage (optional)
MASTER_KEY Encryption key for tokens at rest (min 32 chars) (required)

Platform-specific OAuth credentials:

FHIR_GATEWAY_PLATFORM_AETNA_CLIENT_ID=your-client-id
FHIR_GATEWAY_PLATFORM_AETNA_CLIENT_SECRET=your-client-secret
EHR Platforms (Epic, Cerner, etc.)

EHR platforms like Epic and Cerner are multi-tenant — each hospital or health system runs their own instance with a unique FHIR endpoint. This is different from payers (Aetna, Cigna) which have a single centralized API.

What this means:

  • There's no single "Epic production URL" — each organization (Kaiser, Stanford Health, etc.) has its own
  • Production access requires onboarding with each specific healthcare organization
  • The gateway includes sandbox configs (epic-sandbox-patient, epic-sandbox-clinician) for development

Epic-specific:

  • Register apps at fhir.epic.com
  • Patient vs Clinician access requires separate app registrations with different "Application Audience" settings
  • Patient apps use patient/*.read scopes, Clinician apps use user/*.read scopes
  • Use the Non-Production Client ID for sandbox testing
# Epic sandbox credentials
FHIR_GATEWAY_PLATFORM_EPIC_SANDBOX_PATIENT_CLIENT_ID=your-patient-app-id
FHIR_GATEWAY_PLATFORM_EPIC_SANDBOX_CLINICIAN_CLIENT_ID=your-clinician-app-id

Cerner/Oracle Health:

For production EHR access, create organization-specific platform configs (e.g., epic-kaiser.json) with the specific FHIR endpoint URL provided during onboarding.

API Reference

FHIR Operations
GET  /api/fhir/{platform_id}/metadata              # CapabilityStatement
GET  /api/fhir/{platform_id}/{resource_type}       # Search
GET  /api/fhir/{platform_id}/{resource_type}/{id}  # Read
POST /api/fhir/{platform_id}/{resource_type}       # Create
PUT  /api/fhir/{platform_id}/{resource_type}/{id}  # Update
DELETE /api/fhir/{platform_id}/{resource_type}/{id} # Delete

Example:

curl http://localhost:8000/api/fhir/smarthealthit-sandbox-patient/Patient?family=Smith
Authentication
GET  /auth/{platform_id}/login   # Start OAuth flow
GET  /oauth/callback             # OAuth callback (automatic)
GET  /auth/status                # Check auth status
GET  /auth/{platform_id}/wait    # Wait for auth completion
POST /auth/{platform_id}/logout  # Logout

Example flow:

# 1. Start OAuth (opens browser)
open http://localhost:8000/auth/smarthealthit-sandbox-patient/login

# 2. After auth, make requests (session cookie handles token)
curl http://localhost:8000/api/fhir/smarthealthit-sandbox-patient/Patient

MCP Integration

Note: The gateway uses streamable-http transport (not stdio) because OAuth callbacks require an HTTP server to receive browser redirects. Run fhir-gateway before starting your MCP client.

MCP URL: http://localhost:8000/mcp/

Claude

Claude.ai / Claude Pro: Settings → Connectors → Add Custom Connector

  • Name: FHIR Gateway
  • Remote MCP URL: http://localhost:8000/mcp/

Claude Desktop (~/Library/Application Support/Claude/claude_desktop_config.json):

{
  "mcpServers": {
    "fhir-gateway": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8000/mcp"]
    }
  }
}

Claude Code (~/.claude/settings.json):

{
  "mcpServers": {
    "fhir-gateway": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8000/mcp"]
    }
  }
}
Cursor

~/.cursor/mcp.json:

{
  "mcpServers": {
    "fhir-gateway": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8000/mcp"]
    }
  }
}
Windsurf

~/.codeium/windsurf/mcp_config.json:

{
  "mcpServers": {
    "fhir-gateway": {
      "command": "npx",
      "args": ["-y", "mcp-remote", "http://localhost:8000/mcp"]
    }
  }
}
Cline (VS Code)

Open Cline settings → MCP Servers → Add:

{
  "fhir-gateway": {
    "command": "npx",
    "args": ["-y", "mcp-remote", "http://localhost:8000/mcp"]
  }
}
MCP Tools

FHIR: list_platforms, get_capabilities, search, read, create, update, delete

Auth: start_auth, wait_for_auth, get_auth_status, revoke_auth

Coverage: check_prior_auth, get_questionnaire_package, get_policy_rules

Example workflow:

User: "Get my lab results from Epic"

Agent:
1. get_auth_status(platform_id="epic") → Not authenticated
2. start_auth(platform_id="epic") → Returns OAuth URL
3. Tell user to click link
4. wait_for_auth(platform_id="epic") → Blocks until complete
5. search(platform_id="epic", resource_type="Observation", params={"category": "laboratory"})

Documentation

Note: Most platforms require registering for a developer account on each platform's portal. This process is manual and can take days to weeks for approval. Only SmartHealthIT and HAPI sandboxes work without additional setup.

License

MIT

About

A unified API gateway for healthcare data across 60+ payers and EHR systems. REST API with MCP support for AI agents.

Resources

License

Contributing

Security policy

Stars

Watchers

Forks

Releases

No releases published

Packages

 
 
 

Contributors

Languages