OOOMETA/OS
← Back to Insights

September 2026 · 5 min read

Shopify swaps agent tools mid-checkout

Shopify swaps agent tools mid-checkout

Key Definitions

Tool surface The set of tools an agent can actually call at one decision point; it is decided by the flow stage, not fixed at session start.

Step-up confirmation A fresh, locatable confirmation demanded when a flow enters a higher-risk stage, instead of reusing an earlier vague consent.

Checkout registers its own tools, so an agent’s effective capability changes with the stage of the flow. A tool list read once at session start is not a boundary — and neither is a record that stores only the session-opening grant.

A shopping assistant in the browser helps you pick running shoes: right size, right colour, added to the cart. Then it moves to the next step, checkout. At that moment the set of tools it was holding is swapped out. It tries to change the shipping address using the list it read at the start, and the page answers with an unknown tool, because the storefront group is no longer registered.

Nothing here is broken. Shopify documents this as the normal path, and the path is useful to us because it gives the question “where does an agent’s authority come from” a concrete shape.

1. Checkout registers its own tools

Shopify’s WebMCP tools are registered in two places. The storefront set covers discovery and cart: search_catalog, browse_store, get_product and show_variant handle searching the catalog, browsing collections, and reading product details with available options; get_cart, update_cart and cancel_cart read and change the cart. The checkout page registers a different set, covering reading and updating the checkout and placing the order.

One sentence in the developer docs is the starting point for everything below: “Checkout registers its own WebMCP tools, so the tool list changes when the shopper moves from your storefront into checkout.”

These tools serve the agent the shopper brings to the store in their own browser, not a third-party agent running on a server. Everything the agent does happens in the tab the shopper is looking at: when the agent changes the cart, the cart in front of the shopper changes; when the agent navigates, the shopper’s page moves. The same docs note that the agent’s cart updates go through the standard storefront actions that themes and apps already use, so if a theme opens a cart drawer on add-to-cart, the agent’s add-to-cart pulls the drawer open too.

Look at how the tools are described and one more thing shows up: each tool carries a description written for the model, telling it when to use that tool. The list is not only a capability table, it is also an operating manual. Swapping the list swaps the manual.

A second axis of change comes from the browser. The docs state that these tools serve agents in Chromium-based browsers only today, and that how arguments are passed is changing: in Chrome 153, passing an object fails to parse and the caller must pass a JSON string, while Chrome 155 is planned to accept objects and deprecate strings. The same tool surface therefore also changes shape between browser versions. Treating capability as a constant means betting that both the flow stage and the runtime version hold still.

Put together, one sentence covers it. What an agent can do at a given moment is decided by which stage of the flow it is standing in. The grant issued at session start is a starting point, not a boundary.

2. The docs make re-reading the list a required action

The more informative part is how the docs handle failure. After a checkout tool call errors, is cancelled or times out, the agent is told to do three things in order: refresh the tool list, call get_checkout to read the current state, then compare that state with its own request before deciding whether to retry. The wording is plain: “After an error, cancellation, or timeout, refresh the tool list and call get_checkout. Compare the current state with your request before retrying.”

Why read the state first? Because one class of error means the outcome is unknown. The docs define update_failed as an update that failed or timed out “and might have partly applied”. That is not a state you can replay automatically: address, discounts, shipping and stock may each be half-written. Retrying from there means sending a request whose starting point you do not know, against an order that has already been changed halfway.

Two other places require re-reading the list. When the navigation tool returns null, navigation may have succeeded anyway, and the agent is told to refresh the tool list once the storefront has loaded so it picks up the storefront tools. And the error message returned at completion tells the agent to call get_checkout for the current state and its messages, and not to try to finish the checkout by interacting with the page.

One note from the same docs on identity is worth keeping. Browser requests are signed with Web Bot Auth to identify the agent, rather than putting identity into tool arguments; only registered signing keys count, and unsigned requests can be deprioritised or blocked. So the evidence of who acted is a request signature, not the agent’s self-description. Arguments can be fabricated; a signature cannot. That difference decides attribution after the fact.

The error taxonomy also contains a group that is not failure but waiting: the buyer still has to finish something, the checkout is busy, a completion is already running. The right response in those states is to wait or hand the action back to the buyer, not to retry. Set beside update_failed, they show what the interface expects of an agent: it has to tell “it did not succeed” apart from “it is not yet known whether it succeeded”. The first can be retried; the second has to be read.

Read together, the tool list is not an initialisation parameter in this flow. It is a runtime input that has to be fetched again whenever the agent crosses a stage boundary or recovers from an exception. The consequence for implementation is direct: a client that reads the list once, writes it into the system prompt and reuses it every turn will look healthy until the flow reaches its most expensive step.

3. The protocol already assumes the list is alive

MCP has a place for this. A server that supports changes to its tool list declares the listChanged capability, and when the list changes it should send a notifications/tools/list_changed notification; on receiving it the client re-issues tools/list to fetch the new set. The specification says servers that declared the capability should send a notification when the available tools change.

In other words, “the list can change” is expressible in the protocol; treating it as a constant is a choice made on the client side. That choice usually saves a round trip and keeps prompt caching stable, which in code is one cache line. Its cost only appears when the flow crosses a stage. The same specification adds a line that fits this pattern well: for trust and safety there should always be a human in the loop with the ability to deny tool invocations. In Shopify’s chain that human has a precise location — the order is submitted after the buyer confirms it.

4. The same pattern was standardised in OAuth

