mpp-dev

Comprehensive development support for MPP (Machine Payments Protocol) — the open, IETF-draft-style HTTP payment standard co-authored by Stripe and Tempo, built on HTTP 402 with a Challenge / Credentia

SKILL.md

GitHub

MPP (Machine Payments Protocol) Development Guide

MPP is an open, IETF-draft-style HTTP payment standard, co-authored by Stripe and Tempo, that layers a Payment HTTP Authentication Scheme on top of 402 Payment Required so any HTTP client — especially an AI agent — can pay for a resource as part of the request itself, with no pre-provisioned account, API key, or billing relationship. Spec source: github.com/tempoxyz/mpp-specs (published at paymentauth.org). Docs/directory: mpp.dev. TypeScript SDK: mppx on npm (SDKs also exist for Python, Rust, Go, Ruby).

Quick Orientation

Role Description
Client / Payer Sends a request, receives a Challenge (402), fulfills it (signs a tx, mints a token), retries with a Credential
Server / Payee Returns a Challenge describing accepted methods, verifies the Credential, settles, returns the resource + a Receipt
Relay (optional) Third-party infra exposing only validate/broadcast — MPP defines no relay API shape; Tempo's first-party relay is api.tempo.xyz

Payment Flow:

  1. Client requests resource → server returns 402 with WWW-Authenticate: Payment ... (the Challenge)
  2. Client fulfills the Challenge (signs, pays, mints) → retries with Authorization: Payment ... (the Credential)
  3. Server verifies + settles → returns 200 + Payment-Receipt header (the Receipt)

Always capitalize Challenge, Credential, Receipt as protocol proper nouns in docs/UI copy.


Package Reference

npm i mppx              # TypeScript SDK — client, server, framework middlewares, CLI (package is `mppx`, NOT `mpp` or `@wevm/mppx`)
npm i -g mppx            # also installs the `mppx` CLI (account/session management, curl-like paid requests)
mppx/client       — payment-aware fetch wrapper, hooks, CLI internals
mppx/server       — Mppx.create({ secretKey, methods }), .charge(), .compose()
mppx/express | mppx/hono | mppx/nextjs | mppx/elysia   — framework middlewares + discovery()
mppx/tempo | mppx/evm | mppx/stripe | mppx/x402         — per-method exports

Other official SDKs: tempoxyz/pympp (Python), tempoxyz/mpp-rs (Rust). viem is a required peer dependency for TypeScript client/server code.


Implementation Paths

Choose your path and read the reference file — don't guess wire formats or signing schemes from memory, since this is a fast-moving, brand-new protocol; the reference files below are grounded in the actual spec/SDK source, not general payment-protocol intuition.

Task Reference File
Understand the wire protocol (Challenge/Credential/Receipt formats, status codes, error codes, versioning) [references/protocol-core.md]
Choose/implement a payment method (Tempo, EVM, Stripe, Lightning, ...) and its signing scheme [references/methods.md]
Build a client that pays for resources [references/client.md]
Build a server that charges for resources, incl. multi-method compose() [references/server.md]
Monetize an MCP server tool, or wire up service discovery [references/mcp-and-discovery.md]
Security review an integration, or write tests for one [references/security-testing.md]
Understand or implement MPP ↔ x402 interoperability [references/x402-interop.md]

Minimal Working Examples

Client — TypeScript

import { Mppx, tempo } from 'mppx/client'
import { privateKeyToAccount } from 'viem/accounts'

