mpp-x-2

内容来源:clawhub · 原始地址 · 查看安装指南

原始内容


name: mpp description: "Build with MPP (Machine Payments Protocol) - the open protocol for machine-to-machine payments over HTTP 402. Use when developing paid APIs, payment-gated content, AI agent payment flows, MCP tool payments, pay-per-token streaming, or any service using HTTP 402 Payment Required. Covers the mppx TypeScript SDK with Hono/Express/Next.js/Elysia middleware, pympp Python SDK, and mpp Rust SDK. Supports Tempo stablecoins, Stripe cards, Lightning Bitcoin, and custom payment methods. Includes charge (one-time) and session (streaming pay-as-you-go) intents. Make sure to use this skill whenever the user mentions mpp, mppx, machine payments, HTTP 402 payments, Tempo payments, payment channels, pay-per-token, paid API endpoints, or payment-gated services." metadata: version: "0.9.0" upstream: "mppx@0.8.15, pympp@0.9.1, mpp@0.11.0, @buildonspark/lightning-mpp-sdk@0.1.4, @stellar/mpp@0.7.1, @solana/mpp@0.7.0, @redotpay/mpp@0.1.2, @defuse-protocol/nearintents-mpp-sdk@0.1.2, mpp-card@0.1.8" openclaw: homepage: https://github.com/tenequm/skills/tree/main/skills/mpp emoji: "💸" primaryEnv: MPP_SECRET_KEY envVars: - name: MNEMONIC required: false description: BIP-39 mnemonic for client wallet (testnet/regtest only). - name: MPP_SECRET_KEY required: false description: Server-side MPP signing secret (HMAC-binds challenge IDs). - name: MPP_REALM required: false description: Stable realm identifier for mppscan attribution. - name: MPPX_RPC_URL required: false description: Tempo RPC endpoint override for the mppx CLI. - name: RPC_URL required: false description: Generic RPC endpoint override (mppx CLI fallback). - name: MPPX_ACCOUNT required: false description: Default account name for the mppx CLI. - name: MPPX_CONFIG required: false description: Path to the mppx CLI config file. - name: STRIPE_SECRET_KEY required: false description: Stripe API secret key for the Stripe payment method. - name: STRIPE_API_KEY required: false description: Alias for the Stripe API secret key in some examples. - name: STRIPE_PROFILE_ID required: false description: Stripe crypto profile ID for on-chain deposit flows. - name: STRIPE_NETWORK_ID required: false description: Stripe network identifier for SPT settlement. - name: PRIVY_APP_ID required: false description: Privy App ID for Privy server-wallet signers. - name: PRIVY_APP_SECRET required: false description: Privy App Secret for Privy server-wallet signers. - name: OPENAI_API_KEY required: false description: Upstream OpenAI key when gating OpenAI via the payments proxy. - name: ANTHROPIC_API_KEY required: false description: Upstream Anthropic key when gating Anthropic via the payments proxy.


MPP - Machine Payments Protocol

MPP is an open protocol (co-authored by Tempo and Stripe) that standardizes HTTP 402 Payment Required for machine-to-machine payments. Clients pay in the same HTTP request - no accounts, API keys, or checkout flows needed.

The core protocol spec is submitted to the IETF as the Payment HTTP Authentication Scheme.

Tempo token addresses

Public Tempo token addresses referenced throughout this skill. Use the placeholder names in code; full addresses are in the Tempo documentation.

Token Network Placeholder
USDC.e (mainnet) mainnet <USDC_TEMPO_MAINNET>
pathUSD (testnet) testnet <PATHUSD_TESTNET>

When to Use

  • Building a paid API that charges per request
  • Adding a paywall to endpoints or content
  • Enabling AI agents to pay for services autonomously
  • MCP tool calls that require payment
  • Pay-per-token streaming (LLM inference, content generation)
  • Session-based metered billing (pay-as-you-go)
  • Accepting stablecoins (Tempo), cards (Stripe), or Bitcoin (Lightning) for API access
  • Building a payments proxy to gate existing APIs (OpenAI, Anthropic, etc.)

Core Architecture

