|
| 1 | +# Tutorial 08 -- MPP (Machine Payments Protocol) |
| 2 | + |
| 3 | +| Information | Details | |
| 4 | +|:--------------------|:---------------------------------------------------------------------| |
| 5 | +| Tutorial type | Conversational | |
| 6 | +| Agent type | Single, payment-enabled | |
| 7 | +| Frameworks | Strands Agents | |
| 8 | +| LLM model | Anthropic Claude Sonnet 4 (`anthropic.claude-sonnet-4-6`) | |
| 9 | +| Components | `PaymentManager`, `AgentCorePaymentsPlugin`, MPP endpoints, sessions | |
| 10 | +| Complexity | Intermediate | |
| 11 | + |
| 12 | +> **Reads** the shared `.env` from Tutorial 00 (`PAYMENT_MANAGER_ARN`, `USER_ID`, `INSTRUMENT_ID`). |
| 13 | +> **Does** run a local agent that creates a per-run spending session in-code with the SDK and pays |
| 14 | +> an MPP endpoint automatically under a budget -- nothing new is deployed. |
| 15 | +> -> [How the pieces fit together](../README.md#cli-vs-sdk) |
| 16 | +
|
| 17 | +## Overview |
| 18 | + |
| 19 | +[MPP (Machine Payments Protocol)](https://mpp.dev/overview) is co-authored by Stripe and Tempo and |
| 20 | +is on the IETF standards track. It generalizes HTTP-402 into a payment-method-agnostic, intent-based |
| 21 | +**Challenge -> Credential -> Receipt** flow: |
| 22 | + |
| 23 | +- **Challenge** (server -> client): `WWW-Authenticate: Payment` -- declares cost, method, intent, expiry. |
| 24 | +- **Credential** (client -> server): `Authorization: Payment` -- proof of payment, bound to the challenge. |
| 25 | +- **Receipt** (server -> client): `Payment-Receipt` -- confirms acceptance (proof of delivery). |
| 26 | + |
| 27 | +The shared payment stack -- payment manager, connector, IAM roles, and a funded wallet (instrument) -- |
| 28 | +is already provisioned from [Tutorial 00](../00-setup-agentcore-payments/). Here your agent code uses |
| 29 | +the AgentCore SDK to open a **spending session** (a per-request budget you set per user) and pay each |
| 30 | +MPP 402 automatically. The `AgentCorePaymentsPlugin` intercepts the MPP challenge from the |
| 31 | +`http_request` tool and settles it -- zero payment logic in the agent code. MPP charge |
| 32 | +merchants advertise `feePayer=false`, so the buyer must authorize gas fees. |
| 33 | +The testnet endpoint (`mpp.dev`) sponsors gas (no config needed); mainnet merchants |
| 34 | +(Browserbase, etc.) require `buyer_pays_gas_fees=True` in the plugin config. |
| 35 | + |
| 36 | +> **MPP uses the Stripe/Privy instrument.** MPP `charge` merchants settle on the **Tempo** network |
| 37 | +> (Moderato testnet, chainId 42431). Coinbase-managed instruments cannot sign Tempo -- the service returns |
| 38 | +> "Tempo payments are not supported for Coinbase-managed payment instruments." Set |
| 39 | +> `CREDENTIAL_PROVIDER_TYPE=StripePrivy` in your `.env` and use the Privy instrument from Tutorial 00. |
| 40 | +
|
| 41 | +> **Billable resources.** Each successful MPP call spends stablecoin from your funded Tempo wallet |
| 42 | +> (testnet pathUSD on Moderato, or real funds on mainnet endpoints) and is metered by AgentCore payments. |
| 43 | +> See [AgentCore pricing](https://aws.amazon.com/bedrock/agentcore/pricing/). |
| 44 | +
|
| 45 | +> **Testnet recommended.** Use a Tempo Moderato testnet wallet funded with pathUSD (testnet tokens have |
| 46 | +> no monetary value) against a testnet MPP endpoint. Note the sample endpoints listed later are **live on |
| 47 | +> Tempo mainnet and settle real funds** -- only call those if you intend to pay real money. |
| 48 | +
|
| 49 | +> **Supported regions:** `us-east-1`, `us-west-2`, `eu-west-2`, `eu-central-1`, `ap-southeast-2`. |
| 50 | +
|
| 51 | +## Architecture |
| 52 | + |
| 53 | +### Strands |
| 54 | + |
| 55 | + |
| 56 | + |
| 57 | +``` |
| 58 | +Agent (Strands + http_request tool) |
| 59 | + | |
| 60 | + |--> http_request POST https://mpp.browserbase.com/search |
| 61 | + | | |
| 62 | + | Server returns HTTP 402 + WWW-Authenticate: Payment (MPP Challenge) |
| 63 | + | | |
| 64 | + | AgentCorePaymentsPlugin intercepts the 402 MPP challenge |
| 65 | + | | |
| 66 | + | ProcessPayment -> budget check -> sign Tempo tx -> return MPP Credential |
| 67 | + | | |
| 68 | + | Plugin retries http_request with Authorization: Payment <credential> |
| 69 | + | | |
| 70 | + |--> 200 OK + Payment-Receipt -- agent receives paid content |
| 71 | + | |
| 72 | + +--> Agent summarizes results for the user |
| 73 | +``` |
| 74 | + |
| 75 | +### MPP vs x402 |
| 76 | + |
| 77 | +| Aspect | x402 (Tutorials 01-07) | MPP (this tutorial) | |
| 78 | +|:------------------|:--------------------------------------|:------------------------------------------| |
| 79 | +| 402 challenge | `X-PAYMENT` header | `WWW-Authenticate: Payment` header | |
| 80 | +| Credential header | `X-PAYMENT` | `Authorization: Payment` | |
| 81 | +| Receipt | `X-PAYMENT-RESPONSE` | `Payment-Receipt` | |
| 82 | +| Settlement rail | Base via Coinbase CDP | Tempo via Stripe/Privy | |
| 83 | +| Test network | Base Sepolia | Tempo Moderato testnet (chain 42431) | |
| 84 | +| Intents | schemes: `exact`, `upto` | intents: `charge`, `session` | |
| 85 | + |
| 86 | +## Prerequisites |
| 87 | + |
| 88 | +- **Tutorial 00 completed** -- the shared `.env` (one directory up, at |
| 89 | + [`00-getting-started/.env`](../)) must contain `PAYMENT_MANAGER_ARN`, `USER_ID`, and |
| 90 | + `INSTRUMENT_ID`. The script reads these via `utils.load_tutorial_env()`. |
| 91 | +- **Stripe/Privy instrument** -- set `CREDENTIAL_PROVIDER_TYPE=StripePrivy` in `.env`. MPP charge on |
| 92 | + Tempo cannot be signed by a Coinbase-managed instrument. |
| 93 | +- **Funded Tempo wallet with delegated signing granted** -- the instrument's wallet must hold testnet |
| 94 | + pathUSD on Tempo and have delegated signing enabled (done in Tutorial 00). Without it, the 402 |
| 95 | + payment step fails. |
| 96 | +- **Python 3.10+** and AWS credentials configured (`aws sts get-caller-identity`). |
| 97 | +- **MPP-enabled SDK** -- MPP support requires `bedrock-agentcore >= 1.20.0`. The |
| 98 | + `AgentCorePaymentsPlugin` auto-detects MPP `WWW-Authenticate: Payment` 402 challenges |
| 99 | + natively (no extra code beyond `buyer_pays_gas_fees` on the plugin config): |
| 100 | + ```bash |
| 101 | + pip install -r requirements.txt |
| 102 | + ``` |
| 103 | + |
| 104 | +## Walkthrough |
| 105 | + |
| 106 | +### Step 1 -- Confirm Tutorial 00 populated the shared `.env` |
| 107 | + |
| 108 | +The agent loads its configuration from the shared `.env` one directory up. Confirm the keys it reads |
| 109 | +are present, and that the provider is set to Stripe/Privy: |
| 110 | + |
| 111 | +```bash |
| 112 | +grep -E 'PAYMENT_MANAGER_ARN|INSTRUMENT_ID|USER_ID|CREDENTIAL_PROVIDER_TYPE' ../.env |
| 113 | +``` |
| 114 | + |
| 115 | +If `PAYMENT_MANAGER_ARN`, `INSTRUMENT_ID`, or `USER_ID` is missing, re-run Tutorial 00 |
| 116 | +([`../00-setup-agentcore-payments/`](../00-setup-agentcore-payments/)). For MPP, make sure |
| 117 | +`CREDENTIAL_PROVIDER_TYPE=StripePrivy` so the Privy (Tempo-capable) instrument is used. |
| 118 | + |
| 119 | +### Step 2 -- Run the Strands agent |
| 120 | + |
| 121 | +```bash |
| 122 | +python strands_mpp_agent_testnet.py |
| 123 | +``` |
| 124 | + |
| 125 | +The script loads the manager ARN and Privy instrument from `.env`, creates a per-run spending session |
| 126 | +in-code with the SDK (`manager.create_payment_session(...)`, budget set by the `SESSION_BUDGET` |
| 127 | +constant near the top), wires up `AgentCorePaymentsPlugin`, and asks the agent to call the MPP |
| 128 | +endpoint -- the plugin settles the HTTP 402 MPP challenge automatically within the session budget. |
| 129 | +(This is the flow in the **Strands MPP Payment Flow** diagram under [Architecture](#architecture).) |
| 130 | + |
| 131 | +## Modules |
| 132 | + |
| 133 | +This tutorial includes two modules. Start with testnet (Module A) to learn the flow risk-free, |
| 134 | +then optionally graduate to mainnet (Module B) for a real-world use case. |
| 135 | + |
| 136 | +### Module A -- Testnet (default, zero cost) |
| 137 | + |
| 138 | +```bash |
| 139 | +python strands_mpp_agent_testnet.py |
| 140 | +``` |
| 141 | + |
| 142 | +Runs the full MPP happy path against `mpp.dev/api/ping/paid` on **Tempo Moderato testnet |
| 143 | +(chain 42431)**. Uses free test pathUSD tokens, the merchant covers gas fees. Anyone can run |
| 144 | +it safely to see the complete Challenge -> Credential -> Receipt flow with budget enforcement. |
| 145 | + |
| 146 | +- No real funds spent |
| 147 | +- Wallet funded via testnet faucet (`tempo_fundAddress`) |
| 148 | +- `buyer_pays_gas_fees=False` (merchant sponsors gas) |
| 149 | + |
| 150 | +### Module B -- Mainnet, competitive intelligence research (opt-in) |
| 151 | + |
| 152 | +```bash |
| 153 | +python strands_mpp_agent_mainnet.py |
| 154 | +``` |
| 155 | + |
| 156 | +A research assistant that pays **Browserbase** ($0.01/search) on **Tempo mainnet (chain 4217)** |
| 157 | +to gather competitive and market intelligence on a company or product, then summarizes findings. |
| 158 | +Returns real data from live paid APIs. |
| 159 | + |
| 160 | +- **Spends real funds** -- gated behind explicit opt-in confirmation |
| 161 | +- Wallet funded with real pathUSD on Tempo mainnet |
| 162 | +- `buyer_pays_gas_fees=True` (buyer covers gas) |
| 163 | +- Prompts for a research target, makes 2-3 paid searches, delivers a structured briefing |
| 164 | + |
| 165 | +### Which module to run |
| 166 | + |
| 167 | +| Goal | Module | Script | Cost | |
| 168 | +|:-----|:-------|:-------|:-----| |
| 169 | +| Learn the MPP flow (testnet, safe) | A | `strands_mpp_agent_testnet.py` | Free | |
| 170 | +| Real competitive research (mainnet) | B | `strands_mpp_agent_mainnet.py` | ~$0.01-0.03 | |
| 171 | + |
| 172 | +## Try different budgets (payment limits) |
| 173 | + |
| 174 | +Budget enforcement lives on the session. Change the budget by editing the constant near the top of |
| 175 | +the script, then re-run. For example, set a tiny budget smaller than the API cost: |
| 176 | + |
| 177 | +```python |
| 178 | +# strands_mpp_agent_testnet.py -- creates the session in-code |
| 179 | +SESSION_BUDGET = {"maxSpendAmount": {"value": "0.0001", "currency": "USD"}} |
| 180 | +``` |
| 181 | + |
| 182 | +Re-run the agent -- the payment is rejected because the $0.0001 budget is smaller than the API cost. |
| 183 | +Enforcement is structural (service-level), not agent logic. |
| 184 | + |
| 185 | +```python |
| 186 | +# Read a session's remaining budget in-code with the SDK: |
| 187 | +sess = manager.get_payment_session(user_id=USER_ID, payment_session_id=SESSION_ID) |
| 188 | +print(sess["availableLimits"]["availableSpendAmount"]) |
| 189 | +``` |
| 190 | + |
| 191 | +## What the agent does |
| 192 | + |
| 193 | +| Scenario | How to run it | What it shows | |
| 194 | +|:-------------|:---------------------------------------|:-----------------------------------------------------| |
| 195 | +| Happy path | Default run ($1.00 session) | The MPP 402 -> sign -> retry -> 200 flow, automatic | |
| 196 | +| Budget limit | Set the budget to `$0.0001`, re-run | Server-side budget enforcement rejects the payment | |
| 197 | +| Wrong rail | Use a Coinbase instrument | Service rejects Tempo for Coinbase-managed instrument | |
| 198 | + |
| 199 | +## Sample MPP endpoints (Tempo) |
| 200 | + |
| 201 | +> **These are live mainnet endpoints and settle real funds.** The costs below are charged in |
| 202 | +> real stablecoin on Tempo mainnet, not testnet. To run the happy path without spending real money, |
| 203 | +> point the agent at a testnet MPP endpoint (or your own MPP server on Tempo Moderato testnet, chain |
| 204 | +> 42431) and fund the wallet with testnet pathUSD. Only call the endpoints below if you intend to pay |
| 205 | +> real funds. |
| 206 | +
|
| 207 | +| Service | URL | Cost | |
| 208 | +|:------------|:-------------------------------------------------|:-------| |
| 209 | +| AgentMail | `GET https://mpp.api.agentmail.to/v0/inboxes` | free | |
| 210 | +| Browserbase | `POST https://mpp.browserbase.com/search` | $0.01 | |
| 211 | +| Allium | `POST https://agents.allium.so/api/v1/developer/prices` | $0.02 | |
| 212 | + |
| 213 | +## Security and compliance |
| 214 | + |
| 215 | +- [AWS Shared Responsibility Model](https://aws.amazon.com/compliance/shared-responsibility-model/) |
| 216 | +- [AgentCore payments security best practices](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/payments-security-best-practices.html) |
| 217 | + |
| 218 | +## Observability |
| 219 | + |
| 220 | +Enable observability on your Payment Manager to trace payment operations, monitor |
| 221 | +transaction success rates, and troubleshoot errors. AgentCore payments automatically |
| 222 | +generates spans and metrics for every data plane API call, viewable in Amazon CloudWatch |
| 223 | +and AWS X-Ray via AgentCore Observability. |
| 224 | + |
| 225 | +See: [AgentCore payments observability data](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/observability-payments-metrics.html) |
| 226 | + |
| 227 | +## Troubleshooting |
| 228 | + |
| 229 | +| Symptom | Cause | Resolution | |
| 230 | +|:--------|:------|:-----------| |
| 231 | +| ProcessPayment rejects the challenge over gas fees | The challenge advertises `feePayer=false` (buyer pays gas) and the request did not authorize it. | Set `buyer_pays_gas_fees=True` on the plugin config (already set in this tutorial). | |
| 232 | +| The payment does not settle (agent run stops) | Delegated signing not granted for the wallet, the wallet is not funded with testnet pathUSD, or a Coinbase instrument is configured. | Grant delegated signing (Tutorial 00), fund the Tempo wallet from the testnet faucet, and confirm a Stripe/Privy instrument. | |
| 233 | + |
| 234 | +## References |
| 235 | + |
| 236 | +- [AgentCore payments GA blog](https://aws.amazon.com/blogs/machine-learning/amazon-bedrock-agentcore-payments-is-now-generally-available-enabling-agents-to-transact-safely-and-autonomously-at-scale/) |
| 237 | +- [MPP Protocol Overview](https://mpp.dev/overview) |
| 238 | +- [MPP credential spec](https://mpp.dev/protocol/credentials) |
| 239 | +- [Payment HTTP Authentication spec](https://paymentauth.org/draft-httpauth-payment-00.html) |
| 240 | +- [AgentCore Payments pricing](https://aws.amazon.com/bedrock/agentcore/pricing/) |
0 commit comments