@quackai/q402-mcp 0.5.4 → 0.5.5

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 (3) hide show
  1. package/README.md +1 -1
  2. package/dist/index.js +26 -17
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -9,7 +9,7 @@
9
9
  >
10
10
  > **Trial-scope policy:** API keys minted under the free-trial program (`plan: "trial"`) are restricted to BNB Chain with USDC/USDT — server-side enforcement, returns `403 TRIAL_BNB_ONLY` otherwise. **Paid API keys see the full 9-chain matrix at all times.**
11
11
 
12
- Claude can now reason about stablecoin payments end to end quote a transfer across 9 chains, pick the cheapest route, and (optionally) settle the transaction over [Q402](https://q402.quackai.ai)'s EIP-7702 relayer infrastructure. The recipient receives the full amount; the sender pays $0 in gas.
12
+ AI agents — Claude (Desktop / Code), OpenAI Codex CLI, Cursor, Cline, and any other MCP-compatible client — can now reason about stablecoin payments end to end: quote a transfer across 9 chains, pick the cheapest route, and (optionally) settle the transaction over [Q402](https://q402.quackai.ai)'s EIP-7702 relayer infrastructure. The recipient receives the full amount; the sender pays $0 in gas.
13
13
 
14
14
  ---
15
15
 
package/dist/index.js CHANGED
@@ -748,7 +748,7 @@ async function runPay(input) {
748
748
  token: input.token
749
749
  });
750
750
  guardsApplied.push("mode=sandbox");
751
- const setupHint = resolved.sandboxReason ?? describeSandboxReason(resolved.apiKey ?? "");
751
+ const setupHint = resolved.sandboxReason ?? describeSandboxReason(resolved.apiKey ?? "", resolved.scope);
752
752
  return { result: result2, guardsApplied, setupHint };
753
753
  }
