mppx 0.5.14 → 0.5.16

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 (65) hide show
  1. package/CHANGELOG.md +15 -0
  2. package/dist/Method.d.ts +5 -2
  3. package/dist/Method.d.ts.map +1 -1
  4. package/dist/Method.js.map +1 -1
  5. package/dist/mcp-sdk/server/Transport.d.ts.map +1 -1
  6. package/dist/mcp-sdk/server/Transport.js +8 -2
  7. package/dist/mcp-sdk/server/Transport.js.map +1 -1
  8. package/dist/server/Mppx.d.ts.map +1 -1
  9. package/dist/server/Mppx.js +17 -10
  10. package/dist/server/Mppx.js.map +1 -1
  11. package/dist/server/Request.js +5 -1
  12. package/dist/server/Request.js.map +1 -1
  13. package/dist/server/Transport.d.ts.map +1 -1
  14. package/dist/server/Transport.js +4 -0
  15. package/dist/server/Transport.js.map +1 -1
  16. package/dist/stripe/server/internal/html.gen.d.ts +1 -1
  17. package/dist/stripe/server/internal/html.gen.d.ts.map +1 -1
  18. package/dist/stripe/server/internal/html.gen.js +1 -1
  19. package/dist/stripe/server/internal/html.gen.js.map +1 -1
  20. package/dist/tempo/Methods.d.ts.map +1 -1
  21. package/dist/tempo/Methods.js +4 -2
  22. package/dist/tempo/Methods.js.map +1 -1
  23. package/dist/tempo/client/SessionManager.d.ts.map +1 -1
  24. package/dist/tempo/client/SessionManager.js +20 -10
  25. package/dist/tempo/client/SessionManager.js.map +1 -1
  26. package/dist/tempo/internal/fee-payer.d.ts +4 -1
  27. package/dist/tempo/internal/fee-payer.d.ts.map +1 -1
  28. package/dist/tempo/internal/fee-payer.js +92 -21
  29. package/dist/tempo/internal/fee-payer.js.map +1 -1
  30. package/dist/tempo/server/Session.d.ts.map +1 -1
  31. package/dist/tempo/server/Session.js +43 -20
  32. package/dist/tempo/server/Session.js.map +1 -1
  33. package/dist/tempo/server/internal/html.gen.d.ts +1 -1
  34. package/dist/tempo/server/internal/html.gen.d.ts.map +1 -1
  35. package/dist/tempo/server/internal/html.gen.js +1 -1
  36. package/dist/tempo/server/internal/html.gen.js.map +1 -1
  37. package/dist/tempo/server/internal/transport.d.ts +0 -7
  38. package/dist/tempo/server/internal/transport.d.ts.map +1 -1
  39. package/dist/tempo/server/internal/transport.js +84 -13
  40. package/dist/tempo/server/internal/transport.js.map +1 -1
  41. package/package.json +1 -1
  42. package/src/Method.ts +5 -2
  43. package/src/internal/changeset.test.ts +106 -0
  44. package/src/mcp-sdk/client/McpClient.integration.test.ts +634 -0
  45. package/src/mcp-sdk/server/Transport.test.ts +1 -0
  46. package/src/mcp-sdk/server/Transport.ts +10 -2
  47. package/src/proxy/Proxy.test.ts +149 -1
  48. package/src/server/Mppx.test.ts +120 -0
  49. package/src/server/Mppx.ts +27 -11
  50. package/src/server/Request.test.ts +46 -1
  51. package/src/server/Request.ts +6 -1
  52. package/src/server/Transport.test.ts +2 -0
  53. package/src/server/Transport.ts +4 -0
  54. package/src/stripe/server/internal/html.gen.ts +1 -1
  55. package/src/tempo/Methods.test.ts +13 -0
  56. package/src/tempo/Methods.ts +23 -16
  57. package/src/tempo/client/SessionManager.ts +32 -9
  58. package/src/tempo/internal/fee-payer.test.ts +40 -4
  59. package/src/tempo/internal/fee-payer.ts +105 -21
  60. package/src/tempo/server/Session.test.ts +760 -2
  61. package/src/tempo/server/Session.ts +59 -17
  62. package/src/tempo/server/internal/html.gen.ts +1 -1
  63. package/src/tempo/server/internal/transport.test.ts +321 -10
  64. package/src/tempo/server/internal/transport.ts +101 -14
  65. package/src/viem/Client.test.ts +52 -1
