Skip to content
LogoLogo

Mppx.compose

Present multiple payment options

Combines multiple method handlers into one HTTP or MCP handler that presents every payment offer to the client.

Usage

Present both stablecoin and Stripe card payment options for a single endpoint. The client picks whichever method it supports.

import { , ,  } from 'mppx/server'
 
const  = '0x20c0000000000000000000000000000000000000'
const  = '0x20C000000000000000000000b9537d11c60E8b50'
const  = '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266'
 
const  = .({  })
const  = .({
  : 2,
  : 'acct_1234',
  : ['card'],
  : 'sk_live_...',
})
 
const  = .({ : [, ] })
 
export async function (: Request) {
  const  = await .(
    [, { : '1', :  }],
    [, { : '1', :  }],
    [, { : '1', : 'usd' }],
  )()
 
  if (. === 402) return .
  return .(.({ : '...' }))
}

With MCP

Use instance composition with Transport.mcpSdk() to offer multiple payment options from one MCP tool. The transport returns every Challenge in the payment-required error and dispatches the selected Credential to one matching handler.

import {  } from '@modelcontextprotocol/sdk/server/mcp'
import { , ,  } from 'mppx/server'
 
const  = '0x20c0000000000000000000000000000000000000'
const  = '0x20C000000000000000000000b9537d11c60E8b50'
const  = .({
  : '0xf39Fd6e51aad88F6F4ce6aB8827279cffFb92266',
})
const  = .({
  : [],
  : .(),
})
 
const  = new ({ : 'example', : '1.0.0' })
 
.('premium', { : 'Read premium data' }, async () => {
  const  = await .(
    [.., { : '1', :  }],
    [.., { : '1', :  }],
  )()
 
  if (. === 402) throw .
  return .({ : [{ : 'Premium data', : 'text' }] })
})

Nested compositions

Use the static Mppx.compose() function to combine configured handlers or another composed handler. Credential dispatch and discovery metadata include every nested offer.

const stablecoins = Mppx.compose(
  mppx.tempo.charge({ amount: '1', currency: pathUSD }),
  mppx.tempo.charge({ amount: '1', currency: USDCe }),
)
 
const paid = Mppx.compose(
  stablecoins,
  mppx.stripe.charge({ amount: '1', currency: 'usd' }),
)

Behavior

  • No Credential over HTTP: Calls all handlers and merges their 402 Challenges into a single response with multiple WWW-Authenticate headers.
  • No Credential over MCP: Calls all handlers and combines their Challenges in configured order. MCP SDK transport returns an McpError; raw MCP transport preserves the JSON-RPC request ID.
  • Intent shorthand: When multiple registered methods share an intent, mppx.charge(options) and other intent functions implicitly compose every matching method over HTTP or MCP. Use explicit composition when offers need different options.
  • Offer policies: HTTP composition calls each method's canOffer hook, then the instance-level selectOffers hook, before generating Payment auth, x402, or HTML offers. MCP composition requires methods that use the configured MCP transport and rejects HTTP canOffer hooks. Static compositions apply canOffer; direct method handlers don't.
  • Accept-Payment present: Ranks and filters the merged Challenges by the client's supported method/intent entries. Entries with q=0 are excluded. If the header is invalid or filters out every Challenge, all Challenges are returned.
  • Credential present: Dispatches to exactly one handler matching the Credential's method, intent, request terms, and scope without re-running offer policies. MCP verification failures don't fall through to another offer.
  • Discovery enabled: Exposes every configured offer to discovery() and proxy metadata, including offers from nested compositions.

Signatures

Instance

type InstanceCompose = (
  ...entries: readonly [Method.Server | MethodHandler | string, Options][]
) => ComposedHandler<Transport>

Instance composition supports HTTP, MCP SDK, and raw MCP transports.

Static

type StaticCompose = (
  ...handlers: readonly (ComposedHandler | ConfiguredHandler)[]
) => ComposedHandler

Static composition combines configured HTTP handlers and supports nested compositions.

Return type

type ReturnType<Transport> = (input: InputOf<Transport>) => Promise<
  | { status: 402; challenge: ChallengeOutputOf<Transport> }
  | { status: 200; withReceipt: WithReceipt<Transport> }
>

Parameters

...entries

  • Type: readonly [Method.Server | MethodHandler | string, Options][]

Each entry is a tuple of a method reference (or string key like "tempo/charge") and the request options for that method. Requires at least one entry.

...handlers

  • Type: readonly (ComposedHandler | ConfiguredHandler)[]

Configured route handlers returned by an mppx method or another static composition. Requires at least one handler.