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.
Files changed (290) hide show
  1. package/CHANGELOG.md +41 -0
  2. package/README.md +20 -11
  3. package/dist/Challenge.d.ts.map +1 -1
  4. package/dist/Challenge.js +18 -6
  5. package/dist/Challenge.js.map +1 -1
  6. package/dist/Mcp.d.ts +3 -0
  7. package/dist/Mcp.d.ts.map +1 -1
  8. package/dist/Mcp.js +2 -0
  9. package/dist/Mcp.js.map +1 -1
  10. package/dist/PaymentRequest.d.ts +10 -10
  11. package/dist/PaymentRequest.js +8 -8
  12. package/dist/cli/internal.d.ts +1 -0
  13. package/dist/cli/internal.d.ts.map +1 -1
  14. package/dist/cli/internal.js +1 -15
  15. package/dist/cli/internal.js.map +1 -1
  16. package/dist/client/Mppx.js +2 -2
  17. package/dist/client/Mppx.js.map +1 -1
  18. package/dist/client/Transport.d.ts +11 -16
  19. package/dist/client/Transport.d.ts.map +1 -1
  20. package/dist/client/Transport.js +55 -75
  21. package/dist/client/Transport.js.map +1 -1
  22. package/dist/client/index.d.ts +3 -0
  23. package/dist/client/index.d.ts.map +1 -1
  24. package/dist/client/index.js +1 -0
  25. package/dist/client/index.js.map +1 -1
  26. package/dist/client/internal/Fetch.d.ts.map +1 -1
  27. package/dist/client/internal/Fetch.js +46 -7
  28. package/dist/client/internal/Fetch.js.map +1 -1
  29. package/dist/client/internal/protocols/Mcp.d.ts +7 -0
  30. package/dist/client/internal/protocols/Mcp.d.ts.map +1 -0
  31. package/dist/client/internal/protocols/Mcp.js +159 -0
  32. package/dist/client/internal/protocols/Mcp.js.map +1 -0
  33. package/dist/client/internal/protocols/Mpp.d.ts +4 -0
  34. package/dist/client/internal/protocols/Mpp.d.ts.map +1 -0
  35. package/dist/client/internal/protocols/Mpp.js +18 -0
  36. package/dist/client/internal/protocols/Mpp.js.map +1 -0
  37. package/dist/client/internal/protocols/Protocol.d.ts +10 -0
  38. package/dist/client/internal/protocols/Protocol.d.ts.map +1 -0
  39. package/dist/client/internal/protocols/Protocol.js +2 -0
  40. package/dist/client/internal/protocols/Protocol.js.map +1 -0
  41. package/dist/client/internal/protocols/Shared.d.ts +5 -0
  42. package/dist/client/internal/protocols/Shared.d.ts.map +1 -0
  43. package/dist/client/internal/protocols/Shared.js +20 -0
  44. package/dist/client/internal/protocols/Shared.js.map +1 -0
  45. package/dist/client/internal/protocols/X402.d.ts +8 -0
  46. package/dist/client/internal/protocols/X402.d.ts.map +1 -0
  47. package/dist/client/internal/protocols/X402.js +39 -0
  48. package/dist/client/internal/protocols/X402.js.map +1 -0
  49. package/dist/evm/client/index.d.ts +1 -0
  50. package/dist/evm/client/index.d.ts.map +1 -1
  51. package/dist/evm/client/index.js +1 -0
  52. package/dist/evm/client/index.js.map +1 -1
  53. package/dist/evm/index.d.ts +2 -0
  54. package/dist/evm/index.d.ts.map +1 -1
  55. package/dist/evm/index.js +2 -0
  56. package/dist/evm/index.js.map +1 -1
  57. package/dist/evm/server/index.d.ts +1 -0
  58. package/dist/evm/server/index.d.ts.map +1 -1
  59. package/dist/evm/server/index.js +1 -0
  60. package/dist/evm/server/index.js.map +1 -1
  61. package/dist/mcp/client/McpClient.d.ts +101 -0
  62. package/dist/mcp/client/McpClient.d.ts.map +1 -0
  63. package/dist/mcp/client/McpClient.js +162 -0
  64. package/dist/mcp/client/McpClient.js.map +1 -0
  65. package/dist/mcp/client/index.d.ts.map +1 -0
  66. package/dist/mcp/client/index.js.map +1 -0
  67. package/dist/mcp/server/Transport.d.ts.map +1 -0
  68. package/dist/mcp/server/Transport.js.map +1 -0
  69. package/dist/mcp/server/index.d.ts.map +1 -0
  70. package/dist/mcp/server/index.js.map +1 -0
  71. package/dist/server/Mppx.d.ts +1 -1
  72. package/dist/server/Mppx.d.ts.map +1 -1
  73. package/dist/server/Mppx.js +9 -0
  74. package/dist/server/Mppx.js.map +1 -1
  75. package/dist/server/Transport.d.ts +1 -1
  76. package/dist/server/Transport.d.ts.map +1 -1
  77. package/dist/server/Transport.js +1 -1
  78. package/dist/server/Transport.js.map +1 -1
  79. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  80. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  81. package/dist/stripe/server/internal/html.gen.js +1 -1
  82. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  83. package/dist/tempo/Proof.d.ts +85 -1
  84. package/dist/tempo/Proof.d.ts.map +1 -1
  85. package/dist/tempo/Proof.js +35 -0
  86. package/dist/tempo/Proof.js.map +1 -1
  87. package/dist/tempo/client/Charge.d.ts +13 -1
  88. package/dist/tempo/client/Charge.d.ts.map +1 -1
  89. package/dist/tempo/client/Charge.js +38 -25
  90. package/dist/tempo/client/Charge.js.map +1 -1
  91. package/dist/tempo/client/Methods.d.ts +5 -3
  92. package/dist/tempo/client/Methods.d.ts.map +1 -1
  93. package/dist/tempo/client/Methods.js +4 -2
  94. package/dist/tempo/client/Methods.js.map +1 -1
  95. package/dist/tempo/client/ResolveAccount.d.ts +40 -0
  96. package/dist/tempo/client/ResolveAccount.d.ts.map +1 -0
  97. package/dist/tempo/client/ResolveAccount.js +2 -0
  98. package/dist/tempo/client/ResolveAccount.js.map +1 -0
  99. package/dist/tempo/internal/fee-payer.d.ts +26 -1
  100. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  101. package/dist/tempo/internal/fee-payer.js +83 -30
  102. package/dist/tempo/internal/fee-payer.js.map +1 -1
  103. package/dist/tempo/internal/proof.d.ts +71 -5
  104. package/dist/tempo/internal/proof.d.ts.map +1 -1
  105. package/dist/tempo/internal/proof.js +42 -6
  106. package/dist/tempo/internal/proof.js.map +1 -1
  107. package/dist/tempo/legacy/client/SessionManager.d.ts.map +1 -1
  108. package/dist/tempo/legacy/client/SessionManager.js +10 -3
  109. package/dist/tempo/legacy/client/SessionManager.js.map +1 -1
  110. package/dist/tempo/server/Charge.d.ts.map +1 -1
  111. package/dist/tempo/server/Charge.js +46 -18
  112. package/dist/tempo/server/Charge.js.map +1 -1
  113. package/dist/tempo/server/Methods.d.ts +4 -2
  114. package/dist/tempo/server/Methods.d.ts.map +1 -1
  115. package/dist/tempo/server/Methods.js +4 -2
  116. package/dist/tempo/server/Methods.js.map +1 -1
  117. package/dist/tempo/server/Subscription.d.ts +10 -0
  118. package/dist/tempo/server/Subscription.d.ts.map +1 -1
  119. package/dist/tempo/server/Subscription.js +135 -23
  120. package/dist/tempo/server/Subscription.js.map +1 -1
  121. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  122. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  123. package/dist/tempo/server/internal/html.gen.js +1 -1
  124. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  125. package/dist/tempo/session/client/ChannelOps.d.ts +2 -3
  126. package/dist/tempo/session/client/ChannelOps.d.ts.map +1 -1
  127. package/dist/tempo/session/client/ChannelOps.js +7 -10
  128. package/dist/tempo/session/client/ChannelOps.js.map +1 -1
  129. package/dist/tempo/session/client/ChannelStore.d.ts +51 -0
  130. package/dist/tempo/session/client/ChannelStore.d.ts.map +1 -0
  131. package/dist/tempo/session/client/ChannelStore.js +63 -0
  132. package/dist/tempo/session/client/ChannelStore.js.map +1 -0
  133. package/dist/tempo/session/client/CredentialState.d.ts +7 -24
  134. package/dist/tempo/session/client/CredentialState.d.ts.map +1 -1
  135. package/dist/tempo/session/client/CredentialState.js +51 -49
  136. package/dist/tempo/session/client/CredentialState.js.map +1 -1
  137. package/dist/tempo/session/client/Session.d.ts +8 -2
  138. package/dist/tempo/session/client/Session.d.ts.map +1 -1
  139. package/dist/tempo/session/client/Session.js +22 -8
  140. package/dist/tempo/session/client/Session.js.map +1 -1
  141. package/dist/tempo/session/client/SessionManager.d.ts +4 -40
  142. package/dist/tempo/session/client/SessionManager.d.ts.map +1 -1
  143. package/dist/tempo/session/client/SessionManager.js +124 -174
  144. package/dist/tempo/session/client/SessionManager.js.map +1 -1
  145. package/dist/tempo/session/client/index.d.ts +3 -4
  146. package/dist/tempo/session/client/index.d.ts.map +1 -1
  147. package/dist/tempo/session/client/index.js +1 -0
  148. package/dist/tempo/session/client/index.js.map +1 -1
  149. package/dist/tempo/session/precompile/Voucher.d.ts +3 -3
  150. package/dist/tempo/session/precompile/Voucher.d.ts.map +1 -1
  151. package/dist/tempo/session/precompile/Voucher.js +24 -25
  152. package/dist/tempo/session/precompile/Voucher.js.map +1 -1
  153. package/dist/tempo/session/server/CredentialVerification.d.ts +6 -0
  154. package/dist/tempo/session/server/CredentialVerification.d.ts.map +1 -1
  155. package/dist/tempo/session/server/CredentialVerification.js +13 -0
  156. package/dist/tempo/session/server/CredentialVerification.js.map +1 -1
  157. package/dist/tempo/session/server/Settlement.d.ts.map +1 -1
  158. package/dist/tempo/session/server/Settlement.js +4 -2
  159. package/dist/tempo/session/server/Settlement.js.map +1 -1
  160. package/dist/tempo/session/server/Sse.d.ts.map +1 -1
  161. package/dist/tempo/session/server/Sse.js.map +1 -1
  162. package/dist/tempo/session/server/Ws.d.ts.map +1 -1
  163. package/dist/tempo/session/server/Ws.js.map +1 -1
  164. package/dist/tempo/subscription/KeyAuthorization.d.ts +712 -1
  165. package/dist/tempo/subscription/KeyAuthorization.d.ts.map +1 -1
  166. package/dist/tempo/subscription/Store.d.ts +2 -0
  167. package/dist/tempo/subscription/Store.d.ts.map +1 -1
  168. package/dist/tempo/subscription/Store.js +16 -1
  169. package/dist/tempo/subscription/Store.js.map +1 -1
  170. package/dist/x402/index.d.ts +1 -0
  171. package/dist/x402/index.d.ts.map +1 -1
  172. package/dist/x402/index.js +1 -0
  173. package/dist/x402/index.js.map +1 -1
  174. package/package.json +21 -10
  175. package/src/Challenge.test.ts +40 -0
  176. package/src/Challenge.ts +19 -6
  177. package/src/Mcp.ts +4 -0
  178. package/src/PaymentRequest.ts +10 -10
  179. package/src/cli/cli.test.ts +15 -15
  180. package/src/cli/config.test.ts +13 -7
  181. package/src/cli/internal.ts +1 -16
  182. package/src/client/Mppx.test-d.ts +21 -1
  183. package/src/client/Mppx.test.ts +1 -1
  184. package/src/client/Mppx.ts +2 -2
  185. package/src/client/Transport.test.ts +225 -178
  186. package/src/client/Transport.ts +77 -83
  187. package/src/client/index.ts +14 -0
  188. package/src/client/internal/Fetch.test.ts +207 -2
  189. package/src/client/internal/Fetch.ts +52 -6
  190. package/src/client/internal/protocols/Mcp.test.ts +220 -0
  191. package/src/client/internal/protocols/Mcp.ts +162 -0
  192. package/src/client/internal/protocols/Mpp.ts +21 -0
  193. package/src/client/internal/protocols/Protocol.ts +10 -0
  194. package/src/client/internal/protocols/Shared.ts +25 -0
  195. package/src/client/internal/protocols/X402.ts +42 -0
  196. package/src/discovery/OpenApi.test.ts +1 -1
  197. package/src/evm/PublicInterface.test-d.ts +1 -1
  198. package/src/evm/client/index.ts +1 -0
  199. package/src/evm/index.ts +2 -0
  200. package/src/evm/server/Charge.test.ts +1 -1
  201. package/src/evm/server/index.ts +1 -0
  202. package/src/{mcp-sdk → mcp}/client/McpClient.integration.test.ts +10 -4
  203. package/src/{mcp-sdk → mcp}/client/McpClient.test-d.ts +45 -18
  204. package/src/{mcp-sdk → mcp}/client/McpClient.test.ts +211 -5
  205. package/src/mcp/client/McpClient.ts +307 -0
  206. package/src/{mcp-sdk → mcp}/client/McpClient.unit.test.ts +9 -5
  207. package/src/middlewares/elysia.test.ts +1 -1
  208. package/src/middlewares/express.test.ts +1 -1
  209. package/src/middlewares/hono.test.ts +1 -1
  210. package/src/middlewares/internal/mppx.test.ts +1 -1
  211. package/src/middlewares/nextjs.test.ts +1 -1
  212. package/src/proxy/Proxy.test.ts +1 -1
  213. package/src/proxy/services/anthropic.test.ts +1 -1
  214. package/src/proxy/services/openai.test.ts +1 -1
  215. package/src/proxy/services/stripe.test.ts +1 -1
  216. package/src/server/Mppx.authorize.test.ts +1 -1
  217. package/src/server/Mppx.test-d.ts +1 -1
  218. package/src/server/Mppx.test.ts +20 -2
  219. package/src/server/Mppx.ts +14 -1
  220. package/src/server/Transport.test.ts +6 -6
  221. package/src/server/Transport.ts +1 -1
  222. package/src/stripe/Charge.integration.test.ts +1 -1
  223. package/src/stripe/client/Charge.test.ts +1 -1
  224. package/src/stripe/server/Charge.test.ts +1 -1
  225. package/src/stripe/server/internal/html/package.json +1 -1
  226. package/src/stripe/server/internal/html.gen.ts +1 -1
  227. package/src/tempo/Proof.conformance.test.ts +146 -0
  228. package/src/tempo/Proof.test-d.ts +15 -0
  229. package/src/tempo/Proof.ts +52 -1
  230. package/src/tempo/Subscription.integration.test.ts +1 -1
  231. package/src/tempo/client/Charge.test.ts +173 -0
  232. package/src/tempo/client/Charge.ts +65 -36
  233. package/src/tempo/client/Methods.ts +4 -2
  234. package/src/tempo/client/ResolveAccount.ts +46 -0
  235. package/src/tempo/internal/fee-payer.test.ts +89 -10
  236. package/src/tempo/internal/fee-payer.ts +128 -41
  237. package/src/tempo/internal/proof.test.ts +12 -4
  238. package/src/tempo/internal/proof.ts +55 -6
  239. package/src/tempo/legacy/client/SessionManager.ts +11 -3
  240. package/src/tempo/legacy/server/Session.test.ts +91 -26
  241. package/src/tempo/server/Charge.test.ts +388 -17
  242. package/src/tempo/server/Charge.ts +50 -24
  243. package/src/tempo/server/Methods.ts +4 -2
  244. package/src/tempo/server/Subscription.test.ts +465 -3
  245. package/src/tempo/server/Subscription.ts +174 -19
  246. package/src/tempo/server/internal/html/package.json +2 -2
  247. package/src/tempo/server/internal/html.gen.ts +1 -1
  248. package/src/tempo/session/client/ChannelOps.ts +5 -19
  249. package/src/tempo/session/client/ChannelStore.ts +111 -0
  250. package/src/tempo/session/client/CredentialState.test.ts +206 -62
  251. package/src/tempo/session/client/CredentialState.ts +58 -73
  252. package/src/tempo/session/client/Session.test.ts +41 -1
  253. package/src/tempo/session/client/Session.ts +36 -10
  254. package/src/tempo/session/client/SessionManager.test.ts +154 -65
  255. package/src/tempo/session/client/SessionManager.ts +141 -235
  256. package/src/tempo/session/client/index.ts +8 -5
  257. package/src/tempo/session/precompile/Voucher.test.ts +45 -7
  258. package/src/tempo/session/precompile/Voucher.ts +27 -25
  259. package/src/tempo/session/server/CredentialVerification.test.ts +36 -0
  260. package/src/tempo/session/server/CredentialVerification.ts +18 -0
  261. package/src/tempo/session/server/Session.test.ts +4 -4
  262. package/src/tempo/session/server/Settlement.test.ts +88 -1
  263. package/src/tempo/session/server/Settlement.ts +2 -1
  264. package/src/tempo/session/server/Sse.ts +0 -2
  265. package/src/tempo/session/server/Ws.ts +0 -4
  266. package/src/tempo/subscription/Store.ts +27 -9
  267. package/src/x402/Exact.e2e.test.ts +1 -1
  268. package/src/x402/PublicInterface.test-d.ts +1 -1
  269. package/src/x402/index.ts +1 -0
  270. package/dist/mcp-sdk/client/McpClient.d.ts +0 -85
  271. package/dist/mcp-sdk/client/McpClient.d.ts.map +0 -1
  272. package/dist/mcp-sdk/client/McpClient.js +0 -118
  273. package/dist/mcp-sdk/client/McpClient.js.map +0 -1
  274. package/dist/mcp-sdk/client/index.d.ts.map +0 -1
  275. package/dist/mcp-sdk/client/index.js.map +0 -1
  276. package/dist/mcp-sdk/server/Transport.d.ts.map +0 -1
  277. package/dist/mcp-sdk/server/Transport.js.map +0 -1
  278. package/dist/mcp-sdk/server/index.d.ts.map +0 -1
  279. package/dist/mcp-sdk/server/index.js.map +0 -1
  280. package/src/mcp-sdk/client/McpClient.ts +0 -228
  281. /package/dist/{mcp-sdk → mcp}/client/index.d.ts +0 -0
  282. /package/dist/{mcp-sdk → mcp}/client/index.js +0 -0
  283. /package/dist/{mcp-sdk → mcp}/server/Transport.d.ts +0 -0
  284. /package/dist/{mcp-sdk → mcp}/server/Transport.js +0 -0
  285. /package/dist/{mcp-sdk → mcp}/server/index.d.ts +0 -0
  286. /package/dist/{mcp-sdk → mcp}/server/index.js +0 -0
  287. /package/src/{mcp-sdk → mcp}/client/index.ts +0 -0
  288. /package/src/{mcp-sdk → mcp}/server/Transport.test.ts +0 -0
  289. /package/src/{mcp-sdk → mcp}/server/Transport.ts +0 -0
  290. /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 === 'keychain' && envelope.inner.type === 'secp256k1')
