Make every AI chart write a reviewable proposal. Draft protocol for agent-initiated FHIR writes (Proposal → Decision → Commit → Audit) with web + MCP implementations and a conformance suite. Runs over Medplum, HAPI, Firely, Aidbox, or Oystehr.
claude mcp add last-ehr -- npx -y @lastehr/agent-write-conformance{
"mcpServers": {
"last-ehr": {
"command": "npx",
"args": ["-y", "@lastehr/agent-write-conformance"]
}
}
}Resumen de MCP Servers
# Last EHR
[](https://github.com/cbetz/last-ehr/actions/workflows/ci.yml)
[](LICENSE)
[](https://www.npmjs.com/package/@lastehr/mcp)
[](https://www.npmjs.com/package/@lastehr/agent-write-conformance)
[](https://registry.modelcontextprotocol.io/?q=io.github.cbetz%2Flast-ehr)
**Read the chart broadly. Write only what a human approved.** Last EHR is the
agent layer for a headless FHIR EHR. It does three things, and the write
protocol is only one of them.
**It reads the chart broadly.** 25 of [US Core 9.0.0](https://hl7.org/fhir/us/core/)'s
27 readable resource types, across 23 patient-scoped sections, following
references so authors, encounters, and provenance stop being dead pointers.
Counted in the open, with the ceiling published rather than implied, in
[FHIR coverage](./docs/fhir-coverage.md).
**It is built so the agent cannot fake an absence.** "She has never had a flu
shot" is the answer a chart agent must never invent, so a read reports when
its window was capped, when a reference lookup was refused, and when a code
filter matched nothing in a section that does hold records. Filters that a
section cannot apply are refused with the legal values instead of silently
dropped. These are safety-boundary tests, not best-effort behavior.
**Every write is a proposal.** Show the exact resource, require an explicit
human decision, commit exactly what was reviewed, and record what created it.
That half is a small protocol,
**[Approval-Gated Agent Writes on FHIR](./docs/agent-write-protocol.md)**
(v0.1 draft): Proposal → Decision → Commit → Audit. Two implementations run
here, a web approval card and MCP elicitation-gated write tools, plus a
[conformance suite](./docs/conformance.md) that checks implementations with
its own FHIR reads. Independent implementations and criticism are invited.
Five FHIR backends sit behind one interface. This repository is the reference
implementation: bring your own FHIR backend and model key for a real agent
(Medplum for the authenticated path, or any verified adapter for synthetic
evaluation), or start with the zero-key local synthetic walkthrough.
> **Last EHR is a _layer_, not an EHR.** It runs *on top of* a headless FHIR backend (Medplum for authenticated use; HAPI FHIR, Firely Server, Aidbox, or Oystehr for synthetic evaluation) and talks to it over the FHIR API. It is not the system of record, stores no PHI of its own, and never bundles or forks the backend.
**Status: early / alpha.** APIs, structure, and scope will change. Use synthetic data only. · License: [Apache-2.0](./LICENSE)
**[Try the live demo](https://www.lastehr.com/demo)**: no sign-up, synthetic data, and every write goes through the approval gate.

## Start here
| Goal | Start here |
| --- | --- |
| See the approval loop now | [Try the live synthetic-data demo](https://www.lastehr.com/demo): no sign-up. |
| Read the protocol | [Approval-Gated Agent Writes on FHIR, v0.1 draft](./docs/agent-write-protocol.md) |
| See exactly what the agent can reach in FHIR | [FHIR coverage](./docs/fhir-coverage.md): counted against US Core, ceiling included |
| Test an MCP (stdio) implementation of the protocol | `npx @lastehr/agent-write-conformance` — see the [conformance guide](./docs/conformance.md) |
| Give an MCP client bounded chart tools — read-only by default, opt-in approved writes (Medplum, or the local HAPI stack) | `npx -y @lastehr/mcp init --client claude-code` |
| Try fixture MCP locally without FHIR credentials or a provider API key | `npm run mcp:demo -- --client claude-code` |
| Prove the synthetic web-agent workflow locally | `npm run eval` |
| Inspect the complete flow locally with no account or model key | `npm run demo:local` |
### 30-second synthetic-data walkthrough
1. Open the [live demo](https://www.lastehr.com/demo).
2. Ask: `Find patients named Smith`.
3. Ask: `Record a heart rate of 72 bpm for Maria Garcia`.
4. Inspect the proposed write, then approve it. The data is synthetic and the
approval card shows exactly what will be saved.
For MCP, `@lastehr/mcp` is deliberately a separate surface that is
**read-only by default** (`search_patients` and `show_patient_info`), with
one opt-in: elicitation-gated write proposals a human approves per action.
Configure a least-privilege Medplum token before connecting it to a real
project; full setup is in the
[MCP guide](./docs/mcp.md) and the [Official MCP Registry listing](https://registry.modelcontextprotocol.io/?q=io.github.cbetz%2Flast-ehr).
Want to inspect the read surface before configuring Medplum? From a
checkout, `npm run mcp:demo` starts the included local HAPI stack, resets the
four synthetic fixture charts, and prints a Claude Code/Cursor configuration.
That checkout-only **MCP Local Lab** needs no FHIR/Medplum credentials or
model-provider API key of its own and is restricted to fixture patients. Your
MCP client still uses its normal account and may send those synthetic results
to its model provider. It is not the published package, generic HAPI support,
or a path for PHI.
For a deterministic safety-mechanics check, `npm run eval` creates a separate
synthetic target, proves the proposal/approval and denial paths, verifies chart
association, cleans up, and writes a scrubbed report. It is not a clinical,
authorization, or compliance certification; see the [evaluation guide](./docs/evals.md).
## What it does
- A chat agent (Vercel AI SDK) with FHIR tools. It **reads** the chart (search patients, view a patient chart, read one filtered chart section) and **writes** to it (add a note, record an observation, create a follow-up task), streamed and rendered as structured cards.
- **Writes are confirmation-gated**: the agent proposes a write, you approve it, and only then is it saved. Nothing touches the chart without your click. Approved writes carry the standard AIAST security label, with optional Provenance recording author-agent and human-verifier roles (role labels, not user identity).
- **The gate is a specified protocol, not just app behavior**: [Approval-Gated Agent Writes on FHIR](./docs/agent-write-protocol.md) (v0.1 draft) names the rules — only an explicit approval commits, every ambiguous outcome fails closed, policy can only narrow the gate — and the [conformance suite](./docs/conformance.md) tests an implementing MCP stdio server's gate mechanics with independent FHIR reads. CI runs it against this repository's own server on every pull request and merge.
- Authentication, multi-tenancy, and access control are delegated to your **Medplum** project (`Project` = tenant, `ProjectMembership` = user, `AccessPolicy` = RBAC). Last EHR doesn't reimplement any of that, and writes are bounded by your AccessPolicy.
## Support status
Last EHR is **Medplum-supported** for authenticated deployments and includes a
**local, no-auth HAPI FHIR mode** for synthetic-data evaluation. **Firely
Server** (`FHIR_BACKEND=firely`), **Aidbox** (`FHIR_BACKEND=aidbox`), and
**Oystehr** (`FHIR_BACKEND=oystehr`) are verified synthetic-evaluation
adapters: each passed both contract harnesses and the
[FHIR Agent Safety Eval](./docs/evals.md) against a disposable synthetic
target. Other FHIR R4 backends need an adapter before they are
supported. See the full [support matrix](./docs/support.md) for the web,
SMART, MCP, auth, and evaluation boundaries before choosing a path.
Self-hosted demos can offer a **backend picker** (swap the EHR under the
live agent, per session) and an **"under the hood" panel** streaming the
agent's FHIR operations — both off by default; see the support matrix for
the eligibility rules and `npm run check:backends` for preflight.
## What it isn't
- Not a charting EHR, not a system of record, and not a Medplum replacement. It's a thin agent layer: a small, growing set of tools over your FHIR backend.
- Not a guarantee. The approval gate is a human-in-the-loop boundary, not a safety proof: it stops unilateral writes, but it relies on you reading what you approve. Models can propose wrong or fabricated clinical facts, and approval fatigue is real. Last EHR provides the gate; you provide the review.
## How it works
Next.js 15 (App Router) + React 19. The agent lives in `app/api/chat/route.ts` (`streamText` + FHIR tools); the FHIR calls go through a small backend interface ([`lib/fhir/backend.ts`](./lib/fhir/backend.ts)) with five built-in adapters: Medplum (hosted or self-hosted, token-authenticated), plus local HAPI FHIR, Firely Server, Aidbox, and Oystehr for synthetic evaluation ([docs/support.md](./docs/support.md)). The interface is four methods plus contract notes, so an adapter for another headless EHR is a small, well-scoped contribution. See [docs/adapters.md](./docs/adapters.md) and the [roadmap](./ROADMAP.md).
```mermaid
flowchart LR
B["Browser chat"] --> A["/api/chat<br/>streamText + FHIR tools"]
A -- "reads<br/>search_patients, show_patient_info, read_chart_section" --> M[("Your FHIR backend<br/>(Medplum, HAPI, Firely, Aidbox, Oystehr)")]
A -- "writes<br/>add_note, record_observation,<br/>record_superseding_observation, create_task" --> C{"Approval card"}
C -- "Approve & save" --> M
C -- "Cancel" --> N["Nothing saved"]
M -. "Every call runs as the signed-in user,<br/>bounded by your AccessPolicy.<br/>The layer stores no PHI." .-> A
```
On the public demo, writes are also tagged with yoLo que la gente pregunta sobre last-ehr
¿Qué es cbetz/last-ehr?
+
cbetz/last-ehr es mcp servers para el ecosistema de Claude AI. Make every AI chart write a reviewable proposal. Draft protocol for agent-initiated FHIR writes (Proposal → Decision → Commit → Audit) with web + MCP implementations and a conformance suite. Runs over Medplum, HAPI, Firely, Aidbox, or Oystehr. Tiene 4 estrellas en GitHub y se actualizó por última vez today.
¿Cómo se instala last-ehr?
+
Puedes instalar last-ehr clonando el repositorio (https://github.com/cbetz/last-ehr) o siguiendo las instrucciones del README en GitHub. ClaudeWave también te ofrece bloques de instalación rápida en esta misma página.
¿Es seguro usar cbetz/last-ehr?
+
cbetz/last-ehr aún no ha sido auditado por nuestro agente de seguridad. Revisa el repositorio original en GitHub antes de usarlo en producción.
¿Quién mantiene cbetz/last-ehr?
+
cbetz/last-ehr es mantenido por cbetz. La última actividad registrada en GitHub es de today, con 16 issues abiertos.
¿Hay alternativas a last-ehr?
+
Sí. En ClaudeWave puedes explorar mcp servers similares en /categories/mcp, ordenados por popularidad o actividad reciente.
Despliega last-ehr en tu cloud
Lleva este repo a producción en minutos. Cada plataforma genera su propio entorno con variables de entorno editables.
¿Mantienes este repo? Añade un badge a tu README
Pega el badge en tu README de GitHub para mostrar que está auditado por ClaudeWave. Cada badge enlaza de vuelta a esta página y muestra el Trust Score actual.
[](https://claudewave.com/repo/cbetz-last-ehr)<a href="https://claudewave.com/repo/cbetz-last-ehr"><img src="https://claudewave.com/api/badge/cbetz-last-ehr" alt="Featured on ClaudeWave: cbetz/last-ehr" width="320" height="64" /></a>Más MCP Servers
Fair-code workflow automation platform with native AI capabilities. Combine visual building with custom code, self-host or cloud, 400+ integrations.
User-friendly AI Interface (Supports Ollama, OpenAI API, ...)
An open-source AI agent that brings the power of Gemini directly into your terminal.
The fastest path to AI-powered full stack observability, even for lean teams.
Real-time global intelligence dashboard. AI-powered news aggregation, geopolitical monitoring, and infrastructure tracking in a unified situational awareness interface
🕷️ An adaptive Web Scraping framework that handles everything from a single request to a full-scale crawl!