Inspiration

Voice assistants have learned to answer, to talk naturally, and now to take an action when we ask for it. But look at what a household actually hands to a person it trusts. Not one order at a time: a job, a budget and a few rules. "Get the guest room ready for Saturday. Keep it under $400. Buy the small things yourself, ask me before anything bigger."

That is the part we still keep for ourselves. We hand an assistant one request at a time, and every purchase waits for us, because the only alternative would be a card with no real limit. So the errands stay ours: comparing three cleaners, re-booking everything when a flight moves, chasing a refund.

Mandat closes that gap. It turns Alexa from an assistant you instruct into one you can mandate: you set a budget and the rules once, and Alexa acts inside them, with every dollar visible, reversible, and enforced by the server, not by a prompt. What changes for the household is simple: one sentence and one "yes" instead of an evening of errands, and the certainty that nothing is spent outside the rules it chose.

Why it matters

  • For households: an hour of small errands becomes one sentence and one "yes". Mandat compares, negotiates, books, re-books when plans change, and settles the bill fairly afterwards.
  • For trust: an assistant that can spend money has to be the most predictable thing in the house. Mandat's money rules are code: no payment without the plan's token, nothing above your rule without your spoken yes, payment holds instead of charges until the service is delivered, and one sentence ("Alexa, pause Mandat") that stops everything and releases every hold.
  • For merchants: the demo partners answer through their own AI agents, with a signed identity Mandat verifies before listening to a quote. It is a small, concrete step toward commerce between agents, with the household in charge; real local businesses, which have no agent yet, get a clearly labelled booking request instead.
  • For Alexa+: it shows what the add-on model makes possible end to end, built like a product: account linking, permissions, live activity on the screen, the same flows on an Echo Show and on an Echo Dot, in three languages.

What it does

Enable it like a real add-on. "Alexa, enable Mandat." Alexa links the household's Mandat account in the Alexa app (OAuth 2.1 with PKCE, exactly the flow Amazon documents for MCP add-ons: the tool call answers 401, Alexa reads the metadata, the person signs in on Mandat's own page and approves), then asks which details to share — name, email, address, one switch each. Back on the Echo Show, a settings card opens by itself and Alexa asks one question at a time: the weekly budget, how much freedom Mandat gets (Ask me first · Balanced · Hands-off within limits), the amount it may spend on its own, a daily limit, the categories. Say "200 a week" or "Balanced" and the card changes as you speak; tap it and Alexa confirms aloud. On an Echo Dot the same questions come by voice. "Alexa, open Mandat settings" brings it back anytime; "Alexa, pause Mandat" is an emergency stop that voids every open hold at once; "Delete my Mandat data" erases the household after a yes.

Then you just talk: a budget for a visit ("keep it under $400"), errands, changes.

Where everything comes from. Products are real products with real prices from US online stores (through Channel3), with their photos, brand and a link to the store; Mandat never orders from the store. Cleaners, caterers and plumbers are real local businesses near the household (Google Maps, else OpenStreetMap), with their rating, number of reviews, opening hours and a photo; their price is an estimate from one documented rule ($50 per cleaner-hour, $45 per guest…), and a booking is a request this demo never sends. Agent-to-agent negotiation, which no real local business offers yet, happens with demo partners: simulated merchant agents, labelled as such on every card and in every answer. Payments are PayPal sandbox test money.

Story 1 — "The in-laws are coming." "My in-laws land Saturday. Get the guest room ready: a cleaner Friday, fresh bedding, and dinner for six at home Saturday. Keep it under $400." Mandat finds four real cleaners near the home (Capitol Hill, Seattle), real queen sheet sets under a fifth of the budget, real caterers, and asks three demo partner agents at the same time; each has a signed identity card, which Mandat verifies before it listens to a quote, and one negotiates ($210 → $195 for six guests). It shows one mixed plan, for example a real cleaner at about $100 (estimated), real sheets at $62.99 and the demo partner's dinner at $195: $357.99 live. "Yes, book it" places three PayPal sandbox authorization holds. Nothing is charged until each service is delivered, and Alexa says what is real: the cleaner only has a booking request, no order goes to the store.

Story 2 — "Four days later." A new session: "We're almost out of dog food and laundry detergent." Mandat remembers that Max is allergic to chicken (real foods whose title, description or features mention chicken are skipped) and the household's usual detergent brand. What fits the $40 rule it simply buys, with test money, and says so; what is above the rule, it asks first.