754
754
  const client = new Q402NodeClient({
@@ -765,17 +765,19 @@ async function runPay(input) {
765
765
  guardsApplied.push("mode=live");
766
766
  return { result, guardsApplied };
767
767
  }
768
- function describeSandboxReason(resolvedKey) {
768
+ function describeSandboxReason(resolvedKey, scope) {
769
769
  const missing = [];
770
770
  if (!resolvedKey.startsWith("q402_live_")) missing.push("a live API key (must start with q402_live_)");
771
771
  if (!CONFIG.privateKey) missing.push("Q402_PRIVATE_KEY");
772
772
  if (!CONFIG.realPaymentsRequested) missing.push("Q402_ENABLE_REAL_PAYMENTS=1");
773
773
  if (missing.length === 0) return "Sandbox mode active (no env state change needed).";
774
- return "Sandbox mode is active because the following env vars are missing or not yet set: " + missing.join(", ") + ". Get a live API key at https://q402.quackai.ai/dashboard.";
774
+ const tier = scope === "trial" ? "Free Trial" : "Multichain";
775
+ const url = scope === "trial" ? "https://q402.quackai.ai/event" : "https://q402.quackai.ai/payment";
776
+ return "Sandbox mode is active because the following env vars are missing or not yet set: " + missing.join(", ") + `. Get a live ${tier} key at ${url}.`;
775
777
  }
776
778
  var PAY_TOOL = {
777
779
  name: "q402_pay",
778
- description: "Send a gasless USDC, USDT, or RLUSD payment via Q402. Auto-routing: chain='bnb' + Q402_TRIAL_API_KEY set \u2192 Trial (free sponsored); anything else \u2192 Multichain (paid 9-chain). Same rule for q402_batch_pay. Set keyScope='trial' or 'multichain' to force one explicitly. Trial keys reject any non-BNB chain server-side with TRIAL_BNB_ONLY. Multichain keys cover avax, bnb, eth, xlayer, stable, mantle, injective, monad, scroll \u2014 USDC/USDT on most chains, RLUSD on Ethereum only, Injective USDT-only. SANDBOX BY DEFAULT \u2014 no funds move unless the resolved key is a live key (q402_live_*), Q402_PRIVATE_KEY is set, and Q402_ENABLE_REAL_PAYMENTS=1. The recipient receives the full amount; the sender pays $0 in gas. Note: the first q402_pay on a chain creates a persistent EIP-7702 delegation on the sender's EOA (set-code TX, Pectra). Subsequent payments on the same chain reuse it (gas-efficient). To remove the delegation later, call q402_clear_delegation. ALWAYS get explicit user confirmation of the exact recipient address, amount, chain, and token in conversation immediately before calling this tool.",
780
+ description: "Send a gasless USDC, USDT, or RLUSD payment via Q402. Auto-routing: chain='bnb' + Q402_TRIAL_API_KEY set \u2192 Trial (free sponsored); anything else \u2192 Multichain (paid 9-chain). Same rule for q402_batch_pay. Set keyScope='trial' or 'multichain' to force one explicitly. Trial keys reject any non-BNB chain server-side with TRIAL_BNB_ONLY. Multichain keys cover avax, bnb, eth, xlayer, stable, mantle, injective, monad, scroll \u2014 USDC/USDT on most chains, RLUSD on Ethereum only, Injective USDT-only. SANDBOX BY DEFAULT \u2014 no funds move unless the resolved key is a live key (q402_live_*), Q402_PRIVATE_KEY is set, and Q402_ENABLE_REAL_PAYMENTS=1. The recipient receives the full amount; the sender pays $0 in gas. After the first payment on a chain, follow-up payments on the same chain are faster and cheaper (Q402 reuses the wallet's setup); q402_clear_delegation resets it if the user ever asks. ALWAYS get explicit user confirmation of the exact recipient address, amount, chain, and token in conversation immediately before calling this tool.",
779
781
  inputSchema: {
780
782
  type: "object",
781
783
  properties: {
@@ -901,7 +903,7 @@ async function runBatchPay(input) {
901
903
  (r) => sandboxPay(chain, { to: r.to, amount: r.amount, token: input.token })
902
904
  );
903
905
  guardsApplied.push("mode=sandbox");
904
- const reason = resolved.sandboxReason ?? describeSandboxReason2(resolved.apiKey ?? "");
906
+ const reason = resolved.sandboxReason ?? describeSandboxReason2(resolved.apiKey ?? "", resolved.scope);
905
907
  return {
906
908
  mode: "sandbox",
907
909
  status: "sandbox",
@@ -950,17 +952,19 @@ async function runBatchPay(input) {
950
952
  throw err;
951
953
  }
952
954
  }
953
- function describeSandboxReason2(resolvedKey) {
955
+ function describeSandboxReason2(resolvedKey, scope) {
954
956
  const missing = [];
955
957
  if (!resolvedKey.startsWith("q402_live_")) missing.push("a live API key (must start with q402_live_)");
956
958
  if (!CONFIG.privateKey) missing.push("Q402_PRIVATE_KEY");
957
959
  if (!CONFIG.realPaymentsRequested) missing.push("Q402_ENABLE_REAL_PAYMENTS=1");
958
960
  if (missing.length === 0) return "Sandbox mode active (no env state change needed).";
959
- return "Sandbox mode is active because the following env vars are missing or not yet set: " + missing.join(", ") + ". Get a live API key at https://q402.quackai.ai/dashboard.";
961
+ const tier = scope === "trial" ? "Free Trial" : "Multichain";
962
+ const url = scope === "trial" ? "https://q402.quackai.ai/event" : "https://q402.quackai.ai/payment";
963
+ return "Sandbox mode is active because the following env vars are missing or not yet set: " + missing.join(", ") + `. Get a live ${tier} key at ${url}.`;
960
964
  }
961
965
  var BATCH_PAY_TOOL = {
962
966
  name: "q402_batch_pay",
963
- description: `Send gasless payments to MULTIPLE recipients on a single chain \xD7 token in one call. Auto-routing follows the same rule as q402_pay: chain='bnb' + Q402_TRIAL_API_KEY set \u2192 Trial; else Multichain. Trial keys: max ${RECIPIENT_LIMIT_TRIAL} recipients per call, BNB Chain + USDC/USDT only. Multichain keys: max ${RECIPIENT_LIMIT_PAID} recipients per call across 7 EIP-7702 default chains (avax, bnb, eth, mantle, injective, monad, scroll). xlayer + stable are NOT batchable \u2014 use q402_pay in a loop. AMBIGUITY GATE: when auto would land on Trial AND recipients.length > 5, the tool returns status='ambiguous' WITHOUT executing \u2014 the agent must ask the human whether to (a) trim to 5 with keyScope='trial', (b) send all on the paid Multichain key, or (c) split into two separate calls (5 free + remainder paid). Re-invoke with explicit keyScope after the choice. SANDBOX BY DEFAULT \u2014 real on-chain TX only when the resolved key is live (q402_live_*), Q402_PRIVATE_KEY is set, and Q402_ENABLE_REAL_PAYMENTS=1. Every recipient receives the full amount; the sender pays $0 in gas for the entire batch. Note: same EIP-7702 delegation behaviour as q402_pay \u2014 the first call on a chain creates a persistent set-code delegation on the sender's EOA, reused by subsequent calls. Use q402_clear_delegation to remove. ALWAYS get explicit user confirmation of the complete recipient + amount list, chain, and token in conversation immediately before calling this tool \u2014 the user must approve the full batch, not the individual rows.`,
967
+ description: `Send gasless payments to MULTIPLE recipients on a single chain \xD7 token in one call. Auto-routing follows the same rule as q402_pay: chain='bnb' + Q402_TRIAL_API_KEY set \u2192 Trial; else Multichain. Trial keys: max ${RECIPIENT_LIMIT_TRIAL} recipients per call, BNB Chain + USDC/USDT only. Multichain keys: max ${RECIPIENT_LIMIT_PAID} recipients per call across 7 batchable chains (avax, bnb, eth, mantle, injective, monad, scroll). xlayer + stable are NOT batchable \u2014 use q402_pay in a loop. AMBIGUITY GATE: when auto would land on Trial AND recipients.length > 5, the tool returns status='ambiguous' WITHOUT executing \u2014 the agent must ask the human whether to (a) trim to 5 with keyScope='trial', (b) send all on the paid Multichain key, or (c) split into two separate calls (5 free + remainder paid). Re-invoke with explicit keyScope after the choice. SANDBOX BY DEFAULT \u2014 real on-chain TX only when the resolved key is live (q402_live_*), Q402_PRIVATE_KEY is set, and Q402_ENABLE_REAL_PAYMENTS=1. Every recipient receives the full amount; the sender pays $0 in gas for the entire batch. After the first batch on a chain, follow-up batches on the same chain are faster and cheaper (Q402 reuses the wallet's setup); q402_clear_delegation resets it if the user ever asks. ALWAYS get explicit user confirmation of the complete recipient + amount list, chain, and token in conversation immediately before calling this tool \u2014 the user must approve the full batch, not the individual rows.`,
964
968
  inputSchema: {
965
969
  type: "object",
966
970
  properties: {
@@ -1271,19 +1275,24 @@ async function runWalletStatus() {
1271
1275
  }
1272
1276
  const url = `${CONFIG.relayBaseUrl.replace(/\/$/, "")}/wallet/delegation-status?address=${address}`;
1273
1277
  let body;
1278
+ let res;
1274
1279
  try {
1275
- const res = await fetch(url);
1280
+ res = await fetch(url);
1276
1281
  body = await res.json();
1277
- if (!res.ok) {
1278
- return {
1279
- address,
1280
- error: typeof body === "object" && body && "error" in body ? String(body.error) : `HTTP ${res.status}`
1281
- };
1282
- }
1283
1282
  } catch (e) {
1284
1283
  return {
1285
1284
  address,
1286
- error: e instanceof Error ? e.message : String(e)
1285
+ error: "RELAY_UNREACHABLE",
1286
+ hint: `Could not reach Q402 at ${url}: ${e instanceof Error ? e.message : String(e)}`
1287
+ };
1288
+ }
1289
+ if (!res.ok) {
1290
+ const errBody = body;
1291
+ return {
1292
+ address,
1293
+ error: errBody.error ?? `HTTP ${res.status}`,
1294
+ hint: errBody.hint ?? errBody.reason,
1295
+ retryAfterSec: errBody.retryAfterSec
1287
1296
  };
1288
1297
  }
1289
1298
  const parsed = body;
@@ -1446,7 +1455,7 @@ var CLEAR_DELEGATION_TOOL = {
1446
1455
 
1447
1456
  // src/index.ts
1448
1457
  var PACKAGE_NAME = "@quackai/q402-mcp";
1449
- var PACKAGE_VERSION = "0.5.4";
1458
+ var PACKAGE_VERSION = "0.5.5";
1450
1459
  function jsonText(value) {
1451
1460
  return { type: "text", text: JSON.stringify(value, null, 2) };
1452
1461
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quackai/q402-mcp",
3
- "version": "0.5.4",
3
+ "version": "0.5.5",
4
4
  "description": "MCP server for Q402 — gasless USDC, USDT, and RLUSD payments across 9 EVM chains, 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": [