@quackai/q402-mcp 0.5.7 → 0.5.9

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 +29 -16
  2. package/dist/index.js +29 -7
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -49,16 +49,26 @@ The agent calls `q402_doctor`. On first install, the tool tells the agent to:
49
49
 
50
50
  ### Manual setup (no AI)
51
51
 
52
- Create `~/.q402/mcp.env` yourself:
52
+ Create `~/.q402/mcp.env` yourself. The template below matches what `q402_doctor` writes — every secret line is commented out and `Q402_ENABLE_REAL_PAYMENTS` defaults to `0`. Uncomment the lines you need, paste real values, then flip the live flag to `1`. (Saving the template as-is is safe: invalid placeholders won't trip live mode.)
53
53
 
54
54
  ```bash
55
55
  # ~/.q402/mcp.env
56
- # Pick ONE of these:
57
- Q402_TRIAL_API_KEY=q402_live_...
56
+
57
+ # Free Trial — BNB only, 2,000 sponsored TX (from /event)
58
+ # Q402_TRIAL_API_KEY=q402_live_...
59
+
60
+ # Paid Multichain — all 9 chains (from /payment)
58
61
  # Q402_MULTICHAIN_API_KEY=q402_live_...
59
62
 
60
- Q402_PRIVATE_KEY=0x...
61
- Q402_ENABLE_REAL_PAYMENTS=1
63
+ # Hex EVM private key (0x + 64 hex). Use a FRESH wallet, NOT your main
64
+ # one — Q402 delegates this EOA via EIP-7702 on first payment.
65
+ # Hardware wallets (Ledger / Trezor) are not supported yet.
66
+ # Q402_PRIVATE_KEY=0x...
67
+
68
+ # Start at 0 (sandbox). Flip to 1 only after real values are pasted above.
69
+ Q402_ENABLE_REAL_PAYMENTS=0
70
+
71
+ # Default Q402 deployment. Only change for self-hosted.
62
72
  Q402_RELAY_BASE_URL=https://q402.quackai.ai/api
63
73
 
64
74
  # Optional safety guards:
@@ -66,7 +76,7 @@ Q402_RELAY_BASE_URL=https://q402.quackai.ai/api
66
76
  # Q402_ALLOWED_RECIPIENTS=0xabc...,0xdef...
67
77
  ```
68
78
 
69
- Then `chmod 600 ~/.q402/mcp.env` (Unix) and restart your client. That's the full configuration.
79
+ Then `chmod 600 ~/.q402/mcp.env` (Unix) and restart your client. That's the full configuration. **Heads up on the EIP-7702 side effect:** after your first live payment on a chain, your wallet will show 'Smart account' in MetaMask / OKX — that's the delegation Q402 uses for gasless settlement, reversible anytime via `q402_clear_delegation`.
70
80
 
71
81
  ### Advanced — explicit env injection
72
82
 
@@ -131,30 +141,33 @@ Then export the values in `~/.zshrc` / `~/.bashrc`. See the [Codex config refere
131
141
 
132
142
  ## Sandbox vs live mode
133
143
 
134
- By default the MCP server operates in **sandbox mode**: `q402_pay` returns a random fake transaction hash (32-byte hex, no on-chain broadcast), no funds move, no gas-tank credit is consumed. That makes it safe to plug into any MCP client without worrying about an accidental payment — if the agent misreads the conversation and fires `q402_pay` before you intended, nothing moves.
144
+ By default the MCP server operates in **sandbox mode**: `q402_pay` returns a random fake transaction hash with `success: false` and `sandbox: true`, no funds move, no gas-tank credit is consumed. That makes it safe to plug into any MCP client without worrying about an accidental payment — if the agent misreads the conversation and fires `q402_pay` before you intended, nothing moves AND the response cannot be mistaken for a confirmed settlement.
135
145
 
136
- To enable real on-chain transactions, the resolved API key must be live (`q402_live_*`), `Q402_PRIVATE_KEY` must be set, and `Q402_ENABLE_REAL_PAYMENTS=1`:
146
+ To enable real on-chain transactions, the resolved API key must be live (`q402_live_*`), `Q402_PRIVATE_KEY` must be set to a valid 32-byte hex key, and `Q402_ENABLE_REAL_PAYMENTS=1`. The block below is the template `q402_doctor` writes to `~/.q402/mcp.env` — every secret line is commented out and the live flag defaults to `0`. Uncomment the lines you need, paste real values in your editor, then flip the flag to `1`:
137
147
 
138
148
  ```bash
139
- # Two-key model — set whichever applies (or both for auto-routing).
149
+ # Two-key model — uncomment ONE (or both for auto-routing).
140
150
  # Auto-routing (same for q402_pay AND q402_batch_pay):
141
151
  # chain="bnb" + Q402_TRIAL_API_KEY set → Trial (free sponsored)
142
152
  # anything else → Multichain (paid 9-chain)
143
153
  # Batch ambiguity: 6+ recipient BNB batch with Trial set returns
144
154
  # status="ambiguous" instead of executing — agent asks user to pick.
145
155
  # Override per call with keyScope: "auto" | "trial" | "multichain".
146
- Q402_TRIAL_API_KEY=q402_live_... # BNB-only sponsored Trial key (from /event)
147
- Q402_MULTICHAIN_API_KEY=q402_live_... # paid 9-chain key (per-chain Gas Tank)
148
156
 
149
- Q402_PRIVATE_KEY=0xabc... # signer for the payer EOA
150
- Q402_ENABLE_REAL_PAYMENTS=1 # explicit opt-in
157
+ # Q402_TRIAL_API_KEY=q402_live_... # BNB-only sponsored Trial key (from /event)
158
+ # Q402_MULTICHAIN_API_KEY=q402_live_... # paid 9-chain key (per-chain Gas Tank)
159
+
160
+ # Q402_PRIVATE_KEY=0x... # signer for the payer EOA (32-byte hex)
161
+
162
+ # Start at 0 (sandbox). Flip to 1 only after real values are pasted above —
163
+ # a malformed Q402_PRIVATE_KEY (e.g. the "0x..." placeholder) is rejected at
164
+ # the live-mode gate, so partial setups stay in sandbox with a clear hint.
165
+ Q402_ENABLE_REAL_PAYMENTS=0
151
166
  ```
152
167
 
153
168
  Anything missing for the resolved scope → automatic sandbox fallback with a hint pointing at what to set.
154
169
 
155
- > ⚠️ **Sandbox returns a deterministic-looking fake `txHash` and a synthetic success result.** A user who *expected* a live transfer (e.g. forgot to set `Q402_ENABLE_REAL_PAYMENTS=1`, mis-typed a scoped env var, or hit an impossible chain×scope combination like `keyScope: "trial"` + `chain: "monad"`) gets a "success" back and may believe funds actually moved.
156
- >
157
- > Two-layer mitigation: every sandbox response carries a `setupHint` field on the tool result describing **exactly why** sandbox was selected, and a `method: "sandbox"` field that makes the mode explicit independent of hash inspection. The `txHash` itself is a 32-byte random hex string — visually indistinguishable from a real hash but emitted only when no on-chain TX is broadcast — so don't rely on hash forensics. Always check `setupHint` (or `method`) before showing the user a success message.
170
+ > ⚠️ **Sandbox returns a deterministic-looking fake `txHash` but explicitly NOT a success.** Since v0.5.8 every sandbox response carries `success: false` and `sandbox: true` at the top level of the `PayResult` — so a downstream chatbot summary that lifts the `success` field cannot mistakenly tell the user "payment succeeded" when nothing happened. The `txHash` itself is a 32-byte random hex string (visually indistinguishable from a real hash but emitted only when no on-chain TX is broadcast). Combined with the `mode: "sandbox"` and `method: "sandbox"` markers + the `setupHint` field on the wrapping `PaySummary` describing **exactly why** sandbox was selected, you have four independent signals to branch on before showing the user a confirmation message.
158
171
 
159
172
  ### Hard caps
160
173
 
package/dist/index.js CHANGED
@@ -168,7 +168,7 @@ var isValidPrivateKey = (s) => typeof s === "string" && PRIVATE_KEY_RE.test(s);
168
168
 
169
169
  // src/version.ts
170
170
  var PACKAGE_NAME = "@quackai/q402-mcp";
171
- var PACKAGE_VERSION = "0.5.7";
171
+ var PACKAGE_VERSION = "0.5.9";
172
172
 
173
173
  // src/tools/quote.ts
174
174
  import { z } from "zod";
@@ -750,7 +750,11 @@ function sandboxPay(chain, input) {
750
750
  const tokenAmount = toRawAmount(input.amount, tokenCfg.decimals);
751
751
  const fakeHash = "0x" + hexlify(randomBytes(32)).slice(2);
752
752
  return {
753
- success: true,
753
+ // `success: false` because no funds moved. The `sandbox: true` flag is
754
+ // the canonical "this was a simulation" marker — downstream callers
755
+ // should branch on EITHER field to avoid misreporting a settlement.
756
+ success: false,
757
+ sandbox: true,
754
758
  txHash: fakeHash,
755
759
  tokenAmount,
756
760
  token: input.token,
@@ -836,7 +840,11 @@ async function runPay(input) {
836
840
  token: input.token
837
841
  });
838
842
  guardsApplied.push("mode=live");
839
- return { result, guardsApplied };
843
+ return {
844
+ result,
845
+ guardsApplied,
846
+ postPaymentTip: result.success ? `After this payment your EOA is EIP-7702-delegated to Q402's impl on ${chain.name} \u2014 MetaMask / OKX will show it as a 'Smart account'. That's normal and reversible: q402_clear_delegation removes the delegation on a specific chain (Q402 sponsors the gas, so you pay $0). If you ever try to receive native gas tokens directly to this EOA and the transfer reverts, the delegation is the cause \u2014 clear it for that chain first.` : void 0
847
+ };
840
848
  }
841
849
  function describeSandboxReason(resolvedKey, scope) {
842
850
  const missing = [];
@@ -850,7 +858,7 @@ function describeSandboxReason(resolvedKey, scope) {
850
858
  }
851
859
  var PAY_TOOL = {
852
860
  name: "q402_pay",
853
- 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.",
861
+ 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 as a valid 32-byte hex key, and Q402_ENABLE_REAL_PAYMENTS=1. Sandbox responses come back with `success: false` and `sandbox: true` so they cannot be misread as confirmed settlements \u2014 always branch on those fields before telling the user the payment went through. The recipient receives the full amount; the sender pays $0 in gas. \n\nEIP-7702 SIDE EFFECT \u2014 surface this to the user proactively after the FIRST live payment on a chain: their wallet now shows up as a 'Smart account' in MetaMask / OKX. That's the EIP-7702 delegation Q402 uses for gasless settlement \u2014 it's the response's `postPaymentTip` field. Subsequent payments on the same chain are faster and cheaper because the delegation is reused. \n\nIf the user EVER reports that native gas tokens (BNB / ETH / AVAX / etc.) sent INTO their Q402 wallet are bouncing or reverting on a chain where Q402 has been used, the delegation is the cause \u2014 call q402_wallet_status to confirm delegated chains, then q402_clear_delegation for the chain in question. Q402 sponsors the gas for the clear, so the user pays $0. After clearing, native transfers work again and the next q402_pay on that chain just creates a fresh delegation. \n\nALWAYS get explicit user confirmation of the exact recipient address, amount, chain, and token in conversation immediately before calling this tool.",
854
862
  inputSchema: {
855
863
  type: "object",
856
864
  properties: {
@@ -1569,6 +1577,12 @@ Q402_RELAY_BASE_URL=https://q402.quackai.ai/api
1569
1577
  # Q402_ALLOWED_RECIPIENTS=0xabc...,0xdef...
1570
1578
  `;
1571
1579
  var SECURITY_NOTICE = "Q402 never asks you to paste your private key into chat. The MCP server signs payments LOCALLY on your machine \u2014 your key never leaves your device, never goes to a remote server. If a key was already pasted in chat by mistake, treat the wallet as exposed: move funds to a fresh wallet and use that new key in ~/.q402/mcp.env going forward.";
1580
+ var FIRST_INSTALL_ADVISORY = [
1581
+ "Use a FRESH wallet for Q402 \u2014 don't reuse the one with your main funds.",
1582
+ "After your first payment, that wallet will show 'Smart account' in MetaMask / OKX. That's EIP-7702 delegation (Q402's gasless settlement mechanism), reversible anytime with q402_clear_delegation.",
1583
+ "Hardware wallets (Ledger / Trezor) are NOT supported yet \u2014 they don't sign EIP-7702 type-4 authorizations.",
1584
+ "To get a hex private key from MetaMask: \xB7\xB7\xB7 (3-dot menu) \u2192 Account details \u2192 Show private key \u2192 enter password."
1585
+ ];
1572
1586
  function envSource(name) {
1573
1587
  if (process.env[name] !== void 0) return "process";
1574
1588
  if (Q402_ENV_FILE_KEYS.has(name)) return "file";
@@ -1722,11 +1736,18 @@ async function runDoctor() {
1722
1736
  if (!CONFIG.realPaymentsRequested) missing.push("Q402_ENABLE_REAL_PAYMENTS=1");
1723
1737
  const recommendedActions = [];
1724
1738
  if (!envFile.exists) {
1739
+ recommendedActions.push({
1740
+ id: "ensure-q402-dir",
1741
+ type: "shell",
1742
+ shell: 'mkdir -p "$HOME/.q402"',
1743
+ shellWindows: 'powershell -Command "New-Item -ItemType Directory -Force -Path $env:USERPROFILE\\.q402 | Out-Null"',
1744
+ requiresUserConfirm: false,
1745
+ description: "Ensure the ~/.q402 directory exists before writing the secrets file."
1746
+ });
1725
1747
  recommendedActions.push({
1726
1748
  id: "create-env-file",
1727
1749
  type: "write_file",
1728
1750
  path: Q402_ENV_FILE_PATH,
1729
- createParentDirs: true,
1730
1751
  content: ENV_FILE_TEMPLATE,
1731
1752
  requiresUserConfirm: true,
1732
1753
  description: "Create ~/.q402/mcp.env with placeholder values, then open it in the user's editor.",
@@ -1753,8 +1774,9 @@ async function runDoctor() {
1753
1774
  warnings,
1754
1775
  recommendedActions,
1755
1776
  greeting: phase === "first-install" ? `Q402 MCP is installed (v${PACKAGE_VERSION}).` : `Q402 MCP is installed (v${PACKAGE_VERSION}) \u2014 partially configured.`,
1756
- nextStep: phase === "first-install" ? "Offer to create ~/.q402/mcp.env. After yes, run the recommendedActions[].write_file action, then open the file in the user's editor (e.g. via `code` / `open` / `start` / `xdg-open`). Then walk through filling in the API key and private key, one at a time. Do NOT accept key values via chat \u2014 direct the user to edit the file in their editor." : `Tell the user which env vars are still missing (from the 'missing' list) and how to add them to ~/.q402/mcp.env. Restart needed after editing.`,
1757
- securityNotice: SECURITY_NOTICE
1777
+ nextStep: phase === "first-install" ? "Offer to create ~/.q402/mcp.env. After yes, execute recommendedActions in order: first the `ensure-q402-dir` shell action (use bash on macOS/Linux, PowerShell on Windows via the shellWindows variant), then the `create-env-file` write_file action. Then open the file in the user's editor \u2014 `code` works for VS Code / Cursor / Cline (e.g. `code ~/.q402/mcp.env`); `open` on macOS, `start` on Windows, `xdg-open` on Linux as fallback. Walk through filling in the API key (from /event for free Trial or /payment for paid Multichain) and private key one at a time. Do NOT accept key values via chat \u2014 direct the user to edit the file in their editor. BEFORE they paste a private key, surface the `advisories` array: use a fresh wallet (not their main one), heads-up that the wallet will show 'Smart account' in MetaMask after the first payment (that's normal \u2014 EIP-7702 delegation), hardware wallets aren't supported, MetaMask key export path." : `Tell the user which env vars are still missing (from the 'missing' list) and how to add them to ~/.q402/mcp.env. Restart needed after editing. Per-client restart verb: Claude Desktop \u2192 quit + relaunch; Codex \u2192 exit + relaunch; Cursor \u2192 Cmd/Ctrl+Shift+P \u2192 "Developer: Reload Window"; Cline \u2192 reload VS Code window.`,
1778
+ securityNotice: SECURITY_NOTICE,
1779
+ advisories: phase === "first-install" ? FIRST_INSTALL_ADVISORY : void 0
1758
1780
  };
1759
1781
  }
1760
1782
  let walletAddress;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quackai/q402-mcp",
3
- "version": "0.5.7",
3
+ "version": "0.5.9",
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": [