mppx 0.6.31 → 0.7.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +17 -0
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +9 -7
- package/dist/Challenge.js.map +1 -1
- package/dist/Constants.d.ts +46 -0
- package/dist/Constants.d.ts.map +1 -0
- package/dist/Constants.js +46 -0
- package/dist/Constants.js.map +1 -0
- package/dist/Credential.d.ts.map +1 -1
- package/dist/Credential.js +5 -4
- package/dist/Credential.js.map +1 -1
- package/dist/Method.d.ts +32 -4
- package/dist/Method.d.ts.map +1 -1
- package/dist/Method.js +5 -2
- package/dist/Method.js.map +1 -1
- package/dist/Receipt.d.ts.map +1 -1
- package/dist/Receipt.js +3 -2
- package/dist/Receipt.js.map +1 -1
- package/dist/cli/cli.d.ts.map +1 -1
- package/dist/cli/cli.js +19 -11
- package/dist/cli/cli.js.map +1 -1
- package/dist/cli/plugins/tempo.d.ts.map +1 -1
- package/dist/cli/plugins/tempo.js +17 -6
- package/dist/cli/plugins/tempo.js.map +1 -1
- package/dist/cli/utils.d.ts +5 -0
- package/dist/cli/utils.d.ts.map +1 -1
- package/dist/cli/utils.js +10 -0
- package/dist/cli/utils.js.map +1 -1
- package/dist/client/Methods.d.ts +5 -2
- package/dist/client/Methods.d.ts.map +1 -1
- package/dist/client/Methods.js +5 -2
- package/dist/client/Methods.js.map +1 -1
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +4 -5
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/index.d.ts +2 -1
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +2 -1
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +14 -6
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/evm/server/Methods.d.ts +1 -1
- package/dist/evm/server/Methods.d.ts.map +1 -1
- package/dist/index.d.ts +1 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +1 -0
- package/dist/index.js.map +1 -1
- package/dist/internal/AcceptPayment.d.ts +3 -0
- package/dist/internal/AcceptPayment.d.ts.map +1 -1
- package/dist/internal/AcceptPayment.js +15 -11
- package/dist/internal/AcceptPayment.js.map +1 -1
- package/dist/mcp-sdk/client/McpClient.d.ts +12 -5
- package/dist/mcp-sdk/client/McpClient.d.ts.map +1 -1
- package/dist/mcp-sdk/client/McpClient.js +55 -42
- package/dist/mcp-sdk/client/McpClient.js.map +1 -1
- package/dist/server/Mppx.d.ts +11 -3
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +76 -27
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Response.d.ts.map +1 -1
- package/dist/server/Response.js +2 -1
- package/dist/server/Response.js.map +1 -1
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +4 -3
- package/dist/server/Transport.js.map +1 -1
- package/dist/server/index.d.ts +1 -0
- package/dist/server/index.d.ts.map +1 -1
- package/dist/server/index.js +1 -0
- package/dist/server/index.js.map +1 -1
- package/dist/stripe/client/Charge.d.ts +1 -1
- package/dist/stripe/client/Charge.d.ts.map +1 -1
- package/dist/stripe/client/Charge.js +3 -1
- package/dist/stripe/client/Charge.js.map +1 -1
- package/dist/stripe/server/Charge.d.ts +1 -1
- package/dist/stripe/server/Charge.d.ts.map +1 -1
- package/dist/stripe/server/Charge.js +9 -2
- package/dist/stripe/server/Charge.js.map +1 -1
- package/dist/stripe/server/Methods.d.ts +1 -1
- package/dist/stripe/server/Methods.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts +1 -1
- package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
- package/dist/stripe/server/internal/html.gen.js +1 -1
- package/dist/stripe/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/Methods.d.ts +18 -0
- package/dist/tempo/Methods.d.ts.map +1 -1
- package/dist/tempo/Methods.js +16 -1
- package/dist/tempo/Methods.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts +6 -0
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +9 -2
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +36 -7
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/client/Methods.js +12 -5
- package/dist/tempo/client/Methods.js.map +1 -1
- package/dist/tempo/client/index.d.ts +7 -4
- package/dist/tempo/client/index.d.ts.map +1 -1
- package/dist/tempo/client/index.js +5 -3
- package/dist/tempo/client/index.js.map +1 -1
- package/dist/tempo/index.d.ts +1 -0
- package/dist/tempo/index.d.ts.map +1 -1
- package/dist/tempo/index.js +1 -0
- package/dist/tempo/index.js.map +1 -1
- package/dist/tempo/internal/fee-payer.d.ts +21 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +109 -4
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/{client → legacy/client}/ChannelOps.d.ts +19 -6
- package/dist/tempo/legacy/client/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/{client → legacy/client}/ChannelOps.js +9 -3
- package/dist/tempo/legacy/client/ChannelOps.js.map +1 -0
- package/dist/tempo/{client → legacy/client}/Session.d.ts +23 -4
- package/dist/tempo/legacy/client/Session.d.ts.map +1 -0
- package/dist/tempo/{client → legacy/client}/Session.js +14 -7
- package/dist/tempo/legacy/client/Session.js.map +1 -0
- package/dist/tempo/{client → legacy/client}/SessionManager.d.ts +20 -5
- package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -0
- package/dist/tempo/{client → legacy/client}/SessionManager.js +20 -16
- package/dist/tempo/legacy/client/SessionManager.js.map +1 -0
- package/dist/tempo/legacy/client/index.d.ts +7 -0
- package/dist/tempo/legacy/client/index.d.ts.map +1 -0
- package/dist/tempo/legacy/client/index.js +5 -0
- package/dist/tempo/legacy/client/index.js.map +1 -0
- package/dist/tempo/legacy/index.d.ts +7 -0
- package/dist/tempo/legacy/index.d.ts.map +1 -0
- package/dist/tempo/legacy/index.js +7 -0
- package/dist/tempo/legacy/index.js.map +1 -0
- package/dist/tempo/{server → legacy/server}/Session.d.ts +28 -11
- package/dist/tempo/legacy/server/Session.d.ts.map +1 -0
- package/dist/tempo/{server → legacy/server}/Session.js +12 -10
- package/dist/tempo/legacy/server/Session.js.map +1 -0
- package/dist/tempo/legacy/server/index.d.ts +5 -0
- package/dist/tempo/legacy/server/index.d.ts.map +1 -0
- package/dist/tempo/legacy/server/index.js +5 -0
- package/dist/tempo/legacy/server/index.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/Chain.d.ts +30 -23
- package/dist/tempo/legacy/session/Chain.d.ts.map +1 -0
- package/dist/tempo/{session → legacy/session}/Chain.js +12 -11
- package/dist/tempo/legacy/session/Chain.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/Channel.d.ts +1 -0
- package/dist/tempo/legacy/session/Channel.d.ts.map +1 -0
- package/dist/tempo/legacy/session/Channel.js.map +1 -0
- package/dist/tempo/legacy/session/ChannelStore.d.ts +22 -0
- package/dist/tempo/legacy/session/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/legacy/session/ChannelStore.js +6 -0
- package/dist/tempo/legacy/session/ChannelStore.js.map +1 -0
- package/dist/tempo/legacy/session/Types.d.ts +73 -0
- package/dist/tempo/legacy/session/Types.d.ts.map +1 -0
- package/dist/tempo/legacy/session/Types.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/Voucher.d.ts +4 -4
- package/dist/tempo/legacy/session/Voucher.d.ts.map +1 -0
- package/dist/tempo/{session → legacy/session}/Voucher.js +1 -1
- package/dist/tempo/legacy/session/Voucher.js.map +1 -0
- package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts +1 -0
- package/dist/tempo/{session → legacy/session}/escrow.abi.d.ts.map +1 -1
- package/dist/tempo/{session → legacy/session}/escrow.abi.js +1 -0
- package/dist/tempo/legacy/session/escrow.abi.js.map +1 -0
- package/dist/tempo/legacy/session/index.d.ts +9 -0
- package/dist/tempo/legacy/session/index.d.ts.map +1 -0
- package/dist/tempo/legacy/session/index.js +9 -0
- package/dist/tempo/legacy/session/index.js.map +1 -0
- package/dist/tempo/server/Charge.d.ts +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +13 -16
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +63 -6
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +36 -8
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +1 -1
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/index.d.ts +6 -5
- package/dist/tempo/server/index.d.ts.map +1 -1
- package/dist/tempo/server/index.js +5 -5
- package/dist/tempo/server/index.js.map +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts +1 -1
- package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
- package/dist/tempo/server/internal/html.gen.js +1 -1
- package/dist/tempo/server/internal/html.gen.js.map +1 -1
- package/dist/tempo/server/internal/request-body.d.ts +7 -2
- package/dist/tempo/server/internal/request-body.d.ts.map +1 -1
- package/dist/tempo/server/internal/request-body.js +20 -3
- package/dist/tempo/server/internal/request-body.js.map +1 -1
- package/dist/tempo/server/internal/transport.d.ts +8 -4
- package/dist/tempo/server/internal/transport.d.ts.map +1 -1
- package/dist/tempo/server/internal/transport.js +8 -7
- package/dist/tempo/server/internal/transport.js.map +1 -1
- package/dist/tempo/session/Snapshot.d.ts +32 -0
- package/dist/tempo/session/Snapshot.d.ts.map +1 -0
- package/dist/tempo/session/Snapshot.js +37 -0
- package/dist/tempo/session/Snapshot.js.map +1 -0
- package/dist/tempo/session/client/ChannelOps.d.ts +82 -0
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/session/client/ChannelOps.js +204 -0
- package/dist/tempo/session/client/ChannelOps.js.map +1 -0
- package/dist/tempo/session/client/CredentialState.d.ts +262 -0
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -0
- package/dist/tempo/session/client/CredentialState.js +417 -0
- package/dist/tempo/session/client/CredentialState.js.map +1 -0
- package/dist/tempo/session/client/ReceiptCoordinator.d.ts +26 -0
- package/dist/tempo/session/client/ReceiptCoordinator.d.ts.map +1 -0
- package/dist/tempo/session/client/ReceiptCoordinator.js +61 -0
- package/dist/tempo/session/client/ReceiptCoordinator.js.map +1 -0
- package/dist/tempo/session/client/Runtime.d.ts +464 -0
- package/dist/tempo/session/client/Runtime.d.ts.map +1 -0
- package/dist/tempo/session/client/Runtime.js +499 -0
- package/dist/tempo/session/client/Runtime.js.map +1 -0
- package/dist/tempo/session/client/Session.d.ts +132 -0
- package/dist/tempo/session/client/Session.d.ts.map +1 -0
- package/dist/tempo/session/client/Session.js +55 -0
- package/dist/tempo/session/client/Session.js.map +1 -0
- package/dist/tempo/session/client/SessionManager.d.ts +120 -0
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -0
- package/dist/tempo/session/client/SessionManager.js +627 -0
- package/dist/tempo/session/client/SessionManager.js.map +1 -0
- package/dist/tempo/session/client/Transports.d.ts +449 -0
- package/dist/tempo/session/client/Transports.d.ts.map +1 -0
- package/dist/tempo/session/client/Transports.js +721 -0
- package/dist/tempo/session/client/Transports.js.map +1 -0
- package/dist/tempo/session/client/index.d.ts +12 -0
- package/dist/tempo/session/client/index.d.ts.map +1 -0
- package/dist/tempo/session/client/index.js +5 -0
- package/dist/tempo/session/client/index.js.map +1 -0
- package/dist/tempo/session/index.d.ts +7 -8
- package/dist/tempo/session/index.d.ts.map +1 -1
- package/dist/tempo/session/index.js +7 -8
- package/dist/tempo/session/index.js.map +1 -1
- package/dist/tempo/session/precompile/Chain.d.ts +319 -0
- package/dist/tempo/session/precompile/Chain.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Chain.js +492 -0
- package/dist/tempo/session/precompile/Chain.js.map +1 -0
- package/dist/tempo/session/precompile/Channel.d.ts +46 -0
- package/dist/tempo/session/precompile/Channel.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Channel.js +56 -0
- package/dist/tempo/session/precompile/Channel.js.map +1 -0
- package/dist/tempo/session/precompile/Protocol.d.ts +308 -0
- package/dist/tempo/session/precompile/Protocol.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Protocol.js +264 -0
- package/dist/tempo/session/precompile/Protocol.js.map +1 -0
- package/dist/tempo/session/precompile/Voucher.d.ts +40 -0
- package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -0
- package/dist/tempo/session/precompile/Voucher.js +126 -0
- package/dist/tempo/session/precompile/Voucher.js.map +1 -0
- package/dist/tempo/session/precompile/escrow.abi.d.ts +522 -0
- package/dist/tempo/session/precompile/escrow.abi.d.ts.map +1 -0
- package/dist/tempo/session/precompile/escrow.abi.js +224 -0
- package/dist/tempo/session/precompile/escrow.abi.js.map +1 -0
- package/dist/tempo/session/precompile/index.d.ts +24 -0
- package/dist/tempo/session/precompile/index.d.ts.map +1 -0
- package/dist/tempo/session/precompile/index.js +22 -0
- package/dist/tempo/session/precompile/index.js.map +1 -0
- package/dist/tempo/session/server/ChannelOps.d.ts +56 -0
- package/dist/tempo/session/server/ChannelOps.d.ts.map +1 -0
- package/dist/tempo/session/server/ChannelOps.js +91 -0
- package/dist/tempo/session/server/ChannelOps.js.map +1 -0
- package/dist/tempo/session/server/ChannelStore.d.ts +347 -0
- package/dist/tempo/session/server/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/session/server/ChannelStore.js +404 -0
- package/dist/tempo/session/server/ChannelStore.js.map +1 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts +85 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -0
- package/dist/tempo/session/server/CredentialVerification.js +494 -0
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -0
- package/dist/tempo/session/server/MeteredStream.d.ts +40 -0
- package/dist/tempo/session/server/MeteredStream.d.ts.map +1 -0
- package/dist/tempo/session/server/MeteredStream.js +42 -0
- package/dist/tempo/session/server/MeteredStream.js.map +1 -0
- package/dist/tempo/session/server/RequestState.d.ts +208 -0
- package/dist/tempo/session/server/RequestState.d.ts.map +1 -0
- package/dist/tempo/session/server/RequestState.js +252 -0
- package/dist/tempo/session/server/RequestState.js.map +1 -0
- package/dist/tempo/session/server/Session.d.ts +169 -0
- package/dist/tempo/session/server/Session.d.ts.map +1 -0
- package/dist/tempo/session/server/Session.js +351 -0
- package/dist/tempo/session/server/Session.js.map +1 -0
- package/dist/tempo/session/server/Settlement.d.ts +185 -0
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -0
- package/dist/tempo/session/server/Settlement.js +250 -0
- package/dist/tempo/session/server/Settlement.js.map +1 -0
- package/dist/tempo/session/{Sse.d.ts → server/Sse.d.ts} +9 -56
- package/dist/tempo/session/server/Sse.d.ts.map +1 -0
- package/dist/tempo/session/server/Sse.js +184 -0
- package/dist/tempo/session/server/Sse.js.map +1 -0
- package/dist/tempo/session/server/Transports.d.ts +89 -0
- package/dist/tempo/session/server/Transports.d.ts.map +1 -0
- package/dist/tempo/session/server/Transports.js +149 -0
- package/dist/tempo/session/server/Transports.js.map +1 -0
- package/dist/tempo/session/server/Ws.d.ts +48 -0
- package/dist/tempo/session/server/Ws.d.ts.map +1 -0
- package/dist/tempo/session/server/Ws.js +244 -0
- package/dist/tempo/session/server/Ws.js.map +1 -0
- package/dist/tempo/session/server/index.d.ts +4 -0
- package/dist/tempo/session/server/index.d.ts.map +1 -0
- package/dist/tempo/session/server/index.js +2 -0
- package/dist/tempo/session/server/index.js.map +1 -0
- package/package.json +6 -1
- package/src/Challenge.ts +9 -7
- package/src/Constants.ts +58 -0
- package/src/Credential.ts +5 -4
- package/src/Method.ts +46 -5
- package/src/Receipt.ts +3 -2
- package/src/cli/cli.test.ts +23 -28
- package/src/cli/cli.ts +23 -10
- package/src/cli/mcp.test.ts +21 -7
- package/src/cli/plugins/tempo.ts +21 -8
- package/src/cli/utils.test.ts +25 -1
- package/src/cli/utils.ts +10 -0
- package/src/client/Methods.ts +5 -2
- package/src/client/Mppx.test-d.ts +10 -0
- package/src/client/Mppx.test.ts +75 -0
- package/src/client/Transport.ts +4 -5
- package/src/client/index.ts +11 -1
- package/src/client/internal/Fetch.test.ts +29 -4
- package/src/client/internal/Fetch.ts +17 -5
- package/src/env.d.ts +1 -1
- package/src/index.ts +1 -0
- package/src/internal/AcceptPayment.test.ts +61 -0
- package/src/internal/AcceptPayment.ts +21 -14
- package/src/mcp-sdk/client/McpClient.integration.test.ts +8 -7
- package/src/mcp-sdk/client/McpClient.test-d.ts +7 -0
- package/src/mcp-sdk/client/McpClient.ts +99 -67
- package/src/mcp-sdk/client/McpClient.unit.test.ts +131 -0
- package/src/middlewares/elysia.test.ts +8 -4
- package/src/middlewares/express.test.ts +8 -4
- package/src/middlewares/hono.test.ts +4 -4
- package/src/middlewares/nextjs.test.ts +8 -4
- package/src/proxy/Proxy.test.ts +8 -8
- package/src/server/Mppx.test-d.ts +54 -0
- package/src/server/Mppx.test.ts +200 -7
- package/src/server/Mppx.ts +487 -406
- package/src/server/Response.ts +2 -1
- package/src/server/Transport.ts +4 -3
- package/src/server/index.ts +1 -0
- package/src/stripe/client/Charge.test.ts +20 -5
- package/src/stripe/client/Charge.ts +6 -2
- package/src/stripe/server/Charge.test.ts +114 -1
- package/src/stripe/server/Charge.ts +13 -2
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/AccessKeyAuthorization.test.ts +4 -94
- package/src/tempo/Methods.test.ts +45 -17
- package/src/tempo/Methods.ts +22 -0
- package/src/tempo/PublicExports.test-d.ts +105 -0
- package/src/tempo/client/Charge.test.ts +85 -0
- package/src/tempo/client/Charge.ts +19 -2
- package/src/tempo/client/Methods.ts +18 -6
- package/src/tempo/client/index.ts +15 -4
- package/src/tempo/index.ts +1 -0
- package/src/tempo/internal/fee-payer.test.ts +241 -17
- package/src/tempo/internal/fee-payer.ts +150 -4
- package/src/tempo/internal/fee-token.test.ts +14 -9
- package/src/tempo/legacy/AccessKeyAuthorization.test.ts +162 -0
- package/src/tempo/legacy/README.md +9 -0
- package/src/tempo/{client → legacy/client}/ChannelOps.test.ts +6 -7
- package/src/tempo/{client → legacy/client}/ChannelOps.ts +22 -9
- package/src/tempo/{client → legacy/client}/Session.test.ts +51 -9
- package/src/tempo/{client → legacy/client}/Session.ts +25 -11
- package/src/tempo/{client → legacy/client}/SessionManager.test.ts +81 -9
- package/src/tempo/{client → legacy/client}/SessionManager.ts +41 -20
- package/src/tempo/legacy/client/index.ts +6 -0
- package/src/tempo/legacy/index.ts +6 -0
- package/src/tempo/{server → legacy/server}/Session.test.ts +45 -45
- package/src/tempo/{server → legacy/server}/Session.ts +32 -23
- package/src/tempo/legacy/server/index.ts +4 -0
- package/src/tempo/{session → legacy/session}/Chain.test.ts +3 -4
- package/src/tempo/{session → legacy/session}/Chain.ts +94 -63
- package/src/tempo/{session → legacy/session}/Channel.ts +1 -0
- package/src/tempo/legacy/session/ChannelStore.test.ts +58 -0
- package/src/tempo/legacy/session/ChannelStore.ts +39 -0
- package/src/tempo/legacy/session/Types.ts +91 -0
- package/src/tempo/{session → legacy/session}/Voucher.ts +12 -8
- package/src/tempo/{session → legacy/session}/escrow.abi.ts +1 -0
- package/src/tempo/legacy/session/index.ts +8 -0
- package/src/tempo/server/AtomicStore.test-d.ts +16 -11
- package/src/tempo/server/Charge.test.ts +92 -14
- package/src/tempo/server/Charge.ts +18 -16
- package/src/tempo/server/Methods.ts +54 -8
- package/src/tempo/server/Sse.test.ts +2 -2
- package/src/tempo/server/index.ts +6 -5
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/server/internal/request-body.test.ts +37 -4
- package/src/tempo/server/internal/request-body.ts +25 -6
- package/src/tempo/server/internal/transport.test.ts +4 -4
- package/src/tempo/server/internal/transport.ts +19 -10
- package/src/tempo/session/Snapshot.test.ts +41 -0
- package/src/tempo/session/Snapshot.ts +74 -0
- package/src/tempo/session/client/ChannelOps.test.ts +163 -0
- package/src/tempo/session/client/ChannelOps.ts +344 -0
- package/src/tempo/session/client/CredentialState.test.ts +645 -0
- package/src/tempo/session/client/CredentialState.ts +814 -0
- package/src/tempo/session/client/ReceiptCoordinator.ts +95 -0
- package/src/tempo/session/client/Runtime.test.ts +1092 -0
- package/src/tempo/session/client/Runtime.ts +986 -0
- package/src/tempo/session/client/Session.test.ts +734 -0
- package/src/tempo/session/client/Session.ts +97 -0
- package/src/tempo/session/client/SessionManager.test.ts +1308 -0
- package/src/tempo/session/client/SessionManager.ts +845 -0
- package/src/tempo/session/client/Transports.test.ts +837 -0
- package/src/tempo/session/client/Transports.ts +1292 -0
- package/src/tempo/session/client/index.ts +37 -0
- package/src/tempo/session/index.ts +7 -8
- package/src/tempo/session/precompile/Chain.integration.test.ts +321 -0
- package/src/tempo/session/precompile/Chain.test.ts +1258 -0
- package/src/tempo/session/precompile/Chain.ts +979 -0
- package/src/tempo/session/precompile/Channel.test.ts +138 -0
- package/src/tempo/session/precompile/Channel.ts +103 -0
- package/src/tempo/session/precompile/Protocol.test.ts +358 -0
- package/src/tempo/session/precompile/Protocol.ts +520 -0
- package/src/tempo/session/precompile/Voucher.test.ts +316 -0
- package/src/tempo/session/precompile/Voucher.ts +160 -0
- package/src/tempo/session/precompile/escrow.abi.ts +226 -0
- package/src/tempo/session/precompile/index.ts +33 -0
- package/src/tempo/session/server/ChannelOps.test.ts +129 -0
- package/src/tempo/session/server/ChannelOps.ts +157 -0
- package/src/tempo/session/{ChannelStore.test.ts → server/ChannelStore.test.ts} +536 -29
- package/src/tempo/session/server/ChannelStore.ts +835 -0
- package/src/tempo/session/server/CredentialVerification.test.ts +146 -0
- package/src/tempo/session/server/CredentialVerification.ts +710 -0
- package/src/tempo/session/server/MeteredStream.ts +88 -0
- package/src/tempo/session/server/RequestState.test.ts +531 -0
- package/src/tempo/session/server/RequestState.ts +499 -0
- package/src/tempo/session/server/Session.integration.test.ts +444 -0
- package/src/tempo/session/server/Session.test.ts +3253 -0
- package/src/tempo/session/server/Session.ts +543 -0
- package/src/tempo/session/server/Settlement.test.ts +242 -0
- package/src/tempo/session/server/Settlement.ts +470 -0
- package/src/tempo/session/{Sse.test.ts → server/Sse.test.ts} +37 -3
- package/src/tempo/session/server/Sse.ts +256 -0
- package/src/tempo/session/server/Transports.test.ts +346 -0
- package/src/tempo/session/server/Transports.ts +255 -0
- package/src/tempo/session/{Ws.test.ts → server/Ws.test.ts} +4 -4
- package/src/tempo/session/server/Ws.ts +384 -0
- package/src/tempo/session/server/index.ts +8 -0
- package/dist/tempo/client/ChannelOps.d.ts.map +0 -1
- package/dist/tempo/client/ChannelOps.js.map +0 -1
- package/dist/tempo/client/Session.d.ts.map +0 -1
- package/dist/tempo/client/Session.js.map +0 -1
- package/dist/tempo/client/SessionManager.d.ts.map +0 -1
- package/dist/tempo/client/SessionManager.js.map +0 -1
- package/dist/tempo/server/Session.d.ts.map +0 -1
- package/dist/tempo/server/Session.js.map +0 -1
- package/dist/tempo/session/Chain.d.ts.map +0 -1
- package/dist/tempo/session/Chain.js.map +0 -1
- package/dist/tempo/session/Channel.d.ts.map +0 -1
- package/dist/tempo/session/Channel.js.map +0 -1
- package/dist/tempo/session/ChannelStore.d.ts +0 -117
- package/dist/tempo/session/ChannelStore.d.ts.map +0 -1
- package/dist/tempo/session/ChannelStore.js +0 -172
- package/dist/tempo/session/ChannelStore.js.map +0 -1
- package/dist/tempo/session/Receipt.d.ts +0 -22
- package/dist/tempo/session/Receipt.d.ts.map +0 -1
- package/dist/tempo/session/Receipt.js +0 -34
- package/dist/tempo/session/Receipt.js.map +0 -1
- package/dist/tempo/session/Sse.d.ts.map +0 -1
- package/dist/tempo/session/Sse.js +0 -363
- package/dist/tempo/session/Sse.js.map +0 -1
- package/dist/tempo/session/Types.d.ts +0 -78
- package/dist/tempo/session/Types.d.ts.map +0 -1
- package/dist/tempo/session/Types.js.map +0 -1
- package/dist/tempo/session/Voucher.d.ts.map +0 -1
- package/dist/tempo/session/Voucher.js.map +0 -1
- package/dist/tempo/session/Ws.d.ts +0 -87
- package/dist/tempo/session/Ws.d.ts.map +0 -1
- package/dist/tempo/session/Ws.js +0 -443
- package/dist/tempo/session/Ws.js.map +0 -1
- package/dist/tempo/session/escrow.abi.js.map +0 -1
- package/src/tempo/session/ChannelStore.ts +0 -308
- package/src/tempo/session/Receipt.test.ts +0 -89
- package/src/tempo/session/Receipt.ts +0 -46
- package/src/tempo/session/Sse.ts +0 -462
- package/src/tempo/session/Types.ts +0 -86
- package/src/tempo/session/Ws.ts +0 -576
- /package/dist/tempo/{session → legacy/session}/Channel.js +0 -0
- /package/dist/tempo/{session → legacy/session}/Types.js +0 -0
- /package/src/tempo/{session → legacy/session}/Channel.test.ts +0 -0
- /package/src/tempo/{session → legacy/session}/Voucher.test.ts +0 -0
- /package/src/tempo/session/{Sse.fuzz.test.ts → server/Sse.fuzz.test.ts} +0 -0
|
@@ -4,7 +4,7 @@ import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
|
|
|
4
4
|
import { createClient, defineChain } from 'viem'
|
|
5
5
|
import { describe, expect, test, vi } from 'vp/test'
|
|
6
6
|
import * as Http from '~test/Http.js'
|
|
7
|
-
import { rpcUrl } from '~test/tempo/
|
|
7
|
+
import { rpcUrl } from '~test/tempo/rpc.js'
|
|
8
8
|
import { accounts, asset, chain, client, http } from '~test/tempo/viem.js'
|
|
9
9
|
|
|
10
10
|
import * as Fetch from './Fetch.js'
|
|
@@ -669,7 +669,7 @@ describe('Fetch.from: 402 retry path', () => {
|
|
|
669
669
|
expect(headers.get('Authorization')).toBe('credential')
|
|
670
670
|
})
|
|
671
671
|
|
|
672
|
-
test('sends credential retry to the final 402 response URL', async () => {
|
|
672
|
+
test('sends credential retry to the final same-origin 402 response URL', async () => {
|
|
673
673
|
let callCount = 0
|
|
674
674
|
const calls: { input: RequestInfo | URL; init: RequestInit | undefined }[] = []
|
|
675
675
|
const mockFetch: typeof globalThis.fetch = async (input, init) => {
|
|
@@ -678,7 +678,7 @@ describe('Fetch.from: 402 retry path', () => {
|
|
|
678
678
|
if (callCount === 1) {
|
|
679
679
|
const response = make402()
|
|
680
680
|
Object.defineProperty(response, 'url', {
|
|
681
|
-
value: 'https://
|
|
681
|
+
value: 'https://api.example.com/redirected-protected',
|
|
682
682
|
})
|
|
683
683
|
return response
|
|
684
684
|
}
|
|
@@ -694,10 +694,35 @@ describe('Fetch.from: 402 retry path', () => {
|
|
|
694
694
|
|
|
695
695
|
expect(response.status).toBe(200)
|
|
696
696
|
expect(calls[0]!.input).toBe('https://api.example.com/protected')
|
|
697
|
-
expect(calls[1]!.input).toBe('https://
|
|
697
|
+
expect(calls[1]!.input).toBe('https://api.example.com/redirected-protected')
|
|
698
698
|
expect(new Headers(calls[1]!.init?.headers).get('Authorization')).toBe('credential')
|
|
699
699
|
})
|
|
700
700
|
|
|
701
|
+
test('rejects credential retry to a cross-origin 402 response URL', async () => {
|
|
702
|
+
const calls: { input: RequestInfo | URL; init: RequestInit | undefined }[] = []
|
|
703
|
+
const mockFetch: typeof globalThis.fetch = async (input, init) => {
|
|
704
|
+
calls.push({ input, init })
|
|
705
|
+
const response = make402()
|
|
706
|
+
Object.defineProperty(response, 'url', {
|
|
707
|
+
value: 'https://payments.example.com/protected',
|
|
708
|
+
})
|
|
709
|
+
return response
|
|
710
|
+
}
|
|
711
|
+
|
|
712
|
+
const fetch = Fetch.from({
|
|
713
|
+
fetch: mockFetch,
|
|
714
|
+
methods: [noopMethod],
|
|
715
|
+
})
|
|
716
|
+
|
|
717
|
+
await expect(fetch('https://api.example.com/protected')).rejects.toThrow(
|
|
718
|
+
'Refusing to send payment credential across redirect from https://api.example.com to https://payments.example.com',
|
|
719
|
+
)
|
|
720
|
+
|
|
721
|
+
expect(calls).toHaveLength(1)
|
|
722
|
+
expect(calls[0]!.input).toBe('https://api.example.com/protected')
|
|
723
|
+
expect(new Headers(calls[0]!.init?.headers).get('Authorization')).toBeNull()
|
|
724
|
+
})
|
|
725
|
+
|
|
701
726
|
test('emits client events and allows challenge handler to provide credential', async () => {
|
|
702
727
|
const events: string[] = []
|
|
703
728
|
const createCredential = vi.fn(async () => 'method-credential')
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as Challenge from '../../Challenge.js'
|
|
2
|
+
import * as Constants from '../../Constants.js'
|
|
2
3
|
import * as Expires from '../../Expires.js'
|
|
3
4
|
import * as AcceptPayment from '../../internal/AcceptPayment.js'
|
|
4
5
|
import type { MaybePromise } from '../../internal/types.js'
|
|
@@ -173,7 +174,7 @@ export function from<const methods extends readonly Method.AnyClient[]>(
|
|
|
173
174
|
|
|
174
175
|
const wrappedFetch = async (input: RequestInfo | URL, init?: from.RequestInit<methods>) => {
|
|
175
176
|
const callerHeaders = getCallerHeaders(input, init?.headers)
|
|
176
|
-
const hasExplicitAcceptPayment = callerHeaders.has(
|
|
177
|
+
const hasExplicitAcceptPayment = callerHeaders.has(Constants.Headers.acceptPayment)
|
|
177
178
|
const paymentPreferences = resolvePaymentPreferences(callerHeaders, resolvedAcceptPayment)
|
|
178
179
|
const initialRequest = prepareInitialRequest(
|
|
179
180
|
input,
|
|
@@ -262,7 +263,7 @@ export function from<const methods extends readonly Method.AnyClient[]>(
|
|
|
262
263
|
)
|
|
263
264
|
|
|
264
265
|
const paymentResponse = await baseFetch(
|
|
265
|
-
resolvePaymentRetryInput(response, initialRequest.input),
|
|
266
|
+
resolvePaymentRetryInput(response, initialRequest.input, initialRequest.input),
|
|
266
267
|
transport.setCredential(
|
|
267
268
|
{
|
|
268
269
|
...fetchInit,
|
|
@@ -717,7 +718,7 @@ function prepareInitialRequest<methods extends readonly Method.AnyClient[]>(
|
|
|
717
718
|
callerHeaders.forEach((value, key) => {
|
|
718
719
|
headers.set(key, value)
|
|
719
720
|
})
|
|
720
|
-
headers.set(
|
|
721
|
+
headers.set(Constants.Headers.acceptPayment, header)
|
|
721
722
|
|
|
722
723
|
if (init) {
|
|
723
724
|
// Preserve init identity for callers like websocket upgrade helpers that
|
|
@@ -781,7 +782,7 @@ function resolvePaymentPreferences<methods extends readonly Method.AnyClient[]>(
|
|
|
781
782
|
headers: Headers,
|
|
782
783
|
acceptPayment: AcceptPayment.Resolved<methods>,
|
|
783
784
|
): AcceptPayment.Resolved<methods> {
|
|
784
|
-
const header = headers.get(
|
|
785
|
+
const header = headers.get(Constants.Headers.acceptPayment)
|
|
785
786
|
if (!header) return acceptPayment
|
|
786
787
|
|
|
787
788
|
try {
|
|
@@ -847,6 +848,17 @@ function resolveRequestUrl(input: RequestInfo | URL): URL {
|
|
|
847
848
|
function resolvePaymentRetryInput(
|
|
848
849
|
response: Response,
|
|
849
850
|
fallback: RequestInfo | URL,
|
|
851
|
+
initialInput: RequestInfo | URL,
|
|
850
852
|
): RequestInfo | URL {
|
|
851
|
-
|
|
853
|
+
if (!response.url) return fallback
|
|
854
|
+
|
|
855
|
+
const responseUrl = new URL(response.url)
|
|
856
|
+
const initialUrl = resolveRequestUrl(initialInput)
|
|
857
|
+
if (responseUrl.origin !== initialUrl.origin) {
|
|
858
|
+
throw new Error(
|
|
859
|
+
`Refusing to send payment credential across redirect from ${initialUrl.origin} to ${responseUrl.origin}`,
|
|
860
|
+
)
|
|
861
|
+
}
|
|
862
|
+
|
|
863
|
+
return response.url
|
|
852
864
|
}
|
package/src/env.d.ts
CHANGED
|
@@ -2,7 +2,7 @@
|
|
|
2
2
|
|
|
3
3
|
interface ImportMetaEnv {
|
|
4
4
|
readonly MODE: 'test' | 'production'
|
|
5
|
-
readonly
|
|
5
|
+
readonly VITE_TEMPO_NETWORK: 'localnet' | 'moderato' | 'devnet'
|
|
6
6
|
readonly VITE_HTTP_LOG: 'true' | 'false'
|
|
7
7
|
readonly VITE_RPC_CREDENTIALS: string
|
|
8
8
|
}
|
package/src/index.ts
CHANGED
|
@@ -178,6 +178,67 @@ describe('AcceptPayment', () => {
|
|
|
178
178
|
])
|
|
179
179
|
})
|
|
180
180
|
|
|
181
|
+
test('selectChallengeCandidates supports duplicate method keys with challenge predicates', () => {
|
|
182
|
+
const challenges = [
|
|
183
|
+
{
|
|
184
|
+
id: 'v2',
|
|
185
|
+
intent: 'session',
|
|
186
|
+
method: 'tempo',
|
|
187
|
+
realm: 'test',
|
|
188
|
+
request: { methodDetails: { sessionProtocol: 'v2' } },
|
|
189
|
+
},
|
|
190
|
+
{
|
|
191
|
+
id: 'v1',
|
|
192
|
+
intent: 'session',
|
|
193
|
+
method: 'tempo',
|
|
194
|
+
realm: 'test',
|
|
195
|
+
request: { methodDetails: { sessionProtocol: 'v1' } },
|
|
196
|
+
},
|
|
197
|
+
{
|
|
198
|
+
id: 'old-server',
|
|
199
|
+
intent: 'session',
|
|
200
|
+
method: 'tempo',
|
|
201
|
+
realm: 'test',
|
|
202
|
+
request: {},
|
|
203
|
+
},
|
|
204
|
+
]
|
|
205
|
+
const methods = [
|
|
206
|
+
{
|
|
207
|
+
name: 'tempo',
|
|
208
|
+
intent: 'session',
|
|
209
|
+
canHandleChallenge: ({
|
|
210
|
+
challenge,
|
|
211
|
+
}: {
|
|
212
|
+
challenge: AcceptPayment.ChallengeCandidate['challenge']
|
|
213
|
+
}) =>
|
|
214
|
+
(challenge.request.methodDetails as { sessionProtocol?: string } | undefined)
|
|
215
|
+
?.sessionProtocol === 'v2',
|
|
216
|
+
},
|
|
217
|
+
{
|
|
218
|
+
name: 'tempo',
|
|
219
|
+
intent: 'session',
|
|
220
|
+
canHandleChallenge: ({
|
|
221
|
+
challenge,
|
|
222
|
+
}: {
|
|
223
|
+
challenge: AcceptPayment.ChallengeCandidate['challenge']
|
|
224
|
+
}) => {
|
|
225
|
+
const sessionProtocol = (
|
|
226
|
+
challenge.request.methodDetails as { sessionProtocol?: string } | undefined
|
|
227
|
+
)?.sessionProtocol
|
|
228
|
+
return sessionProtocol === undefined || sessionProtocol === 'v1'
|
|
229
|
+
},
|
|
230
|
+
},
|
|
231
|
+
] as const
|
|
232
|
+
|
|
233
|
+
const candidates = AcceptPayment.selectChallengeCandidates(
|
|
234
|
+
challenges,
|
|
235
|
+
methods,
|
|
236
|
+
AcceptPayment.parse('tempo/session'),
|
|
237
|
+
)
|
|
238
|
+
|
|
239
|
+
expect(candidates.map(({ challenge }) => challenge.id)).toEqual(['v2', 'v1', 'old-server'])
|
|
240
|
+
})
|
|
241
|
+
|
|
181
242
|
test('selectChallenge honors a specific opt-out over a broader wildcard', () => {
|
|
182
243
|
const selected = AcceptPayment.selectChallenge(
|
|
183
244
|
[
|
|
@@ -2,6 +2,7 @@ import type * as Challenge from '../Challenge.js'
|
|
|
2
2
|
import type { MaybePromise } from './types.js'
|
|
3
3
|
|
|
4
4
|
type MethodLike = {
|
|
5
|
+
canHandleChallenge?: ((parameters: { challenge: Challenge.Challenge }) => boolean) | undefined
|
|
5
6
|
intent: string
|
|
6
7
|
name: string
|
|
7
8
|
}
|
|
@@ -179,28 +180,34 @@ export function selectChallengeCandidates<const methods extends readonly MethodL
|
|
|
179
180
|
methods: methods,
|
|
180
181
|
preferences: readonly Entry[],
|
|
181
182
|
): ChallengeCandidate<methods[number]>[] {
|
|
182
|
-
const
|
|
183
|
+
const methodsByKey = new Map<string, methods[number][]>()
|
|
183
184
|
for (const method of methods) {
|
|
184
185
|
const key = keyOf(method)
|
|
185
|
-
|
|
186
|
+
const matchingMethods = methodsByKey.get(key)
|
|
187
|
+
if (matchingMethods) matchingMethods.push(method)
|
|
188
|
+
else methodsByKey.set(key, [method])
|
|
186
189
|
}
|
|
187
190
|
|
|
188
191
|
return challenges
|
|
189
|
-
.
|
|
190
|
-
const
|
|
191
|
-
if (!
|
|
192
|
+
.flatMap((challenge, index) => {
|
|
193
|
+
const matchingMethods = methodsByKey.get(keyOf(challenge))
|
|
194
|
+
if (!matchingMethods) return []
|
|
192
195
|
|
|
193
196
|
const match = bestMatch(challenge, preferences)
|
|
194
|
-
if (!match || match.q <= 0) return
|
|
195
|
-
|
|
196
|
-
return
|
|
197
|
-
challenge
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
197
|
+
if (!match || match.q <= 0) return []
|
|
198
|
+
|
|
199
|
+
return matchingMethods
|
|
200
|
+
.filter((method) => method.canHandleChallenge?.({ challenge }) ?? true)
|
|
201
|
+
.map(
|
|
202
|
+
(method) =>
|
|
203
|
+
({
|
|
204
|
+
challenge,
|
|
205
|
+
index,
|
|
206
|
+
match,
|
|
207
|
+
method,
|
|
208
|
+
}) as ChallengeCandidate<methods[number]> & { match: Match },
|
|
209
|
+
)
|
|
202
210
|
})
|
|
203
|
-
.filter((candidate): candidate is NonNullable<typeof candidate> => Boolean(candidate))
|
|
204
211
|
.sort((left, right) => right.match.q - left.match.q || left.index - right.index)
|
|
205
212
|
.map((candidate) => {
|
|
206
213
|
const { match: _match, ...rest } = candidate
|
|
@@ -6,21 +6,21 @@ import { StreamableHTTPClientTransport } from '@modelcontextprotocol/sdk/client/
|
|
|
6
6
|
import { createMcpExpressApp } from '@modelcontextprotocol/sdk/server/express.js'
|
|
7
7
|
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js'
|
|
8
8
|
import { StreamableHTTPServerTransport } from '@modelcontextprotocol/sdk/server/streamableHttp.js'
|
|
9
|
-
import {
|
|
9
|
+
import { sessionLegacy as tempo_session_client, tempo as tempo_client } from 'mppx/client'
|
|
10
10
|
import { Mppx as Mppx_server, tempo as tempo_server } from 'mppx/server'
|
|
11
11
|
import type { Address } from 'viem'
|
|
12
12
|
import { readContract } from 'viem/actions'
|
|
13
13
|
import { Actions, Addresses } from 'viem/tempo'
|
|
14
14
|
import { beforeAll, describe, expect, test } from 'vp/test'
|
|
15
|
-
import {
|
|
16
|
-
import { deployEscrow, signTopUpChannel } from '~test/tempo/session.js'
|
|
15
|
+
import { tempoNetwork } from '~test/config.js'
|
|
16
|
+
import { deployEscrow, signTopUpChannel } from '~test/tempo/legacy/session.js'
|
|
17
17
|
import { accounts, asset, client as testClient, fundAccount } from '~test/tempo/viem.js'
|
|
18
18
|
|
|
19
19
|
import * as Credential from '../../Credential.js'
|
|
20
20
|
import * as core_Mcp from '../../Mcp.js'
|
|
21
21
|
import * as Store from '../../Store.js'
|
|
22
|
-
import
|
|
23
|
-
import
|
|
22
|
+
import type { SessionReceipt } from '../../tempo/session/precompile/Protocol.js'
|
|
23
|
+
import * as ChannelStore from '../../tempo/session/server/ChannelStore.js'
|
|
24
24
|
import * as McpServer_transport from '../server/Transport.js'
|
|
25
25
|
import * as McpClient from './McpClient.js'
|
|
26
26
|
|
|
@@ -29,6 +29,7 @@ const secretKey = 'test-secret-key'
|
|
|
29
29
|
const chargeAmountRaw = 1_000_000n
|
|
30
30
|
const doubleSessionAmountRaw = chargeAmountRaw * 2n
|
|
31
31
|
const topUpAmountRaw = chargeAmountRaw * 3n
|
|
32
|
+
const isLocalnet = tempoNetwork === 'localnet'
|
|
32
33
|
|
|
33
34
|
let escrowContract: Address
|
|
34
35
|
|
|
@@ -40,7 +41,7 @@ beforeAll(async () => {
|
|
|
40
41
|
await fundAccount({ address: accounts[2].address, token: asset })
|
|
41
42
|
}, 60_000)
|
|
42
43
|
|
|
43
|
-
describe.runIf(
|
|
44
|
+
describe.runIf(isLocalnet)('McpClient.wrap integration', () => {
|
|
44
45
|
const scenarios: readonly Scenario[] = [
|
|
45
46
|
{
|
|
46
47
|
name: 'charge intent settles a paid MCP tool against the live chain',
|
|
@@ -464,7 +465,7 @@ async function createHarness(options?: {
|
|
|
464
465
|
currency: asset,
|
|
465
466
|
getClient: () => testClient,
|
|
466
467
|
}),
|
|
467
|
-
tempo_server.
|
|
468
|
+
tempo_server.sessionLegacy({
|
|
468
469
|
account: accounts[0],
|
|
469
470
|
currency: asset,
|
|
470
471
|
escrowContract,
|
|
@@ -73,8 +73,15 @@ describe('McpClient.wrap', () => {
|
|
|
73
73
|
})
|
|
74
74
|
|
|
75
75
|
expectTypeOf(wrapped.callTool).toBeCallableWith({ name: 'tool' })
|
|
76
|
+
expectTypeOf(wrapped.callTool).toBeCallableWith(null, { name: 'tool' })
|
|
77
|
+
expectTypeOf(wrapped.callTool).toBeCallableWith(() => true, { name: 'tool' })
|
|
76
78
|
expectTypeOf(wrapped.callTool).toBeCallableWith({ name: 'tool' }, {})
|
|
77
79
|
expectTypeOf(wrapped.callTool).toBeCallableWith({ name: 'tool' }, { timeout: 5000 })
|
|
80
|
+
expectTypeOf(wrapped.callTool).toBeCallableWith(
|
|
81
|
+
async (challenge) => challenge.intent === 'charge',
|
|
82
|
+
{ name: 'tool' },
|
|
83
|
+
{ timeout: 5000 },
|
|
84
|
+
)
|
|
78
85
|
})
|
|
79
86
|
|
|
80
87
|
test('callTool result includes receipt', () => {
|
|
@@ -11,6 +11,14 @@ import type * as z from '../../zod.js'
|
|
|
11
11
|
|
|
12
12
|
type AnyClient = Method.Client<any, any>
|
|
13
13
|
|
|
14
|
+
export type CallToolParameters = {
|
|
15
|
+
name: string
|
|
16
|
+
arguments?: Record<string, unknown>
|
|
17
|
+
_meta?: Record<string, unknown>
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export type OnPaymentRequired = (challenge: Challenge.Challenge) => boolean | Promise<boolean>
|
|
21
|
+
|
|
14
22
|
/**
|
|
15
23
|
* Result of a tool call with payment handling.
|
|
16
24
|
* Extends the SDK's callTool return type with an optional payment receipt.
|
|
@@ -55,69 +63,91 @@ export function wrap<
|
|
|
55
63
|
const { methods } = config
|
|
56
64
|
const paymentPreferences = AcceptPayment.resolve(methods)
|
|
57
65
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
66
|
+
const callTool = (async (
|
|
67
|
+
first: CallToolParameters | OnPaymentRequired | null | undefined,
|
|
68
|
+
second?: CallToolParameters | wrap.CallToolOptions<methods>,
|
|
69
|
+
third?: wrap.CallToolOptions<methods>,
|
|
70
|
+
) => {
|
|
71
|
+
const hasApprovalArgument = typeof first === 'function' || first === null || first === undefined
|
|
72
|
+
const params = (hasApprovalArgument ? second : first) as CallToolParameters
|
|
73
|
+
const options = (hasApprovalArgument ? third : second) as
|
|
74
|
+
| wrap.CallToolOptions<methods>
|
|
75
|
+
| undefined
|
|
76
|
+
const onPaymentRequired =
|
|
77
|
+
first === null
|
|
78
|
+
? undefined
|
|
79
|
+
: hasApprovalArgument
|
|
80
|
+
? ((first as OnPaymentRequired | undefined) ?? config.onPaymentRequired)
|
|
81
|
+
: config.onPaymentRequired
|
|
82
|
+
const context = options?.context
|
|
83
|
+
const timeout = options?.timeout
|
|
84
|
+
|
|
85
|
+
try {
|
|
86
|
+
const result = await client.callTool(
|
|
87
|
+
params,
|
|
88
|
+
undefined,
|
|
89
|
+
timeout !== undefined ? { timeout } : undefined,
|
|
90
|
+
)
|
|
91
|
+
|
|
92
|
+
return {
|
|
93
|
+
...result,
|
|
94
|
+
receipt: result._meta?.[core_Mcp.receiptMetaKey] as core_Mcp.Receipt | undefined,
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
// Check if this is a payment required error
|
|
98
|
+
if (!isPaymentRequiredError(error)) throw error
|
|
99
|
+
|
|
100
|
+
const challenges = (error.data as { challenges?: Challenge.Challenge[] })?.challenges
|
|
101
|
+
if (!challenges?.length) throw error
|
|
102
|
+
|
|
103
|
+
const selected = AcceptPayment.selectChallenge(
|
|
104
|
+
challenges,
|
|
105
|
+
methods,
|
|
106
|
+
paymentPreferences.entries,
|
|
107
|
+
)
|
|
108
|
+
if (!selected) {
|
|
109
|
+
const available = challenges.map((c) => `${c.method}.${c.intent}`).join(', ')
|
|
110
|
+
const installed = methods.map((m) => `${m.name}.${m.intent}`).join(', ')
|
|
111
|
+
throw new Error(
|
|
112
|
+
`No compatible payment method. Server offers: ${available}. Client has: ${installed}`,
|
|
113
|
+
{ cause: error },
|
|
69
114
|
)
|
|
115
|
+
}
|
|
70
116
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
receipt: result._meta?.[core_Mcp.receiptMetaKey] as core_Mcp.Receipt | undefined,
|
|
74
|
-
}
|
|
75
|
-
} catch (error) {
|
|
76
|
-
// Check if this is a payment required error
|
|
77
|
-
if (!isPaymentRequiredError(error)) throw error
|
|
78
|
-
|
|
79
|
-
const challenges = (error.data as { challenges?: Challenge.Challenge[] })?.challenges
|
|
80
|
-
if (!challenges?.length) throw error
|
|
81
|
-
|
|
82
|
-
const selected = AcceptPayment.selectChallenge(
|
|
83
|
-
challenges,
|
|
84
|
-
methods,
|
|
85
|
-
paymentPreferences.entries,
|
|
86
|
-
)
|
|
87
|
-
if (!selected) {
|
|
88
|
-
const available = challenges.map((c) => `${c.method}.${c.intent}`).join(', ')
|
|
89
|
-
const installed = methods.map((m) => `${m.name}.${m.intent}`).join(', ')
|
|
90
|
-
throw new Error(
|
|
91
|
-
`No compatible payment method. Server offers: ${available}. Client has: ${installed}`,
|
|
92
|
-
{ cause: error },
|
|
93
|
-
)
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const credential = await createCredential(selected.challenge, {
|
|
97
|
-
context,
|
|
98
|
-
methods,
|
|
99
|
-
})
|
|
100
|
-
const parsed = Credential.deserialize(credential)
|
|
101
|
-
|
|
102
|
-
const retryResult = await client.callTool(
|
|
103
|
-
{
|
|
104
|
-
...params,
|
|
105
|
-
_meta: {
|
|
106
|
-
...params._meta,
|
|
107
|
-
[core_Mcp.credentialMetaKey]: parsed,
|
|
108
|
-
},
|
|
109
|
-
},
|
|
110
|
-
undefined,
|
|
111
|
-
timeout !== undefined ? { timeout } : undefined,
|
|
112
|
-
)
|
|
117
|
+
if (selected.challenge.expires)
|
|
118
|
+
Expires.assert(selected.challenge.expires, selected.challenge.id)
|
|
113
119
|
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
}
|
|
120
|
+
if (onPaymentRequired) {
|
|
121
|
+
const approved = await onPaymentRequired(selected.challenge)
|
|
122
|
+
if (!approved) throw new Error('Payment declined.', { cause: error })
|
|
118
123
|
}
|
|
119
|
-
|
|
120
|
-
|
|
124
|
+
|
|
125
|
+
const credential = await createCredential(selected.challenge, {
|
|
126
|
+
context,
|
|
127
|
+
methods,
|
|
128
|
+
})
|
|
129
|
+
const parsed = Credential.deserialize(credential)
|
|
130
|
+
|
|
131
|
+
const retryResult = await client.callTool(
|
|
132
|
+
{
|
|
133
|
+
...params,
|
|
134
|
+
_meta: {
|
|
135
|
+
...params._meta,
|
|
136
|
+
[core_Mcp.credentialMetaKey]: parsed,
|
|
137
|
+
},
|
|
138
|
+
},
|
|
139
|
+
undefined,
|
|
140
|
+
timeout !== undefined ? { timeout } : undefined,
|
|
141
|
+
)
|
|
142
|
+
|
|
143
|
+
return {
|
|
144
|
+
...retryResult,
|
|
145
|
+
receipt: retryResult._meta?.[core_Mcp.receiptMetaKey] as core_Mcp.Receipt | undefined,
|
|
146
|
+
}
|
|
147
|
+
}
|
|
148
|
+
}) as wrap.McpClient<client, methods>['callTool']
|
|
149
|
+
|
|
150
|
+
return { ...client, callTool } as wrap.McpClient<client, methods>
|
|
121
151
|
}
|
|
122
152
|
|
|
123
153
|
/** Union of all context types from all methods that have context schemas. */
|
|
@@ -133,6 +163,8 @@ export declare namespace wrap {
|
|
|
133
163
|
type Config<methods extends readonly Method.AnyClient[] = readonly Method.AnyClient[]> = {
|
|
134
164
|
/** Array of methods to use. */
|
|
135
165
|
methods: methods
|
|
166
|
+
/** Optional approval hook called before creating a payment credential. */
|
|
167
|
+
onPaymentRequired?: OnPaymentRequired
|
|
136
168
|
}
|
|
137
169
|
|
|
138
170
|
type McpClient<
|
|
@@ -140,14 +172,14 @@ export declare namespace wrap {
|
|
|
140
172
|
methods extends readonly AnyClient[] = readonly AnyClient[],
|
|
141
173
|
> = Omit<client, 'callTool'> & {
|
|
142
174
|
/** Call a tool with automatic payment handling. */
|
|
143
|
-
callTool:
|
|
144
|
-
params:
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
175
|
+
callTool: {
|
|
176
|
+
(params: CallToolParameters, options?: CallToolOptions<methods>): Promise<CallToolResult>
|
|
177
|
+
(
|
|
178
|
+
onPaymentRequired: OnPaymentRequired | null | undefined,
|
|
179
|
+
params: CallToolParameters,
|
|
180
|
+
options?: CallToolOptions<methods>,
|
|
181
|
+
): Promise<CallToolResult>
|
|
182
|
+
}
|
|
151
183
|
}
|
|
152
184
|
|
|
153
185
|
type CallToolOptions<methods extends readonly AnyClient[] = readonly AnyClient[]> = {
|
|
@@ -0,0 +1,131 @@
|
|
|
1
|
+
import type { Client } from '@modelcontextprotocol/sdk/client/index.js'
|
|
2
|
+
import { McpError } from '@modelcontextprotocol/sdk/types.js'
|
|
3
|
+
import { Challenge, Credential, Mcp as core_Mcp, Method } from 'mppx'
|
|
4
|
+
import { Methods } from 'mppx/tempo'
|
|
5
|
+
import { describe, expect, test, vi } from 'vp/test'
|
|
6
|
+
|
|
7
|
+
import * as McpClient from './McpClient.js'
|
|
8
|
+
|
|
9
|
+
describe('MCP client payment approval', () => {
|
|
10
|
+
test('calls an approval hook before creating a credential', async () => {
|
|
11
|
+
const challenge = Challenge.from({
|
|
12
|
+
id: 'approval-test',
|
|
13
|
+
intent: 'charge',
|
|
14
|
+
method: 'tempo',
|
|
15
|
+
realm: 'api.example.com',
|
|
16
|
+
request: {},
|
|
17
|
+
})
|
|
18
|
+
const calls: unknown[] = []
|
|
19
|
+
const client = {
|
|
20
|
+
async callTool(params: unknown) {
|
|
21
|
+
calls.push(params)
|
|
22
|
+
if (calls.length === 1)
|
|
23
|
+
throw new McpError(core_Mcp.paymentRequiredCode, 'Payment Required', {
|
|
24
|
+
challenges: [challenge],
|
|
25
|
+
httpStatus: 402,
|
|
26
|
+
})
|
|
27
|
+
return {
|
|
28
|
+
_meta: {
|
|
29
|
+
[core_Mcp.receiptMetaKey]: {
|
|
30
|
+
method: 'tempo',
|
|
31
|
+
reference: 'test',
|
|
32
|
+
status: 'success',
|
|
33
|
+
timestamp: new Date().toISOString(),
|
|
34
|
+
},
|
|
35
|
+
},
|
|
36
|
+
content: [{ type: 'text', text: 'ok' }],
|
|
37
|
+
}
|
|
38
|
+
},
|
|
39
|
+
}
|
|
40
|
+
const createCredential = vi.fn(async ({ challenge }: { challenge: Challenge.Challenge }) =>
|
|
41
|
+
Credential.serialize({
|
|
42
|
+
challenge,
|
|
43
|
+
payload: { signature: '0xsignature', type: 'transaction' },
|
|
44
|
+
}),
|
|
45
|
+
)
|
|
46
|
+
const onPaymentRequired = vi.fn(() => true)
|
|
47
|
+
const mcp = McpClient.wrap(client as unknown as Pick<Client, 'callTool'>, {
|
|
48
|
+
methods: [Method.toClient(Methods.charge, { createCredential })],
|
|
49
|
+
})
|
|
50
|
+
|
|
51
|
+
const result = await mcp.callTool(onPaymentRequired, { name: 'paid_tool', arguments: {} })
|
|
52
|
+
|
|
53
|
+
expect(result.content).toEqual([{ type: 'text', text: 'ok' }])
|
|
54
|
+
expect(onPaymentRequired).toHaveBeenCalledWith(challenge)
|
|
55
|
+
expect(createCredential).toHaveBeenCalledOnce()
|
|
56
|
+
expect(calls).toHaveLength(2)
|
|
57
|
+
})
|
|
58
|
+
|
|
59
|
+
test('does not create a credential when approval is denied', async () => {
|
|
60
|
+
const challenge = Challenge.from({
|
|
61
|
+
id: 'denied-test',
|
|
62
|
+
intent: 'charge',
|
|
63
|
+
method: 'tempo',
|
|
64
|
+
realm: 'api.example.com',
|
|
65
|
+
request: {},
|
|
66
|
+
})
|
|
67
|
+
const client = {
|
|
68
|
+
async callTool() {
|
|
69
|
+
throw new McpError(core_Mcp.paymentRequiredCode, 'Payment Required', {
|
|
70
|
+
challenges: [challenge],
|
|
71
|
+
httpStatus: 402,
|
|
72
|
+
})
|
|
73
|
+
},
|
|
74
|
+
}
|
|
75
|
+
const createCredential = vi.fn(async ({ challenge }: { challenge: Challenge.Challenge }) =>
|
|
76
|
+
Credential.serialize({
|
|
77
|
+
challenge,
|
|
78
|
+
payload: { signature: '0xsignature', type: 'transaction' },
|
|
79
|
+
}),
|
|
80
|
+
)
|
|
81
|
+
const mcp = McpClient.wrap(client as unknown as Pick<Client, 'callTool'>, {
|
|
82
|
+
methods: [Method.toClient(Methods.charge, { createCredential })],
|
|
83
|
+
})
|
|
84
|
+
|
|
85
|
+
await expect(mcp.callTool(() => false, { name: 'paid_tool' })).rejects.toThrow(
|
|
86
|
+
'Payment declined.',
|
|
87
|
+
)
|
|
88
|
+
expect(createCredential).not.toHaveBeenCalled()
|
|
89
|
+
})
|
|
90
|
+
|
|
91
|
+
test('allows null to bypass a config approval hook', async () => {
|
|
92
|
+
const challenge = Challenge.from({
|
|
93
|
+
id: 'null-bypass-test',
|
|
94
|
+
intent: 'charge',
|
|
95
|
+
method: 'tempo',
|
|
96
|
+
realm: 'api.example.com',
|
|
97
|
+
request: {},
|
|
98
|
+
})
|
|
99
|
+
let calls = 0
|
|
100
|
+
const client = {
|
|
101
|
+
async callTool() {
|
|
102
|
+
calls += 1
|
|
103
|
+
if (calls === 1)
|
|
104
|
+
throw new McpError(core_Mcp.paymentRequiredCode, 'Payment Required', {
|
|
105
|
+
challenges: [challenge],
|
|
106
|
+
httpStatus: 402,
|
|
107
|
+
})
|
|
108
|
+
return {
|
|
109
|
+
content: [{ type: 'text', text: 'ok' }],
|
|
110
|
+
}
|
|
111
|
+
},
|
|
112
|
+
}
|
|
113
|
+
const createCredential = vi.fn(async ({ challenge }: { challenge: Challenge.Challenge }) =>
|
|
114
|
+
Credential.serialize({
|
|
115
|
+
challenge,
|
|
116
|
+
payload: { signature: '0xsignature', type: 'transaction' },
|
|
117
|
+
}),
|
|
118
|
+
)
|
|
119
|
+
const onPaymentRequired = vi.fn(() => false)
|
|
120
|
+
const mcp = McpClient.wrap(client as unknown as Pick<Client, 'callTool'>, {
|
|
121
|
+
methods: [Method.toClient(Methods.charge, { createCredential })],
|
|
122
|
+
onPaymentRequired,
|
|
123
|
+
})
|
|
124
|
+
|
|
125
|
+
await expect(mcp.callTool(null, { name: 'paid_tool' })).resolves.toMatchObject({
|
|
126
|
+
content: [{ type: 'text', text: 'ok' }],
|
|
127
|
+
})
|
|
128
|
+
expect(onPaymentRequired).not.toHaveBeenCalled()
|
|
129
|
+
expect(createCredential).toHaveBeenCalledOnce()
|
|
130
|
+
})
|
|
131
|
+
})
|