feat(x402): seller-side server executor + Express/FastMCP adapters - #119
Open
pcarranzav wants to merge 5 commits into
Open
feat(x402): seller-side server executor + Express/FastMCP adapters#119pcarranzav wants to merge 5 commits into
pcarranzav wants to merge 5 commits into
Conversation
pcarranzav
force-pushed
the
pcv/trm-ts-server-executor
branch
from
June 11, 2026 18:10
e809e27 to
8288d69
Compare
4 tasks
Introduce X402ServerExecutor, a FacilitatorX402ServerExecutor, and an AmpersendX402ServerExecutor that gates incoming payments on the Ampersend authorize-receipt compliance endpoint (SIWE-bearer auth, refresh-on-401, 5s timeout). Adds Express and FastMCP adapters. Both surfaces fail closed: a compliance deny or an Ampersend outage yields a generic "Payment rejected" and the payment is not honored. No reason/code/screening-id is leaked to the buyer; full detail is logged server-side. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Re-exporting the server executors from src/x402/index.ts pulled the node-only server deps (@x402/core/server -> node:module) into every consumer of that barrel — including ampersend/treasurer.ts, which is bundled into downstream browser/client builds. The executors remain available via the dedicated @ampersend_ai/ampersend-sdk/x402/server and .../x402/server/express subpaths. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
The Express adapter advertised x402Version: 1 but forwarded the @x402/core v2 requirements shape (amount/asset/extra), so x402 v1 buyers (and the authorize-receipt v1-wire contract) couldn't parse it. Introduce an AmpersendPaymentRequirements superset (v1 maxAmountRequired/resource/ description/mimeType + v2 amount) and fill resource per-request from the requested URL, so v1 buyers, authorize-receipt, and the @x402/core facilitator are all satisfied. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Rebasing onto main (which migrated to Effect v4, #105) changed Schema.Union to take an array of members rather than variadic args. Wrap the two discriminated-union branches in an array to match the v4 idiom used elsewhere in the codebase. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9mghCaNun4xKteXGWxPGP
pcarranzav
force-pushed
the
pcv/trm-ts-server-executor
branch
from
July 1, 2026 19:43
8288d69 to
5771daf
Compare
Reviewer nit: clarify that the FastMCP deny path emits 402 (retry-invited) while the Express adapter uses 403 to short-circuit retries. Both remain fail-closed and leak-free; comment-only, no behavior change. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_01M9mghCaNun4xKteXGWxPGP
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.
Seller-side x402 server executor (TypeScript)
Brings the seller-side compliance gate to the TypeScript SDK, mirroring the Python
AmpersendX402ServerExecutor/require_payment. Net-newsrc/x402/server/module — no existing behavior changes.What's added
X402ServerExecutorinterface (verifyPayment/settlePayment), using@x402/coreVerifyResponse/SettleResponse.FacilitatorX402ServerExecutor— delegates verify+settle to anx402ResourceServeroverHTTPFacilitatorClient.AmpersendX402ServerExecutor— composes the facilitator executor and gates incoming payments on Ampersend'sPOST /api/v1/agents/:address/payment/authorize-receipt:ApiClient), with refresh-on-401 (re-auth once, retry once).AbortController, overrideAMPERSEND_COMPLIANCE_API_TIMEOUT_SECONDS(seconds) — matches the Python env var."Payment rejected"and the payment is not honored. Noreason/reasonCode/screeningIdleaked to the buyer; full detail logged at WARNING server-side.exactscheme → deny"Unsupported payment scheme".withAmpersendX402Payment(framework-agnostic core), an Express middleware (ampersendPaymentMiddleware, behind the./x402/server/expresssubpath), and a FastMCP wiring (createExecutorOnPayment/withAmpersendX402PaymentMcp) backing the existingwithX402PaymentonPayment. All fail closed.Auth/client
ApiClient.authorizeReceipt(params, { signal })added (discriminated-union response, refresh-on-401), and the internal fetch now honors a callerAbortSignalcombined with the default-timeout signal.Exports
@ampersend_ai/ampersend-sdk//x402//x402/server: the executors,withAmpersendX402Payment, FastMCP helpers,GENERIC_DENY_REASON.@ampersend_ai/ampersend-sdk/x402/server/express:ampersendPaymentMiddleware(express is type-only here + behind its own subpath, so non-Express consumers pull no new coupling).Note on
express(one rejected review finding)The spec said express should be an optional peer dep, but
expressis already a hard runtime dependency of the shipped MCP proxy (src/mcp/proxy/server/server.ts, theampersend-proxybin) onmain— demoting it would break that. The intent (don't force express on consumers via the new adapter) is satisfied instead by a type-only import + dedicated subpath (dist/x402/server/express.jsemits zero expressrequire). No version bump;@x402/expressnot added (the adapter reimplements the route-keyed shape to route through the executor).Testing
pnpm build/pnpm lint/pnpm formatclean; 281/281 tests (28 files), incl. 18 new: deny→generic/no-leak, allow→verify→settle, facilitator failure, network/timeout fail-closed, 401→reauth+retry, unsupported-scheme, Express deny→403, FastMCP deny→402.