diff --git a/src/pages/guides/use-mpp-with-x402.mdx b/src/pages/guides/use-mpp-with-x402.mdx index 3ab01d80..a2fc5dfd 100644 --- a/src/pages/guides/use-mpp-with-x402.mdx +++ b/src/pages/guides/use-mpp-with-x402.mdx @@ -181,6 +181,88 @@ console.log(response.status) // @log: 200 ``` +### Build a client for MPP and x402 + +Register `evm.charge` for x402 exact Challenges and the MPP methods you want to support. The HTTP client reads both `WWW-Authenticate` and `PAYMENT-REQUIRED`, then retries with either `Authorization` or `PAYMENT-SIGNATURE`. + +```ts twoslash [client.ts] +import { Fetch, evm, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount( + '0x0123456789012345678901234567890123456789012345678901234567890123', +) + +const fetch = Fetch.from({ + acceptPaymentPolicy: { + origins: ['https://api.example.com'], + }, + methods: [ + evm.charge({ + account, + currencies: [evm.assets.baseSepolia.USDC], + maxAmount: '1.00', + }), + tempo.charge({ account }), + ], +}) + +const response = await fetch('https://api.example.com/paid') +console.log(response.status) +// @log: 200 +``` + +Use this pattern for coding agents, CLIs, SDKs, and application clients that need to call both native MPP APIs and x402 exact APIs. + +### Send x402 headers manually + +Use `Mppx.create` when you need explicit control over the first request and retry. `createCredential` returns a `PAYMENT-SIGNATURE` value when the selected Challenge came from `PAYMENT-REQUIRED`. + +```ts twoslash [client.ts] +import { Mppx, evm } from 'mppx/client' +import { x402 } from 'mppx' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount( + '0x0123456789012345678901234567890123456789012345678901234567890123', +) + +const mppx = Mppx.create({ + methods: [ + evm.charge({ + account, + currencies: [evm.assets.baseSepolia.USDC], + maxAmount: '1.00', + }), + ], + polyfill: false, +}) + +const challenge = await mppx.rawFetch('https://api.example.com/paid') +if (challenge.status !== 402) throw new Error('Expected payment challenge') + +const paymentRequired = challenge.headers.get(x402.Types.paymentRequiredHeader) +if (!paymentRequired) throw new Error('Missing PAYMENT-REQUIRED') + +const credential = await mppx.createCredential( + new Response(null, { + headers: { + [x402.Types.paymentRequiredHeader]: paymentRequired, + }, + status: 402, + }), +) + +const response = await mppx.rawFetch('https://api.example.com/paid', { + headers: { + [x402.Types.paymentSignatureHeader]: credential, + }, +}) + +console.log(response.status) +// @log: 200 +``` + :::tip Because MPP uses the standard `WWW-Authenticate` scheme, it works with HTTP intermediaries (proxies, CDNs, API gateways) that understand [RFC 7235](https://www.rfc-editor.org/rfc/rfc7235) authentication. ::: diff --git a/src/pages/partner-integrations/cloudflare-agents.mdx b/src/pages/partner-integrations/cloudflare-agents.mdx index a0ba5d22..558d0fbf 100644 --- a/src/pages/partner-integrations/cloudflare-agents.mdx +++ b/src/pages/partner-integrations/cloudflare-agents.mdx @@ -68,6 +68,37 @@ export async function paidPing() { } ``` +## Support MPP and x402 clients + +Register an EVM method beside your Tempo method when the agent needs to call APIs that support either native MPP or x402 exact. The same payment-aware fetch reads MPP `WWW-Authenticate` Challenges and x402 `PAYMENT-REQUIRED` Challenges, then retries with the matching `Authorization` or `PAYMENT-SIGNATURE` header. + +```ts [payments.ts] +import { Mppx, evm, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + +Mppx.create({ + acceptPaymentPolicy: { + origins: ['https://api.example.com'], + }, + methods: [ + // [!code hl:start] + evm.charge({ + account, + currencies: [evm.assets.baseSepolia.USDC], + maxAmount: '1.00', + }), + tempo.charge({ account }), + // [!code hl:end] + ], +}) +``` + +Use this setup before creating MCP transports or calling `fetch`. MCP tool calls can keep using MPP through the wrapped client, while HTTP calls can pay either native MPP or x402 exact endpoints. + +See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the shared client flow. + ## Manage agent spend The examples above use a standard viem account. For long-running apps or agents, use scoped access keys when the runtime should pay in the background with spending limits, call scopes, recipient restrictions, and independent revocation. diff --git a/src/pages/partner-integrations/mcp-sdk.mdx b/src/pages/partner-integrations/mcp-sdk.mdx index d6dac8b6..16e78455 100644 --- a/src/pages/partner-integrations/mcp-sdk.mdx +++ b/src/pages/partner-integrations/mcp-sdk.mdx @@ -58,6 +58,37 @@ export async function paidPing() { } ``` +## Support MPP and x402 clients + +Register an EVM method beside your Tempo method when the agent needs to call APIs that support either native MPP or x402 exact. The same payment-aware fetch reads MPP `WWW-Authenticate` Challenges and x402 `PAYMENT-REQUIRED` Challenges, then retries with the matching `Authorization` or `PAYMENT-SIGNATURE` header. + +```ts [payments.ts] +import { Mppx, evm, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + +Mppx.create({ + acceptPaymentPolicy: { + origins: ['https://api.example.com'], + }, + methods: [ + // [!code hl:start] + evm.charge({ + account, + currencies: [evm.assets.baseSepolia.USDC], + maxAmount: '1.00', + }), + tempo.charge({ account }), + // [!code hl:end] + ], +}) +``` + +Use this setup before creating MCP transports or calling `fetch`. MCP tool calls can keep using MPP through the wrapped client, while HTTP calls can pay either native MPP or x402 exact endpoints. + +See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the shared client flow. + ## Manage agent spend The example above uses a standard viem account. For long-running apps or agents, use scoped access keys when the runtime should pay in the background with spending limits, call scopes, recipient restrictions, and independent revocation. diff --git a/src/pages/partner-integrations/vercel-ai-sdk.mdx b/src/pages/partner-integrations/vercel-ai-sdk.mdx index 24804245..b617fe27 100644 --- a/src/pages/partner-integrations/vercel-ai-sdk.mdx +++ b/src/pages/partner-integrations/vercel-ai-sdk.mdx @@ -78,6 +78,37 @@ console.log(toolResults) The `tempo({ account })` helper used above supports Tempo [charge](/payment-methods/tempo/charge) and [session](/payment-methods/tempo/session) challenges. +## Support MPP and x402 clients + +Register an EVM method beside your Tempo method when the agent needs to call APIs that support either native MPP or x402 exact. The same payment-aware fetch reads MPP `WWW-Authenticate` Challenges and x402 `PAYMENT-REQUIRED` Challenges, then retries with the matching `Authorization` or `PAYMENT-SIGNATURE` header. + +```ts [payments.ts] +import { Mppx, evm, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const account = privateKeyToAccount(process.env.PRIVATE_KEY as `0x${string}`) + +Mppx.create({ + acceptPaymentPolicy: { + origins: ['https://api.example.com'], + }, + methods: [ + // [!code hl:start] + evm.charge({ + account, + currencies: [evm.assets.baseSepolia.USDC], + maxAmount: '1.00', + }), + tempo.charge({ account }), + // [!code hl:end] + ], +}) +``` + +Use this setup before creating MCP transports or calling `fetch`. MCP tool calls can keep using MPP through the wrapped client, while HTTP calls can pay either native MPP or x402 exact endpoints. + +See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the shared client flow. + ## Manage agent spend The example above uses a standard viem account. For long-running apps or agents, use scoped access keys when the runtime should pay in the background with spending limits, call scopes, recipient restrictions, and independent revocation. diff --git a/src/pages/sdk/typescript/middlewares/elysia.mdx b/src/pages/sdk/typescript/middlewares/elysia.mdx index c7b92cd5..a3798cb3 100644 --- a/src/pages/sdk/typescript/middlewares/elysia.mdx +++ b/src/pages/sdk/typescript/middlewares/elysia.mdx @@ -88,3 +88,37 @@ const app = new Elysia() (app) => app.get('/content', () => ({ data: 'session content' })), ) ``` + +## x402-compatible clients + +Elysia apps can serve MPP and x402 clients from the same endpoint when you register [`evm.charge`](/payment-methods/evm/charge) with `x402.facilitator`. + +```ts [server.ts] +import { Elysia } from 'elysia' +import { Mppx, evm } from 'mppx/elysia' + +const mppx = Mppx.create({ + methods: [ + evm.charge({ + currency: evm.assets.baseSepolia.USDC, + recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + x402: { + facilitator: 'https://x402.org/facilitator', + }, + }), + ], + secretKey: process.env.MPP_SECRET_KEY ?? 'local-dev-secret', +}) + +const app = new Elysia().guard( + { + beforeHandle: mppx.evm.charge({ + amount: '0.01', + description: 'Premium API access', + }), + }, + (app) => app.get('/paid', () => ({ data: 'paid content' })), +) +``` + +See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the client setup. diff --git a/src/pages/sdk/typescript/middlewares/express.mdx b/src/pages/sdk/typescript/middlewares/express.mdx index bb1fa726..ece9b839 100644 --- a/src/pages/sdk/typescript/middlewares/express.mdx +++ b/src/pages/sdk/typescript/middlewares/express.mdx @@ -97,3 +97,35 @@ app.get( }, ) ``` + +## x402-compatible clients + +Express routes can serve MPP and x402 clients from the same endpoint when you register [`evm.charge`](/payment-methods/evm/charge) with `x402.facilitator`. + +```ts [server.ts] +import express from 'express' +import { Mppx, evm } from 'mppx/express' + +const app = express() + +const mppx = Mppx.create({ + methods: [ + evm.charge({ + currency: evm.assets.baseSepolia.USDC, + recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + x402: { + facilitator: 'https://x402.org/facilitator', + }, + }), + ], + secretKey: process.env.MPP_SECRET_KEY ?? 'local-dev-secret', +}) + +app.get( + '/paid', + mppx.evm.charge({ amount: '0.01', description: 'Premium API access' }), + (req, res) => res.json({ data: 'paid content' }), +) +``` + +See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the client setup. diff --git a/src/pages/sdk/typescript/middlewares/hono.mdx b/src/pages/sdk/typescript/middlewares/hono.mdx index 4ab0f916..8af4ce1c 100644 --- a/src/pages/sdk/typescript/middlewares/hono.mdx +++ b/src/pages/sdk/typescript/middlewares/hono.mdx @@ -97,3 +97,37 @@ app.get( }, ) ``` + +## x402-compatible clients + +Hono apps, including apps deployed on Cloudflare Workers, can serve MPP and x402 clients from the same endpoint when you register [`evm.charge`](/payment-methods/evm/charge) with `x402.facilitator`. + +```ts [worker.ts] +import { Hono } from 'hono' +import { Mppx, evm } from 'mppx/hono' + +const app = new Hono() + +const mppx = Mppx.create({ + methods: [ + evm.charge({ + currency: evm.assets.baseSepolia.USDC, + recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + x402: { + facilitator: 'https://x402.org/facilitator', + }, + }), + ], + secretKey: 'local-dev-secret', +}) + +app.get( + '/paid', + mppx.evm.charge({ amount: '0.01', description: 'Premium API access' }), + (c) => c.json({ data: 'paid content' }), +) + +export default app +``` + +See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the client setup. diff --git a/src/pages/sdk/typescript/middlewares/nextjs.mdx b/src/pages/sdk/typescript/middlewares/nextjs.mdx index c80d5e46..4c1b5bd7 100644 --- a/src/pages/sdk/typescript/middlewares/nextjs.mdx +++ b/src/pages/sdk/typescript/middlewares/nextjs.mdx @@ -85,3 +85,30 @@ export const GET = return Response.json({ payer }) }) ``` + +## x402-compatible clients + +Next.js route handlers, including routes deployed on Vercel, can serve MPP and x402 clients from the same endpoint when you register [`evm.charge`](/payment-methods/evm/charge) with `x402.facilitator`. + +```ts [app/api/paid/route.ts] +import { Mppx, evm } from 'mppx/nextjs' + +const mppx = Mppx.create({ + methods: [ + evm.charge({ + currency: evm.assets.baseSepolia.USDC, + recipient: '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266', + x402: { + facilitator: 'https://x402.org/facilitator', + }, + }), + ], + secretKey: process.env.MPP_SECRET_KEY ?? 'local-dev-secret', +}) + +export const GET = + mppx.evm.charge({ amount: '0.01', description: 'Premium API access' }) + (() => Response.json({ data: 'paid content' })) +``` + +See [build a client for MPP and x402](/guides/use-mpp-with-x402#build-a-client-for-mpp-and-x402) for the client setup.