Raising requirements mid-flow and demanding a fresh confirmation event is not new to the agent era. OAuth wrote it down in 2023: the step-up authentication challenge defined in RFC 9470 lets a resource server tell the client that the authentication event behind the current request does not meet its requirements, and how to meet them. The example in the document is close to commerce — an API requiring different authentication strengths depending on whether a purchase exceeds a certain threshold.

The value of that design is that it separates two things: the requirement can rise mid-flow, and every rise corresponds to a new, timestamped authentication event rather than a re-use of some earlier, vague consent. Like the tool-list case, it is not the product of new technology; it is a standard answer to an old problem.

One note on scope: RFC 9470 defines a challenge and response between a resource server and a client, which means the mechanism — the requirement rises and the confirmation is redone — is already well worn between servers. What is missing is not the mechanism but the wiring into an agent’s flow record. That is the use of this comparison: what we are asking for is alignment with an existing standard, not a new invention.

5. Our judgment

A session-scoped permission snapshot is the wrong abstraction. An agent’s effective capability is a function of the flow stage multiplied by the moment, so “what was it allowed to do at that point” has to be kept per decision, not per session. This does not depend on any vendor’s implementation; it follows from what a record has to be able to prove.

The reason is short. Any reconstruction after the fact starts from the record. A record that only stores the session-opening grant can answer whether the agent was authorised; it cannot answer what it could call when it acted. When stages swap tool groups and lists are re-read after exceptions, those are two different conclusions: one says authority existed, the other says the action fell inside it. Evidence work needs the second.

For teams building this kind of agent, three checks you can run today:

  • Is the tool list read before each decision, or fetched once at session start and written into the prompt or context? Search where the system prompt is assembled; the snapshot is usually visible there.
  • Is there a path for tool-surface changes? Do you subscribe to list changes and force a re-read at stage boundaries, or do you only learn the list changed when a call fails?
  • Does the record store the session-opening grant, or the tool surface in force at each decision plus the timestamp of the last authorisation event?

The reverse also holds: re-reading before every decision adds round trips, and that cost is real. The trade-off belongs at stage boundaries, not in every turn. A flow from storefront to order crosses a handful of stages, so forcing a re-read there is nearly free; doing it every turn is what slows the system down. The difference is not the size of the cost but the layer where the change gets absorbed.

Not every flow needs this. The trigger is a stage boundary: if a chain has a single capability surface from start to finish, such as a read-only query tool, a session snapshot is fine, because the list cannot change. The more stages a flow crosses, the higher the odds that a snapshot goes stale; once it crosses payment, identity or an external system, the snapshot stops being an optimisation you can skip and becomes a gap you have to close.

6. What the record should hold

Turned into fields, we would want at least five: the action; when it happened; the tool surface in force at that moment, with names and versions; the authorisation event in effect when the action was issued, meaning who confirmed what and when; and a pre-action state digest for results that may have partly applied. The first four answer what the agent could do and on whose authority; the fifth answers what actually changed.

The fifth is the one teams drop, and it is the only arbiter for a failure like update_failed. When the outcome is unknown, the way to tell how much of an action landed is the state digest, not the agent’s own account, because that account is produced inside the same reasoning loop. Which returns to an old rule: a record that can be recomputed independently beats a log that explains itself.

7. What this article does not claim

First the boundary. This is a consumer-side example: the platform defines the permissions, not the enterprise customer, and the agent is brought in by the shopper. It shows that a mid-flow change of the authorisation surface, with a locatable authorisation event, already exists in a public product. It does not show that enterprise delegation, evidence and audit are solved.

One more reading to avoid: the docs state that the order is placed after the buyer confirms it, so this is not a case of an agent checking out without the buyer. Browser-side agent support also currently covers Chromium-based browsers only, which bounds where this is usable today. There are no quantified results to cite here — no conversion data, no dispute rates — and we do not use it as outcome evidence. The mechanics above come from Shopify’s developer documentation, the MCP specification and the OAuth standard, linked below.

8. The question for buyers

If you are building a browser agent, those three checks take an afternoon. If you are designing enterprise delegation, the thing worth writing into your record spec is that the authorisation surface changes over time — it already runs in the most ordinary shopping flow, and internal workflows will cross stages far more often.

One question to leave with teams evaluating this: at your most expensive step, which moment was your agent’s tool list fetched at? If nobody can answer, that list was never treated as something that changes.

References

FAQ

Which stages do Shopify's WebMCP tools cover?+

The storefront set covers discovery and cart: search, browse, product details, add and update. Checkout registers its own set for reading and updating the checkout and placing the order.

When does the tool list change?+

It is swapped as a group when the shopper moves from the storefront into checkout, and the docs require a re-read after an error, cancellation or timeout, and after a navigation call returns null.

What happens if a client caches the list at session start?+

Nothing visible, until the flow crosses a stage boundary. At that point the cached list is no longer registered and calls fail.

Can an agent simply retry a failed call?+

Not safely. The docs define update_failed as possibly partly applied, so the order is: refresh the tool list, read the current state, compare it with the request, then decide.

Where does the agent's identity live?+

In the request signature (Web Bot Auth), not in tool arguments. Only registered keys count, and unsigned requests can be deprioritised or blocked.

Does MCP support a changing tool list?+

Yes. A server that declares the listChanged capability should send notifications/tools/list_changed when the list changes, and the client fetches the list again.

Is enterprise delegation solved by this?+

No. This is a consumer-side example where the platform defines permissions and the shopper brings the agent; there are no quantified results to cite.

What should the record hold?+

The action, its timestamp, the tool surface in force, the authorisation event in effect, and a pre-action state digest.