Tools in the browser
The WebMCP tools every shop page declares, for an agent already in the shopper's browser.
Every other door on this platform is for an agent somewhere else, holding a credential and driving a basket it opened itself. This one is for an agent that is already inside the shopper's browser, such as an assistant built into it.
Every shop page declares tools through WebMCP, and the forms on the page carry WebMCP's declarative attributes too. The agent acts as the shopper. It needs no key, because it uses the shopper's own session.
The registered tools
Every page registers these through document.modelContext, and a product page adds get_current_product:
| Tool | What it does | Hints |
|---|---|---|
search_products | The shop's own search, in the shopper's currency | readOnlyHint |
get_product | One product by handle, with the variant ids a basket takes | readOnlyHint |
answer_shop_question | The shop's answers about delivery, returns, sizing and what goes with what | readOnlyHint, untrustedContentHint |
view_cart | The basket and its totals | readOnlyHint |
add_to_cart | Adds a variant. Quantities add to what is there | |
set_line_quantity | Sets a line's quantity, or zero to remove it | |
apply_discount, apply_gift_card | A code the shopper is holding | |
set_buyer_details | Contact and delivery details, in one post | consequentialHint |
choose_delivery_option | One of the delivery rates the shop quoted for the address | |
place_order | Turns the basket into an order | consequentialHint |
get_current_product | The product this page is about | readOnlyHint |
Every tool states all three hints. answer_shop_question is marked untrusted because its answer is written from the merchant's pages and reviews.
The declarative forms
The forms carry toolname, tooldescription and toolparamdescription, so an agent that reads the page without running its scripts can use them too:
| Form | toolname |
|---|---|
| The search box | search_this_shop |
| The buy button on a product page | add_this_to_cart |
| A basket line's quantity | set_basket_line_quantity |
| The discount and gift card boxes | apply_discount_code, apply_gift_card |
| Store credit, for a shopper who is signed in | use_store_credit, remove_store_credit |
| The details form | set_delivery_details |
| The delivery choice | choose_delivery_option |
| Keeping a line whose subscription the shop stopped offering, to buy once | keep_item_to_buy_once |
| The place order button | place_this_order |
Only the search box carries toolautosubmit, because a search changes nothing. Everything that takes money or sends a message waits for a person.
The declarative forms are in the markup from the first byte. The registered tools are declared once the page's scripts have run, so an agent reading a page that has just loaded may find add_this_to_cart alone for a moment. Poll for the tool you want rather than reading the list straight after a navigation.
What a tool can do
No tool takes a basket id, a session id, a customer id or a token, so there is nothing to forge and nothing to leak into a transcript. Each tool posts to the same route a person's form posts to, so the shop enforces exactly what it enforces for a click: quantity rules, stock, and who owns the basket. The worst a confused model can do is what the shopper could do by clicking.
No tool can type a card number. place_order either returns a settled order, or returns the address where the shopper confirms the card themselves, and says which.
Every basket form is a plain post that answers with a redirect, so it works with scripts off. Send Accept: application/json and the same route answers the outcome as JSON instead.
Calling one by hand
const tool = (await document.modelContext.getTools()).find((t) => t.name === 'add_to_cart');
const answer = await document.modelContext.executeTool(
tool,
JSON.stringify({ variantId, quantity: 2 }),
);
const said = JSON.parse(JSON.parse(answer).content[0].text);executeTool takes the tool object, not its name. The arguments go as a JSON string, even for a tool that takes none. The result is a JSON string whose content holds another, so it is parsed twice.
The tools are registered on document.modelContext, and on navigator.modelContext only where the first is missing. A browser with no model context runs none of this.
Switching it off
The tools are on for every shop. A merchant turns them off under AI assistants in a shopper's browser in the shop's settings.