Skip to main content

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.

Tool Description
fhir_capabilities CapabilityStatement summary — what the server supports
fhir_read Read a single resource by type + logical ID
fhir_search Search a resource type with FHIR query params; returns Bundle with _next_url when paginated
fhir_search_next Follow a _next_url pagination link (SSRF-guarded)
validate_against_profile Validate 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):

Category Cases Pass rate
fhir_capabilities 2 100 %
fhir_read 4 100 %
fhir_search 6 100 %
fhir_search_next 2 100 %
validate_against_profile 6 100 %
Total 20 100 %

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):

Variable Default Description
FHIR_BASE_URL https://hapi.fhir.org/baseR4 FHIR server base URL
HAPI_VALIDATOR_URL http://localhost:8082 HAPI validator sidecar (runs on port 3500 internally; map 8082:3500)
FHIR_TIMEOUT_S 30 HTTP timeout in seconds
FHIR_MAX_RESULTS 20 Default _count for searches
MCP_TRANSPORT stdio stdio or sse
MCP_PORT 8000 Port for SSE transport
LOG_FORMAT json json (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

Variable Default Description
SMART_ENABLED false Set 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_SCOPES system/*.read Space-separated OAuth 2.0 scopes
SMART_GRANT_TYPE client_credentials OAuth grant type (only client_credentials supported)
SMART_TOKEN_TIMEOUT_S 15.0 HTTP 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

  1. Register at https://fhir.epic.com/ → My Apps → create a backend-services app.
  2. Note your Client ID and Client Secret.
  3. 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

  1. Register at https://code.cerner.com/ → create a backend-services app.
  2. Note your Client ID and the token endpoint URL.
  3. 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:

Alias Profile URL
us-core-patient http://hl7.org/fhir/us/core/StructureDefinition/us-core-patient
us-core-observation http://hl7.org/fhir/us/core/StructureDefinition/us-core-observation-clinical-result
us-core-condition http://hl7.org/fhir/us/core/StructureDefinition/us-core-condition-problems-health-concerns
us-core-medication-request http://hl7.org/fhir/us/core/StructureDefinition/us-core-medicationrequest
us-core-encounter http://hl7.org/fhir/us/core/StructureDefinition/us-core-encounter
ips-patient http://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

Metadata

Release files for mcp-fhir 1.1.1

For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.

Source distribution (sdist)

Source distribution for mcp-fhir 1.1.1
File Size Uploaded Provenance Actions
mcp_fhir-1.1.1.tar.gz 21.3 kB With provenance Details for mcp_fhir-1.1.1.tar.gz

Built distribution (wheel)

Table of built distributions (wheels) for mcp-fhir 1.1.1
File Interpreter ABI Platform Actions
mcp_fhir-1.1.1-py3-none-any.whl Python 3 none any Details for mcp_fhir-1.1.1-py3-none-any.whl

Total release size: 43.4 kB

Release files / mcp_fhir-1.1.1.tar.gz

Download URL mcp_fhir-1.1.1.tar.gz
Size 21.3 kB
Tags Source
SHA-256 checksum
How to use checksums
b29fe58083ad553d6c35b03714bfe7190fe4278de3d1fc8da0749436ddc31ea9
BLAKE2b-256 checksum
How to use checksums
291aa7feb2aa7121ef2cb55bdef39faee008964c70aa6f4d02e8c60023770c37
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 20, 2026.

Transparency log

Release files / mcp_fhir-1.1.1-py3-none-any.whl

Download URL mcp_fhir-1.1.1-py3-none-any.whl
Size 22.0 kB
Tags Python 3
SHA-256 checksum
How to use checksums
4e66b264186a95e37d0394fd66ade1635221fb13ebc704055779874f0488f5ac
BLAKE2b-256 checksum
How to use checksums
a6a9ef2abcc329b3c814bfdbc4a36d05388ffcfe46481e8694927a30cafc4d87
Upload date
Uploaded using Trusted Publishing?
What is trusted publishing?
Yes
Uploaded via twine/6.1.0 CPython/3.13.12

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 20, 2026.

Transparency log

Security for mcp-fhir 1.1.1

Provenance

Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.

Full provenance

All 2 files in this release have been uploaded with provenance metadata.

Select from 2 files

Attestation for mcp_fhir-1.1.1.tar.gz

PyPI Publish Attestation

PyPI verified that this artifact, at this checksum, originated from the publisher listed below.

Signed by GitHub Actions, verified by PyPI on Jun 20, 2026.

Transparency log

A note on package security

PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.

To learn more about protecting yourself from malicious actors, visit our package security documentation.

Release history Release notifications | RSS feed

This release

1.1.1 This release

2 release files Full provenance

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page