98
- return Signature.toHex(envelope.inner.signature)
99
- if (envelope.type === 'keychain' || envelope.type !== 'secp256k1')
100
- throw new Error('TIP-1034 voucher signing only supports secp256k1 voucher signatures.')
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 signature
118
+ return SignatureEnvelope.serialize(envelope)
103
119
  }
104
120
 
105
121
  /**
106
122
  * Verify a voucher signature matches the expected signer.
107
123
  *
108
- * Only accepts raw secp256k1 signatures the escrow contract verifies
109
- * via ecrecover. Keychain, p256, and webAuthn signatures are rejected.
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
- // Reject keychain signatures — the escrow contract verifies raw ECDSA
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.update(
262
- accessKeyKey(key),
263
- (current): Store.Change<unknown, SubscriptionAccessKeyRecord> => {
264
- if (current) {
265
- return { op: 'noop', result: current as SubscriptionAccessKeyRecord }
266
- }
267
- return { op: 'set', value: candidate, result: candidate }
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,3 +1,4 @@
1
+ /** @deprecated Use `assets` instead. */
1
2
  export * as Assets from './Assets.js'
2
3
  export * as Header from './Header.js'
3
4
  export * as Types from './Types.js'
@@ -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":"AAAA,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,2CAA2C,CAAA;AACvE,OAAO,KAAK,EAAE,QAAQ,EAAE,MAAM,oCAAoC,CAAA;AAElE,OAAO,KAAK,KAAK,SAAS,MAAM,oBAAoB,CAAA;AAIpD,OAAO,KAAK,QAAQ,MAAM,cAAc,CAAA;AACxC,OAAO,KAAK,KAAK,MAAM,MAAM,iBAAiB,CAAA;AAC9C,OAAO,KAAK,KAAK,CAAC,MAAM,cAAc,CAAA;AAEtC,KAAK,SAAS,GAAG,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,GAAG,CAAC,CAAA;AAExC,MAAM,MAAM,kBAAkB,GAAG;IAC/B,IAAI,EAAE,MAAM,CAAA;IACZ,SAAS,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;IACnC,KAAK,CAAC,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAA;CAChC,CAAA;AAED,MAAM,MAAM,iBAAiB,GAAG,CAAC,SAAS,EAAE,SAAS,CAAC,SAAS,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAA;AAE9F;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,OAAO,CAAC,UAAU,CAAC,MAAM,CAAC,UAAU,CAAC,CAAC,CAAC,GAAG;IACrE,2CAA2C;IAC3C,OAAO,EAAE,QAAQ,CAAC,OAAO,GAAG,SAAS,CAAA;CACtC,CAAA;AAED;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,wBAAgB,IAAI,CAClB,KAAK,CAAC,MAAM,SAAS,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAC7C,KAAK,CAAC,OAAO,SAAS,SAAS,MAAM,CAAC,SAAS,EAAE,EACjD,MAAM,EAAE,MAAM,EAAE,MAAM,EAAE,IAAI,CAAC,MAAM,CAAC,OAAO,CAAC,GAAG,IAAI,CAAC,SAAS,CAAC,MAAM,EAAE,OAAO,CAAC,CAyF/E;AAED,6EAA6E;AAC7E,KAAK,aAAa,CAAC,OAAO,SAAS,SAAS,SAAS,EAAE,IAAI;KACxD,GAAG,IAAI,MAAM,OAAO,GAAG,OAAO,CAAC,GAAG,CAAC,SAAS,MAAM,CAAC,MAAM,CAAC,GAAG,EAAE,MAAM,OAAO,CAAC,GAC1E,OAAO,SAAS,CAAC,CAAC,WAAW,GAC3B,CAAC,CAAC,KAAK,CAAC,OAAO,CAAC,GAChB,SAAS,GACX,SAAS;CACd,CAAC,MAAM,CAAC,CAAA;AAET,MAAM,CAAC,OAAO,WAAW,IAAI,CAAC;IAC5B,KAAK,MAAM,CAAC,OAAO,SAAS,SAAS,MAAM,CAAC,SAAS,EAAE,GAAG,SAAS,MAAM,CAAC,SAAS,EAAE,IAAI;QACvF,+BAA+B;QAC/B,OAAO,EAAE,OAAO,CAAA;QAChB,0EAA0E;QAC1E,iBAAiB,CAAC,EAAE,iBAAiB,CAAA;KACtC,CAAA;IAED,KAAK,SAAS,CACZ,MAAM,SAAS,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,EAClE,OAAO,SAAS,SAAS,SAAS,EAAE,GAAG,SAAS,SAAS,EAAE,IACzD,IAAI,CAAC,MAAM,EAAE,UAAU,CAAC,GAAG;QAC7B,mDAAmD;QACnD,QAAQ,EAAE;YACR,CAAC,MAAM,EAAE,kBAAkB,EAAE,OAAO,CAAC,EAAE,eAAe,CAAC,OAAO,CAAC,GAAG,OAAO,CAAC,cAAc,CAAC,CAAA;YACzF,CACE,iBAAiB,EAAE,iBAAiB,GAAG,IAAI,GAAG,SAAS,EACvD,MAAM,EAAE,kBAAkB,EAC1B,OAAO,CAAC,EAAE,eAAe,CAAC,OAAO,CAAC,GACjC,OAAO,CAAC,cAAc,CAAC,CAAA;SAC3B,CAAA;KACF,CAAA;IAED,KAAK,eAAe,CAAC,OAAO,SAAS,SAAS,SAAS,EAAE,GAAG,SAAS,SAAS,EAAE,IAAI;QAClF,+DAA+D;QAC/D,OAAO,CAAC,EAAE,aAAa,CAAC,OAAO,CAAC,CAAA;QAChC,uCAAuC;QACvC,OAAO,CAAC,EAAE,MAAM,CAAA;KACjB,CAAA;CACF;AAED;;GAEG;AACH,wBAAgB,sBAAsB,CACpC,KAAK,EAAE,OAAO,GACb,KAAK,IAAI,QAAQ,GAAG;IAAE,IAAI,EAAE;QAAE,UAAU,EAAE,SAAS,CAAC,SAAS,EAAE,CAAA;KAAE,CAAA;CAAE,CAMrE"}
@@ -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":"AAIA,OAAO,KAAK,UAAU,MAAM,qBAAqB,CAAA;AACjD,OAAO,KAAK,OAAO,MAAM,kBAAkB,CAAA;AAC3C,OAAO,KAAK,aAAa,MAAM,iCAAiC,CAAA;AAChE,OAAO,KAAK,QAAQ,MAAM,cAAc,CAAA;AAuBxC;;;;;;;;;;;;;;;;;;;;;;;;;;;GA2BG;AACH,MAAM,UAAU,IAAI,CAGlB,MAAc,EAAE,MAA4B;IAC5C,MAAM,EAAE,OAAO,EAAE,GAAG,MAAM,CAAA;IAC1B,MAAM,kBAAkB,GAAG,aAAa,CAAC,OAAO,CAAC,OAAO,CAAC,CAAA;IAEzD,MAAM,QAAQ,GAAG,CAAC,KAAK,EACrB,KAAgE,EAChE,MAA2D,EAC3D,KAAqC,EACrC,EAAE;QACF,MAAM,mBAAmB,GAAG,OAAO,KAAK,KAAK,UAAU,IAAI,KAAK,KAAK,IAAI,IAAI,KAAK,KAAK,SAAS,CAAA;QAChG,MAAM,MAAM,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,MAAM,CAAC,CAAC,CAAC,KAAK,CAAuB,CAAA;QAC3E,MAAM,OAAO,GAAG,CAAC,mBAAmB,CAAC,CAAC,CAAC,KAAK,CAAC,CAAC,CAAC,MAAM,CAExC,CAAA;QACb,MAAM,iBAAiB,GACrB,KAAK,KAAK,IAAI;YACZ,CAAC,CAAC,SAAS;YACX,CAAC,CAAC,mBAAmB;gBACnB,CAAC,CAAC,CAAE,KAAuC,IAAI,MAAM,CAAC,iBAAiB,CAAC;gBACxE,CAAC,CAAC,MAAM,CAAC,iBAAiB,CAAA;QAChC,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,CAAA;QAChC,MAAM,OAAO,GAAG,OAAO,EAAE,OAAO,CAAA;QAEhC,IAAI,CAAC;YACH,MAAM,MAAM,GAAG,MAAM,MAAM,CAAC,QAAQ,CAClC,MAAM,EACN,SAAS,EACT,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAChD,CAAA;YAED,OAAO;gBACL,GAAG,MAAM;gBACT,OAAO,EAAE,MAAM,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAiC;aACjF,CAAA;QACH,CAAC;QAAC,OAAO,KAAK,EAAE,CAAC;YACf,4CAA4C;YAC5C,IAAI,CAAC,sBAAsB,CAAC,KAAK,CAAC;gBAAE,MAAM,KAAK,CAAA;YAE/C,MAAM,UAAU,GAAI,KAAK,CAAC,IAA+C,EAAE,UAAU,CAAA;YACrF,IAAI,CAAC,UAAU,EAAE,MAAM;gBAAE,MAAM,KAAK,CAAA;YAEpC,MAAM,QAAQ,GAAG,aAAa,CAAC,eAAe,CAC5C,UAAU,EACV,OAAO,EACP,kBAAkB,CAAC,OAAO,CAC3B,CAAA;YACD,IAAI,CAAC,QAAQ,EAAE,CAAC;gBACd,MAAM,SAAS,GAAG,UAAU,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBAC7E,MAAM,SAAS,GAAG,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,CAAA;gBACxE,MAAM,IAAI,KAAK,CACb,gDAAgD,SAAS,iBAAiB,SAAS,EAAE,EACrF,EAAE,KAAK,EAAE,KAAK,EAAE,CACjB,CAAA;YACH,CAAC;YAED,IAAI,QAAQ,CAAC,SAAS,CAAC,OAAO;gBAC5B,OAAO,CAAC,MAAM,CAAC,QAAQ,CAAC,SAAS,CAAC,OAAO,EAAE,QAAQ,CAAC,SAAS,CAAC,EAAE,CAAC,CAAA;YAEnE,IAAI,iBAAiB,EAAE,CAAC;gBACtB,MAAM,QAAQ,GAAG,MAAM,iBAAiB,CAAC,QAAQ,CAAC,SAAS,CAAC,CAAA;gBAC5D,IAAI,CAAC,QAAQ;oBAAE,MAAM,IAAI,KAAK,CAAC,mBAAmB,EAAE,EAAE,KAAK,EAAE,KAAK,EAAE,CAAC,CAAA;YACvE,CAAC;YAED,MAAM,UAAU,GAAG,MAAM,gBAAgB,CAAC,QAAQ,CAAC,SAAS,EAAE;gBAC5D,OAAO;gBACP,OAAO;aACR,CAAC,CAAA;YACF,MAAM,MAAM,GAAG,UAAU,CAAC,WAAW,CAAC,UAAU,CAAC,CAAA;YAEjD,MAAM,WAAW,GAAG,MAAM,MAAM,CAAC,QAAQ,CACvC;gBACE,GAAG,MAAM;gBACT,KAAK,EAAE;oBACL,GAAG,MAAM,CAAC,KAAK;oBACf,CAAC,QAAQ,CAAC,iBAAiB,CAAC,EAAE,MAAM;iBACrC;aACF,EACD,SAAS,EACT,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,OAAO,EAAE,CAAC,CAAC,CAAC,SAAS,CAChD,CAAA;YAED,OAAO;gBACL,GAAG,WAAW;gBACd,OAAO,EAAE,WAAW,CAAC,KAAK,EAAE,CAAC,QAAQ,CAAC,cAAc,CAAiC;aACtF,CAAA;QACH,CAAC;IACH,CAAC,CAAgD,CAAA;IAEjD,OAAO,EAAE,GAAG,MAAM,EAAE,QAAQ,EAAqC,CAAA;AACnE,CAAC;AA0CD;;GAEG;AACH,MAAM,UAAU,sBAAsB,CACpC,KAAc;IAEd,IAAI,OAAO,KAAK,KAAK,QAAQ,IAAI,KAAK,KAAK,IAAI;QAAE,OAAO,KAAK,CAAA;IAC7D,IAAI,CAAC,CAAC,MAAM,IAAI,KAAK,CAAC,IAAI,CAAC,CAAC,SAAS,IAAI,KAAK,CAAC;QAAE,OAAO,KAAK,CAAA;IAC7D,IAAK,KAA2B,CAAC,IAAI,KAAK,QAAQ,CAAC,mBAAmB;QAAE,OAAO,KAAK,CAAA;IACpF,MAAM,IAAI,GAAI,KAA6C,CAAC,IAAI,CAAA;IAChE,OAAO,KAAK,CAAC,OAAO,CAAC,IAAI,EAAE,UAAU,CAAC,IAAI,IAAI,CAAC,UAAU,CAAC,MAAM,GAAG,CAAC,CAAA;AACtE,CAAC;AAED,gBAAgB;AAChB,KAAK,UAAU,gBAAgB,CAC7B,SAA8B,EAC9B,MAGC;IAED,MAAM,EAAE,OAAO,EAAE,OAAO,EAAE,GAAG,MAAM,CAAA;IAEnC,MAAM,EAAE,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,CAAC,CAAC,IAAI,KAAK,SAAS,CAAC,MAAM,IAAI,CAAC,CAAC,MAAM,KAAK,SAAS,CAAC,MAAM,CAAC,CAAA;IAC5F,IAAI,CAAC,EAAE;QACL,MAAM,IAAI,KAAK,CACb,wBAAwB,SAAS,CAAC,MAAM,IAAI,SAAS,CAAC,MAAM,iBAAiB,OAAO,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,EAAE,CAAC,GAAG,CAAC,CAAC,IAAI,IAAI,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,IAAI,CAAC,IAAI,CAAC,EAAE,CACtI,CAAA;IAEH,IAAI,SAAS,CAAC,OAAO;QAAE,OAAO,CAAC,MAAM,CAAC,SAAS,CAAC,OAAO,EAAE,SAAS,CAAC,EAAE,CAAC,CAAA;IAEtE,MAAM,aAAa,GAAG,EAAE,CAAC,OAAO,IAAI,OAAO,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC,OAAO,CAAC,KAAK,CAAC,OAAO,CAAC,CAAC,CAAC,CAAC,SAAS,CAAA;IACjG,OAAO,EAAE,CAAC,gBAAgB,CACxB,aAAa,KAAK,SAAS,CAAC,CAAC,CAAC,EAAE,SAAS,EAAE,OAAO,EAAE,aAAa,EAAE,CAAC,CAAC,CAAE,EAAE,SAAS,EAAY,CAC/F,CAAA;AACH,CAAC"}