Skip to content

Releases

Last EHR MCP 0.5.1: live-verified Epic sandbox writes

Choose a tag to compare

@cbetz cbetz released this 05 Nov 17:07
fb24732

Last EHR MCP 0.5.1: sandbox write bodies the servers accepted

This is a draft. Merging the version bump does not publish the package,
cut a tag, or open a GitHub release. npm latest stays on the last
published version until the release process runs
on purpose. 0.5.0 was prepared in this checkout and was not published.

This patch is the fixes from
PR #219. A live run of
0.5.0 (main@74b1ec5) was rejected by the sandboxes. The bodies below
are the ones those servers had already accepted. Synthetic sandbox
patients only. This is not a clinical, regulatory, or compliance claim.

Epic

A heart rate typed as bpm is posted with valueQuantity.unit /min
and UCUM code /min. 0.5.0 sent unit bpm with code /min. Epic
returned OperationOutcome 59102: Invalid unit given: bpm. Unit expected: /min.

The Epic sandbox write was re-verified live on this patch.

Oracle Health

The oral-temperature body matches the create the sandbox accepted
(Observation/VS-209196396):

  • display unit degC, UCUM code Cel
  • encounter when LASTEHR_ENCOUNTER is set
  • performer with event-performerFunction code LA (legal authenticator)
  • no meta.tag unless LASTEHR_INCLUDE_META_TAG=1

doctor searches Patient?_id=<LASTEHR_PATIENT_ID>. Cerner returns
HTTP 400 for Patient?_count=1 with no other parameter. That
unparameterized 400 is reported and does not fail the check, the same
way a missing search scope (HTTP 403) does not. _count is not sent
together with _id.

The Oracle live write is pending. Cerner's sandbox resource store is
returning HTTP 500s, so this patch has not been confirmed with a new
create on that host.

Proof script

scripts/sandbox-proof prints OperationOutcome diagnostics (issue code,
diagnostics, and details text) when FHIR returns HTTP 400. A body that
is not an OperationOutcome is not printed. The transcript still does not
include keys or tokens.

Still not a supported production backend

  • No delete. An approved write persists on the shared sandbox.
  • Not demo-eligible.
  • Not a clinical, regulatory, or compliance claim.

Last EHR MCP 0.5.0: approval-gated writes on the Epic and Oracle Health sandboxes

Choose a tag to compare

@cbetz cbetz released this 05 Nov 15:26
74b1ec5

Last EHR MCP 0.5.0: approval-gated writes on the Epic and Oracle Health sandboxes

This is a draft. Merging the version bump does not publish the package,
cut a tag, or open a GitHub release. npm latest stays on the last
published version until the release process runs
on purpose.

Headline: an MCP client can propose an Observation, a person approves the
exact body the sandbox will accept, and the agent gets a read-back diff of
what was stored. Synthetic sandbox patients only. This is not a clinical,
regulatory, or compliance claim.

What a client runs

npx @lastehr/mcp init --backend epic
npx @lastehr/mcp doctor

init prints a client config of placeholders and the setup steps on
stderr. doctor checks the private key, the JWKS kid when
LASTEHR_JWKS_URL is set, the token exchange, the granted scopes, and
whether Patient search is allowed. It does not print a key, an assertion,
or an access token.

LASTEHR_EXPERIMENTAL_FHIR_ADAPTERS=1 is required. The Epic and Oracle
Health sandboxes do not support delete, so an approved Observation
persists and the safety eval cannot clean up after itself.

Env

Epic:

export LASTEHR_EXPERIMENTAL_FHIR_ADAPTERS=1
export LASTEHR_BACKEND=epic
export LASTEHR_MCP_WRITES=proposal
export LASTEHR_FHIR_BASE_URL='https://fhir.epic.com/interconnect-fhir-oauth/api/FHIR/R4'
export LASTEHR_TOKEN_URL='https://fhir.epic.com/interconnect-fhir-oauth/oauth2/token'
export LASTEHR_CLIENT_ID='<non-production-client-id>'
export LASTEHR_PRIVATE_KEY_PATH='<path-to-pkcs8-pem>'
export LASTEHR_KEY_ID='<jwks-kid>'
export LASTEHR_JWKS_URL='<https-url-of-your-public-jwks>'
export LASTEHR_ENCOUNTER='<Encounter/id>'
export LASTEHR_PATIENT_ID='<sandbox-patient-id>'

