Skip to main content
Glama

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.

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: Maple Cart MCP

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.

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.

The tools

Eleven 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

list_categories

The departments, spoken as a question

browse_category

A department, three products at a time

search_products

Whole spoken words, in-stock first

describe_product

The longer description, on request

add_to_cart

Clamped to real stock, never oversells

remove_from_cart

Takes it back out

read_cart

Reads the basket, including yesterday's

review_order

The spoken review page. Places nothing

place_order

Cash on delivery, refuses without confirmation

order_status

Where it has reached, in plain words

reorder_last

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/mcp

Point 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 -q

Forty three tests. Most cover the shop rules and the WooCommerce mapping directly. Three 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, and an order declined. 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

<p>Strong &amp; malty leaf.</p> and three more paragraphs

one sentence, no markup, no entities

stock_quantity: null because stock management is off

the instock flag, read as availability

price: "" on a variable parent

not offered at all, rather than offered at nothing

weight: 4.2

"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 eleven 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
  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
tests/
  test_voicecart.py    the shop rules
  test_refer.py        what "the second one" means
  test_woo.py          the live shop, against a fake one
  test_mcp_session.py  a whole trip over Streamable HTTP

Building 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.

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.

Maintenance

ActivityMaintained
ResponsivenessNo issues

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

Related MCP Servers

Latest Blog Posts

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/saifbrand/voicecart'

If you have feedback or need assistance with the MCP directory API, please join our Discord server