DocsEvaluate

Technical evaluators and architecture owners

Supported configurations

See exactly what is supported, local-only, or still needs an adapter.

This guide describes the reference implementation as it exists today. Keep the stated support boundary in view as you evaluate or extend it.

Last EHR is an alpha reference implementation for approval-gated FHIR agent workflows. This page states exactly what works today so evaluators can choose a safe path and contributors know where an adapter is needed.

ConfigurationWeb appSMART launchMCPStatusNotes
Medplum, hosted or self-hostedYesYesRead-only by default; opt-in gated write proposalsSupportedThe authenticated path. @lastehr/mcp exposes four chart-reading tools by default (the same read implementations the web agent uses), plus the opt-in write profile; Medplum owns identity, tenancy, AccessPolicy, and audit logs.
HAPI FHIR from this repository's Docker Compose stackYesNoRead-only by default; opt-in gated write proposals (local synthetic only)Local evaluation onlyThe included HAPI server has no auth and Compose binds it to loopback by default. Use synthetic data on one machine; do not expose it or treat browser-session filtering as access control. @lastehr/mcp honors FHIR_BACKEND=hapi with the same local-only caveats; separately, npm run mcp:demo offers two fixture-restricted, read-only MCP tools from a checkout, including an optional zero-key scripted walkthrough restricted to one seeded record and one fixed observation.
Firely Server (FHIR_BACKEND=firely)YesNoNoSynthetic evaluation onlyVerified against Firely's public synthetic sandbox (https://server.fire.ly) with both contract harnesses and the FHIR Agent Safety Eval. Anonymous or static bearer token (FIRELY_ACCESS_TOKEN); the adapter never runs an OAuth flow, and the sandbox enforces no access control, so synthetic data only.
Another no-auth, standard FHIR R4 serverEvaluation onlyNoNoUnverifiedIt may work through the HAPI REST transport, but is not supported until both contract harnesses and the four synthetic workflows pass.
Aidbox (FHIR_BACKEND=aidbox)YesNoNoSynthetic evaluation onlyVerified against a local dev-licensed box (aidboxone:edge) with both contract harnesses and the FHIR Agent Safety Eval. HTTP Basic from an Aidbox Client at the /fhir endpoint (AIDBOX_CLIENT_ID/AIDBOX_CLIENT_SECRET); a dev license from the Aidbox portal is required, and the box's AccessPolicy remains the security boundary.
Oystehr (FHIR_BACKEND=oystehr)Synthetic evaluationNoYes (operator-owned project)Verified 2026-07-21OAuth2 M2M client credentials against Oystehr's hosted FHIR R4 API. Verified against a developer-tier sandbox: real-server contract 5/5 (including the _tag/_tag:not session-isolation clause; a direct probe confirmed the bare-system _tag:not token is honored server-side) and the FHIR Agent Safety Eval 7/7, with meta.security and meta.tag persisting on create. Developer tier is non-production/no-PHI by contract — synthetic data only. Evidence in docs/adapters.md.
FHIR R4 server with authentication or product-specific behaviorNot yet verifiedNot yet verifiedNot yet verifiedAdapter wantedStart from the adapter starter, then implement and verify the auth story and FhirBackend contract before calling it supported.

Model providers

The web app supports OpenAI, Anthropic, and Amazon Bedrock for real agent flows. The local HAPI stack also has one deliberately limited exception:

ModeCredentialScope
AI_PROVIDER=scriptedNoneExplicit local HAPI-only walkthrough: search the seeded Maria Garcia record, propose Heart rate: 72 bpm, and wait for approval. No external model request; no arbitrary chart reads or writes.
OpenAI, Anthropic, or Amazon BedrockTool-capable provider credentialFull four-tool agent flow. Follow the provider's BAA and data-handling requirements before any real-data use.

The scripted path is not a bundled model and does not make the HAPI stack suitable for real PHI. It is a reproducible way to inspect the approval-loop mechanics before configuring a provider.

Demo backend picker and dev output

A deployment can let demo visitors pick which configured backend powers their session (NEXT_PUBLIC_DEMO_BACKENDS, id|Label pairs) and stream an under-the-hood panel of the agent's FHIR operations (NEXT_PUBLIC_DEMO_DEV_OUTPUT). Both default off. The allowlist is bound to this matrix in code: medplum, hapi, aidbox, and oystehr are demo-eligible, and every other adapter is dropped by the parser regardless of the env value. Flipping a backend's eligibility is a governance change — it requires updating this matrix in the same PR plus contract-harness evidence against the concrete target, including the _tag/_tag:not session-isolation semantics.

Aidbox's eligibility evidence (2026-07-18, operator-owned hosted dev sandbox, edge, FHIR 4.0.1): real-server contract 5/5 including the isolation clause, seed, and Safety Eval 7/7. One measured caveat: Aidbox silently ignores the bare-system _tag:not token, so per-session visibility runs on the client-side filter arm — a visitor's own writes are never affected (they ride a separate tagged query), but under heavy concurrent demo load other sessions' rows can crowd seed rows out of the server-side result window. Because the client-side filter drops those rows after the fetch, a crowded window is reported to the model as truncated (measured against what each query asked the server for, not against how many rows survived the filter) so an emptied section can never be read as an exhaustive search. Offer an Aidbox picker option only on a box you own and seed.

Oystehr's eligibility evidence (2026-07-21, operator-owned developer-tier project): real-server contract 5/5 including the isolation clause, with a direct probe confirming the bare-system _tag:not token is honored server-side — the first verified backend needing no client-side filter arm — plus the FHIR Agent Safety Eval 7/7 and meta.security/meta.tag persistence on create. The project is seeded with the demo's synthetic patients. Offer an Oystehr picker option only on a project you own and seed; the developer tier is non-production/no-PHI by contract, and its storage and rate ceilings are Oystehr's.

Operator rules:

  • Each allowlisted backend needs its own server config (per-backend base URL; Medplum needs the quickstart credentials). Preflight with npm run check:backends — the runtime drops bad entries silently by design, and the check script is where you find out loudly.
  • hapi is local evaluation only: never offer it on a publicly reachable deployment.
  • The hosted lastehr.com demo can offer Medplum plus an operator-owned, seeded Aidbox box (the picker needs at least two entries to render). Firely's public sandbox is shared, world-writable, and periodically wiped, so it is unsuitable for any public allowlist.
  • Dev output is for synthetic demo deployments only; see the threat model for its exact boundary.

What "supported" means

A supported configuration has a documented setup path and is expected to work through the four synthetic-data tools:

  1. Search patients.
  2. Show a chart.
  3. Propose and approve a note.
  4. Propose and approve an observation.

For Medplum, that includes authentication and backend-enforced access control. For local HAPI, it means a single-tenant synthetic demonstration only. The scripted zero-key option intentionally covers only its fixed search and approval-gated observation, not the full general-agent surface.

Adding a backend

The extension point is deliberately small. Follow the adapter guide to implement the FhirBackend contract, add contract-style tests, document the authentication and tenancy model, and verify all four workflows with synthetic data. Add a scrubbed FHIR Agent Safety Eval result before calling an adapter verified; it proves deterministic tool/approval mechanics, not the backend's authorization model. Open a backend adapter issue before a large implementation so the verification target is clear.

Do not add backend-specific branches to the agent tools or emulate backend authorization in Last EHR. The backend remains the system of record and the security boundary.

Want a concrete starting point?

Run the limited synthetic HAPI walkthrough before connecting a real backend.

Run locally