New:Microsoft Teams Notifications Are Now Available in Socket.Learn more
Get Started

mcp-fhir

Package Overview
Dependencies
Maintainers
1
Versions
2
Alerts
File Explorer

Advanced tools

Socket logo

Install Socket

Detect and block malicious and high-risk dependencies

Install

mcp-fhir

FHIR R4 MCP server — read, search, validate resources + SMART-on-FHIR auth (Epic/Cerner sandbox)

pipPyPI
Version
1.1.1
Weekly downloads
20
Maintainers
1
Weekly downloads
 
Created

mcp-fhir

FHIR R4 MCP server — read, search, paginate, validate resources against US Core/IPS profiles, and authenticate against Epic/Cerner EHR sandboxes via SMART-on-FHIR OAuth 2.0.
Part of the fhir-mcp-suite monorepo.

PyPI PyPI - Python Version CI License

What it does

mcp-fhir exposes five MCP tools that let any MCP-compatible AI (Claude, GPT-4o, etc.) interact with any FHIR R4 server — and validate resources against US Core and IPS profiles — in a single server process. No other public FHIR MCP server combines search + validation today.

ToolDescription
fhir_capabilitiesCapabilityStatement summary — what the server supports
fhir_readRead a single resource by type + logical ID
fhir_searchSearch a resource type with FHIR query params; returns Bundle with _next_url when paginated
fhir_search_nextFollow a _next_url pagination link (SSRF-guarded)
validate_against_profileValidate a resource via the HAPI validator; supports US Core & IPS aliases

Architecture

Claude Desktop / AI client
        │  MCP (stdio or SSE)
        ▼
┌───────────────────────────────┐
│        mcp-fhir  v1.1         │
│  ┌───────────────────────────┐│
│  │  tools/                   ││
│  │  ├─ fhir_capabilities.py  ││
│  │  ├─ fhir_read.py          ││
│  │  ├─ fhir_search.py        ││   ──► FHIR R4 server
│  │  └─ validate_profile.py   ││   ──► HAPI validator sidecar
│  └───────────────────────────┘│
│  fhir-mcp-shared               │
│  ├─ LangFuse traces (session)  │
│  ├─ Pydantic structured output │
│  └─ structlog JSON logging     │
└───────────────────────────────┘

Quick start

# requires Python 3.12+ and uv
uvx mcp-fhir           # stdio transport (Claude Desktop, default)

# SSE transport (HTTP, for API access)
MCP_TRANSPORT=sse uvx mcp-fhir

Claude Desktop config (claude_desktop_config.json)

{
  "mcpServers": {
    "mcp-fhir": {
      "command": "uvx",
      "args": ["mcp-fhir"],
      "env": {
        "FHIR_BASE_URL": "https://hapi.fhir.org/baseR4",
        "HAPI_VALIDATOR_URL": "http://localhost:8082"
      }
    }
  }
}

See full installation guide for HAPI validator setup, troubleshooting, and self-hosted FHIR server configuration.

Eval results

Golden query suite — run against the public HAPI demo server (hapi.fhir.org/baseR4):

CategoryCasesPass rate
fhir_capabilities2100 %
fhir_read4100 %
fhir_search6100 %
fhir_search_next2100 %
validate_against_profile6100 %
Total20100 %

CI gate: uv run python evals/mcp-fhir/run_eval.py --ci --threshold 0.9
Validator cases require the HAPI sidecar (docker compose up hapi-validator).

Configuration

All settings via environment variables (see .env.example):

VariableDefaultDescription
FHIR_BASE_URLhttps://hapi.fhir.org/baseR4FHIR server base URL
HAPI_VALIDATOR_URLhttp://localhost:8082HAPI validator sidecar (runs on port 3500 internally; map 8082:3500)
FHIR_TIMEOUT_S30HTTP timeout in seconds
FHIR_MAX_RESULTS20Default _count for searches
MCP_TRANSPORTstdiostdio or sse
MCP_PORT8000Port for SSE transport
LOG_FORMATjsonjson (prod) or console (dev)
LANGFUSE_PUBLIC_KEY(unset)LangFuse observability (optional)
LANGFUSE_SECRET_KEY(unset)LangFuse observability (optional)

SMART-on-FHIR authentication

mcp-fhir v1.1.0 adds SMART-on-FHIR backend services (client_credentials grant, RFC 6749) so the server can authenticate against real Epic / Cerner sandboxes — no browser redirect needed.

When SMART_ENABLED=true, every FHIR request gets an Authorization: Bearer <token> header. Tokens are cached in-process and refreshed automatically 30 s before expiry.

SMART environment variables