Three primitives power every MPP payment:

  1. Challenge - server-issued payment requirement (in WWW-Authenticate: Payment header)
  2. Credential - client-submitted payment proof (in Authorization: Payment header)
  3. Receipt - server confirmation of successful payment (in Payment-Receipt header)
Client                                          Server
  │  (1) GET /resource                            │
  ├──────────────────────────────────────────────>│
  │         (2) 402 + WWW-Authenticate: Payment   │
  │<──────────────────────────────────────────────┤
  │  (3) Sign payment proof                       │
  │  (4) GET /resource + Authorization: Payment   │
  ├──────────────────────────────────────────────>│
  │         (5) Verify + settle                   │
  │         (6) 200 OK + Payment-Receipt          │
  │<──────────────────────────────────────────────┤

Payment Methods & Intents

MPP is payment-method agnostic. Each method defines its own settlement rail:

Method Rail SDK Package Status
Tempo TIP-20 stablecoins on Tempo chain mppx (built-in) Production
Stripe Cards/wallets (SPT) + on-chain crypto deposit mppx (built-in) Production
EVM EIP-3009 stablecoin authorizations (x402-exact compatible) mppx (built-in) Production
Lightning Bitcoin over Lightning Network @buildonspark/lightning-mpp-sdk Production
Stellar SEP-41 tokens on Stellar, charge + channel @stellar/mpp Production
Solana Solana-native charge + session (SOL, SPL, Token-2022) @solana/mpp Production
Monad Monad charge (ERC-3009, settlement modes) @monad-crypto/mpp Production
NEAR Intents Cross-chain charge via 1Click deposit addresses @defuse-protocol/nearintents-mpp-sdk Production
RedotPay RedotPay balance (rdt) or stablecoin proof, charge only @redotpay/mpp Production
Card Encrypted network tokens (Visa) mpp-card Production
Custom Any rail Method.from() + Method.toClient/toServer Extensible

Stellar's channel intent is implemented in @stellar/mpp while its wire spec is still being drafted - treat the details as subject to change. NEAR Intents settlement is not trustless: it routes through a settlement backend, advertised as methodDetails.settlementBackend: "near-intents" so agents can apply per-method risk policy.

Intent Pattern Best For
charge One-time payment per request API calls, content access, fixed-price endpoints
session Pay-as-you-go over payment channels LLM streaming, metered billing, high-frequency APIs
subscription Recurring access via an authorized key (Tempo) Plans/tiers where access is separated from per-request billing

Quick Start: Server (TypeScript)

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

const mppx = Mppx.create({
  methods: [tempo({
    currency: '<PATHUSD_TESTNET>', // pathUSD testnet
    recipient: '0xYourAddress',
  })],
})

export async function handler(request: Request) {
  const result = await mppx.charge({ amount: '0.01' })(request)
  if (result.status === 402) return result.challenge
  return result.withReceipt(Response.json({ data: '...' }))
}

Install: npm install mppx viem (mppx 0.8.15 requires viem >= 2.54.0).

Validate the finished server end-to-end with npx mppx validate http://localhost:3000.

Quick Start: Client (TypeScript)

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

// Polyfills globalThis.fetch to handle 402 automatically
Mppx.create({
  methods: [tempo({ account: privateKeyToAccount('0x...') })],
})

const res = await fetch('https://api.example.com/paid')
// Payment happens transparently when server returns 402

To avoid mutating the global, use Fetch.from() for a standalone payment-aware fetch, Fetch.polyfill() / Fetch.restore() to install and remove the global wrapper explicitly, and Mppx.restore() to undo an instance's polyfill. In browsers, mppx 0.6.0 changed the default: polyfilled fetch only sends Accept-Payment to same-origin endpoints, so cross-origin paid APIs need acceptPaymentPolicy ('always' / { origins: [...] }). Client fetch retries incremental challenges up to maxPaymentRetries (default 3). See references/typescript-sdk.md.

Quick Start: Server (Python)

from fastapi import FastAPI
from mpp import Credential, Receipt
from mpp.server import Mpp
from mpp.methods.tempo import tempo, ChargeIntent

app = FastAPI()
server = Mpp.create(method=tempo(
    currency="<PATHUSD_TESTNET>",
    recipient="0xYourAddress", intents={"charge": ChargeIntent()},
))