Story 3 — "Plans change." "Their flight moved to Sunday." Mandat moves its own booking request for the cleaner to Saturday, on the same hold (a business whose published hours say it is closed that day is replaced instead). The demo partner caterer is closed on Sundays: its $195 hold is voided at once and a replacement is proposed ($210, Sunday 7:30 PM) — one more yes. After the visit: "Olive billed $180, and refund $10 on the sheets." Mandat captures $180 and releases the other $30, refunds $10, and shows each order's timeline: held → captured → released → refunded.

On an Echo Dot (no screen) the same flows work by voice alone: at most three options, the total read back, and an explicit "Say yes to confirm".

You see what Alexa is doing, live. While a turn runs, the Echo Show lists each real step as it happens: every real business found near home ("4 cleaners near Capitol Hill · estimated prices"), every demo partner's answer the moment it lands ("Hearth Table Catering $210 → $195 · demo partner · verified agent", with a photo), the plan, each payment hold ("Holding $100 for Seattle Green Maids · not charged until it is done"). The voice stays short; the screen carries the detail.

You judge before you say yes. "Show me the bed linen" opens the item: a photo gallery, the facts, the slot or the delivery, why it was chosen, where it comes from (the store and its real price, or the business's address, hours, Google rating and the estimate rule) and the rule that applies ("Needs your yes: above your $40 rule"), with a link to the store or to Google Maps. Prices are compared only with similar items, and a dearer choice says so honestly, in amber, never in the positive colour: "Costs $27 more than Truly Soft Everyday Sheet Set, the cheapest similar option". "Other options?", "Take the first one instead" (new plan, new total against the budget, and the old plan can no longer be paid; it works off the script too: "the cheaper one", "a king size", "another cleaner", even without asking for options first, and when nothing matches Alexa names the closest options), "Compare the two caterers" (a demo partner's negotiated $195 against a real caterer's estimated $270, rated 4.9). A swap that would break the budget is refused with the numbers. Tapping a plan line or a card button sends the same sentence to Alexa, so the same money rules apply.

In English, French and Spanish. What Alexa says (Amazon Polly neural voices Danielle, Léa, Lucia), the cards, the live activity and the demo script follow the household's language; "oui" and "sí" count as a yes, "oui mais attends" does not. Prices stay in US dollars (the US market) with local formats ("357,99 $", "samedi à 19 h 30"). Alexa+ availability by country is Amazon's call; the add-on is ready.

How we built it

  • MCP server (spec 2025-11-25, Streamable HTTP). @modelcontextprotocol/sdk 1.32.1 in stateless mode with JSON responses, so it runs on AWS Lambda behind a Function URL. The server checks Origin (403 otherwise), the OAuth 2.1 access token on every tool call, and keeps all state under explicit ids in DynamoDB. Twenty-one tools, one per intent, written from the Alexa+ tool-schema guidance (find_home_services, propose_plan, confirm_plan, change_order, close_order, update_settings, pause_spending…), plus two app-only tools (23 in tools/list). Each result has the sentence Alexa says (content[0]), the ids the assistant needs (content[1], "do not read aloud"), structuredContent for the card and _meta.ui with resourceUri and the Alexa+ invoking / invoked texts.
  • Account linking, permissions, autonomy. Our own OAuth 2.1 authorization server on the same Lambda, written to Amazon's account-linking page: authorization code + PKCE S256 only, the resource parameter, refresh tokens rotated on every use (a reused one revokes the link), static clients, exact redirect URIs, RFC 9728 / RFC 8414 metadata, HTTP 401 + WWW-Authenticate on a tool call without a token, revocation, a privacy page; codes and tokens are stored as SHA-256 hashes in the same DynamoDB table. The household always comes from the token, so households are isolated. A Continue as demo household button on the linking page means judges never sign up. Alexa's customer-data permissions are simulated in an Alexa-app view (Amazon does not publish how granted data reaches an MCP add-on). The three autonomy levels, the daily limit, the categories and the emergency stop are enforced by the server on every payment and re-checked at confirmation; settings said aloud, the stop and "delete my data" are handled by the host without the model.
  • MCP Apps cards. Seven ui:// resources (text/html;profile=mcp-app): household status, budget, options carousel, plan, receipt, item, settings (live: a value said aloud lands on the open card in place). Self-contained HTML (empty CSP lists), a dependency-free bridge for ui/initialize, tool input and result notifications, tools/call (the Approve button), ui/update-model-context (carousel selection), ui/request-display-mode (Details), ui/message (Book the replacement) and size changes. The cards read theme and container size from hostContext and shrink their type scale to fit the screen instead of scrolling.
  • Agent Skill. mandat-household-errands (agentskills.io format): money rules, the three playbooks, voice style. It passes the Strands strict validator and is loaded at runtime by Strands' AgentSkills plugin.
  • Strands Agents (TypeScript) on Amazon Bedrock. The "Alexa+ brain" behind the simulator: an Agent with an McpClient to our server over Streamable HTTP and the skill plugin, on Amazon Nova 2 Lite (us.amazon.nova-2-lite-v1:0, with a Strands ModelRouter fallback to the global. profile when throttled). Merchant agents run on Amazon Nova Lite through the Bedrock Converse API: the agent talks, the merchant's own booking rules decide the day and the lowest price, and the server clamps every quote whatever the model says. Money-critical rules live outside the model too: the host writes the user's exact words into user_confirmation (in one live run the model tried to buy a $52 item in the same turn; the server refused, no yes had been said), and refuses any budget change the user did not state; browsing options ("Other options?", "Compare…") can never edit the plan, only a stated choice can (seen live on 19 Oct, now a tested host guard). Measured on 18 Oct: the three stories (11 turns) passed 33/33 on three runs against Bedrock and 22/22 on two runs against the deployed Lambda; median turn 5–6 s.
  • Alexa+ web simulator. A voice-first page that is itself an MCP client (initialize, tools/list, resources/read, tools/call) and an MCP Apps host: Echo Show 15 and Echo Show 8 screens rendered at their own resolution, Echo Dot without a screen, the light bar for listening / thinking / speaking / waiting-for-yes, and a JSON-RPC drawer.
  • AWS. Lambda (Node.js 22, arm64) + Function URL, DynamoDB single table (conditional writes make confirm_plan idempotent), Bedrock, Polly neural voice, least-privilege IAM; one idempotent deploy script.
  • Tests. 128 + 16 automated tests (off-script turns a judge may try, account linking end to end, each autonomy level, the emergency stop, settings by voice in three languages, delete my data, protocol, every tool, money rules with a property test, the three stories over real HTTP, Echo Dot mode, merchant booking-system guards, per-address limits, the shared model budget, the Strands loop and its guardrails, the deploy script against a fake AWS CLI), an MCP Inspector battery (34/34 locally, 33/33 against the live AWS endpoint on 18 Oct, account linking included) and a live replay of the three stories against Amazon Bedrock (33/33 turns, 3 runs).

Alexa+ requirements we honour

Alexa+ add-on guidance Mandat
Complete the task end to end Search → negotiate → plan → yes → hold → change → capture / refund
Glanceable: one title, one or two fields, one action Each card: a title, one number, one primary action; sized for the room; no scrolling
Transaction flow: explicit confirmation, price breakdown, no duplicate orders, change one thing, cancellation Breakdown (base, negotiated, fees, tax, total); confirm_plan needs the plan's token and the user's words; idempotent; change_order; void / partial capture / refund
Shopping must protect customers from unintended purchases Autonomy level, auto-buy amount, daily limit and categories enforced by the server; above them it refuses without a yes; emergency stop
Account linking: OAuth 2.1 code grant, PKCE S256, refresh tokens, metadata document, 401 without a token, privacy policy Own authorization server; checked by tests and by MCP Inspector on the live URL
Permissions: name, email, address granted in the Alexa app, manageable later Simulated Alexa app with one switch per item and a Manage page; only what was allowed is kept
Voice-only: ≤ 5 options, read back, explicit yes, never mention the screen Echo Dot mode: 3 options, total read back, "Say yes to confirm"
Voice and screen consistent Speech and card come from the same tool result

Challenges we ran into

  • No public Alexa+ simulator. The add-on tooling is Private Preview only (Web Simulator and Local Inspector), so we built a device-true MCP Apps host to see our cards the way an Echo Show would.
  • Confirmation without elicitation. On a stateless Lambda MCP server there is no channel for server-to-client questions, so the confirmation became a two-step protocol (token + the user's own yes) that the server enforces.
  • Ids versus voice. Strands and MCP Apps only give the model content; ids had to travel as a separate "do not read aloud" text block so Alexa never reads them.
  • Two MCP generations. Strands needs MCP client v2 and zod 4; the server stays on SDK 1.x — two packages.
  • Plans that change. Moving a booking must never create a second payment; a refused move must release money immediately. Every path is covered by tests.

Accomplishments that we're proud of

  • Money rules enforced by the server, not by the prompt: no payment without the plan's token, no payment above the household rule without a yes, budget checked twice, idempotent confirmation, captures never above the hold.
  • The same flows on a 15-inch screen and on a speaker with no screen.
  • MCP Inspector: 33/33 checks on the live URL, including the 401 and the OAuth metadata; MCP Apps metadata recognised for 23/23 tools.
  • A first run that feels like a product: link, share, set your rules by voice, in about 30 seconds, with no sign-up.

What we learned

  • Designing for voice first makes the screen better: one number, one action.
  • The MCP Apps split (data for the card, text for the model) maps naturally to Alexa's "say it, show it" pattern.
  • A stateless MCP server with explicit ids is simple to run on Lambda and easy to reason about.

What's next for Mandat

Next weeks — a real add-on. As soon as the Alexa+ add-on toolkit opens to us: register Alexa's redirect URIs on our authorization server, put Login with Amazon behind it, receive name, email and address through Amazon's real permissions, and render our cards in the official host.

Products from Amazon itself. In the demo, products are real products from US online stores, found through Channel3, and nothing is ever ordered for real. For everyone, the natural source is Amazon's own catalogue, where households already shop, with their usual delivery and returns; local services would come from partners that accept agent bookings, so the estimate becomes a firm quote. The product and place sources are modules behind the same tools: the mandate on top (the budget, the rules, the holds and the "yes") does not change.

Next months — standing mandates. Today a mandate covers one job. Next, it becomes a routine: "keep the pantry and the dog food stocked, up to $60 a week", "book the cleaner every other Friday", "tell me before any subscription renews". AgentCore runs these long-lived routines, with the same rules and the same emergency stop. More categories (pet sitting, laundry, home repairs) and more merchant networks with signed agent cards.

The bigger idea. Today we tell assistants what to do. Tomorrow we will tell them what we want, and what they may spend. That only works if people can trust the assistant with their money, and Mandat is that layer of trust: a budget, rules, a yes when it matters, and a stop button that always works.

Before → After

Mandat's first commit is dated 1 Oct 2026, inside this hackathon's submission period. It began as a web app (PWA with its own chat, Express on Render, an OpenAI-compatible model, Cloudflare R2) for a payments hackathon. This entry is a new repository whose first commit copies the core modules unchanged (17 Oct 2026, "Baseline: core modules copied from Mandat"); everything after it is the Alexa+ work: the MCP server, MCP Apps cards, the Agent Skill, Strands on Bedrock, household budgets with auto-approve rules, memory across sessions, change / cancel / refund flows, Echo Dot mode, the simulator, and the AWS deployment (git diff $(git rev-list --max-parents=0 HEAD) HEAD: 172 files, ≈ 15,300 lines added as of 19 Oct, lockfiles, images and the saved OpenStreetMap snapshot excluded). The video shows only the new work.

Testing instructions

  • Online, 2 minutes, no sign-up: open https://fonzkp2f5pszfit6xmqqiw7qdm0dybyu.lambda-url.us-east-1.on.aws/simulator/ in Chrome.
    1. Click "Alexa, enable Mandat." In the Alexa app on the right: tap the notification → Link account → Continue as demo household → Allow. 2. Say or click "Make it 200 a week", "Balanced", "That's all" (or tap the card). 3. Then the stories, one line at a time (each is one real turn on Amazon Nova, 2–15 s). Switch Echo Show 15 / Show 8 / Echo Dot at the top; press D for the MCP traffic (the 401 and the token are there); the circular arrow unlinks and starts over.
  • Any MCP client: https://fonzkp2f5pszfit6xmqqiw7qdm0dybyu.lambda-url.us-east-1.on.aws/mcp (Streamable HTTP, 2025-11-25). initialize and tools/list are public, e.g. npx @modelcontextprotocol/[email protected] --cli https://fonzkp2f5pszfit6xmqqiw7qdm0dybyu.lambda-url.us-east-1.on.aws/mcp --transport http --method tools/list; a tools/call needs an access token: node scripts/link-demo.mjs <base URL> demo --token links a fresh demo household with PKCE and prints one (then --header "Authorization: Bearer …").
  • Locally (no account): Node 22 → npm ci && npm test && npm run demo → http://127.0.0.1:8787/simulator/
  • Try the judge-before-yes lines on the right ("Show me the bed linen", "Other options?", "Take the first one instead", "Compare the two caterers"). Products and businesses are real, so totals vary a little from day to day. The language picker (EN · FR · ES) switches the voice, the cards and the script.
  • No real money moves, and you need no PayPal login. Every hold, void, partial capture and refund is a real PayPal sandbox transaction (test money) on one sandbox buyer linked once by the owner (Vault v3 payment token, merchant-initiated authorizations); receipts show "PayPal sandbox · ". Checked on the public URL on 18 Oct with real authorization ids (node scripts/live-paypal-check.mjs); the emergency stop voids real sandbox holds too.

Built With

Share this project:

Updates

Submission history