VariableDefaultDescription
SMART_ENABLEDfalseSet true to activate SMART auth
SMART_CLIENT_ID""App client ID from EHR registration portal
SMART_CLIENT_SECRET""Client secret (stored as SecretStr, never logged)
SMART_TOKEN_URL""Token endpoint URL; auto-discovered if blank
SMART_SCOPESsystem/*.readSpace-separated OAuth 2.0 scopes
SMART_GRANT_TYPEclient_credentialsOAuth grant type (only client_credentials supported)
SMART_TOKEN_TIMEOUT_S15.0HTTP timeout for token requests

When SMART_TOKEN_URL is blank the server performs SMART auto-discovery:
GET {FHIR_BASE_URL}/.well-known/smart-configuration → token_endpoint.
Falls back to {FHIR_BASE_URL}/oauth2/token if discovery returns a non-200.

Epic sandbox quick-start

  • Register at https://fhir.epic.com/My Apps → create a backend-services app.
  • Note your Client ID and Client Secret.
  • Create .env in the monorepo root (already in .gitignore):
FHIR_BASE_URL=https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4
SMART_ENABLED=true
SMART_CLIENT_ID=your-epic-client-id
SMART_CLIENT_SECRET=your-epic-client-secret
# SMART_TOKEN_URL is optional — auto-discovered from FHIR_BASE_URL

Cerner sandbox quick-start

  • Register at https://code.cerner.com/ → create a backend-services app.
  • Note your Client ID and the token endpoint URL.
  • Update .env:
FHIR_BASE_URL=https://fhir-ehr-code.cerner.com/r4/your-tenant-id
SMART_ENABLED=true
SMART_CLIENT_ID=your-cerner-client-id
SMART_CLIENT_SECRET=your-cerner-client-secret
SMART_TOKEN_URL=https://authorization.cerner.com/tenants/your-tenant-id/protocols/oauth2/profiles/smart-v1/token

Claude Desktop config with SMART auth

{
  "mcpServers": {
    "mcp-fhir": {
      "command": "uvx",
      "args": ["mcp-fhir"],
      "env": {
        "FHIR_BASE_URL": "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4",
        "SMART_ENABLED": "true",
        "SMART_CLIENT_ID": "your-epic-client-id",
        "SMART_CLIENT_SECRET": "your-epic-client-secret"
      }
    }
  }
}

Programmatic client (LangGraph / any Python agent)

Use StdioServerParameters to point any Python MCP client at a FHIR server. The same pattern works in LangGraph tool nodes, LangChain agents, or plain asyncio scripts.

from mcp import ClientSession, StdioServerParameters
from mcp.client.stdio import stdio_client
import asyncio, os

server = StdioServerParameters(
    command="uv",
    args=["run", "--package", "mcp-fhir", "mcp-fhir"],
    env={
        **os.environ,
        # ── Public HAPI (no auth) ──────────────────────────────────
        "FHIR_BASE_URL": "https://hapi.fhir.org/baseR4",
        "SMART_ENABLED": "false",

        # ── Epic sandbox (uncomment + fill in) ────────────────────
        # "FHIR_BASE_URL":       "https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4",
        # "SMART_ENABLED":       "true",
        # "SMART_CLIENT_ID":     os.environ["EPIC_CLIENT_ID"],
        # "SMART_CLIENT_SECRET": os.environ["EPIC_CLIENT_SECRET"],

        # ── Cerner sandbox (uncomment + fill in) ──────────────────
        # "FHIR_BASE_URL":       "https://fhir-myrecord.cerner.com/r4/<tenant-id>",
        # "SMART_ENABLED":       "true",
        # "SMART_CLIENT_ID":     os.environ["CERNER_CLIENT_ID"],
        # "SMART_CLIENT_SECRET": os.environ["CERNER_CLIENT_SECRET"],
        # "SMART_TOKEN_URL":     "https://authorization.cerner.com/tenants/<tenant-id>/...",
    },
)

async def main():
    async with stdio_client(server) as (read, write):
        async with ClientSession(read, write) as session:
            await session.initialize()
            result = await session.call_tool(
                "fhir_search",
                arguments={"resource_type": "Patient", "params": {"_count": "3"}},
            )
            print(result.content[0].text)

asyncio.run(main())

See demo/fhir_server_configs.py for ready-to-run configs for all supported servers (public HAPI, SMART Health IT, Epic, Cerner, local Docker).

Running SMART integration tests

# Requires real sandbox credentials in .env
uv run pytest packages/mcp-fhir/tests -m smart_integration -v

Profile validation

The validate_against_profile tool supports shorthand aliases:

AliasProfile URL
us-core-patienthttp://hl7.org/fhir/us/core/StructureDefinition/us-core-patient
us-core-observationhttp://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-clinical-result
us-core-conditionhttp://hl7.org/fhir/us/core/StructureDefinition/us-core-condition-problems-health-concerns
us-core-medication-requesthttp://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest
us-core-encounterhttp://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter
ips-patienthttp://hl7.org/fhir/uv/ips/StructureDefinition/Patient-uv-ips

Profile validation requires the HAPI validator sidecar (included in docker-compose.yml at repo root).

Development

# From monorepo root
uv sync
uv run pytest packages/mcp-fhir/tests -v
# Integration tests (requires HAPI public server access)
uv run pytest packages/mcp-fhir/tests -m integration

Architecture

Client (Claude / GPT-4o)
    │  MCP protocol (stdio or SSE)
    ▼
mcp-fhir server (Python, anyio)
    ├── fhir_read ──────────► FHIR R4 server (configurable)
    ├── fhir_search ─────────► FHIR R4 server
    └── validate_against_profile
              │
              ▼
        HAPI validator sidecar (markiantorno/validator-wrapper)
              │
              ▼
        LangFuse (optional observability)

License

Apache-2.0

Keywords

ai

FAQs

Related posts