@app.get("/resource")
@server.pay(amount="0.50")
async def get_resource(request, credential: Credential, receipt: Receipt):
    return {"data": "paid content", "payer": credential.source}

Install: pip install "pympp[tempo]". See references/python-sdk.md for full patterns.

Quick Start: Server (Rust)

Install: cargo add mpp --features tempo,server. See references/rust-sdk.md for full patterns.

Framework Middleware (TypeScript)

Each framework has its own import (mppx/nextjs, mppx/hono, mppx/express, mppx/elysia):

// Next.js
import { Mppx, tempo } from 'mppx/nextjs'
const mppx = Mppx.create({ methods: [tempo({ currency: '<PATHUSD_TESTNET>', recipient: '0x...' })] })
export const GET = mppx.charge({ amount: '0.1' })(() => Response.json({ data: '...' }))

// Hono
import { Mppx, tempo } from 'mppx/hono'
app.get('/resource', mppx.charge({ amount: '0.1' }), (c) => c.json({ data: '...' }))

See references/typescript-sdk.md for Express and Elysia examples.

Sessions: Pay-as-You-Go Streaming

Sessions open a payment channel once, then use off-chain vouchers for each request - no blockchain transaction per request. Sub-100ms latency, near-zero per-request fees.

Sessions v2 (default since mppx 0.7.0): tempo.session() is the TIP-1034 precompile channel flow. The earlier escrow-contract implementation is Sessions v1, still available as the now-deprecated tempo.sessionLegacy. A v2-expecting client rejects a v1 session and falls back to the charge path, so a server left on old mppx can silently deny newer clients their working path - keep client and server on matching flows. Two client APIs: tempo.session({ account, maxDeposit }) registers the method with Mppx.create() (transparent 402 handling via fetch), while tempo.session.manager({ account, maxDeposit }) returns a managed client for direct lifecycle control (.sse(), .close()).

// Server - session endpoint with automatic settlement
const mppx = Mppx.create({
  methods: [tempo.session({
    currency: '<PATHUSD_TESTNET>', recipient: '0x...',
    store: Store.redis(redis),
    settlementSchedule: { amount: '1.00', intervalMs: 300_000 },
    bootstrap: true, // let returning clients recover their channel on this route
  })],
})
const result = await mppx.session({ amount: '0.001', unitType: 'token' })(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))

Settlement is not automatic unless you configure it. A session server verifies and stores vouchers, but nothing converts them to on-chain funds without either a settlementSchedule or your own sweep via tempo.settle() / tempo.settleBatch(). Without one, revenue accrues as unredeemed vouchers and channels stay open holding payer deposits. See references/sessions.md.

// Server - SSE streaming with per-word billing
export const GET = mppx.session({ amount: '0.001', unitType: 'word' })(
  async () => async function* (stream) {
    for (const word of ['hello', 'world']) {
      await stream.charge()
      yield word
    }
  }
)

// Client - session with auto-managed channel
Mppx.create({ methods: [tempo({ account, maxDeposit: '1' })] })
const res = await fetch('http://localhost:3000/api/resource')
// 1st request: opens channel on-chain; 2nd+: off-chain vouchers

WebSocket transport: Sessions also support WebSocket streaming via Ws.serve(), using typed messages (mpp: 'authorization', mpp: 'message', mpp: 'payment-close-request', etc.) for payment negotiation alongside data delivery. See references/sessions.md for the full lifecycle, settlement, SSE patterns, and WebSocket integration.

Multi-Method Support

Accept Tempo stablecoins, Stripe cards, and Lightning Bitcoin on a single endpoint:

const mppx = Mppx.create({
  methods: [
    tempo({ currency: '<PATHUSD_TESTNET>', recipient: '0x...' }),
    stripe.charge({ client: new Stripe(key), networkId: 'profile_...', paymentMethodTypes: ['card'] }),
    spark.charge({ mnemonic: process.env.MNEMONIC! }),
  ],
})

Use Mppx.compose() to present multiple methods in a single 402 response with per-route pricing. Apply the same branch at the challenge site and the verification site, or the 402 advertises fewer options than the server accepts. See references/typescript-sdk.md.

Payment Links (HTML)

Setting html: true on a payment method config renders a browser-friendly payment page when a 402 endpoint is visited in a browser, with theming, multi-method compose tabs, and Solana wallet support. Service workers handle credential submission, then the page reloads with the paid response.