LASTEHR_SCOPES is optional for Epic. The base and token URLs above are
the defaults when those two variables are omitted.

Oracle Health:

export LASTEHR_EXPERIMENTAL_FHIR_ADAPTERS=1
export LASTEHR_BACKEND=oracle
export LASTEHR_MCP_WRITES=proposal
export LASTEHR_FHIR_BASE_URL='https://fhir-ehr-code.cerner.com/r4/ec2458f2-1e24-41c8-b71b-0e701af7583d'
export LASTEHR_TOKEN_URL='https://authorization.cerner.com/tenants/ec2458f2-1e24-41c8-b71b-0e701af7583d/hosts/fhir-ehr-code.cerner.com/protocols/oauth2/profiles/smart-v1/token'
export LASTEHR_CLIENT_ID='<system-client-id>'
export LASTEHR_PRIVATE_KEY_PATH='<path-to-pkcs8-pem>'
export LASTEHR_KEY_ID='<jwks-kid>'
export LASTEHR_JWKS_URL='<https-url-of-your-public-jwks>'
export LASTEHR_SCOPES='system/Patient.read system/Observation.read system/Observation.write system/Encounter.read'
export LASTEHR_PERFORMER='<Practitioner/id>'
export LASTEHR_PATIENT_ID='<sandbox-patient-id>'

LASTEHR_SCOPES is required for Oracle. LASTEHR_PRIVATE_KEY may replace
the path. LASTEHR_ACCESS_TOKEN skips the assertion. LASTEHR_BACKEND=smart
is the same client without the Epic or Oracle observation profile, and it
requires LASTEHR_FHIR_BASE_URL and LASTEHR_TOKEN_URL.

One write, on purpose

npx tsx scripts/sandbox-proof

The script shows the shaped Observation, records approval, posts it, and
prints the read-back diff. Epic's proof is a heart rate (flowsheet 8 and
LOINC 8867-4). Oracle's proof is an oral temperature (LOINC 8331-1)
with a legal-authenticator performer. LOINC 8867-4 is refused on Oracle
because that sandbox returned HTTP 422. Nothing in the transcript is a
secret, and nothing here says the measurement is clinically correct.

Still not a supported production backend

  • No delete. Writes persist on the shared sandbox.
  • Notes, tasks, and superseding observations are refused on Epic and Oracle.
  • Search HTTP 403 means the app lacks the search scope.
  • Not demo-eligible. Not a compliance claim.

Last EHR MCP 0.4.1: version-pinned client setup

Choose a tag to compare

@cbetz cbetz released this 23 Oct 15:59
8396cda

init now pins generated JSON, Cursor, and Claude Code setup commands to the version of the package running it. For example:

npx -y @lastehr/mcp@0.4.1 init --client claude-code
# Prints:
claude mcp add lastehr -- npx -y @lastehr/mcp@0.4.1

It prints instructions; credentials and client registration still need to be configured. Read-only remains the default. This patch changes no chart tools, write approval behavior, authentication, FHIR permissions, or remote transport behavior.

Verified publication

SHA-512 integrity:

sha512-JgynclafjItnBmdAjSVGvODFcoEQuUVNAGgKTZNxkXbJali7x55fQYJOU4sv215FG5HcuXTErVHD5SFGTByLmA==

The 90-second synthetic browser recording remains pinned to clean source f36d05d / MCP 0.4.0. Its FHIR proof is separate from these package checks.

The October 22 remote MCP report covers real Keycloak with local self-hosted Medplum and scripted SDK decisions. Hosted Medplum, public MCP TLS, a real MCP host approval interface, and actual reviewer identity remain unverified; this setup patch does not extend that evidence.

Last EHR MCP 0.4.0: remote transport and installation checks

Choose a tag to compare

@cbetz cbetz released this 22 Oct 21:47
f36d05d

Last EHR MCP 0.4.0: remote transport and installation checks

Published October 22, 2026. npm latest and the Official MCP Registry were verified at 0.4.0. The GitHub release targets tested source f36d05dde21c6e1c6847f2660a966afd2615c646.

@lastehr/mcp adds an optional HTTP transport and better installation
diagnostics. Stdio and read-only behavior remain the defaults.

Optional HTTP transport

LASTEHR_MCP_TRANSPORT=http runs an OAuth resource server with one MCP server
per session. It verifies a token addressed to this server and exchanges it
for the caller's own Medplum credential. It needs no shared FHIR credential;
each session holds its exchanged token. The caller's backend access policy
still controls access, and sessions cannot be reused by another principal.

