Add an AI agent to Medplum: approval-gated chat over the patient chart
Medplum gives you a FHIR backend and APIs to build on. Last EHR adds an AI agent layer: a chat interface that reads your patient chart and proposes writes (notes, observations) that require your approval before saving. It is open source, self-hosted, and brings your own model key.
What Last EHR adds to a Medplum project
Medplum is a headless FHIR backend. It handles authentication, multi-tenancy, access control, and audit logs. What it does not give you out of the box is a chat interface for working the chart in natural language.
Last EHR is a thin agent layer that runs on top of Medplum and adds that interface. The agent has four tools:
- Search patients by name
- View a patient's chart (conditions, allergies, medications, observations, notes, immunizations)
- Add a free-text note (approval-gated)
- Record an observation: a vital sign or lab value with a numeric value and unit (approval-gated)
Reads execute immediately, so you see patient data in the chat. Writes trigger a confirmation card in the UI: nothing touches your chart until you click Approve. Once approved, the write goes to Medplum under the signed-in user's identity, bounded by your AccessPolicy.
The layer stores no patient data of its own. Every API call runs against your Medplum instance, using the credentials you provide.
Not the Medplum Agent: how this differs from Medplum's own tools
Medplum has its own tools with similar-sounding names, so it is worth being precise about what Last EHR is and is not.
The Medplum Agent is Medplum's on-premise connectivity tool: it bridges devices and legacy feeds (HL7 v2, DICOM) on a local network to your Medplum project. It is integration plumbing, not a chat interface. The name collides; the products do not.
Medplum also ships an MCP server that exposes Medplum data to MCP-compatible clients such as Claude. That is lower-level infrastructure for wiring models to data; it does not include a clinician-facing UI or an approval gate.
Last EHR is a self-contained web chat with the approval gate built in, and it is provider-agnostic: OpenAI, Anthropic, and any model the Vercel AI SDK supports. Medplum continues to ship its own AI features on its own roadmap; this project is independent of that and focuses on one interaction done carefully: approval-gated chat over the chart.
How approval-gated writes work with your AccessPolicy
The approval gate is a UI control, not a permission control. When you approve a write, it still goes through Medplum's AccessPolicy. If your policy says the signed-in user cannot create Communications or Observations, Medplum rejects the write even after approval.
- The agent calls add_note or record_observation (defined with needsApproval: true in the Vercel AI SDK).
- The SDK pauses before the tool's execute function runs.
- The UI shows an approval card with exactly what will be written.
- You click Approve or Cancel.
- Only on Approve does execute run and call Medplum. On Cancel, nothing is saved.
- Medplum then checks the AccessPolicy for the signed-in user and accepts or rejects the write.
The gate surfaces writes for human review; your AccessPolicy stays the source of truth for who can write what. Read the full breakdown in how approval-gated writes work.
Run it on your Medplum: setup, env vars, and your own model key
Last EHR is a Next.js app. The one-click Vercel deploy button in the README prompts for the required env vars. To run it locally:
git clone https://github.com/cbetz/last-ehr.git
cd last-ehr
npm install
cp .env.example .env.local # then edit .env.local
npm run seed # load synthetic patients into your Medplum
npm run dev # http://localhost:3000/demoAt minimum, set:
- A model key: OPENAI_API_KEY (default provider) or ANTHROPIC_API_KEY with AI_PROVIDER=anthropic.
- MEDPLUM_CLIENT_ID and MEDPLUM_CLIENT_SECRET: a Medplum ClientApplication, used by the seed script and by the no-sign-in quickstart mode (NEXT_PUBLIC_QUICKSTART=true).
- MEDPLUM_BASE_URL and NEXT_PUBLIC_MEDPLUM_BASE_URL if you run your own Medplum; leave blank for Medplum's hosted API.
Every variable is documented in .env.example. The seed script loads four synthetic patients so you can test without any real data. Then open /demo and ask the agent to find a patient.
Alpha status, PHI, and BAAs: what you can and cannot do today
Last EHR is in active development. APIs are not stable and the scope will grow. Use synthetic data only at this stage.
Do not point this at real patient data without agreements in place. Last EHR is not a HIPAA-covered service, and the approval gate is a write-safety control, not a privacy control: anything the agent reads is sent to your model provider as context, under your API key. Handling real PHI would require a BAA with your model provider that covers API traffic (consumer plans do not qualify), a HIPAA-eligible FHIR backend with its own BAA (Medplum offers HIPAA-eligible hosted plans; check their current terms), and your own compliance review.
Last EHR is Apache-2.0. Self-hosting is free. A managed tier (hosted Medplum, a signed BAA, multi-tenancy, billing) may follow, built only after the open-source core has proven value.
Frequently asked questions
Is Last EHR a replacement for Medplum?
No. Last EHR is a layer that runs on top of Medplum. Medplum is the system of record: it stores the patient data, handles authentication, manages access control, and logs audit trails. Last EHR is a chat interface that reads from and writes to Medplum. It is not a backend, not a replacement, and not the source of truth.
What does the approval gate do, and what does it not do?
The approval gate is a write-safety control: it surfaces proposed writes to the user and requires an explicit click before anything is saved. It stops unilateral writes and creates a human-in-the-loop checkpoint. What it does not do is enforce access control (that is Medplum's AccessPolicy), or guarantee clinical correctness (that is the clinician's responsibility). The gate stops the agent from writing without your click; it does not stop you from approving a clinically wrong write.
Can I use Last EHR with real patient data today?
You should not. Last EHR is alpha software, and using it with real PHI requires a BAA with your model provider and a HIPAA-eligible backend with its own BAA. Use synthetic data for development, testing, and evaluation. Only consider real data once you have the agreements in place and have done your own compliance review.
Can I add more tools to the agent?
Yes. The tools are defined in lib/ai/tools.ts. You can add new tools by following the Vercel AI SDK tool pattern and writing new FHIR calls. The code is Apache-2.0, so you can fork it and extend it for your use case.
Does Last EHR work with backends other than Medplum?
Today, Medplum is the only supported backend. The architecture is designed to be backend-agnostic (all the FHIR calls go through the same seam), and other FHIR backends such as Aidbox, HAPI, and Firely are a goal for future work. For now, Medplum is the only adapter.
What model providers does Last EHR support?
OpenAI and Anthropic are supported out of the box: set AI_PROVIDER and the matching API key. The agent is built on the Vercel AI SDK, so other providers the SDK supports can be added with a small code change.
See it on synthetic patients
The live demo runs the same four tools against seeded synthetic data. Every write goes through the approval card.