Search by

angeo / module-mcp-checkout

angeo

Guest cart and checkout MCP tools for Magento 2. Adds create_cart, add_to_cart, get_cart, get_shipping_methods, set_shipping_information and place_order to the Angeo MCP Server, letting AI agents (Claude and other MCP clients) complete a full discovery-to-order flow with hard server-side guardrails

Package info

github.com/angeo-dev/module-mcp-checkout

Homepage

Type:magento2-module

pkg:composer/angeo/module-mcp-checkout

Statistics

Installs: 9

Dependents: 1

Suggesters: 2

Stars: 2

Open Issues: 0

3.0.0 2026-11-08 11:19 UTC

This package is not auto-updated.

Last update: 2026-11-08 11:20:46 UTC


README

CI Packagist Version Downloads PHP Magento Mage-OS Directory License: MIT

Six MCP tools that let an AI agent go from a product search to a placed order in a real Magento store โ€” with every guardrail enforced server-side, at the order choke point.

angeo/module-mcp-checkout extends angeo/module-mcp-server 2.2.2+ with guest cart and checkout tools, so Claude or any MCP client can complete a full discovery โ†’ cart โ†’ checkout flow against Magento 2 / Adobe Commerce. No browser automation, no scraping, no headless driver that breaks on the next theme deploy.

Watch it happen: Claude places a real order in Magento 2 via MCP โ€” full tool sequence and a real order_number from a live store.

๐Ÿ“– Module page: angeo.dev/modules/mcp-checkout ยท End-user docs: angeo.dev/docs/mcp-checkout

โš ๏ธ This module lets an AI agent place real orders. It is disabled by default. Read the security model and set every limit deliberately before enabling it on a store with real payment methods.

For reviewers: AI safety summary

What it does. This module lets an AI agent, such as Claude or any MCP client, buy in a real Magento 2 store: search, cart, shipping and order. An order is a real-world action. It reserves stock, starts fulfilment and can start a payment.

The problem. An agent can misread a request, follow an instruction hidden in a web page, retry after a timeout, or order at a total the shopper never saw. A rule in a prompt does not stop this, because the model can ignore it. There is a second problem: Magento also accepts the agent's cart_id on its own anonymous REST API. Anyone who sees it can skip the agent tools completely.

How it stops unauthorized or irreversible actions. Every limit is enforced in PHP on the server, not in the prompt:

  1. Off by default. Checkout tools stay hidden until a merchant enables them, and also when the MCP endpoint does not require a token.
  2. Hard caps: order total, quantity per item, line items, shipping countries, payment methods and orders per hour.
  3. Confirmed total. place_order must carry the total and currency that were shown to the shopper. A difference of one cent refuses the order.
  4. Two choke points. A plugin on placeOrder() and a last check on submit() apply the limits on every order route: the MCP tool, Magento's REST API, GraphQL, and code that skips placeOrder().
  5. Safe retry. A retry after a timeout returns the first order, never a second one.
  6. The human makes the payment. With pay-by-link, money moves only when the person pays on the Stripe, Mollie or Adyen page. The agent never sees card data.
  7. Signed webhooks. Only a verified payment notice can mark an order as paid.

Example: the total changed after the shopper confirmed it.

โ†’ place_order  {"cart_id": "โ€ฆ", "confirmed_total": 42.50, "confirmed_currency": "EUR"}
โ† The order was NOT placed. The cart total is 44.50 EUR, but the confirmed total was 42.50 EUR.
  Show the user the current total 44.50 EUR, ask them to confirm it again, and only then
  call place_order with confirmed_total 44.50 and confirmed_currency "EUR".

Demos, tests and documentation

  • Demo: Claude places a real order in Magento 2 via MCP, the normal flow on a live store.
  • 174 unit tests. Each guardrail is also removed in turn by Test/RedTeam/mutation-check.py, and the tests caught all 54 removals.
  • The rate limit was raced with 40 parallel processes on MariaDB. It never admitted more orders than the limit.
  • Test/RedTeam/run-attacks.php runs 11 attacks on a staging store, such as a leaked cart_id, a wrong total or a forged webhook. On demo.angeo.dev, 8 Nov 2026, every attack that could run there was stopped, and a legal order went through once, with no second order on retry. The webhook attack needs pay-by-link, which is off on the demo, so the unit tests cover it. Full table.
  • Safety case: who we defend against, every safeguard, attacks and evidence, test results and how to reproduce them.