HTTP mode requires operator-provided OAuth infrastructure and TLS for
network-facing use. The local no-auth HAPI mode is refused over HTTP.

The complete run with a real identity provider and Medplum remains
unverified. Existing evidence covers SDK loopback behavior and token exchange
against a disposable Medplum server with a mock identity provider. See the
operator guide
and verification boundary.

Installation and diagnostics

  • doctor remains an offline configuration check.
  • doctor --connect checks R4 metadata and a count-only Patient search for
    the stdio backend. It has bounded requests, refuses redirects, makes no
    chart write, and prints no patient data or counts.
  • HTTP diagnostics explicitly leave caller OAuth and client approval support
    unverified. A configured endpoint is not reported as a working login.
  • init --client cursor declares Cursor's stdio transport.
  • Release verification installs the packed tarball outside the checkout and
    checks CLI execution, client configuration, HTTP exports, MCP reads, and
    capability-gated proposals.

Install the verified release

npx -y @lastehr/mcp@0.4.0 --version
claude mcp add lastehr -- npx -y @lastehr/mcp@0.4.0

Configure least-privilege Medplum credentials before starting the stdio
server. The repository's HAPI option is for local synthetic evaluation. The
init command prints a template with an unversioned package argument; change
that argument to @lastehr/mcp@0.4.0 to keep a generated client configuration
pinned to this release.

The package offers four shared chart-read tools. Optional proposal tools
create notes, Observations and linked corrections, and Tasks after explicit
human approval in a compatible client. Approval controls writes; it does not
control chart context sent by the MCP client to its model provider.

Release verification

CI run 39856748538
passed on the exact source above using Ubuntu 24.04 and Node 22.18.0: 574 unit
tests passed (4 skipped), 12 browser tests passed, and the local HAPI checks,
synthetic workflow evaluation, and strict write-protocol conformance passed.
The run retains fhir-agent-safety-eval and awp-conformance artifacts.

After publication, the downloaded npm 0.4.0 artifact matched the tested
tarball's SHA-512 integrity and passed the isolated-install checks again:
CLI version, client configuration, HTTP exports, read-only diagnostics,
synthetic MCP read, and approval capability gate. These checks do not prove
real client approval behavior or the unverified real identity-provider HTTP
deployment described above.

The Official MCP Registry's exact 0.4.0 record
and latest record
were verified active and pointing to npm @lastehr/mcp@0.4.0 over stdio after
registry publication.
See the setup guide,
source, and
release process.

Last EHR v0.2.2: the adoption release

Choose a tag to compare

@cbetz cbetz released this 13 Jul 02:06
5a42374

Make it runnable, measurable, and honest

v0.2.2 adds no clinical tools on purpose. It makes Last EHR consumable: a pull-and-run image, browser-level proof of the approval flow, a privacy posture that matches the code, and a demo that ends with somewhere to go.

Pull and run the zero-key synthetic lab

git clone https://github.com/cbetz/last-ehr.git && cd last-ehr
npm install
docker compose -f docker-compose.yml -f docker-compose.ghcr.yml up -d
FHIR_BACKEND=hapi FHIR_BASE_URL=http://localhost:8080/fhir npm run fhir:wait
FHIR_BACKEND=hapi FHIR_BASE_URL=http://localhost:8080/fhir npm run seed

Then open http://localhost:3000/demo. ghcr.io/cbetz/last-ehr is multi-arch (amd64 + arm64) and bakes the quickstart and scripted-demo UI on, with analytics, Medplum auth, and model keys off. No model API key, no account, synthetic patients only.

Proof, not promises

  • Browser e2e for the approval flow (Playwright, scripted model only): the proposal card renders, a rejected write leaves HAPI untouched, an approved write persists exactly one session-tagged Observation, asserted over FHIR REST in CI.
  • FHIR Agent Safety Eval (npm run eval): a disposable synthetic workflow runner covering search, chart reads, proposal gating, approved and denied writes, chart association, and cleanup, with a scrubbed JSON report. It proves workflow mechanics, not clinical correctness.
  • MCP Local Lab (npm run mcp:demo): checkout-only, loopback HAPI, fixture-restricted read tools for Claude Code or Cursor with zero FHIR credentials.

