MCP payments are purchases an AI agent makes by calling tools on a payment server over the Model Context Protocol. The agent asks. The server decides. A single-use card comes back only when the request passes rules the agent cannot change. Authoryze is one such server, and this post walks through the protocol, the four tools, why enforcement lives on the server, how each client authenticates, and the threat model.
What MCP is
The Model Context Protocol is an open standard for connecting LLM applications to outside tools and data. Anthropic published it in late 2024 and it is now governed as an open project with its own specification, most recently the July 2026 revision. It uses JSON-RPC 2.0 messages between a host, which is the application running the model, and servers, which expose capabilities. A server can offer three things. Tools are functions the model can call. Resources are data the model can read. Prompts are templated workflows for the user. Every major assistant client speaks it, including Claude, ChatGPT, Claude Code, Codex, Cursor, and VS Code, which is why one server can serve all of them.
For a remote server the transport is Streamable HTTP, which is ordinary HTTPS requests to a single endpoint. The July 2026 revision made the protocol stateless at that layer, so a server behaves like any other HTTP API, and it deprecated the older HTTP with SSE transport with a year-long offramp. Authorization for HTTP transports is defined on top of OAuth 2.1. That combination is what makes MCP a good fit for payments. A payment server needs to know which user and which agent is calling, needs the client to show the user what a tool is about to do, and needs to run anywhere. MCP gives it all three without a custom SDK on the client side.
The four Authoryze tools and what each returns
Authoryze exposes four tools. Each is documented with its full input and response schema in the available tools section of the docs. Here is what each one does and what comes back.
request_purchase submits a purchase. The required inputs are the merchant domain, the amount, a description, a justification, and the merchant's two-letter country code. Optional inputs include the currency, a category hint, and an idempotency key. It returns one of four statuses. approved includes a request id and means a card can be drawn. denied includes the reason and sometimes a suggestion. pending_review means the amount is over the auto-approve threshold and a human has to decide, and it includes the request id to poll with. failed means something went wrong before any card existed, such as a missing payment method, and it is safe to retry with the same idempotency key. No card data is ever returned by this tool. The full schema is at request_purchase.
check_status takes a request id and reports where it stands. It is read-only and never issues, draws, or reveals a card. It returns approved with a boolean saying whether the card is still retrievable, denied with the reason, pending_review if the decision is still open, processing if an idempotent request is still being evaluated, or failed. The agent polls it while a request waits for you. Details are at check_status.
retrieve_card draws the single-use card for an approved request. The agent calls it exactly once, at the moment it is ready to enter card details. The response includes the card number, expiry month and year, CVC, last four digits, and the exact time the card expires. That is the only time the full card details are ever returned. A second call for the same request returns already_retrieved with nothing else. Other responses are not_approved if the request is still pending or was denied, not_funded if the account has no working funding source, and retrieve_failed if the issuer errored, with a flag saying whether a retry is safe. Details are at retrieve_card.
get_spending_summary takes no input and returns spend against every configured limit. For each of the daily, weekly, and monthly windows that you have set, it returns the amount spent, the limit, the remaining headroom, and when the window resets. It also returns lifetime spend against a total budget if one exists, and cross-agent totals if you have set aggregate caps across your account. Limits you have not configured are simply absent. Details are at get_spending_summary.
A typical sequence is summary first, then request, then poll if needed, then retrieve once. The docs include a system prompt snippet that tells an agent to follow that order, but as the next section explains, the order is advice and the rules are not.
Why rules are enforced server-side rather than in the agent
You could put a spending limit in an agent's system prompt. Plenty of people do. It works until it does not, and it stops working in three predictable ways. The model misreads the instruction. The model reasons its way around it because the purchase seems important. Or a web page, a tool result, or a document the agent reads contains text that tells it to ignore the limit, and it does. None of these are exotic. They are the normal failure modes of putting a language model in charge of its own guardrails.
Authoryze evaluates every request on the server before any card exists. The order is fixed. Per-agent rules run first, which cover the per-transaction limit, the daily, weekly, and monthly caps, the total budget, and the merchant allowlist and blocklist with subdomain matching. Then user-level aggregate caps run, which bound the combined spend of every agent on your account so that several agents cannot each stay under their own cap and collectively overshoot yours. Then the auto-approve threshold decides whether the request executes now or waits for you. Any rule violation is a hard denial. A denied request never produces a card, and a pending request produces one only after you approve it.
The agent cannot influence this evaluation by describing the request differently. The merchant domain is what the card network sees. The amount is what the card is scoped to. The justification is recorded for you to read in the approval email and the audit log, but it does not unlock anything. That is the difference between a rule in a prompt and a rule in the evaluator. The first is a request to the model. The second is a property of the system. How to set spending limits for an AI agent goes through each rule type and how to size them.
The same principle applies to duplicates. A retry after a timeout is one of the most common ways agents double-spend. Authoryze handles it with an idempotency key on the request. Pass the same key on a retry and the server returns the original result instead of evaluating a new purchase. How to stop an AI agent from making duplicate purchases covers the details.
OAuth versus API key per client
Authoryze supports two ways for a client to authenticate, and the right one depends on the client.
OAuth is the default for Claude, ChatGPT, Claude Code, Codex, and Cursor, and it is the only option for Claude and ChatGPT because those clients cannot send a static header. The flow is the one the MCP specification defines. The client hits the server without a token and gets a 401 with a pointer to the protected resource metadata. From there it discovers the authorization server, registers itself, and opens a browser. You sign in to Authoryze and pick which agent this connection controls. The client receives an access token bound to that agent, uses PKCE throughout, and refreshes the token when it expires. Every OAuth connection shows up under Connected clients in your Authoryze settings, where you can revoke it. If you connected to the wrong agent, revoke and reconnect. A connection cannot be moved to a different agent afterward.
API key is for headless agents, scripts, and frameworks that do not run an OAuth flow. Each agent has one key. It is shown once when the agent is created, stored as a hash on the server, and can be regenerated from the agent's page if it leaks. The client sends it as a bearer token in the Authorization header. Claude Code takes it as a header flag on the add command. Codex reads it from an environment variable. VS Code prompts for it on first use so it stays out of the config file. Custom frameworks pass it however they pass any bearer token.
| Client | OAuth | API key |
|---|---|---|
| Claude (claude.ai, Desktop, Cowork, mobile) | Yes, only option | No |
| ChatGPT (Developer mode) | Yes, only option | No |
| Claude Code | Yes | Yes |
| Codex | Yes | Yes |
| Cursor | Yes | Yes |
| VS Code | Yes | Yes |
| Custom or headless | Yes, if the framework supports it | Yes |
Both paths bind the connection to one agent, so the agent's rules apply no matter how it authenticated. Both work over Streamable HTTP. Clients that only speak stdio can bridge through mcp-remote, which runs the sign-in for them. Copyable configs for every client are in the MCP configuration docs. For the two chat clients, can Claude make purchases and can ChatGPT buy things walk through the connection screens.
The threat model
Giving a model a payment instrument creates a specific set of failure modes, and the security design of Authoryze is built around five of them. The security section of the docs lists them with the mitigation for each. In short, the threats are these.
- Credential exfiltration through prompt injection. A page or tool result tells the agent to send its payment credentials somewhere. The agent never holds your real card, and the only credential it ever sees is a single-use card scoped to one approved amount.
- A compromised or malfunctioning agent. A jailbroken or buggy agent tries to spend at full speed. Every request goes through the rules engine, and there is no stored card to drain.
- Rogue purchases at unintended merchants. The agent is convinced to buy somewhere it should not. Merchant allowlists and blocklists are enforced before a card exists.
- Replayed or duplicate card draws. Someone calls
retrieve_carda second time hoping for a second card or a second look. The draw is an atomic, at-most-once claim, and every later call returns already_retrieved. - Card data persisting in agent context and logs. The card is returned inline in one tool response and can end up in transcripts or third-party log sinks. Authoryze does not control retention there and says so. The exposure is bounded, not eliminated, because the card is single-use, scoped to one amount, short-lived, and drawable once.
The last point is the one to read carefully, because it is what Authoryze does not claim. The agent does see the single-use card. That is what lets it complete checkout at any merchant. What is isolated is your real funding source, which is the card you enrolled with Basis Theory or your Privacy.com key on the alternate rail. Authoryze also does not stop an injected instruction from causing a request_purchase call. The defense is what happens after the call. What we do not claim spells out each of these limits, and the threat model section has the full list.
Common questions
Is there an MCP payment standard?
Not as of the July 2026 specification. MCP defines tools, resources, prompts, transports, and authorization, and it has an extensions framework for things like asynchronous tasks and inline apps. It does not define a payment primitive. OpenAI and Stripe's Agentic Commerce Protocol is a separate merchant-side standard for accepting purchases from agents, and it is not part of MCP. An MCP payment server like Authoryze is an ordinary MCP server whose tools happen to issue cards. Authoryze vs Stripe Agentic Commerce covers how the two relate.
Can any MCP client use it?
Any client that speaks Streamable HTTP can connect, either through OAuth or with an agent's API key as a bearer token. Clients that only speak stdio can bridge through mcp-remote. Browser-based clients are limited to a short list of allowed origins, which covers claude.ai and chatgpt.com, and server-side clients are unaffected. The other clients section of the docs has a generic config and the Gemini CLI and Windsurf variants.
What does the agent actually see?
Before approval, the agent sees the status of its request and its remaining limits. After approval, it sees one single-use card, once, with the exact expiry returned alongside it. It never sees your enrolled funding card, your Privacy.com key, or the card you use to pay Authoryze's fee. Everything it sees is scoped to the one purchase you allowed.
Do I have to approve every purchase?
No. You set an auto-approve threshold per agent. Requests under it that pass every rule execute without a tap. Requests over it wait for your decision by email or in the dashboard, with no deadline. Set the threshold to zero if you want to approve everything.
Ready to give your agent a payment server it cannot talk its way around? Create an Authoryze account and connect any MCP client in about ten minutes.