Known weaknesses

  • The server cannot prove that a person said yes, because it cannot see the conversation. Only pay-by-link adds a real human step, and that step covers the payment, not the creation of the order.
  • Only the total and currency are bound. Another product at the same price, or another address in an allowed country at the same shipping cost, still passes.
  • The order exists before payment. Cancelling an unpaid order is a store process.
  • Someone who orders within the limits can use up the hourly cap. New agent orders then stop until the hour passes.

All 11 known limits.

Areas for improvement: bind the items and address to the confirmation, not only the total; compare the amount paid with the order total in webhooks; an optional approval link, so that the order is created only after the person clicks it.

Contents

Tools

Tool Purpose
create_cart Create an empty guest cart, returns cart_id
add_to_cart Add a product by SKU โ€” simple, virtual, and configurable (variant SKU, or parent SKU + options); enforces qty / item-count / order-total caps
get_cart Line items and totals
get_shipping_methods Estimate shipping for a destination; enforces country whitelist
set_shipping_information Address + email + shipping method; returns allowed payment methods and final totals
place_order Places the order only if confirmed_total / confirmed_currency match the cart; pre-flights every cap for a clear agent-facing message, then the guardrail plugin re-verifies and reserves the rate-limit slot; tags and audit-logs the order; returns a PSP payment_url when pay-by-link is in use, and replays the original order if the call is retried

Canonical agent flow:

search_products โ†’ add_to_cart โ†’ get_shipping_methods
  โ†’ set_shipping_information โ†’ user confirms total
  โ†’ place_order(cart_id, confirmed_total, confirmed_currency)

confirmed_total and confirmed_currency are required (3.0.0). The agent passes the grand total it showed the user. If the cart total differs โ€” even by one cent, or because a surcharge was added when the payment method was applied โ€” the order is not placed, and the error returns the current total for the agent to confirm again.

The SKU returned by the server's search_products / get_product feeds straight into add_to_cart.

Tool annotations

Every tool declares MCP ToolAnnotations, including a human-readable title, and they match runtime behaviour โ€” which is what a Connectors Directory reviewer verifies:

Tool title readOnly destructive idempotent openWorld
create_cart Create cart false false false false
add_to_cart Add item to cart false false false false
get_cart Get cart contents true false true false
get_shipping_methods Get shipping methods true false true false
set_shipping_information Set shipping information false false true false
place_order Place order false true false true

place_order is the only destructive tool, and the only one with openWorldHint: true โ€” placing the order can call out to a payment provider to mint the pay-by-link. Its idempotentHint is deliberately false: a retry against the same cart is safe and returns the original order, but the same arguments against a new cart are a second, intended order, and the hint is about arguments rather than carts.

Per the MCP spec, annotations are hints for clients, not security boundaries. The security model below is what actually enforces behaviour. The matrix above is pinned by a unit test, so it cannot drift from the code without the build failing.

How it plugs into module-mcp-server

Tools implement Angeo\McpServer\Api\ToolInterface (verified against v1.0.0):

  • Availability gating โ€” isAvailable() returns the admin enable flag, so when checkout is disabled the tools vanish from tools/list entirely. They never appear to agents, rather than failing at call time.
  • Error model โ€” business errors are thrown as \InvalidArgumentException, which the server maps to an isError tool result (structured, agent-readable) rather than a protocol error, exactly as the server contract requires. Internal errors are logged in full and returned as a generic actionable message; no paths or SQL ever reach the agent.
  • Registration โ€” etc/di.xml injects all six tools into Angeo\McpServer\Model\Tool\ToolRegistry via its tools array argument.

No changes to module-mcp-server are required.

Security model

Disabled by default. A limit written into a system prompt is advisory; a limit enforced in PHP before the order is placed is not.

Where enforcement lives

Since 1.1.0 the guardrails are applied in Plugin\AgentOrderGuardrails, on Magento\Quote\Model\CartManagementInterface::placeOrder(), which the MCP tool, the guest-cart REST API and GraphQL all reach. Since 3.0.0 Plugin\AgentQuoteSubmitGuard checks again on submit(), before anything is changed. It refuses an agent cart that skipped the plugin, which is how code such as PayPal Express calling submit() directly is covered. The checks inside the MCP tools remain as a fast pre-flight so the agent gets a specific, recoverable message, but they are not the boundary.

The full threat model, attack table, test evidence and limits are in docs/SAFETY-CASE.md.

This matters more than it sounds. The masked cart_id handed to an agent is also a valid credential for Magento's own anonymous guest-cart REST endpoints:

