feat: add MPP to server protocol detection, settlement, and omni-challenge#147
Merged
feat: add MPP to server protocol detection, settlement, and omni-challenge#147
Conversation
…mni-challenge - detectProtocol() now detects Authorization: Payment header as MPP - ProtocolSettlement.buildRequestBody() handles MPP credential format (parses base64/JSON MppCredential, extracts amount for settle) - buildMppChallenge() creates Tempo MPP challenge from payment options - serializeMppHeader() formats WWW-Authenticate: Payment header - omniChallengeMcpError() includes mpp field in JSON-RPC error data - omniChallengeHttpResponse() includes WWW-Authenticate header when MPP - buildOmniChallenge() accepts optional mppChallengeId - New MppChallengeData type exported from @atxp/server Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Review feedback fixes:
1. Re-export MPPChallenge from @atxp/mpp as MppChallengeData (no type duplication)
2. Add @atxp/mpp as dependency + tsconfig reference for @atxp/server
3. Document PATHUSD_DECIMALS constant (6 decimals assumption)
4. Fail explicitly on malformed MPP credentials (no silent { raw } fallback)
5. Safe amount extraction via String() instead of unsafe cast
6. Update detectProtocol docstring to document MPP detection
7. Fix stale comment in atxpExpress.ts ("X402 or ATXP" → "X402 or MPP")
8. Escape double-quotes in serializeMppHeader values
9. Move verify/mpp test to correct describe('verify') block
10. Add serializeMppHeader → parseMPPHeader round-trip test
11. Add tempo_moderato testnet test case for buildMppChallenge
Also: regenerate package-lock.json to fix stale nested @atxp/common@0.10.12
(was causing pre-existing typecheck failures across all workspace packages).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
SDK middleware (atxpExpress): - New resolveIdentity() extracts user identity from OAuth Bearer token (preferred) or wallet address in payment credential (fallback) - handleProtocolCredential passes sourceAccountId to settle calls via SettlementContext for payment recording/reconciliation in auth ProtocolSettlement: - buildRequestBody for X402 and MPP now includes optional sourceAccountId - SettlementContext type updated with sourceAccountId documentation Identity resolution priority: 1. OAuth sub from Authorization: Bearer (available for X402 since it uses a separate header; available in MCP sessions for MPP) 2. Wallet address from MPP credential (chain:address format) 3. For X402 without OAuth: auth resolves from Permit2 payer address Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Log warn (not debug) when Bearer token is present but check fails (indicates config problem, not just missing token) 2. Identity log changed to debug level (avoids wallet addresses in info) 3. Pass context to verify (not just settle) — enables account-level checks during verification 4. Add test: MPP settle includes sourceAccountId from wallet credential 5. Add test: X402 settle without OAuth has no sourceAccountId 6. Add unit tests: sourceAccountId included in settle body when context provides it (both X402 and MPP) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- buildRequestBody for MPP: remove dead amount extraction (auth gets
amount from credential.challenge.request.amount via mppx now)
- resolveIdentity: parse DID source string (did:pkh:eip155:<chainId>:<addr>)
instead of old {chain, address} object format
- Update all MPP test credentials to standard format:
challenge as object (id, method, intent, request),
payload with type discriminator (transaction/hash/proof),
source as DID string
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…nt, context docs 1. Wrap verify call in try/catch — malformed credentials return 400 instead of uncaught 500 2. Move ProtocolSettlement creation to atxpExpress closure — shared across all requests (stateless, no per-request allocation needed) 3. Document that X402 context.paymentRequirements is undefined in Express middleware (auth handles gracefully — facilitator can verify without them) 4. Re-export MPPChallenge directly instead of type alias (cleaner naming) 5. Pass settlement instance to handleProtocolCredential Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1. Add TODO comment on settlement-after-200 failure (known limitation — needs retry queue for production, payment is lost if settle fails) 2. Add comment explaining base_sepolia → base normalization in X402 challenges (X402 spec behavior) 3. Replace flaky setTimeout(50) in Express tests with promise-based approach (all tests now await settle completion via explicit resolve) Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
requirePayment now uses omniChallengeMcpError instead of paymentRequiredError. MCP errors now include X402 payment requirements and MPP challenge data alongside the existing ATXP-MCP payment request URL. This enables X402 and MPP protocol handlers in the client to detect and respond to challenges — previously they only saw ATXP-MCP data and the client always fell back to the ATXP flow regardless of feature flags. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Server: - omniChallengeMcpError now uses -32042 (MPP_ERROR_CODE) instead of -30402 (PAYMENT_REQUIRED_ERROR_CODE) - error.data contains both ATXP-MCP fields (paymentRequestId, etc.) and MPP fields (data.mpp) — standard MPP clients detect -32042, ATXP clients detect paymentRequestId in the same data Client: - atxpFetcher accepts BOTH -32042 (new omni) and -30402 (legacy) for backwards compatibility with old servers - mcpJson parser checks both error codes New constant: OMNI_PAYMENT_ERROR_CODE = -32042 exported from @atxp/common Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
When the client receives a -32042 MCP error with omni-challenge data, it now checks for X402/MPP data in error.data BEFORE falling back to the ATXP-MCP payment flow. New method buildSyntheticResponseFromMcpError() constructs a synthetic HTTP 402 Response from the MCP error data so existing protocol handlers (X402ProtocolHandler, MPPProtocolHandler) can detect and handle the challenge using their standard detection logic: - X402: x402Version in body - MPP: WWW-Authenticate: Payment header - Protocol flag selects preferred handler when multiple match Falls back to ATXP-MCP flow (handlePaymentRequestError) if no protocol handler matches — backwards compatible with servers that only emit ATXP-MCP challenges. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
ba40e87 to
fc5afe7
Compare
protocolHandlers now defaults to [X402ProtocolHandler, MPPProtocolHandler] instead of empty array. Developers no longer need to manually configure protocol support — all protocols work out of the box. ATXP-MCP doesn't need a handler — it's the native MCP payment flow that runs as the fallback when no protocol handler matches. Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
fc5afe7 to
875deb7
Compare
…t unnecessary)
The type re-export `export type { MPPChallenge as MppChallengeData }`
from @atxp/mpp caused CI failures because tsc --noEmit couldn't resolve
the cross-package type through rollup's dts output + project references.
Fix: define MppChallengeData inline (mirrors MPPChallenge from @atxp/mpp).
Remove project reference to atxp-mpp in tsconfig (only imported
MPP_ERROR_CODE at runtime, which resolves fine).
Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
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.
Summary
detectProtocol()now detectsAuthorization: Paymentheader as MPPProtocolSettlement.buildRequestBody()handles MPP credential format (parses base64/JSON MppCredential, extracts amount for settle)buildMppChallenge()creates Tempo MPP challenge from payment optionsserializeMppHeader()formatsWWW-Authenticate: PaymentheaderomniChallengeMcpError()includesmppfield in JSON-RPC error data (code -32042)omniChallengeHttpResponse()includesWWW-Authenticateheader when MPP availableMppChallengeDatatype exported from@atxp/serverReplaces #3 (which was written against a stale main before Phase 1/2 changes).
Test plan
🤖 Generated with Claude Code