Customize via mppx/html exports (Config, Text, Theme). Html.init(methodName) returns the page context (challenge, config, error, formattedAmount, label, root, submit, text, theme, vars) for building a custom method's payment link.

Zero-Dollar Auth (Proof Credentials)

Authenticate agent identity without payment. Clients sign an EIP-712 proof over the challenge ID instead of creating a transaction - no gas burned, no funds transferred.

// Server - zero-dollar charge, with a store for replay protection
const mppx = Mppx.create({
  methods: [tempo.charge({ currency: '<PATHUSD_TESTNET>', recipient: '0x...', store })],
})
const result = await mppx.charge({ amount: '0' })(request)

As of mppx 0.8.0, zero-amount proof credentials are bound to the payer wallet: the EIP-712 Proof typed-data includes an account field and the domain version is 3 (exposed as tempo.Proof), so a proof signed for one account no longer verifies against another.

Use cases: identity verification, long-running job polling, paid unlock with free subsequent access, multi-step agent pipelines. See mpp.dev/advanced/identity.

Payments Proxy

Gate existing APIs behind MPP payments:

import { openai, Proxy } from 'mppx/proxy'
import { Mppx, tempo } from 'mppx/server'

const mppx = Mppx.create({ methods: [tempo()] })
const proxy = Proxy.create({
  title: 'My API Gateway',
  services: [
    openai({
      apiKey: process.env.OPENAI_API_KEY,
      routes: {
        'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
        'GET /v1/models': true, // literal `true` marks a free route
      },
    }),
  ],
})

Built-in presets openai(), anthropic(), stripe(), plus custom() for any upstream. The proxy exposes fetch and listener handlers and serves /discover, /discover/all, /llms.txt, and GET /openapi.json. Non-proxy servers publish the same discovery document with the discovery() helper. See references/discovery-and-proxy.md.

MCP Transport

MCP tool calls can require payment using JSON-RPC error code -32042 (servers may also issue -32043):

// Server - import tempo from mppx/server, NOT mppx/tempo
import { McpServer } from 'mppx/mcp/server'
import { tempo } from 'mppx/server'
const server = McpServer.wrap(baseServer, { methods: [tempo.charge({ /* ... */ })], secretKey })

// Client - payment-aware MCP client (import tempo from mppx/client)
import { McpClient } from 'mppx/mcp/client'
import { tempo } from 'mppx/client'
const mcp = McpClient.wrap(client, { methods: [tempo({ account })] })
const result = await mcp.callTool({ name: 'premium_tool', arguments: {} })