POST /rest/V1/guest-carts/{cartId}/items
POST /rest/V1/guest-carts/{cartId}/shipping-information
PUT  /rest/V1/guest-carts/{cartId}/order

In 1.0.x every cap lived in the tool layer, so anything holding a cart_id โ€” a compromised agent, a leaked transcript, a verbose log โ€” could add 500 units, ship anywhere, pick any enabled payment method and place the order outside every limit and outside the audit log. Carts created via create_cart are now flagged in angeo_mcp_agent_quote, and the plugin applies the full guardrail set to them wherever the order request originates. Carts that were not created by an agent are untouched.

Treat cart_id as a bearer token. The caps are what bound the damage if it leaks; they are no longer avoidable by changing entry point.

The guardrails

  • Guest checkout only โ€” enforced, not assumed. Carts carrying a customer_id are rejected, as are carts belonging to another store view and carts already ordered. The blast radius of a compromised agent session is one guest cart.
  • Confirmed total (3.0.0) โ€” place_order must carry the grand total and currency the user was shown. Checked against the cart in the tool, and again at the choke point against the final quote after the payment method is applied, so no order is placed at a total the user did not see. The server cannot see the conversation, so this proves the agent read the final totals and that nothing changed since โ€” not that a person said yes. destructiveHint covers the second part on the client side. Orders from routes other than the tool register no confirmation and remain bound by the caps below.
  • Payment method whitelist, default checkmo (offline). Online methods must be explicitly allowed. Verified at place_order and again at the choke point.
  • Order total cap (default 100, base currency) โ€” enforced at add_to_cart with rollback of the offending item, and re-verified against final totals including shipping and tax when the order is placed.
  • Qty / cart-size caps (default 5 per item, 10 line items). The quantity cap applies to the resulting quantity, so repeated additions of the same SKU cannot stack past it, and both are re-verified at order time.
  • Country whitelist for shipping (default NL), checked at estimation, at address save, and again at order time. Independent of Magento's own allowed-countries setting โ€” both must permit the destination.
  • Rate limits โ€” global orders/hour (default 10) and per-IP orders/hour (default 10), counted per store. Slots are reserved before the order and released if it fails, so concurrent calls cannot overshoot the cap and a failing payment method cannot drain the budget. Backed by a DB table (angeo_mcp_order_log), open_basedir-safe, doubling as an audit trail. Important: the server already rate-limits requests per client key (REMOTE_ADDR|sha256(token)), and cloud MCP clients like Claude connect from a small pool of provider egress IPs โ€” so the global cap is the real safety valve; the per-IP cap is a best-effort secondary brake, and behind a proxy it is only as trustworthy as your forwarded-header configuration.
  • Safe retries โ€” MCP clients retry tool calls on transport errors. The quote is deactivated once ordered and CartResolver refuses an inactive cart for mutation, so a retry cannot produce a second order; the audit row lets place_order resolve and return the original order with already_placed: true instead of an unhelpful error.
  • Order tagging โ€” every agent order gets a status-history comment identifying it as agent-placed. The client IP is kept in angeo_mcp_order_log under a seven-day retention rather than in the order history, which is retained indefinitely.
  • Demo hygiene cron โ€” off by default. When enabled it cancels unpaid agent orders older than 24h. Offline methods such as checkmo leave legitimate orders in exactly that state, so enable it on demo and staging only.

Known limits of the model

  • A cart becomes an agent cart at create_cart or place_order. Until then, a cart created by other means is a normal storefront cart.
  • The confirmed total binds the total and currency, not the items or address. The server cannot prove a person said yes. See all known limits.
  • The per-IP cap is advisory in any deployment behind a CDN or load balancer. Rely on the global cap.
  • module-mcp-server owns bearer-token validation and per-client-key rate limiting. This module's fail-safe depends on angeo_mcp/general/require_token.

Found a vulnerability? See SECURITY.md โ€” please do not open a public issue.

Requirements

  • Magento Open Source / Adobe Commerce 2.4.6+
  • PHP 8.1 โ€“ 8.5 (matches module-mcp-server)
  • angeo/module-mcp-server ^2.2.2
  • No PSP SDKs: the pay-by-link adapters call Stripe, Mollie and Adyen REST APIs directly

Installation

composer require angeo/module-mcp-checkout
bin/magento module:enable Angeo_McpCheckout
bin/magento setup:upgrade
bin/magento setup:di:compile
bin/magento cache:flush

Configure and enable at Stores โ†’ Configuration โ†’ Angeo โ†’ MCP Checkout (AI Agents). Review every limit before enabling on a store with real payment methods.