A more honest surface

  • PostHog is now explicit-event-only: no autocapture, no automatic pageviews, no session recording, memory persistence with no cookies. There is a /privacy page that describes what the hosted site actually collects.
  • Server-side chat error logs record error type, message, and status only; raw provider errors are never logged because they can carry the full request body, including chart context.
  • Positioning now leads with the point: add human-approved AI writeback to your FHIR app. Backend claims are tightened to match reality: Medplum is supported today, the local HAPI stack is synthetic evaluation only, other backends go through the adapter contract.

Get involved

The repo is now a GitHub template. Six good first issues are open, and three Discussions are waiting for your input: which FHIR backend next, what was unclear in the demo, and what the reusable piece should be.

Full details in the changelog.

Last EHR v0.2.1 — read-only Medplum MCP

Choose a tag to compare

@cbetz cbetz released this 11 Jul 14:47

A safe MCP entry point for FHIR chart context

v0.2.1 makes Last EHR’s bounded chart-reading surface available as a public,
installable MCP server. The division is deliberate: MCP reads charts; the web
app keeps clinical writes proposal-shaped and approval-gated.

Install the MCP server

npx -y @lastehr/mcp init --client claude-code

@lastehr/mcp@0.1.1 is now
listed in the Official MCP Registry.

It exposes exactly two Medplum tools:

  • search_patients
  • show_patient_info

Both tools carry MCP’s readOnlyHint. The published package has no write tools
and no environment switch that enables them.

Try the complete workflow locally

npm install
npm run demo:local

This starts local HAPI FHIR, seeds synthetic records, and demonstrates a fixed
approval-gated observation flow without a Medplum account or external model
key.

The local route is an evaluation tool—not a general local clinical agent or a
PHI-ready deployment path.

For FHIR implementers

Last EHR includes a bounded backend adapter seam, a reusable FHIR REST
transport, executable adapter starter, and contract harnesses. Medplum is
supported for the MCP package; local HAPI remains available for synthetic
web-app evaluation.

Safety boundaries

  • Read-only MCP access can still return PHI. Use a least-privilege Medplum
    identity and review your MCP client and model provider’s data handling.
  • @lastehr/mcp supports hosted or self-hosted Medplum authentication today.
    It does not claim generic FHIR, HAPI, SMART launch, or browser-approval
    parity.
  • The package is not an authorization layer.
  • Clinical writes remain in the web app’s approval-gated flow; direct MCP
    writes require a future reviewable confirmation protocol and safety review.

See the MCP guide,
quickstart, and
adapter guide for setup and support
boundaries.

Last EHR v0.2.0 — local demo and adapter kit

Choose a tag to compare

@cbetz cbetz released this 10 Jul 03:08

A practical, inspectable starting point for FHIR agents

v0.2.0 makes Last EHR easier to evaluate locally and easier to extend without overstating what is supported.

Highlights

  • Zero-key local walkthrough: npm run demo:local starts local HAPI FHIR, seeds synthetic records, and demonstrates one fixed approval-gated observation flow without a Medplum account or external model key.
  • Approval evidence in CI: the repository now runs a real local-HAPI smoke test that proves the agent pauses before a write and persists the expected synthetic observation only after approval.
  • Adapter contributor kit: a reusable FHIR REST transport, network-free REST contract, real-server contract harness, and executable bearer-token adapter starter make portability a bounded contribution.
  • Honest support boundary: a support matrix distinguishes authenticated Medplum deployments, local synthetic HAPI evaluation, and backend targets that still need verification.
  • Safer browser error handling: chart-adjacent upstream errors are no longer passed through to browser responses or analytics events.
  • Security baseline: React and React DOM are pinned to 19.2.6.

Run the local evaluation

npm install
npm run demo:local

This path is deliberately limited: it uses synthetic data, a no-auth local HAPI stack, a fixed scripted sequence, and one approval-gated observation. It is an evaluation tool—not a general local clinical agent or a PHI-ready deployment path.

See the quickstart, support matrix, and adapter guide for the full boundary and verification path.

v0.1.0 — first public release

Choose a tag to compare

@cbetz cbetz released this 30 Jun 02:31
3be1587

First public release of Last EHR — an open-source AI agent layer over Medplum/FHIR. A layer, not an EHR: runs on top of your FHIR backend, stores no PHI itself, delegates auth/RBAC to Medplum. Read-only agent (search patients, view a chart), BYO backend + model key, no-OAuth quickstart + npm run seed. Alpha; synthetic data only. Quickstart: see README.