Describe the bug
PaymentTransport.handle_async_request forwards the caller's initial request directly to the inner transport, unmodified:
response = await self._inner.handle_async_request(request)
No Accept-Payment header is added, even though PaymentRuntime already tracks every configured (method, intent) pair internally (self._methods: dict[tuple[str, str], Method], built in __init__). A server therefore has no way to know, before it issues a 402 challenge, which payment methods/intents this client actually supports.
Why this isn't hypothetical
Originally surfaced by the mpp-tools Agricola cross-SDK audit (finding AGR-2026-021), comparing this SDK's behavior against the canonical TypeScript reference implementation (wevm/mppx), which derives an Accept-Payment value from configured capabilities and adds it to initial requests by default. go, java, ruby, and rust were all flagged clean in the same audit; only python diverges.
I independently confirmed this by reading src/mpp/client/transport.py directly on current main: there is no code path, before or after the request read, that inspects self._runtime to build a header.
Steps to reproduce
from mpp.client import PaymentTransport
import httpx
transport = PaymentTransport(methods=[tempo(...)], inner=httpx.AsyncHTTPTransport())
async with httpx.AsyncClient(transport=transport) as client:
# Inspect the outgoing request headers (e.g. via a proxy or mock transport):
# no Accept-Payment header is present.
await client.get("https://api.example.com/resource")
Fix
Opening a PR that adds a small PaymentRuntime.accept_payment_header() helper deriving a value like tempo/charge, stripe/charge from the already-tracked (method, intent) pairs, and has PaymentTransport set it on the initial request when the caller hasn't already supplied one explicitly. Includes regression tests for: header added when methods are configured, an explicit caller header is preserved, and no header is added when no methods are configured.
Describe the bug
PaymentTransport.handle_async_requestforwards the caller's initial request directly to the inner transport, unmodified:No
Accept-Paymentheader is added, even thoughPaymentRuntimealready tracks every configured(method, intent)pair internally (self._methods: dict[tuple[str, str], Method], built in__init__). A server therefore has no way to know, before it issues a 402 challenge, which payment methods/intents this client actually supports.Why this isn't hypothetical
Originally surfaced by the mpp-tools Agricola cross-SDK audit (finding AGR-2026-021), comparing this SDK's behavior against the canonical TypeScript reference implementation (
wevm/mppx), which derives anAccept-Paymentvalue from configured capabilities and adds it to initial requests by default.go,java,ruby, andrustwere all flagged clean in the same audit; onlypythondiverges.I independently confirmed this by reading
src/mpp/client/transport.pydirectly on currentmain: there is no code path, before or after the request read, that inspectsself._runtimeto build a header.Steps to reproduce
Fix
Opening a PR that adds a small
PaymentRuntime.accept_payment_header()helper deriving a value liketempo/charge, stripe/chargefrom the already-tracked(method, intent)pairs, and hasPaymentTransportset it on the initial request when the caller hasn't already supplied one explicitly. Includes regression tests for: header added when methods are configured, an explicit caller header is preserved, and no header is added when no methods are configured.