Store prerequisites (easy to miss)

  1. angeo/module-mcp-server enabled (angeo_mcp/general/enabled = 1) and reachable over HTTPS.
  2. Guest checkout on โ€” Stores โ†’ Configuration โ†’ Sales โ†’ Checkout โ†’ Allow Guest Checkout = Yes. This module is guest-only.
  3. A whitelisted payment method enabled โ€” Sales โ†’ Payment Methods โ†’ Check / Money Order (checkmo) for demos, or Pay by secure payment link (angeo_mcp_paylink) for real money. If none is enabled, set_shipping_information reports "no payment methods".
  4. A shipping method + carrier enabled (e.g. Flat Rate) that serves your allowed countries.
  5. Cron running, for the cleanup job.
  6. Salable products in a supported type โ€” simple, virtual and configurable are supported. Grouped products return actionable guidance to add the components individually; bundle and downloadable are out of scope for this release.

Pay by Link (Stripe / Mollie / Adyen)

The structural problem with agentic payments is simple: no card number may ever appear in a chat transcript โ€” not for PCI DSS, not for GDPR, not for basic sanity. Pay by Link splits the flow:

  1. The agent places the order with the angeo_mcp_paylink payment method; the order lands in pending payment.
  2. The module calls the configured PSP and gets back an opaque, hosted payment link, returned by place_order as payment_url with payment_link_expires_at.
  3. The agent shows the link to the customer, who pays on the PSP's own page. Magento, the MCP transport and the conversation never see payment credentials.
  4. The PSP webhook confirms payment and the module flips the order to processing โ€” signature-verified, idempotent, and gated to paylink orders sitting in pending state.
  5. If the link is never paid the order stays pending; enable the demo cleanup cron on staging if you want those cancelled automatically, and align its age with the link validity.

Link generation is fail-soft: a PSP outage never fails an order that has already been placed. The agent is told the store will follow up, and the failure is recorded in the order comments.

Setup:

  1. Enable Pay by secure payment link (Sales โ†’ Payment Methods) and add angeo_mcp_paylink to Allowed Payment Method Codes.
  2. In Pay by Link config, pick a provider and paste its API credentials โ€” stored encrypted.
  3. Register the webhook with the PSP:
    • Stripe โ€” endpoint https://<store>/angeo-mcp-checkout/webhook/stripe, event checkout.session.completed; paste the signing secret into config.
    • Mollie โ€” nothing to register; the webhook URL is attached to every link automatically.
    • Adyen โ€” standard webhook to https://<store>/angeo-mcp-checkout/webhook/adyen with an HMAC key; paste the key (hex) into config.
  4. Test with the PSP's test credentials first. Adyen live mode additionally needs the live URL prefix.

Scope: the webhook marks orders paid. Invoices, captures and refunds are deliberately out of scope โ€” pair with the PSP's official Magento integration if you need the full payment lifecycle. Zero-decimal currencies (JPY, KRW, โ€ฆ) are rejected fail-closed rather than mischarged 100ร—.

Extensibility: add a PSP from any module by implementing Angeo\McpCheckout\Api\PaymentLinkProviderInterface and registering the adapter in ProviderPool via di.xml โ€” the same pattern as the MCP tool registry.

GDPR notes

  • What the PSP receives: amount, currency, and the order increment id as reference. The customer email is forwarded only on explicit merchant opt-in, for the PSP's receipt flow (Mollie payment links take no email at all). Name, address and cart contents are never sent.
  • What the chat sees: the opaque payment URL and the order number. No payment credentials, ever. The tool description instructs the agent to refuse card data in conversation.
  • Roles: the merchant is the controller; Stripe/Mollie/Adyen act per their own DPAs โ€” link those from your privacy policy.
  • Audit log: angeo_mcp_order_log stores the client IP for rate limiting and fraud prevention (legitimate interest, Art. 6(1)(f)); rows are pruned after 7 days by cron, independently of the demo cleanup switch. For cloud-hosted agents the IP identifies the AI provider's egress pool, not the person.
  • Order status history carries no client IP โ€” deliberately, since order comments are retained for the life of the order.

This is engineering documentation, not legal advice.

Connecting Claude

module-mcp-server exposes POST /mcp (Streamable HTTP, single request/response โ€” no SSE, which keeps Varnish and shared-hosting compatibility trivial).

The checkout tools need an authenticated endpoint: with require_token = No they stay hidden and only the read-only catalog tools work.

