Alexa+ track · Care chart · Ray Dunn · post-op day 5

The assistant that takes back what it just said.

Ray is 68, five days home after a hip replacement. His physio changes the plan while Alexa is still reading him the old one. Unsay publishes his care plan as live MCP resources, so the correction reaches the speaker mid-sentence, 200/200 times, p95 461 ms across the public internet. The notes his family wrote about him shape the answer and are never spoken. The server enforces that, not the model.

public proof: replay the version chain with no account api.unsay.edycu.dev/verify

Ray Dunn · post-op day 5 · care://raylive
Weight
bearingv2
Partial weight-bearing, about half your body weight through the operated leg. MA2 days ago
Weight
bearingv3 · now
Full weight-bearing as tolerated.changed just now · Sarah Okafor, physio SOjust now
Riskcare-internal:// never spoken
Fall risk: high. Lives alone Monday to Thursday. Shapes the answer; never reaches the speaker.
MA
ALEXA You can put about half your weight on it—“Wait — don’t do that. Full weight-bearing as tolerated.”
200/200corrections landed inside a spoken sentence, on the deployed serverdocs/proof/bench.remote.txt → 461msp95 from the physio’s write to the speaker holding the correction, across the Pacificdocs/proof/bench.remote.txt → 34/34safety assertions passing, 15 in process and 19 over HTTPdocs/proof/verify.json → 301tests, including 58 that run the Echo Show’s own MCP client against a live servernpm test →

§1 · The demo

It stops. It says what changed, who changed it, and how long ago.

The physio’s write is a real signed HTTP request, and the notification is a real Streamable HTTP frame. Left, the Echo Show at the moment the correction lands. Right, the same demo as code, typed out from a real npm run e2e run.

The Echo Show screen: 'Partial weight-bearing, about half your body weight through the operated leg' struck through in amber; beneath it 'Full weight-bearing as tolerated.' and 'changed 1 second ago · Sarah Okafor, physio'.
Ray’s Echo Show, a real capture of web/echo.html, 2026-09-04. The correction is struck through, never erased.
npm run e2e2026-09-28 · loopback

    

§2 · Try it live

Be Sarah. Change Ray’s plan and watch Alexa take it back.

These are the real screens, served by the deployed server at api.unsay.edycu.dev with a read token it mints for you. Nothing on this page simulates them.

  1. Press change on Weight bearing
  2. Type a new instruction
  3. Publish, and watch the right
not connected
Clinician · Sarah Okaforwrites
Ray’s Echo Showlistens · resources/subscribe

Two screens, one live server.

Connecting loads Sarah’s clinician screen and Ray’s Echo Show from api.unsay.edycu.dev. The patient is fictional, and the server is in memory, so anything you write is gone on the next restart.

Shared sandbox: the clinician screen carries the development write key this repository publishes, on purpose, so anyone can fire a revision. Other visitors’ changes appear too. Open the screens full-size at api.unsay.edycu.dev.

§3 · The line nobody reads aloud

Some of the chart is for Ray. Some of it is about him.

A care chart holds things a patient should hear and things only his carers should know. Unsay puts them behind two URI schemes and two OAuth scopes, and enforces the split where the server reads. It isn’t done with an annotation the assistant is trusted to honour.

care://ray/…

What Ray may hear

“Full weight-bearing as tolerated.”
scope
care.read.user: Ray’s own host, and the assistant
audience
["user"], speakable
care-internal://ray/risk

What only the assistant knows

“Fall risk: HIGH. Lives alone Monday to Thursday. Family disputes the discharge plan.”
scope
care.read.assistant only
Ray’s host
-32002, the same answer as for a URI that doesn’t exist
Alexa’s answer, shaped by the risk note: “Take it slowly the first time, and have someone nearby.”
← from care-internal://ray/risk, reason never spoken

§4 · How it works

MCP isn’t a wrapper here. It is the product.

Ten request handlers and three notification senders on @modelcontextprotocol/sdk 1.30.1, over Streamable HTTP, with the negotiated protocol version asserted at ≥ 2025-11-25. Remove MCP and nothing is left.

resources/subscribe

The care plan is live, not a PDF.

Each instruction is a care:// resource. When a clinician signs a write, the server fires notifications/resources/updated, the subscribed host re-reads, and the answer already in progress is retracted: the old line, who changed it, and how long ago.

v1No weight through the operated leg. Transfers with the frame only.MA
v2Partial, about half your body weight.MA
v3Full weight-bearing as tolerated.SO
annotations.audience

Two audiences, one server.

Speakable facts and assistant-only context live at different URIs behind different scopes. A non-compliant client can’t leak what it is never sent.

SHA-256 chain

A retraction you can audit.

Every version carries the previous one’s hash. /verify replays the chain for anyone, with no account.

lastModified

Stale facts say their age.

A record past its review date is read out with its age: “last changed 9 days ago by Dr Mensah, GP”.

Last-Event-ID

It survives the Wi-Fi dropping.

The stream resumes where it left off. 3 revisions written while it was down, 3 replayed on reconnect.

ui://unsay/echo

An MCP Apps card for the Echo Show.

The retraction screen ships as an MCP Apps resource the host can render. The Alexa+ track’s own rubric names MCP Apps as a creative use.

An Agent Skill, and a fallback that still retracts.

skill/SKILL.md teaches the assistant the two rules. If a host never subscribes, the whats_changed tool returns the previous value too, so the fallback can still retract.

§5 · Honest by design

What this build doesn’t do, in its own words.

“The gating question — whether Alexa+ itself declares capabilities.resources.subscribe — has not been answered, which is exactly why the whats_changed fallback is built and exercised.”

README · What is not here

“The AWS KMS provider is SigV4-signed and shaped but has never been executed against a live key.”

README · What is not here

“Unsay relays what a clinician wrote. It does not generate clinical guidance and is not a medical device.”

web/clinician.html · every screen

§6 · Questions a judge would ask

Before you ask.

Has it run on a real Alexa+ device?
Not yet, and the repo says so: docs/proof/initialize.json is committed with "status": "not-run". The server is driven by the MCP SDK’s own spec-compliant client over Streamable HTTP. If a host turns out not to subscribe, the whats_changed tool retracts instead, and npm run e2e exercises that path on every run.
What is mocked?
Nothing in the demo path. The write is a real HMAC-signed HTTP request, the notification is a real Streamable HTTP frame, and there’s no MOCK=, OFFLINE=1 or --dry-run anywhere in the repo. Ray and Sarah are fictional; their care plan is committed as a seed record.
Is 461 ms fast enough?
A 12-word sentence takes about 3,400 ms to say. That’s an assumed speech rate, not a measurement of Alexa+. Against the deployed server, 200 of 200 corrections landed inside that window, with the client in Indonesia and the server in us-west2. On one machine the same path takes 3.0 ms at p95.
Couldn’t a badly behaved assistant leak the private notes?
It never receives them. The split is enforced where the server reads (LiveResourceStore.read()), and a user-scope token asking for care-internal:// gets -32002, the same answer as for a URI that doesn’t exist. npm run verify asserts it in process and over HTTP.
Can I run it myself?
git clone https://github.com/edycutjong/unsay.git && cd unsay && npm install && npm start. Node 22, one runtime dependency, no database and no cloud account. ./scripts/fresh_clone_check.sh runs every command in DEMO.md from an empty clone.
Is it open source?
MIT, from the first commit. packages/live-resources is the reusable half (versioned resources, the hash chain, the audience partition, the notifier), with its own licence and 29 standalone tests.