earshot
Provides an Alexa+ add-on that lets the voice assistant answer health and care questions privately, with tools for checking medication adherence, next doses, logging doses, reporting symptoms, managing viewing permissions, opening temporary solo windows, and reviewing a disclosure ledger while keeping sensitive details off the spoken channel.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@earshotDid my mom take her heart medication this morning?"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
Earshot
An Alexa+ MCP add-on that treats the room as an untrusted channel.
A home health aide is in the living room. So is a neighbour. Someone asks the Echo whether Mum took her heart medication.
Every privacy model we have assumes there is a private screen. Voice does not have one — in a house, the answer is heard by whoever is standing there. That leaves two bad options: refuse, and the assistant is useless for exactly the household that needed it; or answer, and read someone's medical history aloud to whoever was in the room.
Earshot does the third thing. It answers the question, and moves the part that cannot be said to a channel the room cannot hear.
Margaret is 82, the medication is hers, and she is sitting in the room while her daughter asks about it. Every rule Earshot follows was set by Margaret: Sarah may hear everything, Dana the aide may know what to give and when but not why or who prescribed it, the neighbour has no row at all. And Margaret gets the receipt — she can read what was said about her, to whom, including the requests that were refused on her behalf while she sat there.
That is the part of this that is actually unclaimed. See Prior art.
Sarah: "Alexa, did Mum take her pills today?"
room Yes. Everything due so far today was taken, on time. The next one is
at 8:00 PM. I've sent the details to your phone.
phone Furosemide 40 mg — taken 8:14 AM · Metoprolol succinate 25 mg — 8:19 AM
Apixaban 5 mg — 8:07 AM · Potassium chloride 20 mEq — 8:05 AMThe guarantee, and exactly where it stops
The claim is that a protected value never reaches the spoken channel. Three layers make that true, and it matters which layer stops what.
Layer 1 — a compile error. Protected<T, C> is an opaque object type,
deliberately not T & brand, so Protected<string> is not a string.
SpokenText is a branded string producible only by the say tagged template,
whose interpolations are constrained to string | number | SpokenText. So
say`she took ${med.name}` fails to compile, and so does a plain template
literal assigned to a spoken field. Nine files in src/negative/
prove it: eight must fail, each asserting its specific error code and origin
file, plus a positive control that must compile — otherwise "everything failed"
would look like success.
Layer 2 — no plaintext escape at runtime. The payload lives in an ECMAScript
#private field. Every stringification path — String(), ${}, + '',
JSON.stringify, util.inspect — yields a random per-value taint marker rather
than the value.
Two precise notes, because the difference matters. Reflective copies (spread,
Object.keys, structuredClone) do not reach the payload either, but they
return {label, category, classification} and carry no marker, so a
reflected copy is invisible to the Layer 3 tripwire. And the marker is per value
and random, so it identifies that a protected value escaped, not which one.
Layer 3 — a tripwire that throws. Every channel exit runs assertNoTaint.
It throws; it does not redact. A leak quietly repaired is a leak you never learn
about.
Where it degrades. Layer 1 cannot stop String(p) or p as any —
TypeScript has no way to forbid a call to String. So, precisely:
A protected value is referenced in a spoken string — a compile error for ordinary code, caught and thrown for code that casts past it. We do not claim this is impossible to express. A compile error is not impossibility.
A protected value reaches a spoken string — prevented for every path the attack suite has tried, and the suite is the honest measure of that claim rather than this sentence. See Known findings.
The stronger version of this claim — that no expression recovers the plaintext
without importing a reveal function — is the one worth wanting, and it is exactly
the kind of claim that is easy to write and hard to hold. We only state it where
test/attack/ is actively trying to break it.
The architectural decision underneath all of it: the private payload is
never in the MCP response at all. Putting it in structuredContent and trusting
the model to behave is the theatre this project exists to reject. It goes
out-of-band to GET /inbox, authenticated with the asker's own token. The
model is a client of /mcp and holds no credentials for /inbox, so no prompt
can extract what was never put in its context.
The demo proves this on the wire: it greps the raw tools/call response bytes
for 38 seeded secrets — 1,082 bytes, 0 found — including under an explicit
prompt injection in _meta, where the spoken sentence comes back byte-identical
to the un-attacked call.
Related MCP server: Cairn
Run it
npm install
npm run demo # the seven-scene story, against a real server
npm run test:all # every suite
npm run demo:serve # the two-pane simulator in a browsernpm run demo links a real OAuth 2.1 + PKCE (S256) account, opens a real MCP
session over Streamable HTTP, and calls real tools. Two consecutive runs are
byte-identical once timings and the ephemeral port are normalised.
Measured
npm run test:all, 2026-09-04:
Checks passed | 322, 0 failed — plus 38 findings tracked separately, 13 still failing |
MCP spec negotiated on the wire | 2025-11-25 ( |
Transport | Streamable HTTP, JSON and SSE modes |
Worst tool p95, client round trip | under 20 ms on loopback, every run |
Worst server-side handler time, any tool | under 10 ms |
MCP Apps card | 15 checks, rendered in real Chrome from bytes fetched over MCP |
Platform budget | 500 ms — 0 calls over budget in ~900 |
Loopback round trips do not include Amazon's network. The server-side numbers transfer; the round trip does not.
The eight tools
check_adherence · next_dose · log_dose · report_symptom ·
care_summary · who_can_see · open_solo_window · disclosure_ledger
Identity comes from the OAuth token, not from a voice. Grants are an explicit
table: the daughter and the visiting aide ask the same question on the same
device and get different answers. who_can_see is deliberately spoken aloud —
who holds access is exactly the thing that should be said in the room.
open_solo_window is the escape hatch: five minutes, one device, one asker,
expires on its own, one ledger row per revealed field. It fails closed if
the transport supplies no session id — without one, "this device" is meaningless
and a window would unlock every device at once.
What this does not do
It does not stop someone reading over your shoulder. The private channel is a screen, and screens are visible.
It does not do voice identification. Alexa has voice profiles; Amazon does not document whether an MCP add-on receives any of that, so we did not pretend to have it. Every authorization decision comes from the access token. If that context does exist, this add-on is worse than it needed to be — see
docs/friction-log.md.It cannot unsay what was said to it.
report_symptomtakes the description as a plain string, because the user already spoke it aloud and the model already heard it. Earshot controls everything downstream of that, and nothing upstream.It is not a compliance product. Not HIPAA, not audited, not a medical device.
The solo window trusts you. It cannot verify that you are actually alone.
The MCP Apps card deliberately shows you nothing. It renders a receipt — "7 details went to Sarah's own device", seven masked rows, and why it is blank. That is not a limitation we are dressing up.
resources/readis an ordinary method on the same authenticated session the model drives: we proved it by reading the card's HTML back from a client calling itselfa-client-that-is-not-a-renderer. Anything printed on that card is oneresources/readfrom the model's context, which makes it worse thanstructuredContent, not better.The card has never rendered on Alexa+, and probably cannot today. It renders in our own MCP Apps host under an enforced CSP, proven by 15 checks in real Chrome. But Amazon's documented
initializedeclares noextensionskey, sogetUiCapabilityreturnsundefinedand a conformant server has nothing to offer them. We send the binding anyway and reportearshot/uiExtensionNegotiated: falseon the wire, so the contradiction is in the bytes rather than in this paragraph. Seedocs/friction-log.md.
Known findings
This project is a security claim, so its open findings belong in the README rather than in a tracker nobody reads.
An adversarial review wrote an attacker whose only goal was to make the product
lie, and it won. Each finding is a real executing assertion in
test/attack/, reported by npm run test:all under its own
heading — 38 findings, 25 now closed, 13 still failing and named in the output.
A finding closes because src/ changed; none of the tests were edited.
The worst one falsified this file's predecessor verbatim. The carrier class had a
public unseal() method whose "only callable from this module" was a comment
rather than a modifier: one call, no cast, no import, no capability, and it
ignored classification, so it opened sealed values too. The payload now lives in
a module-scoped WeakMap with no property or symbol on the object at all.
Of the 13 still failing, one is a genuine open limit and should be read as such:
The reveal capabilities are module exports. Any module inside
src/that imports one holds it. Manufacturing a capability is genuinely blocked — forged objects,Object.createandSymbol.foron the guard name are all refused — but "held by X only" describes today's call sites, not an access rule. It is mitigated, not closed: a self-test greps the shipped tree and fails the build if any module beyond the three legitimate holders so much as mentions a capability. Closing it properly needs a module boundary the language does not give us.
The other twelve are assertions that cannot pass as written — four require a
composition helper to succeed at laundering a sealed value, which is the bug;
three contradict a passing test asserting the opposite of the same call; one ends
in assert.ok(false); one asks for an envelope that costs zero time. They are
left failing and visible rather than quietly deleted, because a test suite that
only contains the assertions the code passes is not evidence of anything. Two
describe real, narrower gaps we chose to state instead of fix: care_summary
speaks an item count that is a function of the protected set, and a toJSON
returning marker-free plaintext is invisible to a marker scan by construction.
Prior art
Splitting one spoken answer into a public half and a private half routed to the asker's phone is not a new idea, and we are not claiming it as one. It is claimed in at least three granted patents:
Patent | Assignee | Priority |
US 10,803,859 B1 — Speech processing for public devices | Amazon | 2017-09-05 |
US 11,494,502 B2 — Privacy awareness for personal assistant communications | Microsoft | 2018-10-25 |
US 11,158,312 B2 — Contextually appropriate responses | IBM | 2018-09-25 |
Amazon's worked example splits "Thank you for checking in to the Downtown Plaza. Ms. Smith, your room number is 1312 and your room code is 24601" — public half spoken, room code to her phone. Microsoft's claim 1 recites redacting confidential information and indicating that the rest went over a second channel, and its own worked example is "check your phone for details about your 2 o'clock appointment." Adjacent art covers the medication case specifically: Walgreens' US 11,762,965 B2 substitutes "your heart medication" for the drug name when it may be overheard.
What none of them do is decide whose policy governs. Every one of them protects the asker or the account holder — Amazon protects the hotel guest, Microsoft protects the user from bystanders. In care, the person the data is about is usually neither. She is a third party to her own medical information, and she is frequently the other person in the room.
In Earshot every grant carries grantedBy, and in the seeded household every
grant is grantedBy: MARGARET — the subject, not the account holder, not the
caregiver who set up the device. The subject also receives the complete ledger
about herself, including the requests refused on her behalf. That is the claim
worth defending, and it is a much narrower one than "we invented channel
routing."
The other honest thing to say: nobody has shipped any of this. Apple's HomePod speaks personal content aloud and sends a notification as a receipt. Google's Voice Match refuses outright. Amazon's own caregiving product, Alexa Together, ran for three and a half years and was discontinued in May 2025 without ever addressing the shared room.
Licence
MIT. See LICENSE.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections. Dates show when Glama detected each change.
No tool schema history has been recorded yet.
This server cannot be installed
Maintenance
Resources
Unclaimed servers have limited discoverability.
Looking for Admin?
If you are the server author, to access and configure the admin panel.
Related MCP Connectors
Private cross-media memory for AI assistants: recommendations, progress and controlled actions.
1Cross-device AI memory with encrypted activity capture and context handoff between AI tools
Evidence-bounded static agent security checks with signed redacted receipts.
A secret store for AI agents: the agent never sees the plaintext.
Related MCP Servers
- AlicenseNot gradedqualityCmaintenanceEnables natural-language queries about a patient, their insulin pump or other devices, and consent to share their data, with answers retrieved in real time from a FHIR R4 endpoint.1MIT
- AlicenseNot gradedqualityCmaintenanceEnables voice-first assistants to avoid repeating themselves by remembering what each user has already been told across sessions, and supports follow-up references to earlier briefings by number or partial description.Apache 2.0
- AlicenseNot gradedqualityCmaintenanceGives Alexa+ voice access to track household insurance claims, refunds, repairs, and disputes, and to chase them via disclosed AI phone calls while keeping money and legal decisions with the human.MIT
- AlicenseNot gradedqualityBmaintenanceEnables Alexa+ users to coordinate household pet care by logging feeding, medication, activity, litter, and health observations for dogs and cats, with evidence-grounded triage, Q&A, and daily briefings backed by a verified veterinary library and citations.1MIT
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
MCP directory API
We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/Beeeeen/earshot'
If you have feedback or need assistance with the MCP directory API, please join our Discord server