The task
Amazon opened a brief for Alexa+ add-ons that connect over MCP: a server with tools, Echo Show screens through MCP Apps and a skill that tells the model when to call what. We picked the question we hear every day in the chat of a real electronics store: “will this work with mine?”. Chargers, cables and robot brushes are bought for things people already own, and a bot answers with confidence and no proof.
The main obstacle showed up at once: the Alexa+ toolkit, CLI and web simulator are not given to participants. We never saw how the real Alexa+ trims or displays a tool result. There was nothing to test end to end.
Why, and what the idea was
The idea is two rules. First: devices belong to the shopper, not to the store. Say “I have an iPhone 15” once, and every store on this server knows it, so “will it work with my phone?” works without naming the model. Second: a “yes” exists only when the store itself named the exact model, in a tag or in a “designed for” phrase, and the quote is shown on screen. If the store mentions only the series, the answer says so: “the store lists iPhone 17, not your iPhone 17 Pro”. If the store is silent, the answer is “not confirmed, I will not guess”.
There is no model inside the server. Compatibility, specs and product type are extracted with rules from the store’s data, which is why we can promise zero made-up facts and check it automatically.
Since Alexa+ was closed, we built our own Echo Show in the browser: a real MCP client over Streamable HTTP, the MCP App screen in a sandbox, a JSON-RPC log and Claude Haiku playing Alexa. Every assumption about Alexa+ behaviour went into the skill, not into the code.
How it went
| When | What happened |
|---|---|
| Day 1, morning | A throwaway spike answered three questions before any product code: which SDK, sessions or stateless, how to pack for Lambda. It turned out ext-apps 2.0.3 works only with the new split SDK v2 |
| Day 1 | Library and server: a Shopify catalog adapter with a 6-hour cache, normalization per variant, extraction of watts, ports and devices, BM25F search, 9 tools. An Echo Show 8 and 15 simulator, one HTML page for every screen |
| Day 1, afternoon | First honesty eval: 17 of 39 requests behaved as expected, 2 made-up facts. After the fixes 51 of 51 and 0 made-up, held-out set 9 of 12 |
| Day 1, evening | First independent review: the core was solid, but the device closet was invisible and nothing was hosted. The closet became shared by every store, signed sessions, DynamoDB, deploy to Lambda. Eval 57 of 57 |
| Day 2, morning | The live model as Alexa, 18 conversations: right tool 18 of 18, nothing invented, but reply length held in 13 of 18. Length is now cut in code. Video v1 and v2 |
| Day 2, afternoon | The second review broke “zero made-up” in three requests: iPhone 17 Pro by an “iPhone 17” tag, a MacBook Pro of any year, “my phone” with two phones on file. Contract v1.6, 11 new cases, 68 of 68 |
| Day 2, evening | A meaning audit of quotes on Roborock: 13 new cases, the old code gave 11 false facts. After the fix 81 of 81. OAuth 2.1 with a consent page, the simulator and the model moved to the same Lambda, video v4 and v5. Submitted |
| Three days later | The offline router missed the first line of the video. Fixed, 18 recorded lines: 16 unchanged, 2 fixed. Then a code freeze and a daily automatic check of the live demo |
Tools and method
A TypeScript server on MCP SDK v2, spec 2025-11-25, Streamable HTTP. Stateless: a fresh McpServer per request, so any Lambda instance answers any request. 9 tools, one per shopper intent: find, compare, check compatibility, remember devices, my devices, prepare and place an order, order status, talk to staff. Each returns one or two sentences for speech and structured data for the screen; 8 of 9 open a screen through MCP Apps.
The shopper’s identity comes only from Authorization: Bearer: a forged, expired or foreign token gets 401. Account linking follows MCP Authorization 2025-11-25: resource metadata, PKCE S256, dynamic registration, a “Link Shop Counter to Alexa” page, a 1-hour token bound to the store. The closet and orders live in DynamoDB, model spend is capped in code: $0.50 a day and 60 turns an hour per visitor; over the cap a keyword router with no model takes over.
Evaluation: npm run eval sends 81 shopper requests through a real MCP client on a frozen catalog snapshot and checks every price, stock flag and quote against the raw feed. The audit is written apart from the server code so it does not share its blind spots, and a new check must first turn red on the old code. Separately, 21 conversations with the live model and 29 checks against the real Satechi storefront.
The library went into its own MIT repository, shelf-to-alexa: any Shopify catalog becomes the same server with one config entry. Roborock was connected exactly that way.
Result
A live demo in the browser, nothing to install, no keys: the page, the MCP server and the model playing Alexa on one Lambda. Two real stores from public Shopify catalogs: Satechi, 141 products, and Roborock, 311 products. Every visitor gets a private shopper for 24 hours.
Numbers: 81 of 81 requests with no made-up facts, 173 unit tests, the live model passes 19 of 21 conversations with the right tool in 20 of 21. Model spend for the whole build and both live evals about $0.10. The 2:56 video was recorded on the published page; Alexa’s lines are word for word from that run.
Limits we state plainly: Bedrock on the new AWS account allowed no model at all, so Alexa is played by Claude Haiku through the Anthropic API, and the Bedrock code sits behind a flag. We never saw the real Alexa+, so “two sentences spoken, details on screen” is our assumption, written into the skill.
What we do not know yet: the brief’s results. The demo stays live until the announcement; the daily check reports that everything is in place.
Mistakes and what we took from them





