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,3 @@
|
|
|
1
|
-
import { Signature } from 'ox'
|
|
2
1
|
import { Channel, SignatureEnvelope } from 'ox/tempo'
|
|
3
2
|
import type { Account, Address, Client, Hex } from 'viem'
|
|
4
3
|
import { hashTypedData } from 'viem'
|
|
@@ -45,6 +44,24 @@ function getVoucherDigest(chainId: number, voucher: Voucher): Hex {
|
|
|
45
44
|
}) as Hex
|
|
46
45
|
}
|
|
47
46
|
|
|
47
|
+
function getVoucherPayload(verifyingContract: Address, chainId: number, voucher: Voucher): Hex {
|
|
48
|
+
if (verifyingContract.toLowerCase() === Channel.address.toLowerCase())
|
|
49
|
+
return getVoucherDigest(chainId, voucher)
|
|
50
|
+
return hashTypedData({
|
|
51
|
+
domain: getVoucherDomain(verifyingContract, chainId),
|
|
52
|
+
types: voucherTypes,
|
|
53
|
+
primaryType: 'Voucher',
|
|
54
|
+
message: {
|
|
55
|
+
channelId: voucher.channelId,
|
|
56
|
+
cumulativeAmount: voucher.cumulativeAmount,
|
|
57
|
+
},
|
|
58
|
+
})
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
function isPrimitiveEnvelope(type: string): boolean {
|
|
62
|
+
return type === 'secp256k1' || type === 'p256' || type === 'webAuthn'
|
|
63
|
+
}
|
|
64
|
+
|
|
48
65
|
function signCanonicalTempoVoucher(
|
|
49
66
|
account: Account,
|
|
50
67
|
parameters: {
|
|
@@ -72,7 +89,6 @@ export async function signVoucher(
|
|
|
72
89
|
voucher: Voucher,
|
|
73
90
|
verifyingContract: Address,
|
|
74
91
|
chainId: number,
|
|
75
|
-
_authorizedSigner?: Address | undefined,
|
|
76
92
|
): Promise<Hex> {
|
|
77
93
|
const signature = await (async () => {
|
|
78
94
|
if (verifyingContract.toLowerCase() === Channel.address.toLowerCase()) {
|
|
@@ -94,19 +110,19 @@ export async function signVoucher(
|
|
|
94
110
|
})()
|
|
95
111
|
|
|
96
112
|
const envelope = SignatureEnvelope.from(signature as SignatureEnvelope.Serialized)
|
|
97
|
-
if (envelope.type
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
113
|
+
if (!isPrimitiveEnvelope(envelope.type))
|
|
114
|
+
throw new Error(
|
|
115
|
+
`TIP-1034 vouchers require a TIP-1020 primitive signature; received "${envelope.type}".`,
|
|
116
|
+
)
|
|
101
117
|
|
|
102
|
-
return
|
|
118
|
+
return SignatureEnvelope.serialize(envelope)
|
|
103
119
|
}
|
|
104
120
|
|
|
105
121
|
/**
|
|
106
122
|
* Verify a voucher signature matches the expected signer.
|
|
107
123
|
*
|
|
108
|
-
*
|
|
109
|
-
*
|
|
124
|
+
* Accepts canonical TIP-1020 primitive signatures. Keychain wrappers,
|
|
125
|
+
* multisig signatures, and magic-suffixed encodings are rejected.
|
|
110
126
|
*/
|
|
111
127
|
export function verifyVoucher(
|
|
112
128
|
escrowContract: Address,
|
|
@@ -117,25 +133,11 @@ export function verifyVoucher(
|
|
|
117
133
|
try {
|
|
118
134
|
const envelope = SignatureEnvelope.from(voucher.signature as SignatureEnvelope.Serialized)
|
|
119
135
|
|
|
120
|
-
|
|
121
|
-
// signatures against authorizedSigner, not keychain-wrapped ones.
|
|
122
|
-
if (envelope.type === 'keychain') return false
|
|
123
|
-
if (envelope.type !== 'secp256k1') return false
|
|
136
|
+
if (!isPrimitiveEnvelope(envelope.type)) return false
|
|
124
137
|
if (SignatureEnvelope.serialize(envelope).toLowerCase() !== voucher.signature.toLowerCase())
|
|
125
138
|
return false
|
|
126
139
|
|
|
127
|
-
const payload =
|
|
128
|
-
escrowContract.toLowerCase() === Channel.address.toLowerCase()
|
|
129
|
-
? getVoucherDigest(chainId, voucher)
|
|
130
|
-
: hashTypedData({
|
|
131
|
-
domain: getVoucherDomain(escrowContract, chainId),
|
|
132
|
-
types: voucherTypes,
|
|
133
|
-
primaryType: 'Voucher',
|
|
134
|
-
message: {
|
|
135
|
-
channelId: voucher.channelId,
|
|
136
|
-
cumulativeAmount: voucher.cumulativeAmount,
|
|
137
|
-
},
|
|
138
|
-
})
|
|
140
|
+
const payload = getVoucherPayload(escrowContract, chainId, voucher)
|
|
139
141
|
const signer = SignatureEnvelope.extractAddress({ payload, signature: envelope })
|
|
140
142
|
const valid = SignatureEnvelope.verify(envelope, { address: signer, payload })
|
|
141
143
|
return valid && TempoAddress.isEqual(signer, expectedSigner)
|
|
@@ -3,6 +3,7 @@ import { describe, expect, test } from 'vp/test'
|
|
|
3
3
|
|
|
4
4
|
import * as Channel from '../precompile/Channel.js'
|
|
5
5
|
import {
|
|
6
|
+
assertOpenCredentialCoversRequest,
|
|
6
7
|
requireSessionCredentialAction,
|
|
7
8
|
requireSessionCredentialPayload,
|
|
8
9
|
requireSessionCredentialPayloadHeader,
|
|
@@ -143,4 +144,39 @@ describe('SessionCredentialGuards', () => {
|
|
|
143
144
|
).toThrow('channel descriptor operator does not match server operator')
|
|
144
145
|
})
|
|
145
146
|
})
|
|
147
|
+
|
|
148
|
+
describe('assertOpenCredentialCoversRequest', () => {
|
|
149
|
+
test('accepts deposit and voucher amounts that cover the request', () => {
|
|
150
|
+
expect(() =>
|
|
151
|
+
assertOpenCredentialCoversRequest({
|
|
152
|
+
cumulativeAmount: 100n,
|
|
153
|
+
openDeposit: 100n,
|
|
154
|
+
requestAmount: 100n,
|
|
155
|
+
}),
|
|
156
|
+
).not.toThrow()
|
|
157
|
+
})
|
|
158
|
+
|
|
159
|
+
test.each([
|
|
160
|
+
{
|
|
161
|
+
cumulativeAmount: 100n,
|
|
162
|
+
expected: 'open deposit is less than request amount',
|
|
163
|
+
openDeposit: 99n,
|
|
164
|
+
requestAmount: 100n,
|
|
165
|
+
},
|
|
166
|
+
{
|
|
167
|
+
cumulativeAmount: 99n,
|
|
168
|
+
expected: 'voucher amount is less than request amount',
|
|
169
|
+
openDeposit: 100n,
|
|
170
|
+
requestAmount: 100n,
|
|
171
|
+
},
|
|
172
|
+
])('rejects insufficient open credential funding: $expected', (case_) => {
|
|
173
|
+
expect(() =>
|
|
174
|
+
assertOpenCredentialCoversRequest({
|
|
175
|
+
cumulativeAmount: case_.cumulativeAmount,
|
|
176
|
+
openDeposit: case_.openDeposit,
|
|
177
|
+
requestAmount: case_.requestAmount,
|
|
178
|
+
}),
|
|
179
|
+
).toThrow(case_.expected)
|
|
180
|
+
})
|
|
181
|
+
})
|
|
146
182
|
})
|
|
@@ -113,6 +113,19 @@ export function validateChannelState(state: Chain.ChannelState, amount?: bigint)
|
|
|
113
113
|
}
|
|
114
114
|
}
|
|
115
115
|
|
|
116
|
+
/** Asserts that an opening channel covers the route's requested payment. */
|
|
117
|
+
export function assertOpenCredentialCoversRequest(parameters: {
|
|
118
|
+
cumulativeAmount: bigint
|
|
119
|
+
openDeposit: bigint
|
|
120
|
+
requestAmount: bigint
|
|
121
|
+
}): void {
|
|
122
|
+
const { cumulativeAmount, openDeposit, requestAmount } = parameters
|
|
123
|
+
if (openDeposit < requestAmount)
|
|
124
|
+
throw new VerificationFailedError({ reason: 'open deposit is less than request amount' })
|
|
125
|
+
if (cumulativeAmount < requestAmount)
|
|
126
|
+
throw new VerificationFailedError({ reason: 'voucher amount is less than request amount' })
|
|
127
|
+
}
|
|
128
|
+
|
|
116
129
|
const sessionCredentialActions = [
|
|
117
130
|
'open',
|
|
118
131
|
'topUp',
|
|
@@ -410,6 +423,11 @@ async function handleOpenCredential(
|
|
|
410
423
|
feePayerPolicy: parameters.feePayerPolicy,
|
|
411
424
|
serializedTransaction: payload.transaction,
|
|
412
425
|
async beforeBroadcast(prepared) {
|
|
426
|
+
assertOpenCredentialCoversRequest({
|
|
427
|
+
cumulativeAmount,
|
|
428
|
+
openDeposit: prepared.openDeposit,
|
|
429
|
+
requestAmount: request.amount,
|
|
430
|
+
})
|
|
413
431
|
assertSameDescriptor(prepared.descriptor, payload.descriptor)
|
|
414
432
|
if (cumulativeAmount > prepared.openDeposit)
|
|
415
433
|
throw new AmountExceedsDepositError({ reason: 'voucher amount exceeds open deposit' })
|
|
@@ -1815,7 +1815,7 @@ describe('precompile server session unit guardrails', () => {
|
|
|
1815
1815
|
}),
|
|
1816
1816
|
],
|
|
1817
1817
|
realm: 'api.example.com',
|
|
1818
|
-
secretKey: 'secret',
|
|
1818
|
+
secretKey: 'test-secret-key-test-secret-key-32',
|
|
1819
1819
|
}).session({ amount: '1', decimals: 0, unitType: 'request' })
|
|
1820
1820
|
}
|
|
1821
1821
|
|
|
@@ -1996,7 +1996,7 @@ describe('precompile server session unit guardrails', () => {
|
|
|
1996
1996
|
}),
|
|
1997
1997
|
],
|
|
1998
1998
|
realm: 'api.example.com',
|
|
1999
|
-
secretKey: 'secret',
|
|
1999
|
+
secretKey: 'test-secret-key-test-secret-key-32',
|
|
2000
2000
|
}).session({ amount: '1', decimals: 0, unitType: 'request' })
|
|
2001
2001
|
}
|
|
2002
2002
|
|
|
@@ -2163,7 +2163,7 @@ describe('precompile server session unit guardrails', () => {
|
|
|
2163
2163
|
}),
|
|
2164
2164
|
],
|
|
2165
2165
|
realm: 'api.example.com',
|
|
2166
|
-
secretKey: 'secret',
|
|
2166
|
+
secretKey: 'test-secret-key-test-secret-key-32',
|
|
2167
2167
|
}).session({ amount, decimals: 0, suggestedDeposit: maxDeposit.toString(), unitType })
|
|
2168
2168
|
|
|
2169
2169
|
const fetch = async (input: RequestInfo | URL, init?: RequestInit) => {
|
|
@@ -2312,7 +2312,7 @@ describe('precompile server session unit guardrails', () => {
|
|
|
2312
2312
|
}),
|
|
2313
2313
|
],
|
|
2314
2314
|
realm: 'api.example.com',
|
|
2315
|
-
secretKey: 'secret',
|
|
2315
|
+
secretKey: 'test-secret-key-test-secret-key-32',
|
|
2316
2316
|
}).session({ amount: '1', decimals: 0, suggestedDeposit: maxDeposit.toString() })
|
|
2317
2317
|
|
|
2318
2318
|
const route = async (request: Request) => {
|
|
@@ -1,11 +1,14 @@
|
|
|
1
1
|
import type { Hex } from 'viem'
|
|
2
2
|
import { privateKeyToAccount } from 'viem/accounts'
|
|
3
|
-
import { describe, expect, test } from 'vp/test'
|
|
3
|
+
import { describe, expect, test, vi } from 'vp/test'
|
|
4
4
|
|
|
5
5
|
import * as Challenge from '../../../Challenge.js'
|
|
6
6
|
import type * as Credential from '../../../Credential.js'
|
|
7
|
+
import type * as Method from '../../../Method.js'
|
|
8
|
+
import { createSessionReceipt } from '../precompile/Protocol.js'
|
|
7
9
|
import type * as ChannelStore from './ChannelStore.js'
|
|
8
10
|
import {
|
|
11
|
+
applyVerifiedHttpAccounting,
|
|
9
12
|
isSettlementDue,
|
|
10
13
|
readRequestFeePayer,
|
|
11
14
|
resolveCredentialFeePayer,
|
|
@@ -148,6 +151,90 @@ describe('FeePayerResolution', () => {
|
|
|
148
151
|
})
|
|
149
152
|
})
|
|
150
153
|
|
|
154
|
+
describe('applyVerifiedHttpAccounting', () => {
|
|
155
|
+
const channelId = '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex
|
|
156
|
+
|
|
157
|
+
function receipt() {
|
|
158
|
+
return createSessionReceipt({
|
|
159
|
+
acceptedCumulative: 200n,
|
|
160
|
+
challengeId: 'challenge-1',
|
|
161
|
+
channelId,
|
|
162
|
+
spent: 0n,
|
|
163
|
+
units: 0,
|
|
164
|
+
})
|
|
165
|
+
}
|
|
166
|
+
|
|
167
|
+
function capturedRequest(overrides: Partial<Method.CapturedRequest>): Method.CapturedRequest {
|
|
168
|
+
return {
|
|
169
|
+
hasBody: false,
|
|
170
|
+
headers: new Headers(),
|
|
171
|
+
method: 'GET',
|
|
172
|
+
url: new URL('https://api.example.com/session'),
|
|
173
|
+
...overrides,
|
|
174
|
+
}
|
|
175
|
+
}
|
|
176
|
+
|
|
177
|
+
function chargedChannel(): ChannelStore.State {
|
|
178
|
+
return {
|
|
179
|
+
channelId,
|
|
180
|
+
spent: 75n,
|
|
181
|
+
units: 1,
|
|
182
|
+
} as ChannelStore.State
|
|
183
|
+
}
|
|
184
|
+
|
|
185
|
+
test('precharges SSE GET content and marks the receipt as prepaid', async () => {
|
|
186
|
+
const charge = vi.fn(async () => chargedChannel())
|
|
187
|
+
const markPrepaidReceipt = vi.fn((value) => value)
|
|
188
|
+
|
|
189
|
+
await applyVerifiedHttpAccounting({
|
|
190
|
+
capturedRequest: capturedRequest({ method: 'GET' }),
|
|
191
|
+
charge,
|
|
192
|
+
getRequestAmount: () => 75n,
|
|
193
|
+
markPrepaidReceipt,
|
|
194
|
+
payloadAction: 'voucher',
|
|
195
|
+
receipt: receipt(),
|
|
196
|
+
settleCharged: async () => undefined,
|
|
197
|
+
sseEnabled: true,
|
|
198
|
+
})
|
|
199
|
+
|
|
200
|
+
expect(charge).toHaveBeenCalledWith(channelId, 75n)
|
|
201
|
+
expect(markPrepaidReceipt).toHaveBeenCalledOnce()
|
|
202
|
+
})
|
|
203
|
+
|
|
204
|
+
test('does not charge SSE voucher management POSTs', async () => {
|
|
205
|
+
const charge = vi.fn(async () => chargedChannel())
|
|
206
|
+
|
|
207
|
+
const result = await applyVerifiedHttpAccounting({
|
|
208
|
+
capturedRequest: capturedRequest({ hasBody: true, method: 'POST' }),
|
|
209
|
+
charge,
|
|
210
|
+
getRequestAmount: () => 75n,
|
|
211
|
+
payloadAction: 'voucher',
|
|
212
|
+
receipt: receipt(),
|
|
213
|
+
settleCharged: async () => undefined,
|
|
214
|
+
sseEnabled: true,
|
|
215
|
+
})
|
|
216
|
+
|
|
217
|
+
expect(charge).not.toHaveBeenCalled()
|
|
218
|
+
expect(result.spent).toBe('0')
|
|
219
|
+
})
|
|
220
|
+
|
|
221
|
+
test('keeps non-SSE POST content accounting unchanged', async () => {
|
|
222
|
+
const charge = vi.fn(async () => chargedChannel())
|
|
223
|
+
|
|
224
|
+
await applyVerifiedHttpAccounting({
|
|
225
|
+
capturedRequest: capturedRequest({ hasBody: true, method: 'POST' }),
|
|
226
|
+
charge,
|
|
227
|
+
getRequestAmount: () => 75n,
|
|
228
|
+
payloadAction: 'voucher',
|
|
229
|
+
receipt: receipt(),
|
|
230
|
+
settleCharged: async () => undefined,
|
|
231
|
+
sseEnabled: false,
|
|
232
|
+
})
|
|
233
|
+
|
|
234
|
+
expect(charge).toHaveBeenCalledWith(channelId, 75n)
|
|
235
|
+
})
|
|
236
|
+
})
|
|
237
|
+
|
|
151
238
|
describe('SettlementSchedule', () => {
|
|
152
239
|
const channelId = '0x0000000000000000000000000000000000000000000000000000000000000001' as Hex
|
|
153
240
|
const salt = '0x0000000000000000000000000000000000000000000000000000000000000002' as Hex
|
|
@@ -262,8 +262,9 @@ export async function applyVerifiedHttpAccounting(
|
|
|
262
262
|
): Promise<SessionReceipt> {
|
|
263
263
|
const { capturedRequest, payloadAction, receipt, sseEnabled } = parameters
|
|
264
264
|
if (!capturedRequest) return receipt
|
|
265
|
-
if (!isSessionContentRequest(capturedRequest)) return receipt
|
|
266
265
|
if (payloadAction !== 'open' && payloadAction !== 'voucher') return receipt
|
|
266
|
+
if (sseEnabled && capturedRequest.method === 'POST') return receipt
|
|
267
|
+
if (!isSessionContentRequest(capturedRequest)) return receipt
|
|
267
268
|
|
|
268
269
|
const requestAmount = parameters.getRequestAmount()
|
|
269
270
|
const charged = await parameters.charge(receipt.channelId, requestAmount)
|
|
@@ -8,10 +8,8 @@ import {
|
|
|
8
8
|
formatMessageEvent,
|
|
9
9
|
formatNeedVoucherEvent,
|
|
10
10
|
formatReceiptEvent,
|
|
11
|
-
parseEvent,
|
|
12
11
|
readSessionChallengeAmount,
|
|
13
12
|
requireSessionCredentialContext,
|
|
14
|
-
type SseEvent,
|
|
15
13
|
} from '../precompile/Protocol.js'
|
|
16
14
|
import * as ChannelStore from './ChannelStore.js'
|
|
17
15
|
import { meterIterable, type SessionController } from './MeteredStream.js'
|
|
@@ -4,17 +4,13 @@ import {
|
|
|
4
4
|
createSessionReceipt,
|
|
5
5
|
deserializeSessionReceipt,
|
|
6
6
|
formatApplicationMessage,
|
|
7
|
-
formatAuthorizationMessage,
|
|
8
7
|
formatCloseReadyMessage,
|
|
9
|
-
formatCloseRequestMessage,
|
|
10
8
|
formatErrorMessage,
|
|
11
9
|
formatNeedVoucherMessage,
|
|
12
10
|
formatReceiptMessage,
|
|
13
11
|
parseMessage,
|
|
14
12
|
readSessionChallengeAmount,
|
|
15
13
|
requireSessionCredentialContext,
|
|
16
|
-
type ErrorMessageParameters,
|
|
17
|
-
type Message,
|
|
18
14
|
type SessionCredentialContext,
|
|
19
15
|
type SessionReceipt,
|
|
20
16
|
} from '../precompile/Protocol.js'
|
|
@@ -22,6 +22,8 @@ export type SubscriptionStore = {
|
|
|
22
22
|
get(subscriptionId: string): Promise<SubscriptionRecord | null>
|
|
23
23
|
/** Looks up a generated access key for a resolved request key. */
|
|
24
24
|
getAccessKey(key: string): Promise<SubscriptionAccessKeyRecord | null>
|
|
25
|
+
/** Looks up a generated access key by its public access key address. */
|
|
26
|
+
getAccessKeyByAddress(address: string): Promise<SubscriptionAccessKeyRecord | null>
|
|
25
27
|
/** Looks up the active subscription for a resolved request key. */
|
|
26
28
|
getByKey(key: string): Promise<SubscriptionRecord | null>
|
|
27
29
|
/** Gets or creates the server-owned access key for a resolved request key. */
|
|
@@ -104,6 +106,10 @@ export function fromStore(
|
|
|
104
106
|
return `${accessKeyPrefix}${key}`
|
|
105
107
|
}
|
|
106
108
|
|
|
109
|
+
function accessKeyAddressKey(address: string): string {
|
|
110
|
+
return `${accessKeyPrefix}address:${address.toLowerCase()}`
|
|
111
|
+
}
|
|
112
|
+
|
|
107
113
|
function lookupRecordKey(key: string): string {
|
|
108
114
|
return `${keyPrefix}${key}`
|
|
109
115
|
}
|
|
@@ -243,6 +249,10 @@ export function fromStore(
|
|
|
243
249
|
return (await store.get(accessKeyKey(key))) as SubscriptionAccessKeyRecord | null
|
|
244
250
|
},
|
|
245
251
|
|
|
252
|
+
async getAccessKeyByAddress(address) {
|
|
253
|
+
return (await store.get(accessKeyAddressKey(address))) as SubscriptionAccessKeyRecord | null
|
|
254
|
+
},
|
|
255
|
+
|
|
246
256
|
async getByKey(key) {
|
|
247
257
|
return getByLookupKey(key)
|
|
248
258
|
},
|
|
@@ -258,15 +268,23 @@ export function fromStore(
|
|
|
258
268
|
keyType: account.keyType,
|
|
259
269
|
privateKey,
|
|
260
270
|
} satisfies SubscriptionAccessKeyRecord
|
|
261
|
-
return store
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
271
|
+
return store
|
|
272
|
+
.update(
|
|
273
|
+
accessKeyKey(key),
|
|
274
|
+
(current): Store.Change<unknown, SubscriptionAccessKeyRecord> => {
|
|
275
|
+
if (current) {
|
|
276
|
+
return { op: 'noop', result: current as SubscriptionAccessKeyRecord }
|
|
277
|
+
}
|
|
278
|
+
return { op: 'set', value: candidate, result: candidate }
|
|
279
|
+
},
|
|
280
|
+
)
|
|
281
|
+
.then(async (record) => {
|
|
282
|
+
await store.update(accessKeyAddressKey(record.accessKeyAddress), (current) => {
|
|
283
|
+
if (current) return { op: 'noop', result: undefined }
|
|
284
|
+
return { op: 'set', value: record, result: undefined }
|
|
285
|
+
})
|
|
286
|
+
return record
|
|
287
|
+
})
|
|
270
288
|
},
|
|
271
289
|
|
|
272
290
|
async put(record) {
|
|
@@ -8,7 +8,7 @@ import * as Header from './Header.js'
|
|
|
8
8
|
import * as RouteBinding from './internal/RouteBinding.js'
|
|
9
9
|
import * as Types from './Types.js'
|
|
10
10
|
|
|
11
|
-
const secretKey = 'test-secret'
|
|
11
|
+
const secretKey = 'test-secret-key-test-secret-key-32'
|
|
12
12
|
const transaction = `0x${'1'.repeat(64)}`
|
|
13
13
|
|
|
14
14
|
describe('x402 exact e2e', () => {
|
|
@@ -4,7 +4,7 @@ import { describe, expectTypeOf, test } from 'vp/test'
|
|
|
4
4
|
|
|
5
5
|
import { evm as clientEvm } from '../client/index.js'
|
|
6
6
|
|
|
7
|
-
const secretKey = 'test-secret'
|
|
7
|
+
const secretKey = 'test-secret-key-test-secret-key-32'
|
|
8
8
|
|
|
9
9
|
describe('x402 public interface', () => {
|
|
10
10
|
test('server evm charge accepts known assets without transfer metadata', () => {
|
package/src/x402/index.ts
CHANGED
|
@@ -1,85 +0,0 @@
|
|
|
1
|
-
import type { Client } from '@modelcontextprotocol/sdk/client/index.js';
|
|
2
|
-
import type { McpError } from '@modelcontextprotocol/sdk/types.js';
|
|
3
|
-
import type * as Challenge from '../../Challenge.js';
|
|
4
|
-
import * as core_Mcp from '../../Mcp.js';
|
|
5
|
-
import type * as Method from '../../Method.js';
|
|
6
|
-
import type * as z from '../../zod.js';
|
|
7
|
-
type AnyClient = Method.Client<any, any>;
|
|
8
|
-
export type CallToolParameters = {
|
|
9
|
-
name: string;
|
|
10
|
-
arguments?: Record<string, unknown>;
|
|
11
|
-
_meta?: Record<string, unknown>;
|
|
12
|
-
};
|
|
13
|
-
export type OnPaymentRequired = (challenge: Challenge.Challenge) => boolean | Promise<boolean>;
|
|
14
|
-
/**
|
|
15
|
-
* Result of a tool call with payment handling.
|
|
16
|
-
* Extends the SDK's callTool return type with an optional payment receipt.
|
|
17
|
-
*/
|
|
18
|
-
export type CallToolResult = Awaited<ReturnType<Client['callTool']>> & {
|
|
19
|
-
/** Payment receipt if payment was made. */
|
|
20
|
-
receipt: core_Mcp.Receipt | undefined;
|
|
21
|
-
};
|
|
22
|
-
/**
|
|
23
|
-
* Creates a payment-aware wrapper around an MCP SDK client.
|
|
24
|
-
*
|
|
25
|
-
* Similar to `Fetch.from()` for HTTP, this wraps an MCP client's `callTool`
|
|
26
|
-
* method to automatically handle payment challenges.
|
|
27
|
-
*
|
|
28
|
-
* @example
|
|
29
|
-
* ```ts
|
|
30
|
-
* import { Client } from '@modelcontextprotocol/sdk/client'
|
|
31
|
-
* import { McpClient, tempo } from 'mppx/mcp-sdk/client'
|
|
32
|
-
* import { privateKeyToAccount } from 'viem/accounts'
|
|
33
|
-
*
|
|
34
|
-
* const client = new Client({ name: 'my-client', version: '1.0.0' })
|
|
35
|
-
* await client.connect(transport)
|
|
36
|
-
*
|
|
37
|
-
* const mcp = McpClient.wrap(client, {
|
|
38
|
-
* methods: [
|
|
39
|
-
* tempo({
|
|
40
|
-
* account: privateKeyToAccount('0x...'),
|
|
41
|
-
* }),
|
|
42
|
-
* ],
|
|
43
|
-
* })
|
|
44
|
-
*
|
|
45
|
-
* // Automatically handles payment challenges
|
|
46
|
-
* const result = await mcp.callTool({ name: 'premium_tool', arguments: {} })
|
|
47
|
-
* console.log(result.content, result.receipt)
|
|
48
|
-
* ```
|
|
49
|
-
*/
|
|
50
|
-
export declare function wrap<const client extends Pick<Client, 'callTool'>, const methods extends readonly Method.AnyClient[]>(client: client, config: wrap.Config<methods>): wrap.McpClient<client, methods>;
|
|
51
|
-
/** Union of all context types from all methods that have context schemas. */
|
|
52
|
-
type AnyContextFor<methods extends readonly AnyClient[]> = {
|
|
53
|
-
[key in keyof methods]: methods[key] extends Method.Client<any, infer context> ? context extends z.ZodMiniType ? z.input<context> : undefined : undefined;
|
|
54
|
-
}[number];
|
|
55
|
-
export declare namespace wrap {
|
|
56
|
-
type Config<methods extends readonly Method.AnyClient[] = readonly Method.AnyClient[]> = {
|
|
57
|
-
/** Array of methods to use. */
|
|
58
|
-
methods: methods;
|
|
59
|
-
/** Optional approval hook called before creating a payment credential. */
|
|
60
|
-
onPaymentRequired?: OnPaymentRequired;
|
|
61
|
-
};
|
|
62
|
-
type McpClient<client extends Pick<Client, 'callTool'> = Pick<Client, 'callTool'>, methods extends readonly AnyClient[] = readonly AnyClient[]> = Omit<client, 'callTool'> & {
|
|
63
|
-
/** Call a tool with automatic payment handling. */
|
|
64
|
-
callTool: {
|
|
65
|
-
(params: CallToolParameters, options?: CallToolOptions<methods>): Promise<CallToolResult>;
|
|
66
|
-
(onPaymentRequired: OnPaymentRequired | null | undefined, params: CallToolParameters, options?: CallToolOptions<methods>): Promise<CallToolResult>;
|
|
67
|
-
};
|
|
68
|
-
};
|
|
69
|
-
type CallToolOptions<methods extends readonly AnyClient[] = readonly AnyClient[]> = {
|
|
70
|
-
/** Context to pass to the method intent's createCredential. */
|
|
71
|
-
context?: AnyContextFor<methods>;
|
|
72
|
-
/** Request timeout in milliseconds. */
|
|
73
|
-
timeout?: number;
|
|
74
|
-
};
|
|
75
|
-
}
|
|
76
|
-
/**
|
|
77
|
-
* Checks if an error is a payment required error.
|
|
78
|
-
*/
|
|
79
|
-
export declare function isPaymentRequiredError(error: unknown): error is McpError & {
|
|
80
|
-
data: {
|
|
81
|
-
challenges: Challenge.Challenge[];
|
|
82
|
-
};
|
|
83
|
-
};
|
|
84
|
-
export {};
|
|
85
|
-
//# sourceMappingURL=McpClient.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"McpClient.d.ts","sourceRoot":"","sources":["../../../src/mcp-sdk/client/McpClient.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,118 +0,0 @@
|
|
|
1
|
-
import * as Credential from '../../Credential.js';
|
|
2
|
-
import * as Expires from '../../Expires.js';
|
|
3
|
-
import * as AcceptPayment from '../../internal/AcceptPayment.js';
|
|
4
|
-
import * as core_Mcp from '../../Mcp.js';
|
|
5
|
-
/**
|
|
6
|
-
* Creates a payment-aware wrapper around an MCP SDK client.
|
|
7
|
-
*
|
|
8
|
-
* Similar to `Fetch.from()` for HTTP, this wraps an MCP client's `callTool`
|
|
9
|
-
* method to automatically handle payment challenges.
|
|
10
|
-
*
|
|
11
|
-
* @example
|
|
12
|
-
* ```ts
|
|
13
|
-
* import { Client } from '@modelcontextprotocol/sdk/client'
|
|
14
|
-
* import { McpClient, tempo } from 'mppx/mcp-sdk/client'
|
|
15
|
-
* import { privateKeyToAccount } from 'viem/accounts'
|
|
16
|
-
*
|
|
17
|
-
* const client = new Client({ name: 'my-client', version: '1.0.0' })
|
|
18
|
-
* await client.connect(transport)
|
|
19
|
-
*
|
|
20
|
-
* const mcp = McpClient.wrap(client, {
|
|
21
|
-
* methods: [
|
|
22
|
-
* tempo({
|
|
23
|
-
* account: privateKeyToAccount('0x...'),
|
|
24
|
-
* }),
|
|
25
|
-
* ],
|
|
26
|
-
* })
|
|
27
|
-
*
|
|
28
|
-
* // Automatically handles payment challenges
|
|
29
|
-
* const result = await mcp.callTool({ name: 'premium_tool', arguments: {} })
|
|
30
|
-
* console.log(result.content, result.receipt)
|
|
31
|
-
* ```
|
|
32
|
-
*/
|
|
33
|
-
export function wrap(client, config) {
|
|
34
|
-
const { methods } = config;
|
|
35
|
-
const paymentPreferences = AcceptPayment.resolve(methods);
|
|
36
|
-
const callTool = (async (first, second, third) => {
|
|
37
|
-
const hasApprovalArgument = typeof first === 'function' || first === null || first === undefined;
|
|
38
|
-
const params = (hasApprovalArgument ? second : first);
|
|
39
|
-
const options = (hasApprovalArgument ? third : second);
|
|
40
|
-
const onPaymentRequired = first === null
|
|
41
|
-
? undefined
|
|
42
|
-
: hasApprovalArgument
|
|
43
|
-
? (first ?? config.onPaymentRequired)
|
|
44
|
-
: config.onPaymentRequired;
|
|
45
|
-
const context = options?.context;
|
|
46
|
-
const timeout = options?.timeout;
|
|
47
|
-
try {
|
|
48
|
-
const result = await client.callTool(params, undefined, timeout !== undefined ? { timeout } : undefined);
|
|
49
|
-
return {
|
|
50
|
-
...result,
|
|
51
|
-
receipt: result._meta?.[core_Mcp.receiptMetaKey],
|
|
52
|
-
};
|
|
53
|
-
}
|
|
54
|
-
catch (error) {
|
|
55
|
-
// Check if this is a payment required error
|
|
56
|
-
if (!isPaymentRequiredError(error))
|
|
57
|
-
throw error;
|
|
58
|
-
const challenges = error.data?.challenges;
|
|
59
|
-
if (!challenges?.length)
|
|
60
|
-
throw error;
|
|
61
|
-
const selected = AcceptPayment.selectChallenge(challenges, methods, paymentPreferences.entries);
|
|
62
|
-
if (!selected) {
|
|
63
|
-
const available = challenges.map((c) => `${c.method}.${c.intent}`).join(', ');
|
|
64
|
-
const installed = methods.map((m) => `${m.name}.${m.intent}`).join(', ');
|
|
65
|
-
throw new Error(`No compatible payment method. Server offers: ${available}. Client has: ${installed}`, { cause: error });
|
|
66
|
-
}
|
|
67
|
-
if (selected.challenge.expires)
|
|
68
|
-
Expires.assert(selected.challenge.expires, selected.challenge.id);
|
|
69
|
-
if (onPaymentRequired) {
|
|
70
|
-
const approved = await onPaymentRequired(selected.challenge);
|
|
71
|
-
if (!approved)
|
|
72
|
-
throw new Error('Payment declined.', { cause: error });
|
|
73
|
-
}
|
|
74
|
-
const credential = await createCredential(selected.challenge, {
|
|
75
|
-
context,
|
|
76
|
-
methods,
|
|
77
|
-
});
|
|
78
|
-
const parsed = Credential.deserialize(credential);
|
|
79
|
-
const retryResult = await client.callTool({
|
|
80
|
-
...params,
|
|
81
|
-
_meta: {
|
|
82
|
-
...params._meta,
|
|
83
|
-
[core_Mcp.credentialMetaKey]: parsed,
|
|
84
|
-
},
|
|
85
|
-
}, undefined, timeout !== undefined ? { timeout } : undefined);
|
|
86
|
-
return {
|
|
87
|
-
...retryResult,
|
|
88
|
-
receipt: retryResult._meta?.[core_Mcp.receiptMetaKey],
|
|
89
|
-
};
|
|
90
|
-
}
|
|
91
|
-
});
|
|
92
|
-
return { ...client, callTool };
|
|
93
|
-
}
|
|
94
|
-
/**
|
|
95
|
-
* Checks if an error is a payment required error.
|
|
96
|
-
*/
|
|
97
|
-
export function isPaymentRequiredError(error) {
|
|
98
|
-
if (typeof error !== 'object' || error === null)
|
|
99
|
-
return false;
|
|
100
|
-
if (!('code' in error) || !('message' in error))
|
|
101
|
-
return false;
|
|
102
|
-
if (error.code !== core_Mcp.paymentRequiredCode)
|
|
103
|
-
return false;
|
|
104
|
-
const data = error.data;
|
|
105
|
-
return Array.isArray(data?.challenges) && data.challenges.length > 0;
|
|
106
|
-
}
|
|
107
|
-
/** @internal */
|
|
108
|
-
async function createCredential(challenge, config) {
|
|
109
|
-
const { context, methods } = config;
|
|
110
|
-
const mi = methods.find((m) => m.name === challenge.method && m.intent === challenge.intent);
|
|
111
|
-
if (!mi)
|
|
112
|
-
throw new Error(`No method found for "${challenge.method}.${challenge.intent}". Available: ${methods.map((m) => `${m.name}.${m.intent}`).join(', ')}`);
|
|
113
|
-
if (challenge.expires)
|
|
114
|
-
Expires.assert(challenge.expires, challenge.id);
|
|
115
|
-
const parsedContext = mi.context && context !== undefined ? mi.context.parse(context) : undefined;
|
|
116
|
-
return mi.createCredential(parsedContext !== undefined ? { challenge, context: parsedContext } : { challenge });
|
|
117
|
-
}
|
|
118
|
-
//# sourceMappingURL=McpClient.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"McpClient.js","sourceRoot":"","sources":["../../../src/mcp-sdk/client/McpClient.ts"],"names":[],"mappings":"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"}
|