feat(mpp): add the MPP seller tutorial (two accounts, end to end) - #66
Draft
eruizgar91 wants to merge 1 commit into
Draft
feat(mpp): add the MPP seller tutorial (two accounts, end to end)#66eruizgar91 wants to merge 1 commit into
eruizgar91 wants to merge 1 commit into
Conversation
An ordinary Express service becomes a paid agent with one middleware, is
published to the Nevermined catalog, and is then bought and called by a
DIFFERENT account over MPP — no MPP secret on the seller, no protocol code
on the buyer.
Nothing is mocked: it publishes a real plan and a real agent and burns real
credits. The plan is free, so no testnet USDC is needed.
Four acts, in this order because each proves something the next one hides:
the raw 402/credential/receipt wire, the same call through
`payments.mpp.fetch`, the negatives (a replayed credential and a swapped
body, both refused), and the ledger.
Notes for reviewers:
- Two accounts are required and the demo refuses to run when the two keys
are identical — MPP is about one account buying from another.
- The API base is derived from the key prefix via `getEnvironmentFromApiKey`,
so no environment variable selects it; `NVM_API_BASE` only overrides.
- A live API key is refused outright, and so is a live API base. The two
checks are separate because `NVM_API_BASE` can point somewhere the key
did not choose.
- `captureRawBody` is load-bearing under `mpp: { bindBody: true }`:
re-serializing `req.body` does not reproduce the bytes the buyer signed
over, so the digest never matches.
- `selfcheck.mjs` runs the guards offline — no network, no credentials,
nothing written.
TRANSCRIPT.md is a real run against the sandbox API.
Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01KJ695LwV6AkP44PnfGxybu
eruizgar91
marked this pull request as draft
September 4, 2026 10:55
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
mpp-examples/mpp-seller/— the first MPP tutorial, and the first that needs two accounts.An ordinary Express service becomes a paid agent by adding one middleware, is published to the Nevermined catalog, and is then bought and called by a different account over MPP: no MPP secret on the seller, no protocol code on the buyer.
Nothing is mocked. It publishes a real plan and a real agent and burns real credits. The plan is free, so no testnet USDC is needed.
What it runs
Four acts, in this order because each proves something the next one hides:
fetch, the402withWWW-Authenticate: Payment,buildCredentialHeader, thePayment-Receipt.payments.mpp.fetch— the whole 402-retry cycle behind one call, which is what a buyer actually writes.Notes for reviewers
getEnvironmentFromApiKey(sandbox:maps toapi.sandbox.nevermined.app). No environment variable selects it;NVM_API_BASEonly overrides.NVM_API_BASEcan point somewhere the key prefix did not choose, so a key-prefix check alone would not hold. The tutorial publishes real rows and creates a real spend mandate ($50/1h, revoked at the end of every run).captureRawBodyis load-bearing undermpp: { bindBody: true }— re-serializingreq.bodydoes not reproduce the bytes the buyer signed over, so the digest never matches and every paid request is re-challenged. That is act 3's second negative.selfcheck.mjsruns the guards offline — no network, no credentials, nothing written. 12 checks, all green..demo-state.jsonreuses the pair by default;FORCE_PROVISION=yespublishes a new one.Verification
Run end to end against the sandbox API, reusing an existing plan/agent pair: selfcheck 12/12,
402then credential then200with a receipt,payments.mpp.fetchreportingpaid=true settled=true credentialsPresented=1 creditsPresented=1, replay and body-swap both402, 2 credits burned.TRANSCRIPT.mdis that run.Two disclosures are in the transcript itself: it ran against
api.sandbox.nevermined.dev(where the two demo accounts live — the base is derived from the key prefix either way), and one[DEPRECATED]line the SDK prints against itself was removed.Also
Root
README.mdgains the tutorial as entry 5;CLAUDE.mdgains thempp-examples/tree entry, an MPP row in the protocol table, and a short section on the three non-obvious things above.🤖 Generated with Claude Code
https://claude.ai/code/session_01KJ695LwV6AkP44PnfGxybu