MPP - マシン決済プロトコル
MPPは、TempoとStripeが共同で策定したオープンプロトコルで、HTTP 402 Payment Required をマシン間決済のために標準化します。クライアントは同じHTTPリクエスト内で支払います。アカウント、APIキー、チェックアウトフローは不要です。
コアプロトコル仕様は、IETFにPayment HTTP Authentication Schemeとして提出されています。
このスキルのコードでは、プレースホルダーのトークン名(<USDC_TEMPO_MAINNET>、<PATHUSD_TESTNET>)を使用しています。実際のアドレスはTempoドキュメントと references/tempo-method.md にあります。
コアアーキテクチャ
3つのプリミティブがすべてのMPP決済を支えます。
- チャレンジ - サーバーが発行する支払い要求(
WWW-Authenticate: Paymentヘッダー内) - クレデンシャル - クライアントが送信する支払い証明(
Authorization: Paymentヘッダー内) - レシート - サーバーが返す支払い成功の確認(
Payment-Receiptヘッダー内)
決済方法とインテント
MPPは決済方法に依存しません。各方法が独自の決済レールを定義します。
| メソッド | レール | SDKパッケージ | ステータス |
|---|---|---|---|
| Tempo | Tempoチェーン上のTIP-20ステーブルコイン | mppx (組み込み) | 本番環境 |
| Stripe | カード/ウォレット (SPT) + オンチェーン暗号資産入金 | mppx (組み込み) | 本番環境 |
| EVM | EIP-3009ステーブルコイン認可 (x402-exact互換) | mppx (組み込み) | 本番環境 |
| Lightning | Lightning Network上のビットコイン | @buildonspark/lightning-mpp-sdk | 本番環境 |
| Stellar | Stellar上のSEP-41トークン、チャージ + channel | @stellar/mpp | 本番環境 (channel ワイヤー仕様はまだ草案段階で、変更される可能性があります) |
| Solana | Solanaネイティブのチャージ + セッション (SOL、SPL、Token-2022) | @solana/mpp | 本番環境 |
| Monad | Monadチャージ (ERC-3009、決済モード) | @monad-crypto/mpp | 本番環境 |
| NEAR Intents | 1Click入金アドレスによるクロスチェーンチャージ | @defuse-protocol/nearintents-mpp-sdk | 本番環境 (トラストレスではありません - 決済バックエンドを経由します。メソッドごとのリスクポリシーのために methodDetails.settlementBackend: "near-intents" としてアドバタイズされます) |
| RedotPay | RedotPay残高 (rdt) またはステーブルコイン証明、チャージのみ | @redotpay/mpp | 本番環境 |
| Card | 暗号化ネットワークトークン (Visa) | mpp-card | 本番環境 |
| カスタム | 任意のレール | Method.from() + Method.toClient/toServer | 拡張可能 |
メソッド別詳細: references/tempo-method.md、references/stripe-method.md、references/lightning-method.md、references/custom-methods.md。
| インテント | パターン | 最適な用途 |
|---|---|---|
| charge | リクエストごとの1回払い | API呼び出し、コンテンツアクセス、固定価格エンドポイント |
| session | 支払いチャネル経由の従量課金 | LLMストリーミング、従量課金、高頻度API |
| subscription | 認証キーによる定期アクセス (Tempo) - references/subscriptions.md 参照 | プラン/ティアでアクセスとリクエストごとの課金を分離する場合 |
クイックスタート: サーバー (TypeScript)
import { Mppx, tempo } from 'mppx/server'
const mppx = Mppx.create({
methods: [tempo({
currency: '<PATHUSD_TESTNET>', // pathUSD テストネット
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: '...' }))
}
インストール: npm install mppx viem (mppx 0.8.15は viem >= 2.54.0 が必要)。
完成したサーバーをエンドツーエンドで検証するには: npx mppx validate http://localhost:3000
クイックスタート: クライアント (TypeScript)
import { privateKeyToAccount } from 'viem/accounts'
import { Mppx, tempo } from 'mppx/client'
// globalThis.fetch をポリフィルして 402 を自動処理
Mppx.create({
methods: [tempo({ account: privateKeyToAccount('0x...') })],
})
const res = await fetch('https://api.example.com/paid')
// サーバーが 402 を返した場合、支払いは透過的に行われます
ブラウザでは、mppx 0.6.0 からデフォルトが変更されました。ポリフィルされた fetch は 同一オリジン のエンドポイントにのみ Accept-Payment を送信するため、クロスオリジンの有料APIでは acceptPaymentPolicy ('always' / { origins: [...] }) が必要です。クライアントのfetchは、maxPaymentRetries (デフォルト3) まで増分チャレンジを再試行します。グローバル以外の代替手段 (Fetch.from/polyfill/restore、Mppx.restore()) については、references/typescript-sdk.md を参照してください。
クイックスタート: サーバー (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}
インストール: pip install "pympp[tempo]"。完全なパターンについては references/python-sdk.md を参照してください。
クイックスタート: サーバー (Rust)
インストール: cargo add mpp --features tempo,server。完全なパターンについては references/rust-sdk.md を参照してください。
フレームワークミドルウェア (TypeScript)
各フレームワークには独自のインポートがあります (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: '...' }))
Express と Elysia の例については references/typescript-sdk.md を参照してください。
セッション: 従量課金ストリーミング
セッションは支払いチャネルを1回だけ開設し、以降の各リクエストではオフチェーン voucher を使用します。リクエストごとにブロックチェーントランザクションは発生しません。レイテンシは100ms未満、リクエストごとの手数料はほぼゼロです。
セッション v2 (mppx 0.7.0以降のデフォルト): tempo.session() は TIP-1034 プリコンパイルチャネルフローです。以前のエスクローコントラクト実装は セッション v1 であり、非推奨の tempo.sessionLegacy として引き続き利用可能です。v2 を期待するクライアントは v1 セッションを拒否し、チャージパスにフォールバックするため、クライアントとサーバーを一致したフローに保ってください。2つのクライアントAPI: tempo.session({ account, maxDeposit }) はメソッドを Mppx.create() に登録し (fetch による透過的な402処理)、tempo.session.manager({ account, maxDeposit }) は直接ライフサイクル制御 (.sse()、.close()) のためのマネージドクライアントを返します。
// サーバー - 自動決済によるセッションエンドポイント
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, // 戻ってきたクライアントがこのルートでチャネルを復旧できるようにする
})],
})
const result = await mppx.session({ amount: '0.001', unitType: 'token' })(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
// サーバー - 単語単位の課金によるSSEストリーミング
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
}
}
)
// クライアント - 自動管理チャネルによるセッション
Mppx.create({ methods: [tempo({ account, maxDeposit: '1' })] })
const res = await fetch('http://localhost:3000/api/resource')
// 1回目のリクエスト: オンチェーンでチャネルを開設; 2回目以降: オフチェーン voucher
セッションは Ws.serve() を介してWebSocket上でもストリーミングされます。完全なライフサイクル、決済、ストア、SSEおよびWebSocketパターン、チャネル復旧については references/sessions.md を参照してください。
マルチメソッドサポート
単一のエンドポイントでTempoステーブルコイン、Stripeカード、Lightning Bitcoinを受け入れます:
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! }),
],
})
Mppx.compose() を使用すると、ルートごとの価格設定で複数のメソッドを単一の402レスポンスで提示できます。チャレンジサイトと検証サイトの両方で同じブランチを適用するか、そうでなければ402でサーバーが受け入れるよりも少ないオプションがアドバタイズされます。references/typescript-sdk.md を参照してください。
ペイメントリンク (HTML)
決済メソッド設定で html: true を設定すると、ブラウザで402エンドポイントにアクセスしたときにブラウザフレンドリーな支払いページが表示され、テーマ設定、マルチメソッドコンポーズタブ、Solanaウォレットサポートが利用可能になります。サービスワーカーがクレデンシャルの送信を処理し、その後ページが有料レスポンスとともにリロードされます。
カスタマイズは mppx/html のエクスポート (Config、Text、Theme) を介して行い、Html.init(methodName) でカスタムメソッドのペイメントリンクを構築します。
ゼロドル認証 (Proof Credentials)
支払いなしでエージェントのアイデンティティを認証します。クライアントはトランザクションを作成する代わりに、チャレンジIDに対してEIP-712証明に署名します。ガス代は発生せず、資金は移動しません。
// サーバー - ゼロドルチャージ、リプレイ保護用のストア付き
const mppx = Mppx.create({
methods: [tempo.charge({ currency: '<PATHUSD_TESTNET>', recipient: '0x...', store })],
})
const result = await mppx.charge({ amount: '0' })(request)
mppx 0.8.0 以降、これらの証明は 支払いウォレットにバインド されます。EIP-712 Proof 型付きデータ (tempo.Proof として公開) は、ドメインバージョン 3 で account フィールドを持つため、あるアカウント用に署名された証明は、別のアカウントに対して検証できなくなります。
ユースケース: アイデンティティ検証、長時間実行ジョブのポーリング、有料アンロック後の無料アクセス、マルチステップエージェントパイプライン。詳細は mpp.dev/advanced/identity を参照してください。
ペイメントプロキシ
既存のAPIをMPP決済でゲートします:
// import { openai, Proxy } from 'mppx/proxy' - Proxy.create({ services: [...] }) 内のサービス
openai({
apiKey: process.env.OPENAI_API_KEY,
routes: {
'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
'GET /v1/models': true, // リテラル `true` は無料ルートを示す
},
})
組み込みプリセット openai()、anthropic()、stripe()、および任意の上流API用の custom()。Proxy.create()、それが提供するディスカバリーエンドポイント、および非プロキシサーバー用の discovery() ヘルパーについては references/discovery-and-proxy.md を参照してください。
MCP トランスポート
MCPツール呼び出しは、JSON-RPCエラーコード -32042 を使用して支払いを要求できます (サーバーは -32043 を発行することもあります):
// サーバー - mppx/server から tempo をインポート (mppx/tempo ではない)
import { McpServer } from 'mppx/mcp/server'
import { tempo } from 'mppx/server'
const server = McpServer.wrap(baseServer, { methods: [tempo.charge({ /* ... */ })], secretKey })
// クライアント - 決済対応MCPクライアント (mppx/client から tempo をインポート)
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: {} })
MCP-over-HTTP チャレンジは同じ決済対応 fetch で解決され、トランスポートは両側で Transport.from/http/mcp/mcpSdk を介してプラグ可能です。references/transports.md を参照してください。
Privy サーバーウォレット
@privy-io/node/viem からの createViemAccount (@privy-io/node >= 0.20.0 が必要) は、Privy サーバーウォレットによってバックアップされた viem Account を返すため、ローカルアカウントを使う場所ならどこでも tempo({ account }) にそのまま挿入できます。
サーバーサイド署名は、アプリ所有のサーバーウォレット で機能します。ユーザー所有の埋め込みウォレットでは、認可キーまたはキークォーラムが必要です。完全なセットアップと手動の toAccount() 構築については references/typescript-sdk.md を参照してください。
テストとCLI
# アカウントを作成 (キーチェーンに保存)、テストネットで資金を入金
npx mppx account create
npx mppx account fund --network testnet
# 有料リクエストを行う
npx mppx http://localhost:3000/resource
# 署名せずにチャレンジを解析
npx mppx sign --dry-run --challenge '<www-authenticate value>'
# サーバー実装をエンドツーエンドで検証
npx mppx validate http://localhost:3000
CLIは init、sessions (一覧表示/表示/クローズ)、discover、services、mcp add、skills add もカバーしています。設定は MPPX_CONFIG または明示的な --config から取得されます。ワーキングディレクトリからの自動検出はありません。完全なリファレンス: references/cli.md。
SDKパッケージ
| 言語 | パッケージ | インストール |
|---|---|---|
| TypeScript | mppx | npm install mppx |
| Python | pympp | pip install "pympp[tempo]" |
| Rust | mpp | cargo add mpp --features tempo,client,server |
| Ruby | mpp-rb (公式、Stripe提供) | リポジトリでgem名を確認 |
| Go | mpp-go (公式、Tempo提供) | go get github.com/tempoxyz/mpp-go |
| Elixir | mpp (コミュニティ) | hex.pm/packages/mpp |
| Swift | mpp-swift (コミュニティ) | リポジトリを確認 |
機能に関する注意点 (ドキュメントのマトリックスではなくSDKソースに基づいて確認。上流は2つの矛盾するマトリックスを公開しています):
- Session インテント: TypeScript と Rust のみ。
- Proof Credentials (ゼロドル認証): TypeScript のみ。
...
[翻訳のため省略]