DEV Community

Paul Crinigan
Paul Crinigan

Posted on

Taking Payments Inside an MCP Server

If you have shipped an MCP server, you have probably hit the moment where the tool is genuinely useful and there is still no clean way to charge for it. This is a walkthrough of an open source module that fills that gap, and of the correctness problems it handles that are easy to underestimate until real traffic arrives.

Why Checkout Is the Missing MCP Primitive

MCP gives an assistant tools: search this catalog, look up that account, generate this asset. What the protocol never defined was how to take money. The moment a company wants to sell a Pro license, a credit pack or a digital product through its MCP tool, it inherits a pile of undifferentiated work: cart state, price math, checkout sessions, a payment provider integration, webhook verification, idempotency, and a new protocol that agent platforms expect merchants to speak.

That protocol is ACP, the Agentic Commerce Protocol. It defines how an agent platform creates a checkout session with a merchant, updates it and completes payment. OpenAI publishes the specification and Stripe provides the matching payment rails, which is what turns a merchant from linkable into buyable inside an assistant.

The ACP Payment Module packages all of that as a drop-in library and reference server. It publishes as mcp-commerce, runs on Node.js 20 or newer, and is free under the Elastic License 2.0.

Three Ways to Run the Module

Adoption is a gradient rather than a cliff, which matters when you already have a server you like.

Standalone server. npm start, the Docker image or the bundled AWS Lambda handler gives you an MCP endpoint at /mcp, the ACP checkout endpoints under /checkout_sessions, a Stripe webhook receiver, and an ACP product feed. This is the fastest path when your MCP tool does not exist yet or lives somewhere else.

Embedded. Import the commerce core into the server you already run and call registerCommerceTools(). Your transport, your auth and your existing tools stay exactly as they are, and you gain search_products, add_to_cart, checkout and the rest alongside them.

Library only. Skip MCP entirely. createCommerce() and createCheckoutService() return plain objects with promise-returning methods, so a REST API, a Discord bot or a cron job can drive a cart just as well.

What Actually Happens During a Sale

Five stages, and seeing them end to end makes the architecture obvious.

Discovery. The agent calls search_products or get_product against your catalog, a single JSON file of products, variants and integer-cent prices that can live on disk, behind a URL, or in S3.

Cart building. add_to_cart returns a cart_id that the agent passes back on later calls, because the MCP transport is stateless by design. Totals are always computed server side rather than accepted from the client.

Checkout. The checkout tool converts the cart into a session and asks Stripe for a hosted payment URL, which the agent hands to the human. An agent platform can instead drive the ACP REST endpoints directly and complete with a delegated payment token. Both paths share one checkout service and one set of state rules.

Payment. Stripe confirms the charge, either through the hosted page with a webhook marking the session paid, or through an immediate token charge on the ACP complete call. Declines and 3DS outcomes map to typed errors.

Handoff. On completion the module POSTs a signed order payload to your webhook and then forgets the order. It is not a CRM and not an order database, which keeps your compliance surface small.

The Correctness Work You Do Not Want to Redo

A capable team can hand-roll all of this, since the building blocks are public. What takes the time is the long tail that only shows up under real traffic.

Idempotency. Every POST requires an idempotency key scoped to the caller and endpoint, with body-hash conflict detection and in-flight serialization. An agent that retries on a timeout is the normal case rather than the edge case, and without this it double-charges a card.

Price integrity. Unit prices are never read from client input or trusted from storage. They are recomputed from the live catalog on every read and every mutation, and completed or in-flight sessions are frozen so a live hosted payment is never re-priced underneath the shopper.

Money representation. Integer minor units end to end, which removes the floating point rounding class of bug entirely.

Closed defaults. The ACP endpoints reject every request until a bearer token is configured, optional HMAC request signatures fail closed, and late webhooks cannot resurrect a canceled session.

Each of those is a day of thought and a page of tests, and all of them are already written and documented.

Takeaway

If you run an MCP tool and have something digital to sell inside it, the interesting question is no longer whether an agent can complete a purchase. It is whether your checkout survives a retry storm without charging anyone twice. That is the part worth reading somebody else's implementation of before writing your own.

Top comments (0)