@quackai/q402-mcp 0.8.24 → 0.8.25

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 (2) hide show
  1. package/dist/index.js +46 -7
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -211,7 +211,7 @@ var isValidPrivateKey = (s) => typeof s === "string" && PRIVATE_KEY_RE.test(s);
211
211
  // package.json
212
212
  var package_default = {
213
213
  name: "@quackai/q402-mcp",
214
- version: "0.8.24",
214
+ version: "0.8.25",
215
215
  description: "MCP server for Q402 \u2014 gasless USDC/USDT/RLUSD payments on 10 EVM chains + Chainlink CCIP USDC bridge on the eth/avax/arbitrum triangle, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.",
216
216
  mcpName: "io.github.bitgett/q402-mcp",
217
217
  keywords: [
@@ -1116,6 +1116,10 @@ async function runPay(input) {
1116
1116
  to: input.to.toLowerCase(),
1117
1117
  amount: input.amount,
1118
1118
  token: input.token,
1119
+ // Bind the funding source too — the user is consenting to spend from THIS
1120
+ // wallet, so a different walletMode/walletId needs a fresh preview.
1121
+ wm: effectiveMode,
1122
+ wid: (input.walletId ?? "").toLowerCase(),
1119
1123
  ...input.hookParams?.splits ? { splits: input.hookParams.splits.map((s) => ({ r: s.recipient.toLowerCase(), bps: s.bps })) } : {}
1120
1124
  };
1121
1125
  const consent = checkConsent(consentIntent, input.consentToken);
@@ -1530,7 +1534,10 @@ async function runBatchPay(input) {
1530
1534
  t: "batch",
1531
1535
  chain: input.chain,
1532
1536
  token: input.token,
1533
- recipients: input.recipients.map((r) => ({ to: r.to.toLowerCase(), amount: r.amount }))
1537
+ recipients: input.recipients.map((r) => ({ to: r.to.toLowerCase(), amount: r.amount })),
1538
+ // Bind the funding source too (see q402_pay).
1539
+ wm: input.walletMode ?? "",
1540
+ wid: (input.walletId ?? "").toLowerCase()
1534
1541
  };
1535
1542
  const consent = checkConsent(consentIntent, input.consentToken);
1536
1543
  if (!consent.ok) {
@@ -3262,7 +3269,9 @@ async function runBridgeSend(input) {
3262
3269
  src: input.src,
3263
3270
  dst: input.dst,
3264
3271
  amount: input.amount,
3265
- feeToken: input.feeToken === "native" ? "native" : "LINK"
3272
+ feeToken: input.feeToken === "native" ? "native" : "LINK",
3273
+ // Bind the funding wallet too (see q402_pay).
3274
+ wid: (input.walletId ?? "").toLowerCase()
3266
3275
  };
3267
3276
  const consent = checkConsent(consentIntent, input.consentToken);
3268
3277
  if (input.confirm !== true || !consent.ok) {
@@ -3641,6 +3650,9 @@ var YieldDepositInputSchema = z16.object({
3641
3650
  ),
3642
3651
  confirm: z16.boolean().optional().describe(
3643
3652
  "MUST be true to actually supply funds. Set this only after the user has explicitly approved this exact deposit (amount, token, chain, wallet) in the conversation. When omitted or false the tool previews the action and does NOT move any funds."
3653
+ ),
3654
+ consentToken: z16.string().optional().describe(
3655
+ "Two-phase consent. LEAVE UNSET on the first call: the tool previews the deposit (no funds move) and returns a consentToken. Relay the preview to the user, get an explicit yes, then re-call with confirm:true AND this consentToken. The token is re-derived from (chain, token, amount, wallet) and refused on mismatch, so a previewed deposit can't be swapped."
3644
3656
  )
3645
3657
  });
3646
3658
  var YIELD_DEPOSIT_TOOL = {
@@ -3674,6 +3686,10 @@ var YIELD_DEPOSIT_TOOL = {
3674
3686
  confirm: {
3675
3687
  type: "boolean",
3676
3688
  description: "MUST be true to actually supply funds \u2014 set only after the user explicitly approved this exact deposit in chat. Omit (or false) to preview without moving funds."
3689
+ },
3690
+ consentToken: {
3691
+ type: "string",
3692
+ description: "Two-phase consent token. Leave unset on the first call to get a preview + token; re-call with confirm:true AND this token after the user approves. Bound to (chain, token, amount, wallet) and refused on mismatch."
3677
3693
  }
3678
3694
  },
3679
3695
  required: ["token", "amount"],
@@ -3692,12 +3708,20 @@ async function runYieldDeposit(input) {
3692
3708
  }
3693
3709
  const walletId = typeof input.walletId === "string" && input.walletId.length > 0 ? input.walletId.toLowerCase() : CONFIG.walletId ?? void 0;
3694
3710
  const idempotencyKey = typeof input.idempotencyKey === "string" && input.idempotencyKey.length > 0 ? input.idempotencyKey : hexlify2(randomBytes2(32));
3695
- if (input.confirm !== true) {
3711
+ const consentIntent = {
3712
+ t: "yield-deposit",
3713
+ chain: input.chain,
3714
+ token: input.token,
3715
+ amount: input.amount,
3716
+ walletId: walletId ?? null
3717
+ };
3718
+ const consent = checkConsent(consentIntent, input.consentToken);
3719
+ if (input.confirm !== true || !consent.ok) {
3696
3720
  const walletDesc = walletId ? `wallet ${walletId}` : "your default Agent Wallet";
3697
3721
  return {
3698
3722
  content: [{
3699
3723
  type: "text",
3700
- text: `Will supply ${input.amount} ${input.token} into Aave on ${input.chain} from ${walletDesc}. This MOVES FUNDS. Re-call with confirm:true to execute.`
3724
+ text: `Will supply ${input.amount} ${input.token} into Aave on ${input.chain} from ${walletDesc}. This MOVES FUNDS. Confirm with the user, then re-call with confirm:true AND consentToken="${consent.expected}".`
3701
3725
  }]
3702
3726
  };
3703
3727
  }
@@ -3821,6 +3845,9 @@ var YieldWithdrawInputSchema = z17.object({
3821
3845
  ),
3822
3846
  confirm: z17.boolean().optional().describe(
3823
3847
  "MUST be true to actually withdraw funds. Set this only after the user has explicitly approved this exact withdrawal (amount, token, chain, wallet) in the conversation. When omitted or false the tool previews the action and does NOT move any funds."
3848
+ ),
3849
+ consentToken: z17.string().optional().describe(
3850
+ "Two-phase consent. LEAVE UNSET on the first call: the tool previews the withdrawal (no funds move) and returns a consentToken. Relay the preview to the user, get an explicit yes, then re-call with confirm:true AND this consentToken. The token is re-derived from (chain, token, amount, wallet)."
3824
3851
  )
3825
3852
  });
3826
3853
  var YIELD_WITHDRAW_TOOL = {
@@ -3854,6 +3881,10 @@ var YIELD_WITHDRAW_TOOL = {
3854
3881
  confirm: {
3855
3882
  type: "boolean",
3856
3883
  description: "MUST be true to actually withdraw funds \u2014 set only after the user explicitly approved this exact withdrawal in chat. Omit (or false) to preview without moving funds."
3884
+ },
3885
+ consentToken: {
3886
+ type: "string",
3887
+ description: "Two-phase consent token. Leave unset on the first call to get a preview + token; re-call with confirm:true AND this token after the user approves. Bound to (chain, token, amount, wallet)."
3857
3888
  }
3858
3889
  },
3859
3890
  required: ["token", "amount"],
@@ -3873,12 +3904,20 @@ async function runYieldWithdraw(input) {
3873
3904
  const walletId = typeof input.walletId === "string" && input.walletId.length > 0 ? input.walletId.toLowerCase() : CONFIG.walletId ?? void 0;
3874
3905
  const idempotencyKey = typeof input.idempotencyKey === "string" && input.idempotencyKey.length > 0 ? input.idempotencyKey : hexlify3(randomBytes3(32));
3875
3906
  const amountDesc = input.amount === "max" ? "the FULL position" : `${input.amount} ${input.token}`;
3876
- if (input.confirm !== true) {
3907
+ const consentIntent = {
3908
+ t: "yield-withdraw",
3909
+ chain: input.chain,
3910
+ token: input.token,
3911
+ amount: input.amount,
3912
+ walletId: walletId ?? null
3913
+ };
3914
+ const consent = checkConsent(consentIntent, input.consentToken);
3915
+ if (input.confirm !== true || !consent.ok) {
3877
3916
  const walletDesc = walletId ? `wallet ${walletId}` : "your default Agent Wallet";
3878
3917
  return {
3879
3918
  content: [{
3880
3919
  type: "text",
3881
- text: `Will withdraw ${amountDesc} from Aave on ${input.chain} back to ${walletDesc}. This MOVES FUNDS. Re-call with confirm:true to execute.`
3920
+ text: `Will withdraw ${amountDesc} from Aave on ${input.chain} back to ${walletDesc}. This MOVES FUNDS. Confirm with the user, then re-call with confirm:true AND consentToken="${consent.expected}".`
3882
3921
  }]
3883
3922
  };
3884
3923
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quackai/q402-mcp",
3
- "version": "0.8.24",
3
+ "version": "0.8.25",
4
4
  "description": "MCP server for Q402 — gasless USDC/USDT/RLUSD payments on 10 EVM chains + Chainlink CCIP USDC bridge on the eth/avax/arbitrum triangle, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.",
5
5
  "mcpName": "io.github.bitgett/q402-mcp",
6
6
  "keywords": [