VoiceCart
Allows a voice assistant to shop a WooCommerce store: browse categories, search products, manage a persistent cart, and place cash-on-delivery orders that are written back into WooCommerce.
Click on "Deploy 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., "@VoiceCartShow me teas under 500 taka and add the first one to my cart."
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.
VoiceCart
A whole storefront you can shop by voice: browse, hear products described, fill a basket that is still there tomorrow, and order, without ever seeing a screen.
A self-hosted MCP server over Streamable HTTP, built for the Alexa+ track of the Amazon Developer Hackathon.
Who this is for
Online shopping assumes you can see. Not the checkout, the whole thing: you skim twenty results in two seconds, glance at a cart badge, scan a review page before paying. Take sight away and every one of those becomes a sentence somebody has to read to you, in order, with no skipping.
Screen readers make a shop operable. They do not make it quick. A blind shopper working through a category page hears the navigation, the filters, the promo banner and the cookie notice before the first product, and has no way to skim past any of it.
That is measured, not asserted. Across seven live WooCommerce shops, read through Chrome's own accessibility tree, the median category page spends 98 words before it names a single product - and one spends 270. VoiceCart finishes an entire order in 119. The method, the numbers and the scripts that produce them are in MEASUREMENT.md.
A voice assistant can be quicker than a screen, not just an alternative to one. But only if the shop on the other end is built for listening. That is what this is.
Related MCP server: Shoppingate MCP Server
Built for listening, not for reading aloud
Most voice integrations take a screen-shaped API and read the JSON out. This one is shaped by how hearing works.
Three at a time, never twenty. A sighted shopper skims a long list; a listener cannot skip. So results come three at a time with the count of what is left, and the assistant offers rather than continues.
Sylhet black tea, 320 taka for a 500 gram pack. Kalijira rice, 260 taka for a
2 kilo bag. Sundarban wild honey, 850 taka for a 500 gram jar. There is one
more. Say next to hear it.The disqualifying fact goes first. "Out of stock" belongs before the price, not after it, or the listener spends the whole sentence deciding to buy something they cannot have.
Roasted cashew nuts, out of stock at the moment.The basket outlives the conversation. A sighted shopper leaves a browser
tab open. A voice shopper has no tab, so state that dies with the session
means starting again every morning. Carts here are keyed to the shopper and
written to disk, which is also what lets reorder_last mean "the usual".
One sentence is the review page. There is no page to glance at before paying, so the confirmation carries the two facts that cost money to get wrong, and nothing else:
That is 10,450 taka, paid in cash to the courier, delivered to House 12,
Dhanmondi. Say yes to place the order.place_order refuses to run without confirmed=true. Asking twice is not
friction here; it is the only review step that exists.
"The second one", because nobody says a SKU. On a screen you point. In a conversation you refer back, so the shop remembers the last list it read to you and resolves against it.
you Show me the home and bath things.
alexa Neem and turmeric soap, 140 taka. Cotton hand towel, 480 taka.
Brass table lamp, 3,200 taka, only 3 left. There is one more.
you Add the second one.
alexa Added Cotton hand towel, pair. Your basket is now 1,330 taka.Positions, names and pointing all work, and the recent list wins over the rest of the shop: two products have cotton in the name, but right after hearing one of them, "add some cotton" is not ambiguous. When it genuinely is, nothing is added and the reply asks which, because putting the wrong thing in the basket of somebody who cannot see it is worse than asking twice.
Things you would have seen, said instead. Allergens on food, every time. A warning when the parcel is heavy enough that somebody should be at the door. Low stock when there are three left. None of that is decoration on a voice channel, it is the label you cannot pick up and read.
More than a tool list
Most MCP servers expose tools and stop. This one uses the rest of the protocol where the rest of the protocol is the right answer.
Resources, because reading is not an action. An assistant deciding what
to say next should not have to invoke something to find out what is already
true. shop://catalogue, shop://category/{name}, shop://cart/{shopper}
and shop://order/{id} are the same facts, addressable, with no side effect
attached.
Subscriptions, so a client watching shop://cart/{shopper} is told when
the basket stops being what it was, rather than polling to find out. The SDK
does not serve resources/subscribe at 2025-11-25, so the shop registers
the handler itself - see voicecart/subscriptions.py, and the two entries it
earned in the friction log.
Completion, so the assistant says a department that exists rather than
guessing at one, and can complete an item against the products it just
read out.
A prompt, shop_by_voice, which tells a client how to run this
conversation: read the speech field exactly, pass the shopper's own words
through as item, never read a SKU aloud.
Elicitation, which is the interesting one. The order is never placed on the assistant's own judgement. If the client can ask, the shop asks it directly through the protocol and waits:
place_order(address="House 90, Bashundhara")
-> elicit: "That is 320 taka, paid in cash to the courier, delivered to
House 90, Bashundhara. Say yes to place the order."
-> accepted: place_the_order = true
<- "Ordered. Your number is 21, 8570."Elicitation is optional in MCP, so this degrades rather than fails. A client that cannot ask gets the same question handed back as speech, for the assistant to put in its own words. Either way nothing is ordered without a yes; only the route the question travels changes.
Cash on delivery, and why that matters here
This shop is paid in cash when the courier arrives, which is how most of South Asia buys online. It also removes the worst moment in voice commerce: nobody is ever asked to say a card number out loud, in a room, to a device. The server instructions tell the assistant never to ask for payment details at all. There is nothing to pay until somebody knocks.
Speech corrects itself
A person shopping out loud does not issue clean commands. They say "no, not that one", "take the last one back", "start again" - and they say it halfway through the sentence they are already in. A shop that only understands forward motion makes the shopper carry the correction: remember what went in, name it exactly, ask for it to come out.
So a correction is its own intent. repair takes the shopper's words
verbatim and walks the shop backwards:
said | what happens |
"take the last one back" | the basket goes back to what it was before the last change |
"no, not that one" | the same undo, and that product stops being offered for those words |
"start again" | the basket empties - and that can itself be taken back |
"no, take the rice out" | the apology is stripped off and the rice comes out |
The undo is a snapshot of the whole basket, not a reversed operation, because an add is not reversible on its own: it may have been clamped to what was in stock, or merged into a line already there. Only an order cannot be undone - handing somebody back a basket they have already bought would be a lie about what they owe.
The tools
Twelve small tools rather than one large shop tool, because a conversation
arrives one intent at a time and changes its mind between them.
Tool | What it does |
| The departments, spoken as a question |
| A department, three products at a time |
| Whole spoken words, in-stock first |
| The longer description, on request |
| Clamped to real stock, never oversells |
| Takes it back out |
| "No, not that one" - undoes, and stops offering it |
| Reads the basket, including yesterday's |
| The spoken review page. Places nothing |
| Cash on delivery, refuses without confirmation |
| Where it has reached, in plain words |
| What "the usual" means |
Every one returns the same shape: speech to read aloud exactly as written,
and cards to render only if there is a screen. Both are declared in the
output schema, so a client knows there is a carousel to show without being
told in prose.
describe_product, add_to_cart and remove_from_cart take an item
phrase rather than a SKU: a position, a name, or a bare "that one" all
resolve, and an ambiguous phrase comes back as a question.
Search matches whole spoken words, not substrings. Somebody says "honey", never "hon", and a substring match on "hon" would hand them honey as though they had asked for it.
Run it
pip install -r requirements.txt
python -m voicecart.server # http://127.0.0.1:8080/mcpPoint any MCP client at /mcp. The server negotiates protocol 2025-11-25.
Then watch a whole visit play out as a conversation:
python demo.py you Add the second one.
-> add_to_cart(item='the second one')
alexa Added Cotton hand towel, pair. Your basket is now 1,330 taka.
you And nine of the brass lamp.
-> add_to_cart(item='brass lamp', quantity=9)
alexa I could only add 3 of Brass table lamp, that is all there is.
you Order it to House 12, Dhanmondi.
-> place_order(address='House 12, Dhanmondi')
alexa That is 10,450 taka, paid in cash to the courier, delivered to
House 12, Dhanmondi. Say yes to place the order.python -m pytest tests -qSeventy five tests. Most cover the shop rules, the corrections and the WooCommerce mapping directly. Four start the server as its own process and drive it through a real MCP client over Streamable HTTP: a whole shopping trip, an order confirmed by elicitation, an order declined, and a client subscribing to a basket and being told when it changes. The transport, the negotiated protocol version, the tool schemas and the shop rules are all exercised together.
Point it at a real shop
It already runs against live WooCommerce. Four settings, no code:
STORE_SOURCE=woocommerce
WOO_BASE_URL=https://your-shop.example
WOO_KEY=ck_...
WOO_SECRET=cs_...Products are then read from the shop and orders are written back into it as real cash-on-delivery orders, which appear in the shop's own order list.
The work is not the HTTP, it is the mapping. A WooCommerce payload is written for a page, and a page can afford to be vague in ways a listener cannot skim past:
What the shop sends | What a listener gets |
| one sentence, no markup, no entities |
| the |
| not offered at all, rather than offered at nothing |
| "someone should be there to take it" |
an Allergens attribute | spoken every time, before the price |
A spoken address goes into the order whole. Splitting "House 4, Lane 3, Uttara Sector 7" into WooCommerce's separate fields would mean guessing which part is the street, and a wrong guess puts a parcel somewhere else.
voicecart/woo.py is the only file that knows WooCommerce exists. Sixteen
tests cover it against a fake shop that serves each awkward row a real store
produces, so they run offline.
Layout
demo.py a whole visit, played out on the terminal
voicecart/
server.py the MCP server and its twelve tools
models.py the reply shape every tool declares
speech.py products turned into something worth hearing
refer.py working out which product somebody meant
repair.py working out what somebody is taking back
subscriptions.py telling a client the basket changed
woo.py reading a live WooCommerce shop, and ordering from it
catalogue.py the storefront
carts.py baskets that outlive the conversation
orders.py placing and tracking, cash on delivery
data/
catalogue.json the demo shop
measure/
screen_reader_cost.py what a shop says before its first product
voice_cost.py what this shop says for a whole order
tests/
test_voicecart.py the shop rules
test_refer.py what "the second one" means
test_repair.py taking things back
test_subscriptions.py who gets told, and who does not
test_woo.py the live shop, against a fake one
test_mcp_session.py a whole trip over Streamable HTTPBuilding this
FRICTION-LOG.md records what cost time while building against the MCP Python SDK and the Alexa+ track: what I expected, what happened, and what I would change. It includes the things that worked, and the one gap that mattered most, which is that there is no way to test an Alexa+ integration against Alexa+.
Licence
MIT. See LICENSE.
This server cannot be deployed
Maintenance
Related MCP Connectors
AI shopping gateway for product search, inventory, carts, and merchant-hosted checkout.
Product search for AI agents: Amazon + Shopify, cart-to-checkout buy path. Pay-per-call, no API key.
Agentic commerce gateway: discovery, search, checkout across Shopify/Woo/Odoo/PrestaShop.
Multi-seller shopping for AI agents. Settle via Stripe MPP or x402 USDC on Base. Hosted.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables interaction with Amazon services through AI assistants, allowing users to search products, manage their cart, view order history, and place orders using natural language.3 npmMIT
- FlicenseBqualityBmaintenanceEnables merchants and customers to interact with the Shoppingate AI Platform, including product searches, inventory management, order tracking, promotions, and personalized recommendations via natural language.14-
- AlicenseNot gradedqualityBmaintenanceEnables product discovery, session-scoped carts, and confirmation-gated checkout through natural language, with deterministic routing and safety features.4 npmISC
- AlicenseNot gradedqualityCmaintenanceConnects autonomous AI shopping agents over HTTP/SSE and WebSockets to aggregate product inquiries and buyer leads, then streams the captured telemetry live to an operator dashboard.GPL 3.0