mppx 0.7.0 → 0.8.1
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 +41 -0
- package/README.md +20 -11
- package/dist/Challenge.d.ts.map +1 -1
- package/dist/Challenge.js +18 -6
- package/dist/Challenge.js.map +1 -1
- package/dist/Mcp.d.ts +3 -0
- package/dist/Mcp.d.ts.map +1 -1
- package/dist/Mcp.js +2 -0
- package/dist/Mcp.js.map +1 -1
- package/dist/PaymentRequest.d.ts +10 -10
- package/dist/PaymentRequest.js +8 -8
- package/dist/cli/internal.d.ts +1 -0
- package/dist/cli/internal.d.ts.map +1 -1
- package/dist/cli/internal.js +1 -15
- package/dist/cli/internal.js.map +1 -1
- package/dist/client/Mppx.js +2 -2
- package/dist/client/Mppx.js.map +1 -1
- package/dist/client/Transport.d.ts +11 -16
- package/dist/client/Transport.d.ts.map +1 -1
- package/dist/client/Transport.js +55 -75
- package/dist/client/Transport.js.map +1 -1
- package/dist/client/index.d.ts +3 -0
- package/dist/client/index.d.ts.map +1 -1
- package/dist/client/index.js +1 -0
- package/dist/client/index.js.map +1 -1
- package/dist/client/internal/Fetch.d.ts.map +1 -1
- package/dist/client/internal/Fetch.js +46 -7
- package/dist/client/internal/Fetch.js.map +1 -1
- package/dist/client/internal/protocols/Mcp.d.ts +7 -0
- package/dist/client/internal/protocols/Mcp.d.ts.map +1 -0
- package/dist/client/internal/protocols/Mcp.js +159 -0
- package/dist/client/internal/protocols/Mcp.js.map +1 -0
- package/dist/client/internal/protocols/Mpp.d.ts +4 -0
- package/dist/client/internal/protocols/Mpp.d.ts.map +1 -0
- package/dist/client/internal/protocols/Mpp.js +18 -0
- package/dist/client/internal/protocols/Mpp.js.map +1 -0
- package/dist/client/internal/protocols/Protocol.d.ts +10 -0
- package/dist/client/internal/protocols/Protocol.d.ts.map +1 -0
- package/dist/client/internal/protocols/Protocol.js +2 -0
- package/dist/client/internal/protocols/Protocol.js.map +1 -0
- package/dist/client/internal/protocols/Shared.d.ts +5 -0
- package/dist/client/internal/protocols/Shared.d.ts.map +1 -0
- package/dist/client/internal/protocols/Shared.js +20 -0
- package/dist/client/internal/protocols/Shared.js.map +1 -0
- package/dist/client/internal/protocols/X402.d.ts +8 -0
- package/dist/client/internal/protocols/X402.d.ts.map +1 -0
- package/dist/client/internal/protocols/X402.js +39 -0
- package/dist/client/internal/protocols/X402.js.map +1 -0
- package/dist/evm/client/index.d.ts +1 -0
- package/dist/evm/client/index.d.ts.map +1 -1
- package/dist/evm/client/index.js +1 -0
- package/dist/evm/client/index.js.map +1 -1
- package/dist/evm/index.d.ts +2 -0
- package/dist/evm/index.d.ts.map +1 -1
- package/dist/evm/index.js +2 -0
- package/dist/evm/index.js.map +1 -1
- package/dist/evm/server/index.d.ts +1 -0
- package/dist/evm/server/index.d.ts.map +1 -1
- package/dist/evm/server/index.js +1 -0
- package/dist/evm/server/index.js.map +1 -1
- package/dist/mcp/client/McpClient.d.ts +101 -0
- package/dist/mcp/client/McpClient.d.ts.map +1 -0
- package/dist/mcp/client/McpClient.js +162 -0
- package/dist/mcp/client/McpClient.js.map +1 -0
- package/dist/mcp/client/index.d.ts.map +1 -0
- package/dist/mcp/client/index.js.map +1 -0
- package/dist/mcp/server/Transport.d.ts.map +1 -0
- package/dist/mcp/server/Transport.js.map +1 -0
- package/dist/mcp/server/index.d.ts.map +1 -0
- package/dist/mcp/server/index.js.map +1 -0
- package/dist/server/Mppx.d.ts +1 -1
- package/dist/server/Mppx.d.ts.map +1 -1
- package/dist/server/Mppx.js +9 -0
- package/dist/server/Mppx.js.map +1 -1
- package/dist/server/Transport.d.ts +1 -1
- package/dist/server/Transport.d.ts.map +1 -1
- package/dist/server/Transport.js +1 -1
- package/dist/server/Transport.js.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/Proof.d.ts +85 -1
- package/dist/tempo/Proof.d.ts.map +1 -1
- package/dist/tempo/Proof.js +35 -0
- package/dist/tempo/Proof.js.map +1 -1
- package/dist/tempo/client/Charge.d.ts +13 -1
- package/dist/tempo/client/Charge.d.ts.map +1 -1
- package/dist/tempo/client/Charge.js +38 -25
- package/dist/tempo/client/Charge.js.map +1 -1
- package/dist/tempo/client/Methods.d.ts +5 -3
- package/dist/tempo/client/Methods.d.ts.map +1 -1
- package/dist/tempo/client/Methods.js +4 -2
- package/dist/tempo/client/Methods.js.map +1 -1
- package/dist/tempo/client/ResolveAccount.d.ts +40 -0
- package/dist/tempo/client/ResolveAccount.d.ts.map +1 -0
- package/dist/tempo/client/ResolveAccount.js +2 -0
- package/dist/tempo/client/ResolveAccount.js.map +1 -0
- package/dist/tempo/internal/fee-payer.d.ts +26 -1
- package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
- package/dist/tempo/internal/fee-payer.js +83 -30
- package/dist/tempo/internal/fee-payer.js.map +1 -1
- package/dist/tempo/internal/proof.d.ts +71 -5
- package/dist/tempo/internal/proof.d.ts.map +1 -1
- package/dist/tempo/internal/proof.js +42 -6
- package/dist/tempo/internal/proof.js.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/legacy/client/SessionManager.js +10 -3
- package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
- package/dist/tempo/server/Charge.d.ts.map +1 -1
- package/dist/tempo/server/Charge.js +46 -18
- package/dist/tempo/server/Charge.js.map +1 -1
- package/dist/tempo/server/Methods.d.ts +4 -2
- package/dist/tempo/server/Methods.d.ts.map +1 -1
- package/dist/tempo/server/Methods.js +4 -2
- package/dist/tempo/server/Methods.js.map +1 -1
- package/dist/tempo/server/Subscription.d.ts +10 -0
- package/dist/tempo/server/Subscription.d.ts.map +1 -1
- package/dist/tempo/server/Subscription.js +135 -23
- package/dist/tempo/server/Subscription.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/session/client/ChannelOps.d.ts +2 -3
- package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
- package/dist/tempo/session/client/ChannelOps.js +7 -10
- package/dist/tempo/session/client/ChannelOps.js.map +1 -1
- package/dist/tempo/session/client/ChannelStore.d.ts +51 -0
- package/dist/tempo/session/client/ChannelStore.d.ts.map +1 -0
- package/dist/tempo/session/client/ChannelStore.js +63 -0
- package/dist/tempo/session/client/ChannelStore.js.map +1 -0
- package/dist/tempo/session/client/CredentialState.d.ts +7 -24
- package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
- package/dist/tempo/session/client/CredentialState.js +51 -49
- package/dist/tempo/session/client/CredentialState.js.map +1 -1
- package/dist/tempo/session/client/Session.d.ts +8 -2
- package/dist/tempo/session/client/Session.d.ts.map +1 -1
- package/dist/tempo/session/client/Session.js +22 -8
- package/dist/tempo/session/client/Session.js.map +1 -1
- package/dist/tempo/session/client/SessionManager.d.ts +4 -40
- package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
- package/dist/tempo/session/client/SessionManager.js +124 -174
- package/dist/tempo/session/client/SessionManager.js.map +1 -1
- package/dist/tempo/session/client/index.d.ts +3 -4
- package/dist/tempo/session/client/index.d.ts.map +1 -1
- package/dist/tempo/session/client/index.js +1 -0
- package/dist/tempo/session/client/index.js.map +1 -1
- package/dist/tempo/session/precompile/Voucher.d.ts +3 -3
- package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
- package/dist/tempo/session/precompile/Voucher.js +24 -25
- package/dist/tempo/session/precompile/Voucher.js.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.d.ts +6 -0
- package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
- package/dist/tempo/session/server/CredentialVerification.js +13 -0
- package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
- package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
- package/dist/tempo/session/server/Settlement.js +4 -2
- package/dist/tempo/session/server/Settlement.js.map +1 -1
- package/dist/tempo/session/server/Sse.d.ts.map +1 -1
- package/dist/tempo/session/server/Sse.js.map +1 -1
- package/dist/tempo/session/server/Ws.d.ts.map +1 -1
- package/dist/tempo/session/server/Ws.js.map +1 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts +712 -1
- package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
- package/dist/tempo/subscription/Store.d.ts +2 -0
- package/dist/tempo/subscription/Store.d.ts.map +1 -1
- package/dist/tempo/subscription/Store.js +16 -1
- package/dist/tempo/subscription/Store.js.map +1 -1
- package/dist/x402/index.d.ts +1 -0
- package/dist/x402/index.d.ts.map +1 -1
- package/dist/x402/index.js +1 -0
- package/dist/x402/index.js.map +1 -1
- package/package.json +21 -10
- package/src/Challenge.test.ts +40 -0
- package/src/Challenge.ts +19 -6
- package/src/Mcp.ts +4 -0
- package/src/PaymentRequest.ts +10 -10
- package/src/cli/cli.test.ts +15 -15
- package/src/cli/config.test.ts +13 -7
- package/src/cli/internal.ts +1 -16
- package/src/client/Mppx.test-d.ts +21 -1
- package/src/client/Mppx.test.ts +1 -1
- package/src/client/Mppx.ts +2 -2
- package/src/client/Transport.test.ts +225 -178
- package/src/client/Transport.ts +77 -83
- package/src/client/index.ts +14 -0
- package/src/client/internal/Fetch.test.ts +207 -2
- package/src/client/internal/Fetch.ts +52 -6
- package/src/client/internal/protocols/Mcp.test.ts +220 -0
- package/src/client/internal/protocols/Mcp.ts +162 -0
- package/src/client/internal/protocols/Mpp.ts +21 -0
- package/src/client/internal/protocols/Protocol.ts +10 -0
- package/src/client/internal/protocols/Shared.ts +25 -0
- package/src/client/internal/protocols/X402.ts +42 -0
- package/src/discovery/OpenApi.test.ts +1 -1
- package/src/evm/PublicInterface.test-d.ts +1 -1
- package/src/evm/client/index.ts +1 -0
- package/src/evm/index.ts +2 -0
- package/src/evm/server/Charge.test.ts +1 -1
- package/src/evm/server/index.ts +1 -0
- package/src/{mcp-sdk → mcp}/client/McpClient.integration.test.ts +10 -4
- package/src/{mcp-sdk → mcp}/client/McpClient.test-d.ts +45 -18
- package/src/{mcp-sdk → mcp}/client/McpClient.test.ts +211 -5
- package/src/mcp/client/McpClient.ts +307 -0
- package/src/{mcp-sdk → mcp}/client/McpClient.unit.test.ts +9 -5
- package/src/middlewares/elysia.test.ts +1 -1
- package/src/middlewares/express.test.ts +1 -1
- package/src/middlewares/hono.test.ts +1 -1
- package/src/middlewares/internal/mppx.test.ts +1 -1
- package/src/middlewares/nextjs.test.ts +1 -1
- package/src/proxy/Proxy.test.ts +1 -1
- package/src/proxy/services/anthropic.test.ts +1 -1
- package/src/proxy/services/openai.test.ts +1 -1
- package/src/proxy/services/stripe.test.ts +1 -1
- package/src/server/Mppx.authorize.test.ts +1 -1
- package/src/server/Mppx.test-d.ts +1 -1
- package/src/server/Mppx.test.ts +20 -2
- package/src/server/Mppx.ts +14 -1
- package/src/server/Transport.test.ts +6 -6
- package/src/server/Transport.ts +1 -1
- package/src/stripe/Charge.integration.test.ts +1 -1
- package/src/stripe/client/Charge.test.ts +1 -1
- package/src/stripe/server/Charge.test.ts +1 -1
- package/src/stripe/server/internal/html/package.json +1 -1
- package/src/stripe/server/internal/html.gen.ts +1 -1
- package/src/tempo/Proof.conformance.test.ts +146 -0
- package/src/tempo/Proof.test-d.ts +15 -0
- package/src/tempo/Proof.ts +52 -1
- package/src/tempo/Subscription.integration.test.ts +1 -1
- package/src/tempo/client/Charge.test.ts +173 -0
- package/src/tempo/client/Charge.ts +65 -36
- package/src/tempo/client/Methods.ts +4 -2
- package/src/tempo/client/ResolveAccount.ts +46 -0
- package/src/tempo/internal/fee-payer.test.ts +89 -10
- package/src/tempo/internal/fee-payer.ts +128 -41
- package/src/tempo/internal/proof.test.ts +12 -4
- package/src/tempo/internal/proof.ts +55 -6
- package/src/tempo/legacy/client/SessionManager.ts +11 -3
- package/src/tempo/legacy/server/Session.test.ts +91 -26
- package/src/tempo/server/Charge.test.ts +388 -17
- package/src/tempo/server/Charge.ts +50 -24
- package/src/tempo/server/Methods.ts +4 -2
- package/src/tempo/server/Subscription.test.ts +465 -3
- package/src/tempo/server/Subscription.ts +174 -19
- package/src/tempo/server/internal/html/package.json +2 -2
- package/src/tempo/server/internal/html.gen.ts +1 -1
- package/src/tempo/session/client/ChannelOps.ts +5 -19
- package/src/tempo/session/client/ChannelStore.ts +111 -0
- package/src/tempo/session/client/CredentialState.test.ts +206 -62
- package/src/tempo/session/client/CredentialState.ts +58 -73
- package/src/tempo/session/client/Session.test.ts +41 -1
- package/src/tempo/session/client/Session.ts +36 -10
- package/src/tempo/session/client/SessionManager.test.ts +154 -65
- package/src/tempo/session/client/SessionManager.ts +141 -235
- package/src/tempo/session/client/index.ts +8 -5
- package/src/tempo/session/precompile/Voucher.test.ts +45 -7
- package/src/tempo/session/precompile/Voucher.ts +27 -25
- package/src/tempo/session/server/CredentialVerification.test.ts +36 -0
- package/src/tempo/session/server/CredentialVerification.ts +18 -0
- package/src/tempo/session/server/Session.test.ts +4 -4
- package/src/tempo/session/server/Settlement.test.ts +88 -1
- package/src/tempo/session/server/Settlement.ts +2 -1
- package/src/tempo/session/server/Sse.ts +0 -2
- package/src/tempo/session/server/Ws.ts +0 -4
- package/src/tempo/subscription/Store.ts +27 -9
- package/src/x402/Exact.e2e.test.ts +1 -1
- package/src/x402/PublicInterface.test-d.ts +1 -1
- package/src/x402/index.ts +1 -0
- package/dist/mcp-sdk/client/McpClient.d.ts +0 -85
- package/dist/mcp-sdk/client/McpClient.d.ts.map +0 -1
- package/dist/mcp-sdk/client/McpClient.js +0 -118
- package/dist/mcp-sdk/client/McpClient.js.map +0 -1
- package/dist/mcp-sdk/client/index.d.ts.map +0 -1
- package/dist/mcp-sdk/client/index.js.map +0 -1
- package/dist/mcp-sdk/server/Transport.d.ts.map +0 -1
- package/dist/mcp-sdk/server/Transport.js.map +0 -1
- package/dist/mcp-sdk/server/index.d.ts.map +0 -1
- package/dist/mcp-sdk/server/index.js.map +0 -1
- package/src/mcp-sdk/client/McpClient.ts +0 -228
- /package/dist/{mcp-sdk → mcp}/client/index.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/client/index.js +0 -0
- /package/dist/{mcp-sdk → mcp}/server/Transport.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/server/Transport.js +0 -0
- /package/dist/{mcp-sdk → mcp}/server/index.d.ts +0 -0
- /package/dist/{mcp-sdk → mcp}/server/index.js +0 -0
- /package/src/{mcp-sdk → mcp}/client/index.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/Transport.test.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/Transport.ts +0 -0
- /package/src/{mcp-sdk → mcp}/server/index.ts +0 -0
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import type { Account } from 'viem'
|
|
1
|
+
import type { Account, Address } from 'viem'
|
|
2
2
|
import { describe, expectTypeOf, test } from 'vp/test'
|
|
3
3
|
|
|
4
4
|
import * as Challenge from '../Challenge.js'
|
|
@@ -9,6 +9,7 @@ import { tempo } from '../tempo/client/Methods.js'
|
|
|
9
9
|
import type * as AutoSwap from '../tempo/internal/auto-swap.js'
|
|
10
10
|
import * as Methods from '../tempo/Methods.js'
|
|
11
11
|
import * as z from '../zod.js'
|
|
12
|
+
import type { ResolveAccount } from './index.js'
|
|
12
13
|
import * as Fetch from './internal/Fetch.js'
|
|
13
14
|
import { session, sessionManager, sessionMethod } from './Methods.js'
|
|
14
15
|
import * as Mppx from './Mppx.js'
|
|
@@ -78,6 +79,25 @@ describe('create.Config', () => {
|
|
|
78
79
|
expectTypeOf(mppx.fetch).toBeFunction()
|
|
79
80
|
})
|
|
80
81
|
|
|
82
|
+
test('tempo common accepts one resolveAccount hook for charge and session', () => {
|
|
83
|
+
const resolveAccount: ResolveAccount = (info) => {
|
|
84
|
+
expectTypeOf(info).toHaveProperty('account')
|
|
85
|
+
expectTypeOf(info.chainId).toEqualTypeOf<number>()
|
|
86
|
+
if (info.operation.kind === 'executeCalls') {
|
|
87
|
+
expectTypeOf(info.operation.calls).toMatchTypeOf<
|
|
88
|
+
readonly { data: `0x${string}`; to: Address }[] | undefined
|
|
89
|
+
>()
|
|
90
|
+
}
|
|
91
|
+
if (info.operation.kind === 'authorizePaymentChannel') {
|
|
92
|
+
expectTypeOf(info.operation.authority).toEqualTypeOf<Address | undefined>()
|
|
93
|
+
}
|
|
94
|
+
return info.account
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const methods = tempo({ account: {} as Account, resolveAccount })
|
|
98
|
+
expectTypeOf(methods).toMatchTypeOf<readonly Method.AnyClient[]>()
|
|
99
|
+
})
|
|
100
|
+
|
|
81
101
|
test('orderChallenges receives supported challenge candidates', () => {
|
|
82
102
|
const mppx = Mppx.create({
|
|
83
103
|
methods: [tempo({ account: {} as Account })],
|
package/src/client/Mppx.test.ts
CHANGED
|
@@ -7,7 +7,7 @@ import * as Http from '~test/Http.js'
|
|
|
7
7
|
import { accounts, asset, client } from '~test/tempo/viem.js'
|
|
8
8
|
|
|
9
9
|
const realm = 'api.example.com'
|
|
10
|
-
const secretKey = 'test-secret-key'
|
|
10
|
+
const secretKey = 'test-secret-key-test-secret-key-32'
|
|
11
11
|
|
|
12
12
|
afterEach(() => {
|
|
13
13
|
Mppx.restore()
|
package/src/client/Mppx.ts
CHANGED
|
@@ -187,8 +187,8 @@ export function create<
|
|
|
187
187
|
options?: createCredential.Options<FlattenMethods<methods>>,
|
|
188
188
|
) {
|
|
189
189
|
const challenges = transport.getChallenges
|
|
190
|
-
? transport.getChallenges(response as never)
|
|
191
|
-
: [transport.getChallenge(response as never)]
|
|
190
|
+
? await transport.getChallenges(response as never)
|
|
191
|
+
: [await transport.getChallenge(response as never)]
|
|
192
192
|
const preferences = resolveChallengePreferences(acceptPayment.entries, options?.acceptPayment)
|
|
193
193
|
|
|
194
194
|
let challenge: Challenge.Challenge | undefined
|
|
@@ -4,6 +4,8 @@ import { Methods } from 'mppx/tempo'
|
|
|
4
4
|
import { Header as x402_Header, Types as x402_Types, type PaymentRequired } from 'mppx/x402'
|
|
5
5
|
import { describe, expect, test } from 'vp/test'
|
|
6
6
|
|
|
7
|
+
import * as x402_ChallengeBrand from '../x402/internal/ChallengeBrand.js'
|
|
8
|
+
|
|
7
9
|
const realm = 'api.example.com'
|
|
8
10
|
const secretKey = 'test-secret-key'
|
|
9
11
|
|
|
@@ -138,13 +140,13 @@ describe('http', () => {
|
|
|
138
140
|
}),
|
|
139
141
|
name: 'Payment auth and x402 challenges when both are present',
|
|
140
142
|
},
|
|
141
|
-
])('returns $name', ({ expectedIds, expectedMethods, headers }) => {
|
|
143
|
+
])('returns $name', async ({ expectedIds, expectedMethods, headers }) => {
|
|
142
144
|
const transport = Transport.http()
|
|
143
145
|
const response = new Response(null, {
|
|
144
146
|
status: 402,
|
|
145
147
|
headers: headers(),
|
|
146
148
|
})
|
|
147
|
-
const challenges = transport.getChallenges?.(response) ?? []
|
|
149
|
+
const challenges = (await transport.getChallenges?.(response)) ?? []
|
|
148
150
|
|
|
149
151
|
expect(challenges.map((entry) => entry.id)).toEqual(expectedIds)
|
|
150
152
|
expect(challenges.map((entry) => entry.method)).toEqual(expectedMethods)
|
|
@@ -160,20 +162,6 @@ describe('http', () => {
|
|
|
160
162
|
expectedValue: Credential.serialize(credential),
|
|
161
163
|
name: 'Payment auth credential for Payment auth challenge',
|
|
162
164
|
},
|
|
163
|
-
{
|
|
164
|
-
challenge: Transport.http().getChallenges!(
|
|
165
|
-
new Response(null, {
|
|
166
|
-
status: 402,
|
|
167
|
-
headers: {
|
|
168
|
-
'PAYMENT-REQUIRED': x402_Header.encodePaymentRequired(x402PaymentRequired),
|
|
169
|
-
},
|
|
170
|
-
}),
|
|
171
|
-
)[0],
|
|
172
|
-
credential: 'x402-signature',
|
|
173
|
-
expectedHeader: 'PAYMENT-SIGNATURE',
|
|
174
|
-
expectedValue: 'x402-signature',
|
|
175
|
-
name: 'raw x402 credential for x402 challenge',
|
|
176
|
-
},
|
|
177
165
|
{
|
|
178
166
|
challenge,
|
|
179
167
|
credential: 'custom-credential',
|
|
@@ -197,6 +185,24 @@ describe('http', () => {
|
|
|
197
185
|
expect(headers.get(expectedHeader)).toBe(expectedValue)
|
|
198
186
|
})
|
|
199
187
|
|
|
188
|
+
test('writes x402 credentials for x402 challenges from the same transport', () => {
|
|
189
|
+
const transport = Transport.http()
|
|
190
|
+
const [x402Challenge] = transport.getChallenges!(
|
|
191
|
+
new Response(null, {
|
|
192
|
+
status: 402,
|
|
193
|
+
headers: {
|
|
194
|
+
'PAYMENT-REQUIRED': x402_Header.encodePaymentRequired(x402PaymentRequired),
|
|
195
|
+
},
|
|
196
|
+
}),
|
|
197
|
+
) as Challenge.Challenge[]
|
|
198
|
+
|
|
199
|
+
const result = transport.setCredential({}, 'x402-signature', { challenge: x402Challenge })
|
|
200
|
+
const headers = result.headers as Headers
|
|
201
|
+
|
|
202
|
+
expect(headers.get('Authorization')).toBeNull()
|
|
203
|
+
expect(headers.get(x402_Types.paymentSignatureHeader)).toBe('x402-signature')
|
|
204
|
+
})
|
|
205
|
+
|
|
200
206
|
test('does not treat unbranded Payment-auth challenges as x402', () => {
|
|
201
207
|
const transport = Transport.http()
|
|
202
208
|
const untrustedChallenge = Challenge.from({
|
|
@@ -216,16 +222,53 @@ describe('http', () => {
|
|
|
216
222
|
expect(headers.get(x402_Types.paymentSignatureHeader)).toBeNull()
|
|
217
223
|
})
|
|
218
224
|
|
|
219
|
-
test('
|
|
225
|
+
test('routes JSON-RPC requests with native 402 challenges through Authorization', () => {
|
|
226
|
+
const transport = Transport.http()
|
|
227
|
+
const jsonRpcRequest = {
|
|
228
|
+
method: 'POST',
|
|
229
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'tools/call', params: {} }),
|
|
230
|
+
} satisfies RequestInit
|
|
231
|
+
const [nativeChallenge] = transport.getChallenges!(
|
|
232
|
+
new Response(null, {
|
|
233
|
+
status: 402,
|
|
234
|
+
headers: { 'WWW-Authenticate': Challenge.serialize(challenge) },
|
|
235
|
+
}),
|
|
236
|
+
jsonRpcRequest,
|
|
237
|
+
) as Challenge.Challenge[]
|
|
238
|
+
|
|
239
|
+
const result = transport.setCredential(jsonRpcRequest, 'credential', {
|
|
240
|
+
challenge: nativeChallenge,
|
|
241
|
+
})
|
|
242
|
+
const headers = new Headers(result.headers)
|
|
243
|
+
|
|
244
|
+
expect(headers.get('Authorization')).toBe('credential')
|
|
245
|
+
expect(result.body).toBe(jsonRpcRequest.body)
|
|
246
|
+
})
|
|
247
|
+
|
|
248
|
+
test('marks x402 challenges with the brand the evm charge client reads', async () => {
|
|
249
|
+
// Provenance guard: `evm/client/Charge.ts` recognizes x402 challenges via the same
|
|
250
|
+
// brand the x402 adapter stamps. If the marking site drifts, EVM x402 charges misroute.
|
|
251
|
+
const [x402Challenge] = await Transport.http().getChallenges!(
|
|
252
|
+
new Response(null, {
|
|
253
|
+
status: 402,
|
|
254
|
+
headers: {
|
|
255
|
+
'PAYMENT-REQUIRED': x402_Header.encodePaymentRequired(x402PaymentRequired),
|
|
256
|
+
},
|
|
257
|
+
}),
|
|
258
|
+
)
|
|
259
|
+
expect(x402_ChallengeBrand.is(x402Challenge)).toBe(true)
|
|
260
|
+
})
|
|
261
|
+
|
|
262
|
+
test('removes stale credential headers before setting the retry credential', async () => {
|
|
220
263
|
const transport = Transport.http()
|
|
221
|
-
const x402Challenge =
|
|
264
|
+
const [x402Challenge] = await transport.getChallenges!(
|
|
222
265
|
new Response(null, {
|
|
223
266
|
status: 402,
|
|
224
267
|
headers: {
|
|
225
268
|
'PAYMENT-REQUIRED': x402_Header.encodePaymentRequired(x402PaymentRequired),
|
|
226
269
|
},
|
|
227
270
|
}),
|
|
228
|
-
)
|
|
271
|
+
)
|
|
229
272
|
|
|
230
273
|
const result = transport.setCredential(
|
|
231
274
|
{
|
|
@@ -258,191 +301,195 @@ describe('http', () => {
|
|
|
258
301
|
})
|
|
259
302
|
})
|
|
260
303
|
|
|
261
|
-
describe('
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
304
|
+
describe('http (MCP-over-HTTP)', () => {
|
|
305
|
+
// An MCP-over-HTTP call: a JSON-RPC POST whose response carries the -32042 challenge.
|
|
306
|
+
const jsonRpcRequest = {
|
|
307
|
+
method: 'POST',
|
|
308
|
+
headers: { accept: 'application/json, text/event-stream' },
|
|
309
|
+
body: JSON.stringify({
|
|
310
|
+
jsonrpc: '2.0',
|
|
311
|
+
id: 1,
|
|
312
|
+
method: 'tools/call',
|
|
313
|
+
params: { name: 'premium' },
|
|
314
|
+
}),
|
|
315
|
+
} satisfies RequestInit
|
|
316
|
+
const errorMessage = {
|
|
317
|
+
jsonrpc: '2.0',
|
|
318
|
+
id: 1,
|
|
319
|
+
error: {
|
|
320
|
+
code: Mcp.paymentRequiredCode,
|
|
321
|
+
message: 'Payment Required',
|
|
322
|
+
data: { challenges: [challenge] },
|
|
266
323
|
},
|
|
267
324
|
}
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
jsonrpc: '2.0',
|
|
274
|
-
id: 1,
|
|
275
|
-
error: {
|
|
276
|
-
code: Mcp.paymentRequiredCode,
|
|
277
|
-
message: 'Payment Required',
|
|
278
|
-
data: {
|
|
279
|
-
httpStatus: 402,
|
|
280
|
-
challenges: [challenge],
|
|
281
|
-
},
|
|
282
|
-
},
|
|
283
|
-
}
|
|
284
|
-
|
|
285
|
-
expect(transport.isPaymentRequired(response)).toBe(true)
|
|
325
|
+
const jsonBody = () =>
|
|
326
|
+
new Response(JSON.stringify(errorMessage), { headers: { 'content-type': 'application/json' } })
|
|
327
|
+
const sseBody = () =>
|
|
328
|
+
new Response(`event: message\ndata: ${JSON.stringify(errorMessage)}\n\n`, {
|
|
329
|
+
headers: { 'content-type': 'text/event-stream' },
|
|
286
330
|
})
|
|
287
331
|
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
}
|
|
295
|
-
|
|
296
|
-
expect(transport.isPaymentRequired(response)).toBe(false)
|
|
332
|
+
test('detects -32042 in a JSON body (JSON-RPC request)', async () => {
|
|
333
|
+
expect(await Transport.http().isPaymentRequired(jsonBody(), jsonRpcRequest)).toBe(true)
|
|
334
|
+
})
|
|
335
|
+
test('ignores a JSON-RPC response for a different request id', async () => {
|
|
336
|
+
const response = new Response(JSON.stringify({ ...errorMessage, id: 2 }), {
|
|
337
|
+
headers: { 'content-type': 'application/json' },
|
|
297
338
|
})
|
|
298
339
|
|
|
299
|
-
|
|
300
|
-
const transport = Transport.mcp()
|
|
301
|
-
const response: Mcp.Response = {
|
|
302
|
-
jsonrpc: '2.0',
|
|
303
|
-
id: 1,
|
|
304
|
-
error: {
|
|
305
|
-
code: -32600,
|
|
306
|
-
message: 'Invalid Request',
|
|
307
|
-
},
|
|
308
|
-
}
|
|
309
|
-
|
|
310
|
-
expect(transport.isPaymentRequired(response)).toBe(false)
|
|
311
|
-
})
|
|
340
|
+
expect(await Transport.http().isPaymentRequired(response, jsonRpcRequest)).toBe(false)
|
|
312
341
|
})
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
342
|
+
test('detects -32042 in an SSE body', async () => {
|
|
343
|
+
expect(await Transport.http().isPaymentRequired(sseBody(), jsonRpcRequest)).toBe(true)
|
|
344
|
+
})
|
|
345
|
+
test('does not scan past the first SSE data event', async () => {
|
|
346
|
+
const notification = { jsonrpc: '2.0', method: 'notifications/progress', params: {} }
|
|
347
|
+
const response = new Response(
|
|
348
|
+
`event: message\ndata: ${JSON.stringify(notification)}\n\n` +
|
|
349
|
+
`event: message\ndata: ${JSON.stringify(errorMessage)}\n\n`,
|
|
350
|
+
{ headers: { 'content-type': 'text/event-stream' } },
|
|
351
|
+
)
|
|
352
|
+
|
|
353
|
+
expect(await Transport.http().isPaymentRequired(response, jsonRpcRequest)).toBe(false)
|
|
354
|
+
})
|
|
355
|
+
test('detects -32042 in an open SSE stream without waiting for stream close', async () => {
|
|
356
|
+
const encoder = new TextEncoder()
|
|
357
|
+
let timeout: ReturnType<typeof setTimeout> | undefined
|
|
358
|
+
const openSse = new Response(
|
|
359
|
+
new ReadableStream({
|
|
360
|
+
start(controller) {
|
|
361
|
+
controller.enqueue(
|
|
362
|
+
encoder.encode(`event: message\ndata: ${JSON.stringify(errorMessage)}\n\n`),
|
|
363
|
+
)
|
|
327
364
|
},
|
|
328
|
-
}
|
|
365
|
+
}),
|
|
366
|
+
{ headers: { 'content-type': 'text/event-stream' } },
|
|
367
|
+
)
|
|
368
|
+
|
|
369
|
+
try {
|
|
370
|
+
const result = await Promise.race([
|
|
371
|
+
Transport.http().isPaymentRequired(openSse, jsonRpcRequest),
|
|
372
|
+
new Promise<'timeout'>((resolve) => {
|
|
373
|
+
timeout = setTimeout(() => resolve('timeout'), 100)
|
|
374
|
+
}),
|
|
375
|
+
])
|
|
329
376
|
|
|
330
|
-
expect(
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
377
|
+
expect(result).toBe(true)
|
|
378
|
+
} finally {
|
|
379
|
+
if (timeout) clearTimeout(timeout)
|
|
380
|
+
}
|
|
381
|
+
})
|
|
382
|
+
test('does not read the body for a non-JSON-RPC request', async () => {
|
|
383
|
+
expect(
|
|
384
|
+
await Transport.http().isPaymentRequired(sseBody(), { method: 'POST', body: 'plain' }),
|
|
385
|
+
).toBe(false)
|
|
386
|
+
})
|
|
387
|
+
test('does not treat generic JSON-RPC as MCP-over-HTTP', async () => {
|
|
388
|
+
const request = {
|
|
389
|
+
method: 'POST',
|
|
390
|
+
headers: { accept: 'application/json' },
|
|
391
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'eth_call', params: [] }),
|
|
392
|
+
} satisfies RequestInit
|
|
393
|
+
|
|
394
|
+
expect(await Transport.http().isPaymentRequired(sseBody(), request)).toBe(false)
|
|
395
|
+
})
|
|
396
|
+
test('ignores a 200 success', async () => {
|
|
397
|
+
const ok = new Response(JSON.stringify({ jsonrpc: '2.0', id: 1, result: {} }), {
|
|
398
|
+
headers: { 'content-type': 'application/json' },
|
|
342
399
|
})
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
400
|
+
expect(await Transport.http().isPaymentRequired(ok, jsonRpcRequest)).toBe(false)
|
|
401
|
+
})
|
|
402
|
+
test('getChallenges extracts the MCP challenge (via the mcp protocol)', async () => {
|
|
403
|
+
const challenges = await Transport.http().getChallenges!(sseBody(), jsonRpcRequest)
|
|
404
|
+
expect(challenges.map((entry) => entry.id)).toEqual([challenge.id])
|
|
405
|
+
})
|
|
406
|
+
test('setCredential routes the MCP challenge into the JSON-RPC _meta', async () => {
|
|
407
|
+
const transport = Transport.http()
|
|
408
|
+
const [mcpChallenge] = await transport.getChallenges!(sseBody(), jsonRpcRequest)
|
|
409
|
+
const result = transport.setCredential(jsonRpcRequest, Credential.serialize(credential), {
|
|
410
|
+
challenge: mcpChallenge,
|
|
411
|
+
}) as RequestInit
|
|
412
|
+
const parsed = JSON.parse(result.body as string)
|
|
413
|
+
expect(parsed.params['_meta'][Mcp.credentialMetaKey]).toMatchObject({
|
|
414
|
+
payload: { type: 'transaction' },
|
|
353
415
|
})
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
httpStatus: 402,
|
|
365
|
-
challenges: [],
|
|
366
|
-
},
|
|
367
|
-
},
|
|
368
|
-
}
|
|
369
|
-
|
|
370
|
-
expect(() => transport.getChallenge(response)).toThrow('No challenge in error response')
|
|
416
|
+
})
|
|
417
|
+
test('detects -32042 in a JSON body containing a "data:" substring', async () => {
|
|
418
|
+
const body = JSON.stringify({
|
|
419
|
+
jsonrpc: '2.0',
|
|
420
|
+
id: 1,
|
|
421
|
+
error: {
|
|
422
|
+
code: Mcp.paymentRequiredCode,
|
|
423
|
+
message: 'Payment Required',
|
|
424
|
+
data: { challenges: [{ ...challenge, realm: 'data:text/plain,x' }] },
|
|
425
|
+
},
|
|
371
426
|
})
|
|
427
|
+
const response = new Response(body, { headers: { 'content-type': 'application/json' } })
|
|
428
|
+
expect(await Transport.http().isPaymentRequired(response, jsonRpcRequest)).toBe(true)
|
|
372
429
|
})
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
const transport = Transport.mcp()
|
|
377
|
-
const response: Mcp.Response = {
|
|
430
|
+
test('ignores malformed payment challenge data', async () => {
|
|
431
|
+
const response = new Response(
|
|
432
|
+
JSON.stringify({
|
|
378
433
|
jsonrpc: '2.0',
|
|
379
434
|
id: 1,
|
|
380
435
|
error: {
|
|
381
436
|
code: Mcp.paymentRequiredCode,
|
|
382
437
|
message: 'Payment Required',
|
|
383
|
-
data: {
|
|
384
|
-
httpStatus: 402,
|
|
385
|
-
challenges: [challenge, { ...challenge, id: 'alternate', method: 'stripe' }],
|
|
386
|
-
},
|
|
438
|
+
data: { challenges: [{ ...challenge, realm: undefined }] },
|
|
387
439
|
},
|
|
388
|
-
}
|
|
440
|
+
}),
|
|
441
|
+
{ headers: { 'content-type': 'application/json' } },
|
|
442
|
+
)
|
|
389
443
|
|
|
390
|
-
|
|
391
|
-
challenge.id,
|
|
392
|
-
'alternate',
|
|
393
|
-
])
|
|
394
|
-
})
|
|
444
|
+
expect(await Transport.http().isPaymentRequired(response, jsonRpcRequest)).toBe(false)
|
|
395
445
|
})
|
|
446
|
+
test('detects -32042 for any JSON-RPC method (no method allowlist)', async () => {
|
|
447
|
+
const request = {
|
|
448
|
+
method: 'POST',
|
|
449
|
+
headers: { accept: 'application/json, text/event-stream' },
|
|
450
|
+
body: JSON.stringify({ jsonrpc: '2.0', id: 1, method: 'custom/paidMethod', params: {} }),
|
|
451
|
+
} satisfies RequestInit
|
|
452
|
+
expect(await Transport.http().isPaymentRequired(sseBody(), request)).toBe(true)
|
|
453
|
+
})
|
|
454
|
+
})
|
|
396
455
|
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
456
|
+
describe('mcp', () => {
|
|
457
|
+
const mcpRequest: Mcp.Request = {
|
|
458
|
+
method: 'tools/call',
|
|
459
|
+
params: {
|
|
460
|
+
name: 'test-tool',
|
|
461
|
+
},
|
|
462
|
+
}
|
|
401
463
|
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
amount: '1000',
|
|
415
|
-
currency: '0x20c0000000000000000000000000000000000001',
|
|
416
|
-
recipient: '0x742d35Cc6634C0532925a3b844Bc9e7595f8fE00',
|
|
417
|
-
},
|
|
418
|
-
},
|
|
419
|
-
payload: {
|
|
420
|
-
signature: '0xabc123',
|
|
421
|
-
type: 'transaction',
|
|
422
|
-
},
|
|
423
|
-
},
|
|
424
|
-
},
|
|
425
|
-
name: 'test-tool',
|
|
426
|
-
},
|
|
427
|
-
})
|
|
428
|
-
})
|
|
464
|
+
const mcpResponse: Mcp.Response = {
|
|
465
|
+
jsonrpc: '2.0',
|
|
466
|
+
id: 1,
|
|
467
|
+
error: {
|
|
468
|
+
code: Mcp.paymentRequiredCode,
|
|
469
|
+
message: 'Payment Required',
|
|
470
|
+
data: {
|
|
471
|
+
httpStatus: 402,
|
|
472
|
+
challenges: [challenge],
|
|
473
|
+
},
|
|
474
|
+
},
|
|
475
|
+
}
|
|
429
476
|
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
existingKey: 'existingValue',
|
|
439
|
-
},
|
|
440
|
-
},
|
|
441
|
-
}
|
|
477
|
+
test('extracts payment-required challenges from JSON-RPC errors', async () => {
|
|
478
|
+
const transport = Transport.mcp()
|
|
479
|
+
|
|
480
|
+
expect(await transport.isPaymentRequired(mcpResponse)).toBe(true)
|
|
481
|
+
expect((await transport.getChallenges?.(mcpResponse))?.map((entry) => entry.id)).toEqual([
|
|
482
|
+
challenge.id,
|
|
483
|
+
])
|
|
484
|
+
})
|
|
442
485
|
|
|
443
|
-
|
|
486
|
+
test('sets credentials in JSON-RPC _meta', () => {
|
|
487
|
+
const result = Transport.mcp().setCredential(mcpRequest, Credential.serialize(credential))
|
|
444
488
|
|
|
445
|
-
|
|
489
|
+
expect(result.params?.['_meta']?.[Mcp.credentialMetaKey]).toMatchObject({
|
|
490
|
+
payload: {
|
|
491
|
+
type: 'transaction',
|
|
492
|
+
},
|
|
446
493
|
})
|
|
447
494
|
})
|
|
448
495
|
})
|