Install
$ agentstack add skill-tenequm-skills-mpp ✓ scanned · ✓ verified, works with Claude Code, Cursor, and more.
Security review
✓ PassedNo issues found. Passed automated security review. · v0.1.0 How review works →
- ✓ Prompt-injection patterns
- ✓ Secret / credential exfiltration
- ✓ Dangerous shell & filesystem operations
- ✓ Untrusted network calls
- ✓ Known-malicious package signatures
What it can access
- ● Network access Used
- ✓ Filesystem access No
- ✓ Shell / process execution No
- ● Environment & secrets Used
- ✓ Dynamic code execution No
From automated source analysis of v0.1.0. “Used” means the capability is present in the source — more access means more to trust, not that it’s unsafe.
Verified badge
Passed review? Show it. Paste this badge into your README, it links to the public security report.
Reliability & compatibility
Declared compatibility
Compatibility is declared by the source manifest. End-to-end runtime verification is coming, see below.
We're building live execution health for every listing: tool-call success rate, median latency, uptime, and last-checked timestamps, measured, not self-reported. It isn't live yet, so we don't show numbers we can't stand behind.
How agent discovery & health will work →About
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 | ` | | pathUSD (testnet) | 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:
- Challenge - server-issued payment requirement (in
WWW-Authenticate: Paymentheader) - Credential - client-submitted payment proof (in
Authorization: Paymentheader) - Receipt - server confirmation of successful payment (in
Payment-Receiptheader)
Client Server
│ (1) GET /resource │
├──────────────────────────────────────────────>│
│ (2) 402 + WWW-Authenticate: Payment │
││
│ (5) Verify + settle │
│ (6) 200 OK + Payment-Receipt │
│', // 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
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
In browsers, mppx 0.6.0 changed the default: polyfilled fetch only sends Accept-Payment to same-origin endpoints. For cross-origin paid APIs set acceptPaymentPolicy ('always' / { origins: [...] }). See references/typescript-sdk.md.
Quick Start: Server (Python)
from fastapi import FastAPI, Request
from mpp.server import Mpp
from mpp.methods.tempo import tempo, ChargeIntent
app = FastAPI()
mpp = Mpp.create(method=tempo(
currency="",
recipient="0xYourAddress", intents={"charge": ChargeIntent()},
))
@app.get("/resource")
async def get_resource(request: Request):
result = await mpp.charge(authorization=request.headers.get("Authorization"), amount="0.50")
if isinstance(result, Challenge):
return JSONResponse(status_code=402, content={"error": "Payment required"},
headers={"WWW-Authenticate": result.to_www_authenticate(mpp.realm)})
return {"data": "paid content"}
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: '0x20c0...', 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.
// Server - session endpoint
const result = await mppx.session({
amount: '0.001',
unitType: 'token',
})(request)
if (result.status === 402) return result.challenge
return result.withReceipt(Response.json({ data: '...' }))
// Server - SSE streaming with per-word billing
const mppx = Mppx.create({
methods: [tempo({ currency: '0x20c0...', recipient: '0x...', sse: true })],
})
export const GET = mppx.session({ amount: '0.001', unitType: 'word' })(
async () => {
const words = ['hello', 'world']
return async function* (stream) {
for (const word of words) {
await stream.charge()
yield word
}
}
}
)
// Client - session with auto-managed channel
import { Mppx, tempo } from 'mppx/client'
Mppx.create({
methods: [tempo({ account, maxDeposit: '1' })], // Lock up to 1 pathUSD
})
const res = await fetch('http://localhost:3000/api/resource')
// 1st request: opens channel on-chain
// 2nd+ requests: off-chain vouchers (no on-chain tx)
WebSocket transport: Sessions also support WebSocket streaming via Ws.serve(). The WebSocket protocol uses typed messages (mpp: 'authorization', mpp: 'message', mpp: 'payment-close-request', etc.) for payment negotiation alongside data delivery. See references/sessions.md for the full session lifecycle, escrow contracts, SSE patterns, and WebSocket integration.
Multi-Method Support
Accept Tempo stablecoins, Stripe cards, and Lightning Bitcoin on a single endpoint:
import Stripe from 'stripe'
import { Mppx, tempo, stripe } from 'mppx/server'
import { spark } from '@buildonspark/lightning-mpp-sdk/server'
const mppx = Mppx.create({
methods: [
tempo({ currency: '0x20c0...', recipient: '0x...' }),
stripe.charge({ client: new Stripe(key), networkId: 'internal', paymentMethodTypes: ['card'] }),
spark.charge({ mnemonic: process.env.MNEMONIC! }),
],
})
Use compose() to present multiple methods in a single 402 response with per-route pricing. See references/typescript-sdk.md for compose patterns.
Payment Links (HTML)
Payment links render a browser-friendly payment UI when a 402 endpoint is visited in a browser. Set html: true on the payment method config. Supports theming, multi-method compose (Tempo + Stripe tabs), and Solana wallets.
import { Mppx, tempo } from 'mppx/nextjs'
const mppx = Mppx.create({
methods: [tempo.charge({
currency: '0x20c0...', recipient: '0x...',
html: true, // auto-renders payment page for browsers
})],
})
export const GET = mppx.charge({ amount: '0.1' })(() => Response.json({ data: '...' }))
Customize via mppx/html exports (Config, Text, Theme). Service workers handle credential submission - the page reloads with the paid response. For multi-method compose, tabs auto-switch between payment options.
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 (amount: '0')
const result = await mppx.charge({ amount: '0' })(request)
// Enable replay protection (makes each proof single-use)
const mppx = Mppx.create({
methods: [tempo.charge({ currency: '0x20c0...', recipient: '0x...', store: Store.memory() })],
})
Use cases: identity verification, long-running job polling (prove identity once, poll freely), paid unlock with free subsequent access, multi-step agent pipelines. See mpp.dev/advanced/identity for full patterns.
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',
description: 'Paid access to AI APIs',
services: [
openai({
apiKey: 'sk-...', // pragma: allowlist secret
routes: {
'POST /v1/chat/completions': mppx.charge({ amount: '0.05' }),
'GET /v1/models': mppx.free(), // mppx.free() marks a route as free (no payment)
},
}),
],
})
// Discovery: GET /openapi.json (x-payment-info document), plus live /discover, /discover/all, /llms.txt
MCP Transport
MCP tool calls can require payment using JSON-RPC error code -32042 (servers may also issue -32043):
// Server - MCP with payment (import tempo from mppx/server, NOT mppx/tempo)
import { McpServer } from 'mppx/mcp-sdk/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-sdk/client'
import { tempo } from 'mppx/client'
const mcp = McpClient.wrap(client, { methods: [tempo({ account })] })
const result = await mcp.callTool({ name: 'premium_tool', arguments: {} })
See references/transports.md for the full MCP encoding (challenge in error.data.challenges, credential in _meta).
Privy Server Wallets
Use Privy server wallets as MPP signers for agentic payment flows. The pattern: create a custom viem Account via toAccount() that delegates signMessage, signTransaction, and signTypedData to Privy's API (@privy-io/node), then pass it to tempo({ account }). Tempo's custom serializer requires using signSecp256k1 (raw hash signing) for transactions instead of Privy's higher-level signTransaction.
Install: npm install @privy-io/node mppx viem. See references/typescript-sdk.md for the full implementation, Privy agentic wallets docs, and the demo app.
Testing & CLI
# Create an account (stored in keychain, auto-funded on testnet)
npx mppx account create
# Make a paid request
npx mppx http://localhost:3000/resource
# Inspect challenge without paying
npx mppx --inspect http://localhost:3000/resource
CLI config file: Extend the CLI with custom payment methods via mppx.config.(js|mjs|ts):
// mppx.config.ts
import { defineConfig } from 'mppx/cli'
export default defineConfig({ plugins: [myCustomMethod()] })
SDK Packages
| Language | Package | Install | |----------|---------|---------| | TypeScript | mppx | npm install mppx | | Python | pympp | pip install pympp or 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 | mppx (community) | go get github.com/cp0x-org/mppx | | Elixir | mpp (community) | mix deps / hex.pm/packages/mpp |
Per the official SDK capability matrix, the session intent is implemented in TypeScript and Rust only; Python/Ruby/Go cover the charge intent (plus Stripe and fee sponsorship). Stellar uses @stellar/mpp (repo).
TypeScript subpath exports (mppx 0.6.30):
- Server:
mppx/server(generic),mppx/hono,mppx/express,mppx/nextjs,mppx/elysia(framework middleware) - Client:
mppx/client - Proxy:
mppx/proxy - Built-in methods:
mppx/stripe(+/client,/server),mppx/evm(+/client,/server) - x402 interop:
mppx/x402 - MCP:
mppx/mcp-sdk/server,mppx/mcp-sdk/client - HTML:
mppx/html(exportsConfig,Text,Themetypes andinit()for payment link customization) - Discovery:
mppx/discovery(OpenAPI-first discovery tooling) - CLI:
mppx/cli,mppx/cli/plugins - SSE utilities:
mppx/tempo(exportsSessionwithSession.Sse.iterateDatafor SSE stream parsing)
Always import Mppx and tempo from the appropriate subpath for your context (e.g. mppx/hono for Hono, mppx/server for generic/MCP server, mppx/client for client). Note: Mppx and tempo are NOT exported from mppx/tempo - that subpath only exports Session.
Key Concepts
- Challenge/Credential/Receipt: The three protocol primitives. Challenge IDs are HMAC-SHA256 bound to prevent tampering. See
references/protocol-spec.md - Payment methods: Tempo (stablecoins), Stripe (cards), Lightning (Bitcoin), Card (network tokens), or custom. See method-specific references
- Intents:
charge(one-time) andsession(streaming). Seereferences/sessions.mdfor session details - Payment links: Browser-rendered 402 payment pages with
html: true. Supports theming and multi-method compose tabs - Zero-dollar auth:
proofcredential type for identity without payment. Amount'0'triggers EIP-712 proof signing. Addstorefor replay protection - Split payments: Distribute a charge across multiple recipients in a single transaction (Tempo charge, 0.4.12+)
- Subscriptions: Recurring access on Tempo via an authorized key - activation collects the first period, later requests reuse the key (no per-request payment), the server renews in the background (needs a durable store), and access is cancelled/revoked server-side.
mppx.tempo.subscription(+dev_secondtest periods) - Refunds: MPP defines no refund protocol. For charge, refund out-of-protocol by sending funds back to the payer's address. For session, refund by closing the channel - unspent escrow returns to the client
- Transports: HTTP (headers), MCP (JSON-RPC), and WebSocket (streaming). See
references/transports.md - Tempo gas model: Tempo has no native gas token (no ETH equivalent). All transaction fees are paid in stablecoins (USDC, pathUSD) via the
feeTokentransaction field. Accounts must either setfeeTokenper-transaction or callsetUserTokenon the FeeManager precompile to set a default. Without this, transactions fail withgas_limit: 0. Seereferences/tempo-method.md - Fee sponsorship: Server pays gas fees on behalf of clients (Tempo). See
references/tempo-method.md - Push/pull modes: Client broadcasts tx (push) or server broadcasts (pull). See
references/tempo-method.md - Custom methods: Implement any payment rail with
Method.from(). Seereferences/custom-methods.md
Production Gotchas
Tempo Gas (CRITICAL)
Tempo has no native gas token. Unlike Ethereum (ETH for gas) or Solana (SOL for fees), Tempo charges transaction fees in stablecoins. Every transaction must specify which stablecoin pays for gas. There are two ways:
- Per-transaction
feeToken- set in the transaction itself:
const prepared = await prepareTransactionRequest(client, {
account,
calls: [{ to, data }],
feeToken: '', // USDC mainnet (see Tempo token addresses table)
} as never)
- Account-level default via
setUserToken- one-time setup, applies to all future transactions:
import { setUserToken } from 'viem/tempo'
await client.fee.setUserTokenSync({
token: '', // USDC mainnet
})
**Without either, transactions fai
…
Source & license
This open-source skill is cataloged on AgentStack and links to its original source — we do not rehost the code.
- Author: tenequm
- Source: tenequm/skills
- License: MIT
- Homepage: https://clawhub.ai/u/tenequm
Install and usage instructions live in the source repository linked above.
Reviews
No reviews yet, be the first.
Write a review
Versions
- v0.1.0 Imported from the upstream source.