SEP Draft: Payments — a payment-required flow for MCP tools
- Status: pre-submission draft (maintained in the Stipendex repository)
- Type: Standards Track (protocol extension)
- Author: Stipendex project
- Reference implementation: stipendex/stipendex —
@stipendex/server,@stipendex/wallet, showcase over the official TypeScript SDK
Draft to be adapted to the MCP Specification Enhancement Proposal template at submission time. Everything below already runs today via
experimentalcapabilities and_metafields — the proposal is a standardization of a working pattern, not a design on paper.
Motivation
MCP gave agents a uniform way to call tools, but no way to pay for them. The consequences are structural:
- Tool servers cannot charge per use. Monetization today means out-of-band API keys and subscriptions per service — exactly the friction MCP removed for invocation, reintroduced for payment.
- Hosts that want to support paid tools must invent proprietary flows.
- Payment logic gravitates toward the model — the one place it must never live, since model context is injectable by hostile content.
This SEP defines the minimal transport contract for "this call costs money":
a capability, an error code, and two _meta fields. It deliberately does
NOT define wallets, mandates, settlement rails, receipts semantics, or trust
registries — those are payment-protocol concerns (Stipendex is one
implementation; x402-style schemes fit the same envelope). MCP only needs to
standardize how a server says "payment required" and how a client attaches
proof of payment.
Specification
Capability
Servers supporting paid tools declare, during initialization:
{ "capabilities": { "payments": { "version": "1" } } }
(Until accepted: experimental["stipendex/payments"].)
Pricing hints
Tools MAY advertise costs in their _meta:
{
"name": "web_search",
"_meta": { "payments/pricing": { "model": "per_call", "estimate": "0.005 USD" } }
}
Hints are planning signals for hosts and models. They are not offers; the only binding price is the quote in the payment-required error.
The payment-required error
A call to a priced tool without payment MUST fail with JSON-RPC error code
-32402 (mnemonic: HTTP 402):
{
"code": -32402,
"message": "Payment required",
"data": { "quote": { /* payment-protocol-specific offer */ } }
}
data.quote is opaque to MCP: its schema belongs to the payment protocol
named inside it. Requirements on the quote at the MCP layer are minimal: it
MUST be self-describing (identify its protocol/version) and SHOULD be signed
by the tool server so hosts can verify provenance before paying.
Retrying with payment
The host retries the same call with proof of payment in request _meta:
{
"method": "tools/call",
"params": {
"name": "web_search",
"arguments": { "query": "…" },
"_meta": { "payments/payment": { /* protocol-specific proof */ } }
}
}
The server verifies the proof (per its payment protocol), executes the tool, and MAY attach a receipt to the response:
{ "content": [ … ], "_meta": { "payments/receipt": { /* signed receipt */ } } }
Host behavior (normative)
- Hosts SHOULD intercept
-32402below the model: the error and the payment flow are host mechanics. The model never sees quotes, proofs, or any operation with an arbitrary payee — this is the security boundary that makes prompt-injected "pay X" attacks structurally impossible. - A host without payment support MUST surface the error as an ordinary tool
failure (backward compatible:
-32402is just an error code to old hosts). - Payment decisions belong in deterministic host components (wallets) governed by user-controlled policy; this SEP does not prescribe their design but exists to make them possible uniformly.
Idempotency (normative)
Servers MUST treat a repeated proof as idempotent: same result, no second charge. Networks retry; money must not.
Backward compatibility
Fully additive. Servers without the capability never emit -32402; hosts
without support see a standard error. No changes to existing messages.
Security considerations
- The quote travels in an error to the HOST, not to the model. Reference threat model: Stipendex security docs (prompt injection, replay, double-charge, receiver substitution).
- Hosts MUST verify quote provenance per the payment protocol before paying (signature against a key trusted out-of-band — registry, pinning).
- Receipts give users a provable "paid → received" chain; servers SHOULD issue them.
Prior art
- HTTP 402 / x402 — the same pattern at the HTTP layer; this SEP is its MCP-native equivalent, protocol-agnostic on purpose.
- Stripe MPP, AP2 — settlement and delegation layers that can ride this transport unchanged.
- Stipendex — the reference end-to-end implementation: paywall SDK,
wallet kernel with signed mandates, working showcase over the official
MCP TypeScript SDK (
InMemoryTransporttest suite + stdio server).
Open questions for the working group
- Error code: is
-32402acceptable within the JSON-RPC reserved range conventions used by MCP, or should it move to an MCP-assigned range? - Should pricing hints be standardized further (currency handling, localization) or stay free-form?
- Elicitation integration: standardize a human-approval elicitation shape for above-threshold payments, or leave to hosts?
- Namespacing:
payments/*vs a vendor-neutral registry of_metakeys.