@@ -181,7 +181,7 @@ export function session<const parameters extends session.Parameters>(
181
181
  }
182
182
  },
183
183
 
184
- async verify({ credential, request }) {
184
+ async verify({ credential, envelope, request }) {
185
185
  const { challenge, payload } = credential as Credential.Credential<SessionCredentialPayload>
186
186
 
187
187
  const resolvedRequest = Methods.session.schema.request.parse(request)
@@ -255,6 +255,28 @@ export function session<const parameters extends session.Parameters>(
255
255
  })
256
256
  }
257
257
 
258
+ // In the default HTTP request/response mode, each successful content
259
+ // request consumes one unit immediately after the credential is accepted.
260
+ // This keeps equal-voucher replays bounded by the voucher's remaining
261
+ // balance instead of serving repeated responses for free.
262
+ if (
263
+ !parameters.sse &&
264
+ envelope &&
265
+ isBillableContentRequest(envelope.capturedRequest) &&
266
+ (payload.action === 'open' || payload.action === 'voucher')
267
+ ) {
268
+ const charged = await charge(
269
+ store,
270
+ sessionReceipt.channelId,
271
+ BigInt(resolvedRequest.amount),
272
+ )
273
+ sessionReceipt = {
274
+ ...sessionReceipt,
275
+ spent: charged.spent.toString(),
276
+ units: charged.units,
277
+ }
278
+ }
279
+
258
280
  return sessionReceipt
259
281
  },
260
282
 
@@ -265,28 +287,24 @@ export function session<const parameters extends session.Parameters>(
265
287
  //
266
288
  // close and topUp are always gated (204) — they are pure management.
267
289
  //
268
- // open and voucher are gated only for bodyless POSTs (management
269
- // updates). POSTs with a body are content requests — the client's
270
- // original request piggybacked on the credential so they fall
271
- // through to serve content. GETs always fall through so auto-mode
272
- // clients (whose fetch wrapper bundles open+voucher into a single
273
- // GET retry) receive content as expected.
274
- respond({ credential, input }) {
290
+ // open and voucher share the same captured-request classifier used
291
+ // during verification. Non-billable requests are treated as management
292
+ // updates; billable requests fall through to the application handler.
293
+ respond({ credential, envelope, input }) {
275
294
  const { payload } = credential as Credential.Credential<SessionCredentialPayload>
276
295
 
277
296
  if (payload.action === 'close') return new Response(null, { status: 204 })
278
297
  if (payload.action === 'topUp') return new Response(null, { status: 204 })
279
298
 
280
- // open and voucher: gate only bodyless POSTs (management updates).
281
- // POSTs with a body are content requests — fall through so the
282
- // upstream response is returned to the client.
283
- if (input.method === 'POST') {
284
- const contentLength = input.headers.get('content-length')
285
- if (contentLength !== null && contentLength !== '0') return undefined
286
- if (input.headers.has('transfer-encoding')) return undefined
287
- return new Response(null, { status: 204 })
299
+ const capturedRequest = envelope?.capturedRequest ?? {
300
+ hasBody: input.body !== null,
301
+ headers: input.headers,
302
+ method: input.method,
303
+ url: new URL(input.url),
288
304
  }
289
- return undefined
305
+
306
+ if (isBillableContentRequest(capturedRequest)) return undefined
307
+ return new Response(null, { status: 204 })
290
308
  },
291
309
  })
292
310
  }
@@ -452,6 +470,30 @@ function validateOnChainChannel(
452
470
  }
453
471
  }
454
472
 
473
+ function isBillableContentRequest(input: {
474
+ hasBody?: boolean | undefined
475
+ headers: Headers
476
+ method: string
477
+ }): boolean {
478
+ if (input.method === 'POST') return hasCapturedRequestBody(input)
479
+
480
+ if (input.method === 'HEAD') return false
481
+
482
+ return true
483
+ }
484
+
485
+ function hasCapturedRequestBody(input: {
486
+ hasBody?: boolean | undefined
487
+ headers: Headers
488
+ }): boolean {
489
+ const contentLength = input.headers.get('content-length')
490
+ const headerIndicatesBody =
491
+ (contentLength !== null && contentLength !== '0') || input.headers.has('transfer-encoding')
492
+
493
+ if (input.hasBody === true) return true
494
+ return headerIndicatesBody
495
+ }
496
+
455
497
  /**
456
498
  * Shared logic for verifying an incremental voucher and updating channel state.
457
499
  * Used by both handleVoucher and (indirectly) handleOpen.