sharp-on-fhir-mcp
A clean-room SHARP-on-MCP compliant FHIR R4 MCP server that enables AI agents to interact with any FHIR R4 endpoint using SHARP context headers, without server-side OAuth. It provides clinical tools, lab results, imaging, and interactive MCP-UI dashboards.
Ask AI about sharp-on-fhir-mcp
Powered by Claude Β· Grounded in docs
I know everything about sharp-on-fhir-mcp. Ask me about installation, configuration, usage, or troubleshooting.
0/500
Reviews
Documentation
sharp-fhir-mcp
A clean-room SHARP-on-MCP compliant FHIR R4 MCP server with interactive MCP-UI clinical dashboards.
Built for the Prompt Opinion "Build the Future of Healthcare AI" Hackathon β a vendor-neutral MCP server that any SMART-on-FHIR app, agent, or LLM host can plug into without server-side OAuth, API keys, or proprietary auth flows.
Why SHARP?
The SHARP (Standardised Healthcare Agent Remote Protocol) spec describes a headers-based context model for MCP servers in healthcare:
| Header | Purpose |
|---|---|
X-FHIR-Server-URL | Base URL of the patient's FHIR R4 endpoint |
X-FHIR-Access-Token | Bearer token already minted by the agent host |
X-Patient-ID | Optional default Patient resource id |
Per SHARP Β§3.2, the MCP server never runs an OAuth dance itself. The agent host (e.g. a SMART-on-FHIR launch container) obtains the token and forwards it on every call. This means a single deployment of this server works against Epic, Cerner, MEDITECH, athenahealth, eClinicalWorks, ConnectEHR, HAPI, or any other FHIR R4 endpoint β there's nothing vendor-specific.
The server advertises capabilities.experimental.fhir_context_required = true
on every initialise response so SHARP-aware clients know to forward those
headers automatically.
What's included
π©Ί Clinical FHIR tools
fhir_get_capability_statementβ discover the connected FHIR serverfhir_get_patient,fhir_search,fhir_read,fhir_patient_everythingβ generic R4 accessclinical_search_patients,clinical_get_patient_summaryclinical_get_appointments,clinical_get_encountersclinical_get_problems,clinical_get_medications,clinical_get_allergies,clinical_get_immunizationsclinical_get_health_recordβ one-shot consolidated recordclinical_get_contextβ full visit context (demographics + allergies + meds + problems + labs + vitals + encounters + alerts) in parallel
π¬ Labs, vitals & imaging
lab_get_results,lab_get_vital_signs,lab_get_diagnostic_reportsimaging_get_documentsβ DocumentReference search
π§ Optional persistent clinical memory (mem0)
Backed by mem0 (Apache-2.0). mem0 is
embedded as a Python library β no separate service or database. Memory
state persists in a Docker volume (mem0_data).
mem0 is text-only at the storage layer, but is well-suited to clinical
narrative use (encounters, alerts, notes, transcripts). For non-text
inputs (radiology films, audio dictation, video clips), the agent host
should pre-process β caption images via a VLM, transcribe audio with
Whisper, summarise video β and persist the resulting text via
memory_store_note. This keeps memory cleanly searchable and lets each
host pick the right model per modality.
Available when OPENAI_API_KEY (or OPENAI_API_BASE for an OpenAI-compat
provider) is set, and MEM0_DISABLED is not 1:
memory_store_encounterβ visit summary with diagnoses, plan, complaintmemory_store_alertβ persistent clinical flag (allergy, drug interaction, β¦)memory_store_noteβ free-text note (use for VLM/Whisper outputs)memory_search_historyβ semantic search scoped to current patientmemory_get_patient_historyβ list memories for the patientmemory_deleteβ remove a single memory by idmemory_reset_patientβ wipe all memories for one patient (irreversible)
π MCP-UI visualisations
visualize_lab_trendβ Chart.js line chart of one lab over timevisualize_vitalsβ multi-chart vitals dashboardvisualize_patient_dashboardβ full HTML clinical page (demographics, alerts, allergies, meds, problems, labs, encounters, immunisations + Chart.js trends)
All visual tools return MCP-UI ui:// resources that the host renders in its inspector pane.
Quickstart
1. Install
git clone https://github.com/your-org/sharp-fhir-mcp.git
cd sharp-fhir-mcp
pip install -e .
2. Run the server
sharp-fhir-mcp # http (streamable) on 0.0.0.0:8000
sharp-fhir-mcp --port 9000 # custom port
SHARP_STRICT_CONTEXT=1 sharp-fhir-mcp # reject calls missing FHIR headers
The MCP endpoint is http://localhost:8000/mcp.
For the full stack (FHIR MCP server + embedded mem0 memory) use the
one-shot bring-up script β it handles the broken-docker-symlink case
(common after migrating off OrbStack), seeds .env if missing, optionally
prunes build cache, and starts the stack detached:
./scripts/start.sh # build + start, detached
./scripts/start.sh --no-memory # disable mem0 (memory_* tools omitted)
./scripts/start.sh --logs # follow logs after start
./scripts/start.sh --prune --build # free disk + rebuild from scratch
./scripts/start.sh --down # tear down + remove memory volume
Or run compose directly:
cp .env.example .env # set OPENAI_API_KEY (or OPENAI_API_BASE)
docker compose up --build -d
Note:
localhosthere refers to localhost of the machine where you are running the server. To access it remotely, deploy the server (see below) or port-forward to your local instance.
3. Connect from any SHARP-aware MCP client
Send these headers on every JSON-RPC request:
X-FHIR-Server-URL: https://hapi.fhir.org/baseR4
X-FHIR-Access-Token: <bearer token from your SMART launch>
X-Patient-ID: 12345 # optional
4. Try a public sandbox without writing a SMART app
The HAPI public FHIR R4 sandbox is read-only and does not require auth β useful for kicking the tires:
curl -X POST http://localhost:8000/mcp \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-H 'X-FHIR-Server-URL: https://hapi.fhir.org/baseR4' \
-H 'X-FHIR-Access-Token: anonymous' \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list"}'
Deployment
Vercel (Python serverless)
This server runs as a stateless Streamable-HTTP endpoint, which works on Vercel out of the box. You can re-use an existing Next.js MCP scaffold by either:
-
Adding the Python ASGI handler β drop the
appStarlette instance intoapi/index.py:# api/index.py from sharp_fhir_mcp.server import app # noqa: F401plus a minimal
vercel.json:{ "builds": [{"src": "api/index.py", "use": "@vercel/python"}], "routes": [{"src": "/(.*)", "dest": "api/index.py"}] } -
Or running it as a sidecar behind your existing Vercel front-end and reverse-proxying
/mcpto a longer-lived host (Fly.io, Railway, Render).
The server respects the Vercel-injected PORT environment variable.
Local development
cp .env.example .env # set FHIR_SERVER_URL etc. for fallbacks
sharp-fhir-mcp # http://localhost:8000/mcp
Docker / docker-compose
A single-service stack: the FHIR MCP server runs in one container and embeds mem0 as a library.
cp .env.example .env
# Set OPENAI_API_KEY (or OPENAI_API_BASE for an OpenAI-compat provider:
# OpenRouter, Ollama OpenAI-mode, vLLM, LM Studio, Together).
docker compose up --build
- MCP endpoint:
http://localhost:8000/mcp - Persistent memory volume:
mem0_data(mounted at/datain container)
Local LLM + embeddings (no OpenAI calls)
Point both the LLM and embedder at a local Ollama:
OPENAI_API_BASE=http://host.docker.internal:11434/v1
MEM0_LLM_PROVIDER=ollama
MEM0_LLM_MODEL=llama3.1
MEM0_EMBED_PROVIDER=ollama
MEM0_EMBED_MODEL=nomic-embed-text
Disabling memory
Pass MEM0_DISABLED=1 (or ./scripts/start.sh --no-memory) to skip mem0
entirely. The FHIR/clinical/lab/visualisation tools still work; only the
memory_* tools are omitted.
Architecture
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β MCP Client / Agent / LLM host (Claude, Cursor, custom) β
β β’ Knows the patient's FHIR endpoint + access token β
β β’ Sends X-FHIR-Server-URL, X-FHIR-Access-Token headers β
ββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββββββββ
β Streamable HTTP (SHARP-on-MCP)
POST /mcp + JSON-RPC + SHARP headers
βΌ
βββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ
β sharp-fhir-mcp β
β β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β SharpContextMiddleware β β
β β β’ Parses X-FHIR-Server-URL / X-FHIR-Access-Token β β
β β β’ Stores in ContextVar for the request scope β β
β βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β FastMCP tool registry β β
β β ββ fhir_* (generic R4 search/read) β β
β β ββ clinical_* (patient/encounter/medication/β¦) β β
β β ββ lab_* / imaging_*(observations, reports, docs) β β
β β ββ memory_* (optional, embedded mem0) β β
β β ββ visualize_* (MCP-UI Chart.js dashboards) β β
β βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
β βΌ β
β ββββββββββββββββββββββββββββββββββββββββββββββββββββββββββ β
β β Vendor-neutral FHIR R4 client (httpx, async) β β
β βββββββββββββββββββββββββββ¬βββββββββββββββββββββββββββββββ β
ββββββββββββββββββββββββββββββΌβββββββββββββββββββββββββββββββββ
βΌ
FHIR R4 server (Epic / Cerner / HAPI / β¦)
See CLAUDE.md for detailed module-by-module notes and the
SHARP compliance check-list.
SHARP compliance check-list
| Requirement | Status |
|---|---|
| Streamable-HTTP transport (stdio not in scope) | β |
Read FHIR endpoint from X-FHIR-Server-URL header | β |
Read bearer token from X-FHIR-Access-Token header | β |
Optional X-Patient-ID header for default patient context | β |
Advertise capabilities.experimental.fhir_context_required | β |
| No server-side OAuth / token storage | β |
| Vendor-neutral FHIR R4 client | β |
Structured fhir_context_required errors when headers absent | β |
Optional strict context enforcement (SHARP_STRICT_CONTEXT=1) | β |
License
MIT β see LICENSE.
