Work / Agents and botsopen brief

Shop Counter: Alexa+ remembers your devices and answers "will it fit?" only in the store's own words

What it was

Electronics stores expect the shopper to remember their own devices, and every listing words compatibility its own way. Chatbots answer with confidence and no proof, and the box goes back. Amazon opened a brief for Alexa+ add-ons over MCP, but the Alexa+ toolkit itself was not given to participants: there was nothing to test against.

What we did

Built an MCP server with 9 tools that reads a public Shopify catalog, keeps the shopper's devices in one closet shared by every store, and says "it fits" only when the store names the exact model, with the quote on screen. Since Alexa+ was closed, we built an Echo Show simulator in the browser, a real MCP client. Deployed it all on AWS Lambda, added OAuth 2.1 and an 81-request honesty eval.

What the client got

A live demo in the browser with nothing to install, two real stores, a 2:56 video and an open library, shelf-to-alexa, that turns any Shopify catalog into the same server. 81 of 81 requests with no made-up facts, the live model passes 19 of 21 conversations, model spend for the whole build about $0.10.

ClientAmazon, open brief on Devpost (Alexa+ track)
My roleIdea, MCP server and library, Echo Show simulator, honesty eval, OAuth, AWS deploy, video, submission texts
StatusSubmitted, results 3 December 2026. The Lambda demo is live, the daily check is green
Result81 of 81 shopper requests with no made-up fact, checked against the store's raw feed · Open
Rejected: the store tags "iPhone 17" and the server said "confirmed for iPhone 17 Pro". A series match passed as compatibility. Reconstruction, not a screenshot
Rejected: the store tags "iPhone 17" and the server said "confirmed for iPhone 17 Pro". A series match passed as compatibility. Reconstruction, not a screenshot
Accepted: the store never mentions the Galaxy S24, so the answer is "not confirmed, I will not guess". A "yes" exists only with a quote for the exact model
Accepted: the store never mentions the Galaxy S24, so the answer is "not confirmed, I will not guess". A "yes" exists only with a quote for the exact model

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

A literal match is not compatibility."(Not compatible with Saros 10R)" produced a "yes", and a merchandising tag "magsafe charger" on a plain cable made it MagSafe. The store's negation now means "incompatible", and a tag proves fit only when the tag is the device itself.
A series match passed as confirmation.A closet with an iPhone 17 Pro got "confirmed" by the "iPhone 17" tag, and "MacBook Pro 2017 Intel" was trimmed to "MacBook Pro". Proof now needs the exact model, the shopper's wording is kept as is, and the audit runs on Pro models.
The honesty audit checked the form of the quote, not its meaning.On Roborock "for Qrevo Curv" gave a "yes" for a plain Qrevo, and 68 of 68 let it through. The audit now reads model boundaries, "only", "upgraded from" and negations. A new check must first turn red on the old code: 11 false facts in 13 cases.
The model answered from the product title, skipping the tool.Seeing "Not compatible" in the title, Haiku answered on its own. The scenario now checks the tool call, not just the phrase.
A prompt does not hold reply length.The model appended "Want me to…?" in 7 of 28 turns. Hard limits on form are done in code: the server cuts to two sentences, the host speaks only those.
Check access to a cloud model with one call before writing code.We wrote the Bedrock integration, and the account turned out closed for every model. One manual call in the console would have saved hours.
The daily check caused its own 429.The script fetched Shopify feed pages back to back with no pause and ran twice from one address. Now a 2 s pause between requests, one retry per Retry-After, and a 429 is a warning, not a failure.

Tools

TypeScriptMCP SDK v2MCP AppsAWS LambdaDynamoDBOAuth 2.1Claude Haiku 5.5Shopify products.jsonClaude Code