fhir-journey-mcp
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@fhir-journey-mcpwhich discharged patients last week never got a follow-up booked?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
fhir-journey-mcp
A read-only MCP server that gives an AI agent a patient's context from any FHIR R4 server: who they are, when they were last seen, what is booked next, and which discharged patients never got a follow-up.
Hospitals already run HIS, EMR and scheduling systems that speak (some) FHIR. An agent that calls patients or answers the front desk needs that context before it says a word, and it should not need write access to get it.
Tools
Tool | What it answers |
| Find a patient by name, phone, identifier (MRN, ABHA number) or birth date. Requires at least one filter. |
| Encounters + appointments as one dated timeline, last seen, next appointment, no-show and cancellation counts. |
| Appointments by patient, practitioner, status and date range, e.g. all |
| Free slots for a practitioner (chained through |
| Finished encounters in a window with no live follow-up within N days. Cancelled or no-show follow-ups don't count as booked. Each row says whether the window is already |
fhir_find_followups_due output, from the live smoke run below:
[
{ "patient": "Meera Demo", "discharged": "2026-10-09T12:00:00.000Z", "dueBy": "2026-10-16T12:00:00.000Z", "lateBooking": "2026-10-28T10:00:00.000Z" },
{ "patient": "Rohan Demo", "discharged": "2026-10-13T12:00:00.000Z", "dueBy": "2026-10-20T12:00:00.000Z", "lateBooking": null }
]Related MCP server: MCP FHIR Server
Run it
npm install
npm run build
FHIR_BASE_URL=https://your-server/fhir FHIR_TOKEN=... npx fhir-journey-mcpClaude Desktop / any MCP client:
{
"mcpServers": {
"fhir-journey": {
"command": "node",
"args": ["/path/to/fhir-journey-mcp/dist/mcp/server.js"],
"env": { "FHIR_BASE_URL": "https://hapi.fhir.org/baseR4" }
}
}
}With no FHIR_BASE_URL it uses the public HAPI test server, which holds synthetic data only.
Verify it
npm test # 22 offline tests against a fake FHIR server
npm run seed # loads 4 synthetic patients with known answers, tagged per run
npm run smoke # starts the server over stdio and checks every tool's answerscripts/seed.ts documents the expected answers (who is booked, who is due, whose follow-up was cancelled and rebooked late). npm run smoke passes 10/10 against the public HAPI server.
Design choices
Read-only. Booking, cancelling and messaging patients belong behind a human or a separate, audited tool.
No full-table reads. Search tools refuse to run without a filter.
Ids are validated before they reach a URL path; a model can't turn
patientIdinto../Practitioner/1.Paging links are only followed on the configured origin, so a bearer token is never sent to a host the server named in a
nextlink.Search URLs are redacted from errors, since they carry names and phone numbers.
GET retries on 408/429/5xx with backoff; transaction POSTs are never replayed.
Compact summaries, not raw resources: less token spend, fewer fields for a model to misread.
meta.tagfilter on every search tool, for scoping to one tenant or one test dataset.
Not done / honest limits
Tested against HAPI FHIR only. Real hospital systems use FHIR profiles and partial implementations differently (chained search and
_sortsupport vary). Not tested against any production EMR/HIS.No ABDM gateway integration, consent artefact handling, or SMART-on-FHIR auth flow; it takes a bearer token you already have.
"Follow-up" means any live appointment for the patient after discharge, not one matched to the same department or condition.
fhir_find_followups_duemakes one appointment query per patient (5 in parallel). Fine for a daily worklist, not for a 10,000-discharge backfill.
MIT licensed.
Available Tools
5 toolsfhir_find_followups_dueFollow-ups dueA
Finished encounters that ended in a date range with no live follow-up appointment booked within withinDays of discharge. Cancelled or no-show follow-ups do not count as booked. Returns the patients to contact, oldest discharge first, whether the window has already closed (overdue) or can still be met, and any follow-up booked too late.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Restrict to resources carrying this meta.tag, as `system|code` or `code`. Useful for scoping to one tenant or one demo dataset. | |
| withinDays | No | ||
| dischargedTo | No | Defaults to now. | |
| maxEncounters | No | ||
| dischargedFrom | Yes | ||
| encounterClass | No | v3 ActCode, e.g. IMP (inpatient), EMER, AMB. |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden and does meaningful work: it defines what counts as 'booked' (cancelled/no-show excluded), specifies result ordering (oldest discharge first), and describes the returned state (overdue vs. still-met, plus late bookings). It omits truncation/pagination behavior for maxEncounters and any permission context.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences, front-loaded with the cohort definition followed by exclusions and return semantics; every clause earns its place. Slightly dense with parenthetical output detail, but no filler.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
No annotations and no output schema, so the description must supply both safety framing and return shape, and it does define the result contents and ordering. Gaps remain around truncation (maxEncounters cap of 200), tag-based scoping, and encounterClass meaning for a 6-parameter clinical query.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50%, and withinDays has no schema description at all, so the description's gloss ('booked within withinDays of discharge') is genuinely additive. However, tag, encounterClass, and maxEncounters are unexplained in either place, leaving half the parameters ambiguous.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a precise cohort: finished encounters whose discharge date falls in a range and that lack a live follow-up booked within withinDays. That 'find the gap' verb/resource pair is clearly distinguishable from the sibling list/search tools (fhir_list_appointments, fhir_find_open_slots) without opening a schema.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Usage is implied by the cohort definition (call this to surface patients needing follow-up contact), but there is no explicit when-to-use/when-not guidance and no sibling is named as an alternative. An agent can infer intent but must guess the boundary with fhir_list_appointments.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fhir_find_open_slotsFind open slotsA
Free Slots in a date range, earliest first, for one practitioner or one schedule. Defaults to the next 7 days. Read-only: it does not hold or book the slot.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| tag | No | Restrict to resources carrying this meta.tag, as `system|code` or `code`. Useful for scoping to one tenant or one demo dataset. | |
| from | No | ||
| limit | No | ||
| scheduleId | No | ||
| practitionerId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations are supplied, so the description carries the full burden. It usefully discloses that the operation is read-only and does not hold or book the slot, plus a sort order (earliest first) and a default window. It omits pagination behavior tied to the limit parameter and the shape of returned data.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Three short sentences, zero waste, front-loaded with the core purpose followed by the default and the read-only guarantee. Every sentence earns its place.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 6-parameter tool with no annotations and no output schema, the description covers purpose, scoping, default range and read-only safety, which is a good core. It leaves gaps around pagination (limit cap of 100) and the result shape an agent would need to interpret the response.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 17% (just tag documented), so the description must compensate. It conveys meaning for the date range (from/to) and the practitioner-or-schedule scoping (practitionerId/scheduleId) plus the 7-day default, but says nothing about limit, and adds no syntax detail beyond the schema's patterns.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (find) and resource (free/open slots) with scope: date range, earliest first, for one practitioner or one schedule. This is clearly distinguishable from fhir_list_appointments, but the description never names the sibling to make the contrast explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Provides context ('for one practitioner or one schedule', defaults to next 7 days) but never states when to use this over fhir_list_appointments or fhir_find_followups_due, nor any exclusions. Usage is implied by the scoping constraints rather than explicit.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fhir_get_patient_journeyPatient journeyA
One patient's encounters and appointments merged into a dated timeline, plus when they were last seen, their next upcoming appointment, and no-show/cancellation counts. Use this before contacting a patient so the agent knows their context.
| Name | Required | Description | Default |
|---|---|---|---|
| since | No | Only include events on or after this date. | |
| patientId | Yes |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
No annotations, so the description carries the full load. It discloses the composition of the return payload (timeline, last seen, next appointment, no-show counts), which is genuinely useful behavior context. It does not state that the operation is read-only, whether it requires any patient-scoped permissions, or how large a timeline can get.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two sentences with zero filler: the first enumerates the payload, the second gives the usage trigger. Front-loaded with the concrete output list rather than restating the tool name.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
With no output schema, the description must describe return values and does so well, enumerating the four components of the journey. The remaining gaps are the role of the 'since' filter and any error/empty-patient behavior, which are minor for a two-parameter read tool.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema coverage is only 50%: 'since' is documented in the schema, but patientId carries no description at all. The description never mentions the 'since' filter or its effect on the timeline, so it adds no semantics beyond the schema and does not compensate for the undocumented parameter.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description says exactly what is returned: 'One patient's encounters and appointments merged into a dated timeline, plus when they were last seen, their next upcoming appointment, and no-show/cancellation counts.' That is a much sharper picture than the vague name/title 'Patient journey'. It implies a difference from fhir_list_appointments (aggregated, per-patient), but never names the sibling to make that explicit.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
'Use this before contacting a patient so the agent knows their context' gives a concrete trigger scenario, which is more than most tools offer. However it names no alternative (e.g. fhir_list_appointments for a flat appointment list) and states no exclusions, so routing between siblings still requires inference.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fhir_list_appointmentsList appointmentsA
Appointments filtered by patient, practitioner, status and date range, soonest first. At least one of patientId, practitionerId, from or tag is required.
| Name | Required | Description | Default |
|---|---|---|---|
| to | No | ||
| tag | No | Restrict to resources carrying this meta.tag, as `system|code` or `code`. Useful for scoping to one tenant or one demo dataset. | |
| from | No | ||
| limit | No | ||
| status | No | Any of these statuses, e.g. ['noshow','cancelled'] to find visits worth rebooking. | |
| patientId | No | ||
| practitionerId | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description carries the full burden. It adds the result ordering ('soonest first') and the mandatory-filter guard, and 'list' implies a read-only operation, but it says nothing about pagination/limit behavior, result volume, or what the response contains.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Two tight sentences with zero waste: the filter/scope statement first, the hard constraint second. Nothing redundant and nothing that could be trimmed without losing information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a 7-parameter, no-annotation, no-output-schema tool with 29% schema coverage, the description is minimally adequate. It covers filtering and ordering but omits pagination/limit behavior and the shape of the returned collection, which an agent would need to page results correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 29% across 7 parameters, so the description must compensate. It does name patientId, practitionerId, from and tag and explains the required-one-of relationship, but it never mentions the 'to' bound or the 'limit' parameter, leaving two parameters with no semantics anywhere.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb (list) and resource (appointments) plus the filter dimensions (patient, practitioner, status, date range) and result ordering. It implicitly separates itself from siblings like fhir_find_open_slots and fhir_find_followups_due by being the general appointment listing, though it never names those alternatives.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives one hard usage constraint — at least one of patientId, practitionerId, from or tag is required — which is genuinely useful and prevents an unbounded query. However it never says when to prefer this tool over fhir_find_open_slots or fhir_find_followups_due, so the selection guidance is only implied.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
fhir_search_patientsSearch patientsA
Find patients by name, phone, identifier (e.g. MRN or ABHA number, as system|value or value) or birth date. At least one filter is required. Returns compact summaries, not full resources.
| Name | Required | Description | Default |
|---|---|---|---|
| tag | No | Restrict to resources carrying this meta.tag, as `system|code` or `code`. Useful for scoping to one tenant or one demo dataset. | |
| name | No | ||
| limit | No | ||
| phone | No | ||
| birthdate | No | ||
| identifier | No |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
With no annotations, the description must carry the behavioral burden. It usefully discloses that results are "compact summaries, not full resources" and that at least one filter is required, but it omits read-only status, authorization needs, pagination, maximum results, and error behavior.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
The description is front-loaded and efficient: it names the search fields, states the key filter constraint, and clarifies the return shape in three compact sentences with no wasted text.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a six-parameter search tool with no output schema and no annotations, it provides enough to attempt a call and notes the compact return shape. It remains incomplete because `tag`, `limit`, and result/pagination behavior are not described.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is only 17%, so the description must compensate. It clarifies name, phone, identifier formats (`system|value` or `value`), and birth date, but it does not mention the `tag` or `limit` parameters, leaving two of six parameters undocumented.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
The description states a specific verb and resource: "Find patients" by name, phone, identifier, or birth date. It clearly identifies the searchable fields, though it does not explicitly distinguish this from the patient-oriented sibling fhir_get_patient_journey.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
It gives a clear invocation constraint: "At least one filter is required," and lists supported filters. However, it does not explain when to use this versus the patient journey or appointment-related siblings, nor does it describe exclusions or alternative search tools.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
5 tool updates
v0.1.0- First observed
fhir_find_followups_due - First observed
fhir_find_open_slots - First observed
fhir_get_patient_journey - First observed
fhir_list_appointments - First observed
fhir_search_patients
TDQS
Scored across 5 tools
Each tool targets a distinct resource or workflow: patient search, patient timeline, appointment listing, open slots, and follow-up detection. The only mild overlap is that get_patient_journey already embeds a patient's appointments while list_appointments offers a filtered appointment view, so an agent could occasionally hesitate on which to use for appointment data.
All tools use a uniform fhir_ prefix followed by a consistent verb_noun pattern (search_patients, get_patient_journey, list_appointments, find_open_slots, find_followups_due). The convention is predictable and readable throughout.
Five tools is well-scoped for a focused patient-journey server; each one earns its place covering search, context, appointments, availability, and follow-up detection without redundancy.
The read-only surface covers discovery and identification well, but the implied workflow dead-ends: find_open_slots explicitly cannot hold or book, and find_followups_due returns patients to contact with no tool to actually schedule the follow-up. Booking/rescheduling operations are notable gaps for the scheduling workflow the tools describe.
Maintenance
Related MCP Connectors
Read patient-authorized EHR records: medications, labs, conditions, allergies. Consent-bounded.
Read Spruce Health contacts, conversations, messages, phone lines and team members (read-only).
Read Elation Health patients, appointments, problems, allergies, medications, notes and vitals.
HL7 FHIR R4 health-data API for AI agents: free sandbox, 30 synthetic patients, AI-ready context.
Related MCP Servers
- AlicenseAqualityDmaintenanceEnables LLM-based agents to interact with FHIR healthcare data through natural language prompts, providing full CRUD operations on FHIR resources, document processing, and semantic search capabilities.13102MIT
- FlicenseAqualityDmaintenanceProvides read/write access to any FHIR-compliant healthcare API with built-in validation, supporting resource management, search operations, and granular permissions through natural language.51-
- AlicenseAqualityCmaintenanceProvides safe read-only access to synthetic healthcare data via FHIR R4, enabling AI assistants to search patients, retrieve vitals, and summarize conditions using a public sandbox.45 npmMIT
- AlicenseNot gradedqualityDmaintenanceEnables AI agents to securely interact with FHIR R4-based EMRs, supporting read, search, create, and update operations on any FHIR resource type across systems like Epic, Cerner, and OpenEMR.203 npmMIT