From e91e124872df147629bd993d84b4cc98849fda74 Mon Sep 17 00:00:00 2001 From: Brendan Ryan <1572504+brendanjryan@users.noreply.github.com> Date: Tue, 15 Sep 2026 09:19:42 -0400 Subject: [PATCH] docs: sync mppx request preparation --- .mppx-docs-sync | 4 +- package.json | 2 +- pnpm-lock.yaml | 10 +- src/pages.gen.ts | 1 + src/pages/sdk/typescript/cli.mdx | 19 ++- .../sdk/typescript/client/Mppx.create.mdx | 14 ++ .../typescript/client/Mppx.preparePayment.mdx | 2 + .../typescript/client/Mppx.prepareRequest.mdx | 153 ++++++++++++++++++ vocs.config.ts | 4 + 9 files changed, 200 insertions(+), 9 deletions(-) create mode 100644 src/pages/sdk/typescript/client/Mppx.prepareRequest.mdx diff --git a/.mppx-docs-sync b/.mppx-docs-sync index bf5015f0..14a7138b 100644 --- a/.mppx-docs-sync +++ b/.mppx-docs-sync @@ -5,5 +5,5 @@ # When updating docs from mppx changes, bump this SHA to HEAD of mppx main # after incorporating the new features/changes into the docs site. -mppx_version=0.0.0-main-20260911134810 -mppx_sha=3c14a65a7ea55247f943a6c209ce67a1e8c57239 +mppx_version=0.0.0-main-20260914191442 +mppx_sha=eda059a13e4667368e065cdca51789f2a17777e6 diff --git a/package.json b/package.json index 44e3a3c3..8dddd067 100644 --- a/package.json +++ b/package.json @@ -39,7 +39,7 @@ "hono": "^4.12.27", "lottie-web": "^5.13.0", "mermaid": "^11.15.0", - "mppx": "0.0.0-main-20260911134810", + "mppx": "0.0.0-main-20260914191442", "nuqs": "2.9.1", "react": "^19", "react-dom": "^19", diff --git a/pnpm-lock.yaml b/pnpm-lock.yaml index 6976453d..6d5013bb 100644 --- a/pnpm-lock.yaml +++ b/pnpm-lock.yaml @@ -45,8 +45,8 @@ importers: specifier: ^11.15.0 version: 11.15.0 mppx: - specifier: 0.0.0-main-20260911134810 - version: 0.0.0-main-20260911134810(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3))(express@5.2.1)(hono@4.12.27)(typescript@6.0.3)(viem@2.54.0(typescript@6.0.3)(zod@4.4.3)) + specifier: 0.0.0-main-20260914191442 + version: 0.0.0-main-20260914191442(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3))(express@5.2.1)(hono@4.12.27)(typescript@6.0.3)(viem@2.54.0(typescript@6.0.3)(zod@4.4.3)) nuqs: specifier: 2.9.1 version: 2.9.1(react@19.2.7) @@ -4098,8 +4098,8 @@ packages: mlly@1.8.2: resolution: {integrity: sha512-d+ObxMQFmbt10sretNDytwt85VrbkhhUA/JBGm1MPaWJ65Cl4wOgLaB1NYvJSZ0Ef03MMEU/0xpPMXUIQ29UfA==} - mppx@0.0.0-main-20260911134810: - resolution: {integrity: sha512-xYebkX1tJv47+7sYcBNMQivVZCgtvThokEXHsTM9nOSEQSw8TRLvLCIj5MLzg7Eo2aZqDxHm2uSFMU9mxTF5XQ==} + mppx@0.0.0-main-20260914191442: + resolution: {integrity: sha512-S4Csd9QqfWxynXu+jnLlUuNEffUhzrvk+lrfXLolxRlUzRR6LGv/nxJBrA++YeQIakIzeFxfRQ1yaBPe6cS0zg==} hasBin: true peerDependencies: '@modelcontextprotocol/sdk': '>=1.25.0' @@ -9721,7 +9721,7 @@ snapshots: pkg-types: 1.3.1 ufo: 1.6.3 - mppx@0.0.0-main-20260911134810(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3))(express@5.2.1)(hono@4.12.27)(typescript@6.0.3)(viem@2.54.0(typescript@6.0.3)(zod@4.4.3)): + mppx@0.0.0-main-20260914191442(@modelcontextprotocol/sdk@1.29.0(@cfworker/json-schema@4.1.1)(zod@4.4.3))(express@5.2.1)(hono@4.12.27)(typescript@6.0.3)(viem@2.54.0(typescript@6.0.3)(zod@4.4.3)): dependencies: '@stripe/stripe-js': 9.13.0 eventsource-parser: 3.1.1 diff --git a/src/pages.gen.ts b/src/pages.gen.ts index 2cf4655e..7dce4ec7 100644 --- a/src/pages.gen.ts +++ b/src/pages.gen.ts @@ -133,6 +133,7 @@ type Page = | { path: '/sdk/typescript/client/Method.tempo.subscription'; render: 'static' } | { path: '/sdk/typescript/client/Mppx.create'; render: 'static' } | { path: '/sdk/typescript/client/Mppx.preparePayment'; render: 'static' } + | { path: '/sdk/typescript/client/Mppx.prepareRequest'; render: 'static' } | { path: '/sdk/typescript/client/Mppx.restore'; render: 'static' } | { path: '/sdk/typescript/client/Transport.from'; render: 'static' } | { path: '/sdk/typescript/client/Transport.http'; render: 'static' } diff --git a/src/pages/sdk/typescript/cli.mdx b/src/pages/sdk/typescript/cli.mdx index d866aaa2..8ad8dc22 100644 --- a/src/pages/sdk/typescript/cli.mdx +++ b/src/pages/sdk/typescript/cli.mdx @@ -68,7 +68,7 @@ x402 payments support compatible EVM `exact` Challenges and use the same account ## Validate command -Use `mppx validate` to automatically verify an MPP server implementation end-to-end. The command tests discovery, challenge formats, error handling, and the full payment flow. +Use `mppx validate` to automatically verify an MPP server implementation end-to-end. The command tests `/llms.txt`, OpenAPI discovery, Challenge formats, error handling, and the full payment flow. A missing, empty, or non-text `/llms.txt` appears as a non-blocking `suggested` result. ```bash [terminal] $ mppx validate https://api.example.com @@ -87,6 +87,23 @@ Run `mppx validate` against both test and production versions of your server. On When your config declares payment methods, `mppx validate` uses their signing accounts, approval hooks, chain policies, and Session stores instead of substituting built-in methods or preflighting the local CLI wallet. +### Programmatic validation + +Import `validate` from `mppx/validation` when you need structured results. + +```ts twoslash [validate.ts] +import { validate } from 'mppx/validation' + +const result = await validate({ + skipPayment: true, + url: 'https://api.example.com', +}) + +console.log(result.summary.suggested) +``` + +The `summary` contains `failed`, `passed`, `skipped`, `suggested`, and `warnings` counts. Individual checks use the matching `severity`, including `'suggested'` for optional improvements such as publishing `/llms.txt`. Suggested checks don't make validation fail. + ## Environment variables | Variable | Description | diff --git a/src/pages/sdk/typescript/client/Mppx.create.mdx b/src/pages/sdk/typescript/client/Mppx.create.mdx index 31db960b..c8a91e8b 100644 --- a/src/pages/sdk/typescript/client/Mppx.create.mdx +++ b/src/pages/sdk/typescript/client/Mppx.create.mdx @@ -333,6 +333,16 @@ type Mppx = { request?: RequestInit }, ) => Promise + /** Prepares a Challenge with the exact HTTP request that produced it. */ + prepareRequest: ( + input: RequestInfo | URL, + init?: RequestInit, + options?: { + acceptPayment?: string | AcceptPayment.Entry[] + maxRedirects?: number + orderChallenges?: OrderChallenges + }, + ) => Promise /** The original, unwrapped fetch — bypasses payment interception. */ rawFetch: typeof globalThis.fetch /** The transport used. */ @@ -344,6 +354,10 @@ type Mppx = { Selects and snapshots a supported Challenge without signing it. The returned object lets you inspect the terms, create the Credential later, and attach it through the correct Payment auth, x402, or MCP protocol. See [`Mppx.preparePayment`](/sdk/typescript/client/Mppx.preparePayment). +### `prepareRequest` + +Follows safe pre-payment redirects and prepares the exact HTTP request that returned the Challenge. Use it in Node.js when approval must happen before signing; the paid request doesn't follow redirects. See [`Mppx.prepareRequest`](/sdk/typescript/client/Mppx.prepareRequest). + ### `rawFetch` The original `fetch` function, before payment interception. Use `rawFetch` when you need to make requests that bypass the 402 handler—for example, probing a 402 endpoint for websocket auth tokens or calling APIs that return 402 for non-payment reasons. diff --git a/src/pages/sdk/typescript/client/Mppx.preparePayment.mdx b/src/pages/sdk/typescript/client/Mppx.preparePayment.mdx index 44a91a5d..9707360e 100644 --- a/src/pages/sdk/typescript/client/Mppx.preparePayment.mdx +++ b/src/pages/sdk/typescript/client/Mppx.preparePayment.mdx @@ -2,6 +2,8 @@ Selects a supported Challenge without creating or attaching its Credential. +For manual HTTP requests that can redirect before payment, use [`Mppx.prepareRequest`](/sdk/typescript/client/Mppx.prepareRequest) to keep the Credential pinned to the exact request that produced the Challenge. + ## Usage ```ts twoslash [client.ts] diff --git a/src/pages/sdk/typescript/client/Mppx.prepareRequest.mdx b/src/pages/sdk/typescript/client/Mppx.prepareRequest.mdx new file mode 100644 index 00000000..6a20127a --- /dev/null +++ b/src/pages/sdk/typescript/client/Mppx.prepareRequest.mdx @@ -0,0 +1,153 @@ +# `Mppx.prepareRequest` [Prepare a request-bound payment] + +Follows safe pre-payment redirects and prepares the exact HTTP request that returned a Challenge. + +## Usage + +```ts twoslash [client.ts] +import { Mppx, tempo } from 'mppx/client' +import { privateKeyToAccount } from 'viem/accounts' + +const mppx = Mppx.create({ + methods: [ + tempo({ + account: privateKeyToAccount( + '0x0123456789012345678901234567890123456789012345678901234567890123', + ), + }), + ], + polyfill: false, +}) + +// [!code hl:start] +const payment = await mppx.prepareRequest('https://api.example.com/checkout', { + body: JSON.stringify({ plan: 'pro' }), + headers: { 'Content-Type': 'application/json' }, + method: 'POST', +}) + +console.log(payment.challenge.request) +console.log(payment.redirects) + +const response = await payment.pay() +// [!code hl:end] + +console.log(response.status) +// @log: 200 +``` + +`prepareRequest` sends the request without creating a Credential, follows pre-payment redirects, and stops at the payment-required response. Use it when you need to inspect or approve payment terms while keeping the Credential bound to the request that produced the Challenge. + +The method rejects HTTPS downgrade redirects, removes credentials and sensitive headers on cross-origin redirects, and applies standard redirect method changes. `pay` creates the Credential at most once and sends it only to the prepared URL with redirects disabled. + +:::warning[Runtime support] +Use `prepareRequest` in Node.js and runtimes that expose manual redirect responses. Browsers return opaque manual redirects, so this method rejects browser redirect chains. +::: + +### With request-local selection + +Override the configured payment preferences for one request. + +```ts twoslash [client.ts] +import { Mppx, tempo } from 'mppx/client' +import type { Account } from 'viem' + +declare const account: Account + +const mppx = Mppx.create({ + methods: [tempo({ account })], + polyfill: false, +}) + +const payment = await mppx.prepareRequest( + 'https://api.example.com/paid', + undefined, + { + acceptPayment: 'tempo/charge;q=1, tempo/session;q=0', + maxRedirects: 5, + orderChallenges: (candidates) => candidates.filter(({ challenge }) => + challenge.request.amount !== '0' + ), + }, +) +``` + +## Return type + +```ts +type PreparedRequest = Readonly<{ + challenge: Challenge + challenges: readonly Challenge[] + createCredential: (context?: Context) => Promise + method: Method.Client + pay: (context?: Context) => Promise + redirects: readonly Readonly<{ + from: string + status: number + to: string + }>[] + request: Request + response: Response + setCredential: (request: RequestInit, credential: string) => RequestInit +}> +``` + +### challenge + +The selected immutable Challenge. + +### challenges + +All supported Challenges extracted from the payment-required response. + +### createCredential + +Creates the selected Challenge's Credential. Repeated or concurrent calls return the same promise. + +### method + +An immutable snapshot of the configured client method selected for the Challenge. + +### pay + +Creates and sends the Credential to the prepared request. The paid request uses `redirect: 'manual'`, so the Credential doesn't follow a redirect to another URL. + +### redirects + +Immutable pre-payment redirect records with each source URL, status code, and destination URL. + +### request + +The exact request that returned the payment-required response. + +### response + +The payment-required response returned for `request`. + +### setCredential + +Returns a request initializer with the Credential attached through the protocol that produced the Challenge. + +## Parameters + +### init (optional) + +- **Type:** `RequestInit` + +Request options for the initial HTTP request. + +### input + +- **Type:** `RequestInfo | URL` + +URL or request to prepare. + +### options (optional) + +- **Type:** `{ acceptPayment?: string | readonly AcceptPayment.Entry[]; maxRedirects?: number; orderChallenges?: OrderChallenges }` + +Request-local Challenge selection and redirect limits. + +- `acceptPayment` overrides the configured payment preferences. +- `maxRedirects` sets the maximum pre-payment redirects. Defaults to `20`. +- `orderChallenges` filters or sorts supported Challenge candidates. diff --git a/vocs.config.ts b/vocs.config.ts index 1e740817..10e9ee39 100644 --- a/vocs.config.ts +++ b/vocs.config.ts @@ -638,6 +638,10 @@ export default defineConfig({ text: ".preparePayment", link: "/sdk/typescript/client/Mppx.preparePayment", }, + { + text: ".prepareRequest", + link: "/sdk/typescript/client/Mppx.prepareRequest", + }, { text: ".restore", link: "/sdk/typescript/client/Mppx.restore",