const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`)
const mppx = Mppx.create({ methods: [tempo({ account })] })

const res = await mppx.fetch('/api/photo')   // transparently handles 402 → sign → retry
const data = await res.json()

Server — Express

import express from 'express'
import { Mppx, tempo } from 'mppx/express'

const app = express()
const mppx = Mppx.create({ methods: [tempo()] })   // reads MPP_SECRET_KEY from env

app.get('/premium', mppx.charge({ amount: '1' }), (req, res) => {
  res.json({ data: 'paid content' })
})

MCP Server — Payment-Gated Tool

import { Mppx, tempo } from 'mppx/server'
import { Transport } from 'mppx'
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'

const mppx = Mppx.create({
  secretKey: process.env.MPP_SECRET_KEY!,
  methods: [tempo.charge({ /* ... */ })],
  transport: Transport.mcpSdk(),
})

const server = new McpServer({ name: 'paid-tools', version: '1.0.0' })

server.tool('get_premium_data', schema, async (params, extra) => {
  const result = await mppx.charge({ amount: '0.01' })(extra)
  if (result.status === 402) throw result.challenge   // -> McpError code -32042
  return result.withReceipt({ content: [{ type: 'text', text: 'premium data' }] })
})

Status Code Cheat Sheet

Meaning Status
Payment needed (no/expired/invalid credential) 402 — always, including retries after a failed proof
Non-payment auth failure 401
Payment succeeded, still not authorized by policy 403 — never 402
Payment succeeded 200 + Payment-Receipt header

Cache-Control: no-store is mandatory on every 402; Cache-Control: private is mandatory whenever a Payment-Receipt is present. Full error-code table in [references/protocol-core.md].


Common Issues & Solutions

Issue Solution
Missing secret key at server startup Set MPP_SECRET_KEY (≥32 bytes) or pass secretKey to Mppx.create()
Payment retried endlessly / server keeps issuing fresh Challenges Cap maxPaymentRetries on the client; check the server isn't rejecting valid Credentials
Credential accepted from a different Challenge (replay) Bug in method implementation — the signature must derive from challenge.id/challenge.realm; see the binding checklist in [references/security-testing.md]
Want to accept both crypto and cards on one route Use mppx.compose() with tempo.charge() + stripe.charge(), see [references/server.md]
Want one route to accept both native MPP and x402 clients Use evm.charge({ x402: { facilitator } }), see [references/x402-interop.md]
Stripe SPT creation failing / insecure SPT must be minted server-side with a Stripe secret key — never client-side
Using Tempo chain id 98865 Deprecated — use mainnet 4217 or testnet (Moderato) 42431
Terminology nitpicks in review (escrow, USDC vs USDC.e) Say "reserve"/"lock up" not "escrow"; say "USDC.e" for Tempo's bridged token, plain "USDC" elsewhere

Environment Variables

# Server
MPP_SECRET_KEY=...             # >=32 bytes; HMAC root for Challenge-id binding — treat like a JWT signing key
WALLET_ADDRESS=0x...           # or Tempo account — recipient of settled funds

# Client
PRIVATE_KEY=0x...              # EVM/Tempo signer (viem Account)

# Stripe method (server-side only)
STRIPE_SECRET_KEY=sk_...
STRIPE_NETWORK_ID=...          # for Shared Payment Token issuance

Reference Files

  • [references/protocol-core.md] — Wire protocol: Challenge/Credential/Receipt formats, HTTP status semantics, Challenge-id HMAC binding, error codes, versioning, intents (charge/session/subscription/zero-dollar)
  • [references/methods.md] — Per-rail signing schemes: Tempo (TIP-20, gasless, sessions), EVM (Permit2/EIP-3009/tx/hash), Stripe (Shared Payment Tokens), plus pointers for Lightning/Solana/Hedera/Stellar
  • [references/client.md] — mppx/client patterns: fetch wrapper, hooks, dual-protocol clients, agent spend limits (access keys), CLI
  • [references/server.md] — mppx/server patterns: Express/Hono/Next.js/Elysia middlewares, compose(), discovery, refunds
  • [references/mcp-and-discovery.md] — MCP transport binding (_meta fields, -32042/-32043), OpenAPI discovery, MPPScan/MPP Services directory, agent-permissions.json
  • [references/security-testing.md] — MPP_SECRET_KEY handling, Challenge-binding review checklist, common protocol mistakes, test conventions and a concrete test checklist
  • [references/x402-interop.md] — MPP vs. x402 positioning, dual-protocol evm.charge() pattern, when to use which