Set require_token = Yes. Create a Magento Integration (System โ†’ Extensions โ†’ Integrations) granting only the Angeo_McpServer::agent_access resource, activate it, and use its Access Token as a Bearer token. Revoking the integration instantly cuts agent access.

Note: the claude.ai custom-connector UI negotiates OAuth, not static bearer tokens. For static-token testing use the MCP Inspector or Claude Code:

claude mcp add --transport http my-store https://your-store.example/mcp \
  -H "Authorization: Bearer <token>"

For current connector steps see https://support.claude.com.

Deployment note: the server sets Cache-Control: no-store on /mcp, but add a Varnish VCL bypass for the route โ€” full-page cache in front of /mcp is the number one Magento-specific pitfall.

Example prompt:

"I need a gift โ€” a ceramic vase under โ‚ฌ60, neutral colours, shipped to the Netherlands. Find options, compare them, and place the order to [address]."

Design decisions

  • Magento service contracts, not HTTP self-calls โ€” GuestCartManagementInterface, GuestCartItemRepositoryInterface, GuestShipmentEstimationInterface, GuestShippingInformationManagementInterface, GuestPaymentInformationManagementInterface. Same code paths as core REST, no extra hop, compatible with checkout extensions built on those contracts.
  • Compact tool outputs with a next_step hint on each โ€” verbose payloads measurably degrade agent decisions and waste context.
  • Fail-closed on missing data โ€” an empty payment whitelist, a cart that cannot be loaded, a missing address or a breached cap blocks the order. Explicit settings can relax a limit: an empty country list allows all countries, and 0 turns off the total cap or a rate cap.

FAQ

Can an AI agent really place a live order? Yes โ€” that is what the six tools do, and there is a recorded demo with a real order number. Which is precisely why every guardrail is enforced in Magento rather than requested in a prompt, and why the module ships disabled.

What stops an agent buying a thousand units? Server-side caps: order total, quantity per item, line-item count, and orders per hour (global and per IP). All of them are re-verified inside CartManagementInterface::placeOrder() and again inside submit(), not only when the item is added โ€” so they hold even if the order request never goes through the place_order tool. The quantity cap applies to the resulting line quantity, so repeated small additions cannot stack past it.

Is this the same as ACP Instant Checkout? No. ACP Instant Checkout was OpenAI's purchase flow inside ChatGPT. OpenAI stepped back from in-chat checkout in March 2026, and module-openai-instant-checkout is no longer developed. MCP checkout is an open tool surface any MCP client can drive, with no approval process. Different clients, different gatekeeping, both worth having.

Why guest checkout only? So there are no stored credentials for an agent session to compromise, and no customer account to take over. It also keeps the module out of any flow that touches saved payment data.

Can I use it with online payment methods? Technically yes โ€” the whitelist is configurable. Do it only after you have exercised the flow on staging and reviewed the caps, and review it against your own PCI scope. The default is offline (checkmo) for a reason.

Does it support configurable or bundle products? Configurable, yes โ€” either by the exact variant SKU or by the parent SKU plus an options map; an incomplete selection comes back with every required attribute and its available values, so the agent can correct itself in one more call. Grouped products return the component SKUs to add individually. Bundle and downloadable are still out of scope.

How does the agent pay without handling card data? It doesn't handle card data at all. With pay-by-link enabled, place_order returns a payment_url to a page hosted by Stripe, Mollie or Adyen, and the customer pays there. Card tokenisation needs a PCI-compliant browser form, so the agent flow stays outside PCI scope entirely.

What happens if the agent retries place_order? It gets the order that was already placed, with already_placed: true โ€” not a duplicate and not an error. The cart is deactivated at order time, so a second order from the same cart is impossible regardless of how the retry arrives.

Do I need this to appear in ChatGPT or Claude? No. Discovery and transaction are separate problems. This closes the transaction loop once agents can already find you โ€” start with an AEO audit to see whether they can.

The Angeo agentic stack

MIT-licensed, no paid tier, no licence key.

Layer Module
Crawler access module-robots-txt-aeo
Discovery files module-llms-txt
Structured data module-rich-data
ChatGPT Shopping feed module-openai-product-feed
Live agent access module-mcp-server
Agent checkout module-mcp-checkout โ† you are here
UCP profile module-ucp ยท module-ucp-catalog
Measurement module-aeo-audit

All modules: https://angeo.dev/modules/

Support

Issues and feature requests: GitHub Issues. Security reports: SECURITY.md โ€” not a public issue. Implementation and audits: info@angeo.dev

License

MIT โ€” see LICENSE.