The MCP subpaths moved to mppx/mcp/server and mppx/mcp/client in mppx 0.8.0; mppx/mcp-sdk/* remain as aliases, and McpClient.wrap is the single client-wrap API. MCP-over-HTTP challenges settle in the same payment-aware fetch: Transport.http() extracts -32042 challenges and retries with the credential in MCP metadata. Transports are pluggable via Transport.from/http/mcp/mcpSdk on both sides. See references/transports.md.

Privy Server Wallets

Use Privy server wallets as MPP signers for agentic payment flows. createViemAccount from @privy-io/node/viem (requires @privy-io/node >= 0.20.0) returns a viem Account that delegates signing to the Privy wallet, so it drops into tempo({ account }) wherever a local account would go:

import { createViemAccount } from '@privy-io/node/viem'
import { Mppx, tempo } from 'mppx/client'

const account = createViemAccount(privy, { walletId, address })
const mppx = Mppx.create({ polyfill: false, methods: [tempo({ account })] })

Install: npm install @privy-io/node mppx viem. Server-side signing works with app-owned server wallets; user-owned embedded wallets require authorization keys or key quorums. See references/typescript-sdk.md for the manual toAccount() construction and the Privy agentic wallets docs.

Testing & CLI

# Create an account (stored in keychain), then fund it on testnet
npx mppx account create
npx mppx account fund --network testnet

# Make a paid request
npx mppx http://localhost:3000/resource

# Parse a challenge without signing it
npx mppx sign --dry-run --challenge '<www-authenticate value>'

# Validate a server implementation end-to-end
npx mppx validate http://localhost:3000

The CLI also covers init, sessions (list/view/close), discover, services, mcp add, and skills add. Config comes from MPPX_CONFIG or an explicit --config - there is no auto-discovery from the working directory. Full reference: references/cli.md.

SDK Packages

Language Package Install
TypeScript mppx npm install mppx
Python pympp pip install "pympp[tempo]"
Rust mpp cargo add mpp --features tempo,client,server
Ruby mpp-rb (official, by Stripe) see repo for gem name
Go mpp-go (official, by Tempo) go get github.com/tempoxyz/mpp-go
Elixir mpp (community) hex.pm/packages/mpp
Swift mpp-swift (community) see repo

Capability notes, checked against SDK source rather than the docs matrices (upstream publishes two that disagree):

  • Session intent: TypeScript and Rust only.
  • Proof Credentials (zero-dollar auth): TypeScript, Rust, and Ruby. Not pympp - the Python Tempo method implements only hash and transaction payload types.
  • Stripe, MCP, and event handling: TypeScript, Python, Rust, Ruby. Not the official mpp-go, which ships client/server/charge/fee-sponsorship/proof with net/http, Gin, Echo, and Chi middleware. A separate community Go mppx (cp0x) also exists.

Go and Ruby have first-class SDK doc pages at mpp.dev/sdk/go and mpp.dev/sdk/ruby.

TypeScript subpath exports (mppx 0.8.15):

  • Server: mppx/server (generic), mppx/hono, mppx/express, mppx/nextjs, mppx/elysia
  • Client: mppx/client, mppx/client/node (Node SQLite session channel store)
  • Proxy and discovery: mppx/proxy, mppx/discovery
  • Built-in methods: mppx/stripe (+ /client, /server), mppx/evm (+ /client, /server)
  • x402 interop: mppx/x402
  • MCP: mppx/mcp/server, mppx/mcp/client (mppx/mcp-sdk/* retained as aliases)
  • HTML: mppx/html; Validation: mppx/validation
  • CLI: mppx/cli, mppx/cli/plugins
  • SSE utilities: mppx/tempo (exports Session with Session.Sse.iterateData)

Always import Mppx and tempo from the subpath matching your context. Note: Mppx and tempo are NOT exported from mppx/tempo - that subpath only exports Session and Ws.

Key Concepts

  • Challenge/Credential/Receipt: The three protocol primitives. Challenge IDs are HMAC-SHA256 bound to prevent tampering. See references/protocol-spec.md
  • Zero-dollar auth: proof credential type for identity without payment. Amount '0' triggers EIP-712 proof signing; add store for replay protection
  • Split payments: One charge across multiple recipients in a single transaction (1-10 splits, per-split memos, expectedRecipients). See references/tempo-method.md
  • Refunds: No refund protocol exists. Charge refunds out-of-protocol by sending funds back to the payer; session v2 refunds unclaimed reserved funds by default; v1 refunds by closing the channel
  • Credential lifecycle: mppx.validateCredential() is a non-mutating pre-check; mppx.broadcastCredential() settles. verifyCredential() combines both and is deprecated
  • Tempo gas model: No native gas token. Fees are paid in stablecoins via feeToken or a setUserToken default; without one, transactions fail with gas_limit: 0. See references/tempo-method.md
  • Fee sponsorship: Server pays gas on behalf of clients, capped by maxInFlightReservations / maxInFlightTotalFee
  • Relays: Delegate credential validation and broadcast to Tempo API or a compatible relay via tempo.charge({ relay })
  • Push/pull modes: Client broadcasts the transaction (push) or the server does (pull)
  • Client chain pinning: tempo.charge({ expectedChainId }) rejects challenges for the wrong Tempo network
  • Reusable client channels: pass a channelStore to persist and reuse payer session channels across processes
  • x402 interop: evm.charge({ x402: { facilitator } }) serves native MPP and x402 "exact" challenges from one route; the client prefers Payment-auth challenges
  • Custom methods: Implement any payment rail with Method.from(). See references/custom-methods.md

Payment Hooks

Attach logging, metrics, or tracing without touching the handler. Register on the object returned by Mppx.create(); each registration returns an unsubscribe function.

  • Server (mppx/server): onChallengeCreated, onPaymentSuccess, onPaymentFailed, onSessionSettlement, on('*')
  • Client (mppx/client): onChallengeReceived, onCredentialCreated, onPaymentResponse, onPaymentFailed

Server handlers are awaited inline and sequentially on the request path - keep them fast. All hooks except client onChallengeReceived (which may return a credential string to override) are pure observers whose thrown errors are swallowed. Filter by intent with method.intent or challenge.intent. onPaymentFailed is the practical way to see the real error behind an opaque 402. See references/typescript-sdk.md and mpp.dev/advanced/payment-hooks.

Managing Agent Spend

Bound an agent's payment authority with Tempo access keys - delegated signing keys with built-in spend controls, their own expiry, and a revocation path.

import { Expiry } from 'accounts'
import { numberToHex, parseUnits } from 'viem'
import { Scopes } from 'viem/tempo'

const accessKey = {
  expiry: Expiry.days(7),
  limits: [{ token: usdc, limit: numberToHex(parseUnits('10', 6)), period: 86_400 }], // 10 USDC/day
  scopes: [Scopes.tip20(usdc).transfer({ recipients: [recipientAddress] })],
}
// Authorize via provider.request({ method: 'wallet_connect',
//   params: [{ capabilities: { authorizeAccessKey: accessKey } }] })

Mppx.create({
  methods: [tempo({
    account: provider.getAccount(),
    ...provider.getMppxParameters({ accessKey: accessKeyAddress }),
  })],
})

Spend limits are hex-encoded - pass numberToHex(parseUnits(...)), not a raw bigint. Separate keys per app/tool/deployment keep delegated runtimes isolated. See mpp.dev/guides/managing-agent-spend and Tempo access keys.

Production Gotchas

The failure modes that cost the most time. Full detail in references/production-gotchas.md:

  • Tempo has no native gas token. Set feeToken or call setUserToken, or transactions fail with gas_limit: 0. "Fund with ETH" errors mean "fund with the stablecoin fee token"
  • Sessions do not settle themselves. Configure settlementSchedule or run your own tempo.settle() sweep, paired with a close policy for idle channels
  • Charge settles before your handler runs. Use validateCredential then broadcastCredential when payment should depend on the work succeeding. Challenges expire after 5 minutes by default
  • Never use Store.memory() in production. Lost channel state means deposits stay reserved indefinitely
  • Set realm explicitly. Env vars outrank the per-request hostname, and Kubernetes HOSTNAME rotates every deploy, breaking mppscan attribution
  • Session voucher, close, and topUp credentials are bodyless POSTs, so a body validator running before mppx.session() rejects them with a spurious 400. Clone the request before reading its body, or mppx sees an empty one and returns 402
  • Large 402 headers overflow nginx's 4k default buffer and surface as 502

References

File Content
references/protocol-spec.md Core protocol: Challenge/Credential/Receipt structure, status codes, error handling, security, caching, extensibility
references/typescript-sdk.md mppx TypeScript SDK: server/client/middleware, transports, stores, validation, Privy wallets
references/cli.md mppx CLI: requests, validate, sign, accounts, sessions, discovery, config, env vars
references/production-gotchas.md Field-tested failure modes: gas, settlement, stores, realm, request handling, infrastructure
references/sessions.md Sessions: payment channels, vouchers, settlement, SSE/WebSocket streaming, recovery
references/subscriptions.md Subscription intent: activation, renewal worker, cancellation, period units
references/tempo-method.md Tempo: charge + session, fee sponsorship, relays, push/pull, auto-swap, split payments
references/stripe-method.md Stripe: fiat SPT flow, on-chain crypto deposit, server/client config, Elements, metadata
references/discovery-and-proxy.md Payments proxy, custom services, discovery documents, registries
references/transports.md HTTP, MCP, and WebSocket transport bindings: header/message encoding, comparison
references/python-sdk.md pympp Python SDK: @server.pay decorator, async client, charge intent
references/rust-sdk.md mpp Rust SDK: server/client, feature flags, sessions, reqwest middleware
references/lightning-method.md Lightning: charge (BOLT11), session (bearer tokens), Spark SDK
references/custom-methods.md Custom payment methods: Method.from, toClient, toServer patterns

Official Resources