Mcp Fhir

io.github.pcmedsingePublished by GitHub account pcmedsingev1.1.1Updated Oct 24, 2026

FHIR R4 read/search/validate MCP server — US Core/IPS profiles, SMART-on-FHIR auth (Epic/Cerner).

VerifiedSTDIODesktop onlySecurity & MonitoringData & Analytics

Overview

AI-generated overview

Lets an assistant read, search, paginate, and profile-validate FHIR R4 healthcare resources, optionally authenticating to Epic or Cerner sandboxes.

What you can do
Exposes five tools over stdio: fhir_capabilities summarizes a FHIR server's CapabilityStatement, fhir_read fetches one resource by type and logical ID, fhir_search runs FHIR query parameters and returns a Bundle with a _next_url when paginated, fhir_search_next follows that pagination link with SSRF guarding, and validate_against_profile checks a resource against US Core or IPS profiles through a HAPI validator sidecar. It can also authenticate to Epic or Cerner EHR sandboxes using SMART-on-FHIR backend services.
When to use it
Useful when an assistant needs to explore or query a FHIR R4 clinical data server, pull patient or observation resources, or check resources against US Core and IPS profiles. It fits health-data prototyping and integration work against public demo servers or EHR sandboxes.
Requirements
Runs locally as a Python package (Python 3.12+ with uv, launched via uvx). Needs FHIR_BASE_URL pointing at a FHIR R4 server; defaults to the public HAPI demo server. Profile validation needs a HAPI validator sidecar, configured with HAPI_VALIDATOR_URL. SMART authentication requires SMART_ENABLED plus SMART_CLIENT_ID and SMART_CLIENT_SECRET, with SMART_TOKEN_URL optional. Optional LangFuse keys enable observability.
Before you install
With SMART_ENABLED=true, requests carry a bearer token and the server needs SMART_CLIENT_ID and SMART_CLIENT_SECRET, which are real EHR credentials and should be kept out of version control. Queries go to whatever FHIR server FHIR_BASE_URL names, so clinical data may leave the machine; the default is a public demo server. Optional LANGFUSE_PUBLIC_KEY and LANGFUSE_SECRET_KEY send observability traces to a third party. Tools are read-only, but the data is sensitive health information.

Installation

In SourceWeft

  1. Open Mcp Fhir in the dashboard and add it to a workspace.
  2. Enable the server for the chats that should use its tools.

Desktop only via STDIO. STDIO servers start a local process, so they need the SourceWeft desktop host.

Other MCP clients

Follow the launch instructions in the repository.

README

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

bash
# requires Python 3.12+ and uvuvx 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)

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

  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):
dotenv
FHIR_BASE_URL=https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4SMART_ENABLED=trueSMART_CLIENT_ID=your-epic-client-idSMART_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:
dotenv
FHIR_BASE_URL=https://fhir-ehr-code.cerner.com/r4/your-tenant-idSMART_ENABLED=trueSMART_CLIENT_ID=your-cerner-client-idSMART_CLIENT_SECRET=your-cerner-client-secretSMART_TOKEN_URL=https://authorization.cerner.com/tenants/your-tenant-id/protocols/oauth2/profiles/smart-v1/token

Claude Desktop config with SMART auth

json
{  "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.

python
from mcp import ClientSession, StdioServerParametersfrom mcp.client.stdio import stdio_clientimport 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

bash
# Requires real sandbox credentials in .envuv 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

bash
# From monorepo rootuv syncuv 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

Source: packages/mcp-fhir/README.md at commit 07fc507

Tools

0
SourceWeft has not started this package in its sandbox yet, so its tools are not known.

Version history

1
  1. v1.1.1LatestSep 16, 2026

Score

40% of the applicable checks met

How complete this listing is, computed from the registry entry, the endpoint and public sources. Not a rating of the server and not a statement that it is safe or working today: reachability is a dated check, and no installs or reviews go into the score.

  • Reached in the last 30 days25 pts

    Keep the endpoint answering the MCP handshake; SourceWeft probes it every 30 days.

  • Auth declared10 pts

    Declare the credential headers in the registry entry, or keep the endpoint answering the probe so its auth can be classified.

  • Tools listed15 pts

    Answer tools/list without a credential, or ship a package that starts in a sandbox without one.

  • DescriptionRequired15 pts
  • Logo10 pts
  • Documentation15 pts
  • License10 pts

    Declare a license in the repository or the registry entry.