Engineering notes · 2026-09-14
Two bugs Stripe's own docs don't mention: building an agent-payable endpoint on MPP
Stripe's Machine Payments Protocol (MPP), co-authored with Tempo and launched in March 2026, lets an AI agent pay per request over plain HTTP 402 — no API key, no subscription, no crypto wallet. We wired it up on a real data endpoint. The sample code in the docs doesn't build, and the "default" integration path silently isn't fiat-only. Here's both bugs, found by installing the real package instead of trusting the docs.
The endpoint
We already run a free fundamentals lookup — real SEC EDGAR XBRL numbers (EPS, book value, working capital), not an LLM guessing — behind the calculator tools on this site. MPP made it possible to expose the same lookup to an AI agent directly: no signup on their end, $0.50 per ticker, settled instantly over HTTP. We'd already tried the crypto route (x402 + on-chain USDC settlement) for a different product and hit a real dead end — the free facilitator only settles EVM on testnet, and the x402 Solana SDK is broken against current solanareleases. MPP sidesteps both problems: it settles through Stripe's existing card rails via Shared Payment Tokens, so there's no facilitator and no wallet on either side.
Bug 1: the "default" method isn't fiat-only
Stripe's integration sample calls a convenience method to set up payment options. It reads like the safe, boring choice. It isn't — read the actual shipped type declarations instead of the doc prose and the method returns two payment methods with the same intent, one of which is crypto:
// Stripe's own sample uses defaultMethods(). Reading the shipped .d.ts
// shows that actually returns [TempoServer, SptServer] -- a crypto
// (Tempo) method AND a card (SPT) method, both declaring the same
// "charge" intent. Two methods sharing an intent turns mppx.charge()
// into an ambiguous multi-method call, and silently pulls in a Tempo
// deposit-address dependency you never configured.
// Broken (from the docs):
const stripeMachinePayments = stripeMpp.create({ client: stripe, networkId, livemode });
const mppx = Mppx.create({
methods: [stripeMachinePayments.defaultMethods()], // crypto + card, ambiguous
secretKey,
});
// Fixed -- explicit card-only method, confirmed in a different section
// of the same docs:
const mppx = Mppx.create({
methods: [stripeMachinePayments.spt.charge()], // Shared Payment Token, fiat only
secretKey,
});Nothing about this fails loudly. The build succeeds, the type checker is happy, and the ambiguity only shows up as a confusing runtime error the first time mppx.charge() tries to resolve which method to use. If your integration is meant to be card-only, say so explicitly.
Bug 2: an unreachable import still breaks the build
Second failure, unrelated to the first: a clean Turbopack production build failed on a module our code never imports.
// Turbopack failure: "Module not found: @modelcontextprotocol/sdk/types.js"
// mppx's server bundle has a dynamic import of the MCP SDK, used only on
// an error path for MCP-transport mode -- which a plain HTTP Request/
// Response handler never touches. Turbopack still resolves it statically
// because it's reachable in the module graph, and fails the whole build.
// Fix: install the dependency even though nothing in your code calls it.
npm install @modelcontextprotocol/sdkThe package's server bundle has a dynamic import used only on an MCP-transport error path — dead code for anyone using a plain HTTP handler, which is most people. Turbopack still walks the whole module graph statically and fails the build on a dependency you'll never actually load. The fix costs one unused line in package.json, but it's the kind of failure that only shows up once you actually run npm run buildagainst the real package, not against the doc's copy-pasted snippet.
The general lesson
Both bugs share a cause: the docs describe intent, not the shipped code. "Use the default methods" sounds safe until you read the type declarations and find crypto bundled in. "Just import the client" sounds safe until Turbopack statically resolves a dependency your runtime path never touches. For anything new enough that the ecosystem hasn't stress-tested it yet, run the real build against the real package before trusting the sample — the failure mode is never in the part of the code you copied.
Charging AI agents by the request for grounded, sourced data is still new enough that nobody has fully worked the kinks out of the tooling — which is exactly why it's worth building on now. Every Edge Thirteen value pick already runs on the same SEC-sourced numbers this endpoint serves; the method is on how it works. Same discipline (never trust a plausible-looking result you haven't checked against an outside source) is what caught a silent field-mapping bug in bank regulatory data.
Share:X / TwitterLinkedIn
See the pipeline this feeds
Every week, Edge Thirteen screens roughly 5,000 U.S. stocks, reads the 10-K on every name that clears the numbers, and sends the genuine value picks — for $13/month. Cancel anytime.
Secure checkout via Stripe · cancel anytime · terms & refund policy.
Not ready for $13/month?
Get one real issue free, no card required — the same research, sent straight to your inbox.