Alexa+ · Amazon Developer Hackathon
The assistant that can be corrected.
An MCP server whose resources can be revised mid-sentence — so the assistant stops, retracts what it just said, and names what changed, who changed it, and how long ago.
Ray Dunn is 68, five days after a hip replacement, and he is about to put full weight on the wrong leg — his physio changed the instruction thirty seconds ago, and the assistant beside his bed is still reading the discharge PDF.
Ray
Alexa
Protocol
Not a recording of a live session. The wording is the scripted
retraction and the values are the committed seed record; the offsets are the measured
loopback latencies from docs/proof/bench.json. The live run is
npm run e2e.
Served from npm start, these two carry a
care.read.user token this server mints when the page is requested — a
different token from the ones the terminal printed, good for twelve hours. The clinician
link also carries the write key in its query string, and only while that key is the dev
secret this repository publishes. Opened any other way they carry neither, and each
screen asks for what it needs.
The mechanism
Three MCP surfaces, doing the work of a product.
Remove MCP and there is nothing left. The resource graph, the notification and the audience split are the application — not a wrapper around one.
The assistant subscribes
Before answering, the host subscribes to the facts it is about to speak. Unsay declares the capability, so a compliant host can.
resources/subscribe · capabilities.resources.subscribeThe physio writes
One tap on a tablet between appointments. The write is HMAC-signed, appended as a new version, and hash-chained to the one before it.
POST /write · X-Unsay-SignatureThe sentence stops
The notification reaches the host mid-utterance. It re-reads, and the retraction is spoken with the age and the author — no apology, because the system was not wrong.
notifications/resources/updatedThe safety property
Not everything true is speakable.
The reason Alexa says “have someone nearby” is a fact Ray must never hear. In healthcare that is not a rendering preference — it is a boundary, and a boundary needs a control, not an annotation.
Full weight-bearing as tolerated.
scope: care.read.user
Fall risk: HIGH. Lives alone Monday to Thursday. Family disputes the discharge plan — daughter believes discharge was premature.
scope: care.read.assistant
Two URI schemes behind two OAuth scopes, enforced at the resource server.
A principal holding only care.read.user gets JSON-RPC -32002 on
every care-internal:// URI — not “forbidden”, which would confirm the
record exists. It is never sent the content, so it cannot leak it.
We do not rely on annotations.audience, because the 2025-11-25 spec places no
obligation on a client to honour it. That gap is filed as
FRICTION.md F-002, with proposed spec language. The enforcement
point is about fifteen lines: packages/live-resources/src/store.ts
read(). The reads that must fail are asserted as failures by
npm run verify.
The number
The correction has to land before the sentence ends.
A twelve-word utterance is roughly 3,400 ms of speech. That is the whole budget: the clinician's signed write → HMAC verified → notification → authorized re-read → new value at the client.
What this number is not. These are loopback Streamable-HTTP figures over
200 revisions, produced by npm run bench -- --n 200 and committed to
docs/proof/bench.txt. The change bus is in-process — the notifier in
packages/live-resources, not a queue and not a stream — because the build plan
said to move it in-process unless this p95 went past 500 ms, and it did not. Nothing is
deployed, so no network hop between the clinician and the host is inside these figures. It is
not a production figure and is never quoted as one.
Proof
Four commands, no flags.
There is no MOCK=, no OFFLINE=1, no
--dry-run anywhere in the reproduce path. If a flag could switch off the thing
being judged, the submission would be worthless.
The safety property, asserted as failures
34 assertions — fifteen in process, nineteen against a real HTTP server. A user-scoped principal must fail to reach every internal URI, a tampered version must break the chain and be located exactly.
DEMO.md · scripts/verify.ts npm run e2eThe whole demo, as code
Capability negotiation, completion/complete resolved through
context.arguments, the write landing mid-answer, the retraction, the stale
fact announcing its age, and the fallback tool.
The latency, with its caveat attached
p50, p95 and max for each segment, plus the count of runs that landed inside the speech window. The caveat is printed by the script, not added afterwards.
docs/proof/bench.txt · docs/proof/bench.json FRICTION.mdWhat fought back
14 entries written the day each was hit, including 7 proposed changes to the MCP specification and its extensions, found by building against them.
FRICTION.md — root level GET /verifyThe judge’s instrument
Public and unauthenticated. Every chains entry replayed from
SHA-256(prev ‖ value ‖ writtenAt ‖ authorId), the versions count, the
atRest receipt read back off the stored bytes, and intact —
without a token or an account.
The write path you can drive yourself
Signs the request body with HMAC-SHA256 in the browser and posts it. With no server
reachable it signs anyway and hands you the curl to replay — it never claims
to have published.
Friction log
7 of these propose changes to the spec.
Written the day each was hit, not assembled the week of the deadline. Five of 14 shown. Read all 14 →
notifications/resources/updated.
The spec settles delivery, not consequence. A conforming host may receive the notification
and never re-read — and Ray hears the stale answer to completion.
Medium
client.subscribeResource(), not
subscribe(), and no subscription example in the README.
Low
ui:// template at all.
Medium