Stipendex

SEP Draft: Payments — a payment-required flow for MCP tools

Draft to be adapted to the MCP Specification Enhancement Proposal template at submission time. Everything below already runs today via experimental capabilities and _meta fields — 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:

  1. 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.
  2. Hosts that want to support paid tools must invent proprietary flows.
  3. 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)

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

Prior art

Open questions for the working group

  1. Error code: is -32402 acceptable within the JSON-RPC reserved range conventions used by MCP, or should it move to an MCP-assigned range?
  2. Should pricing hints be standardized further (currency handling, localization) or stay free-form?
  3. Elicitation integration: standardize a human-approval elicitation shape for above-threshold payments, or leave to hosts?
  4. Namespacing: payments/* vs a vendor-neutral registry of _meta keys.