@quackai/q402-mcp 0.5.13 → 0.5.15

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 +25 -9
  2. package/dist/index.js +47 -27
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -25,13 +25,28 @@ Two steps:
25
25
  | Client | Command / config |
26
26
  |---|---|
27
27
  | **Claude Desktop / Claude Code** | `claude mcp add q402 -- npx -y @quackai/q402-mcp` |
28
- | **OpenAI Codex CLI** | `codex mcp add q402 -- npx -y @quackai/q402-mcp` |
28
+ | **OpenAI Codex CLI** | `codex mcp add q402 -- npx -y @quackai/q402-mcp` (Windows fallback: see below) |
29
29
  | **Cursor** | Add to `~/.cursor/mcp.json`: `{ "mcpServers": { "q402": { "command": "npx", "args": ["-y", "@quackai/q402-mcp"] } } }` |
30
30
  | **Cline** | Cline → Settings → MCP Servers → Edit JSON. Same shape as Cursor. |
31
31
  | **Any other stdio MCP client** | Point it at `npx -y @quackai/q402-mcp`. No client-specific code. |
32
32
 
33
33
  That's it — secrets are NOT configured here. The MCP server reads them from `~/.q402/mcp.env` at startup (same pattern as AWS CLI / Stripe CLI / gh CLI), so every client uses the same file with no per-client wiring.
34
34
 
35
+ <details>
36
+ <summary>Windows: <code>codex mcp add</code> returns "Access is denied"</summary>
37
+
38
+ The bundled `codex.exe` on some Windows setups refuses to write its own config from the `mcp add` subcommand. Add the equivalent stanza to `~/.codex/config.toml` by hand:
39
+
40
+ ```toml
41
+ [mcp_servers.q402]
42
+ command = "npx"
43
+ args = ["-y", "@quackai/q402-mcp"]
44
+ ```
45
+
46
+ Then restart Codex. Same effect as `codex mcp add q402 -- npx -y @quackai/q402-mcp`.
47
+
48
+ </details>
49
+
35
50
  ### 2. First-time setup
36
51
 
37
52
  Restart your client, then ask your agent:
@@ -49,27 +64,28 @@ The agent calls `q402_doctor`. On first install, the tool tells the agent to:
49
64
 
50
65
  ### Manual setup (no AI)
51
66
 
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 `1`. Uncomment the lines you need and paste real values; the server only flips into live mode once both a `q402_live_*` API key AND a valid 32-byte private key are configured, so saving the template as-is is safe (placeholders stay in sandbox). Change the flag to `0` if you want to force sandbox even with real keys (e.g. for chained testing).
67
+ Create `~/.q402/mcp.env` yourself. The template below matches what `q402_doctor` writes — the three secret lines (`Q402_TRIAL_API_KEY`, `Q402_MULTICHAIN_API_KEY`, `Q402_PRIVATE_KEY`) ship empty, with `Q402_ENABLE_REAL_PAYMENTS=1`. Paste real values on the right of `=` for the key(s) you have and your wallet key. The server only flips into live mode once both a `q402_live_*` API key AND a valid 32-byte private key are present, so saving the template as-is is safe (empty values fail the gate and stay in sandbox). Change the flag to `0` if you want to force sandbox even with real keys (e.g. for chained testing).
53
68
 
54
69
  ```bash
55
70
  # ~/.q402/mcp.env
56
71
 
57
72
  # Free Trial — BNB only, 2,000 sponsored TX (from /event)
58
- # Q402_TRIAL_API_KEY=q402_live_...
73
+ Q402_TRIAL_API_KEY=
59
74
 
60
75
  # Paid Multichain — all 9 chains (from /payment)
61
- # Q402_MULTICHAIN_API_KEY=q402_live_...
76
+ Q402_MULTICHAIN_API_KEY=
62
77
 
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...
78
+ # Hex EVM private key (0x + 64 hex). A separate MetaMask account
79
+ # dedicated to Q402 keeps your existing balances and history tidy.
80
+ # Hardware wallets (Ledger / Trezor) are not supported yet — Q402
81
+ # needs a raw hex key it can sign EIP-7702 type-4 authorizations with.
82
+ Q402_PRIVATE_KEY=
67
83
 
68
84
  # Live mode switch:
69
85
  # 0 = sandbox (test mode, no funds move)
70
86
  # 1 = real on-chain payments
71
87
  # Default 1 — safe because mode only flips to live when BOTH a live
72
- # API key AND a valid 32-byte private key are uncommented above.
88
+ # API key AND a valid 32-byte private key are populated above.
73
89
  Q402_ENABLE_REAL_PAYMENTS=1
74
90
 
75
91
  # Default Q402 deployment. Only change for self-hosted.
package/dist/index.js CHANGED
@@ -72,6 +72,7 @@ function loadQ402EnvFileFromPath(path) {
72
72
  const hashIdx = rawVal.search(/\s#/);
73
73
  if (hashIdx >= 0) rawVal = rawVal.slice(0, hashIdx).trimEnd();
74
74
  }
75
+ if (rawVal === "") continue;
75
76
  out[k] = rawVal;
76
77
  }
77
78
  return out;
@@ -186,7 +187,7 @@ var isValidPrivateKey = (s) => typeof s === "string" && PRIVATE_KEY_RE.test(s);
186
187
 
187
188
  // src/version.ts
188
189
  var PACKAGE_NAME = "@quackai/q402-mcp";
189
- var PACKAGE_VERSION = "0.5.13";
190
+ var PACKAGE_VERSION = "0.5.15";
190
191
 
191
192
  // src/tools/quote.ts
192
193
  import { z } from "zod";
@@ -926,7 +927,7 @@ function describeSandboxReason(resolvedKey, scope) {
926
927
  }
927
928
  var PAY_TOOL = {
928
929
  name: "q402_pay",
929
- 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\nSENDER ECHO \u2014 every response includes a `senderWallet` field with the address derived from the configured `Q402_PRIVATE_KEY`. Show this alongside the recipient/amount when you confirm the payment with the user (e.g. 'Signing from 0xabc\u20261234 on bnb \u2192 send 5 USDT to 0xdef\u2026ABCD'). Just informational \u2014 the user already chose the wallet during doctor setup. \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.",
930
+ description: "USE THIS TOOL whenever the user asks to send, transfer, or pay USDC / USDT / RLUSD \u2014 even when they don't mention Q402 by name (\"send 5 USDT to 0x... on bnb\" should route here automatically). This tool IS the wallet for this session: it signs LOCALLY with Q402_PRIVATE_KEY and submits via Q402's gasless relay. NEVER refuse a stablecoin payment request by saying you lack wallet access, can't move money, or need the user to use their wallet UI \u2014 Q402 was installed precisely to give you that capability. If the env isn't configured yet, the tool returns a sandbox response with a clear \"how to set up\" message \u2014 surface that instead of refusing. \n\nAuto-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\nSENDER ECHO \u2014 when a valid `Q402_PRIVATE_KEY` is configured, the response includes a `senderWallet` field with the address derived from that key. Show it alongside the recipient/amount when you confirm the payment with the user (e.g. 'Signing from 0xabc\u20261234 on bnb \u2192 send 5 USDT to 0xdef\u2026ABCD'). Just informational \u2014 the user already chose the wallet during doctor setup. Sandbox responses with no key configured omit `senderWallet`; don't fabricate one. \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.",
930
931
  inputSchema: {
931
932
  type: "object",
932
933
  properties: {
@@ -1139,7 +1140,9 @@ function describeSandboxReason2(resolvedKey, scope) {
1139
1140
  }
1140
1141
  var BATCH_PAY_TOOL = {
1141
1142
  name: "q402_batch_pay",
1142
- 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.`,
1143
+ description: `USE THIS TOOL whenever the user asks to send the same token to multiple recipients on a single chain in one call ("pay 0x1 / 0x2 / 0x3 each 1 USDC on bnb", "airdrop USDT to these wallets", payroll-shaped requests, etc.) \u2014 even without an explicit "via Q402" qualifier. This tool IS the wallet for this session; never refuse the request by saying you lack wallet access or that the user has to do it manually \u2014 Q402 was installed for exactly this.
1144
+
1145
+ 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.`,
1143
1146
  inputSchema: {
1144
1147
  type: "object",
1145
1148
  properties: {
@@ -1635,48 +1638,61 @@ var CLEAR_DELEGATION_TOOL = {
1635
1638
  import { z as z8 } from "zod";
1636
1639
  import { Wallet as Wallet6 } from "ethers";
1637
1640
  var DoctorInputSchema = z8.object({});
1638
- var ENV_FILE_TEMPLATE = `# \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1641
+ var ENV_FILE_TEMPLATE = `# \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1639
1642
  # Q402 MCP \u2014 secrets
1643
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1640
1644
  # Read automatically by @quackai/q402-mcp on startup.
1641
1645
  # Edit this file in your editor. NEVER paste your private key into chat.
1642
1646
  # After editing, restart your MCP client (Codex / Claude / Cursor / Cline).
1643
1647
  #
1644
- # SAFE-BY-DEFAULT: this template ships with both api-key + private-key
1645
- # lines COMMENTED OUT. Even though Q402_ENABLE_REAL_PAYMENTS defaults
1646
- # to 1, the live-mode gate refuses to settle until BOTH a real api key
1647
- # AND a valid 32-byte private key are configured. Saving this template
1648
- # as-is and restarting your client just leaves you in sandbox.
1649
- # \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1648
+ # SAFE-BY-DEFAULT: the api-key + private-key lines below ship EMPTY.
1649
+ # Q402_ENABLE_REAL_PAYMENTS defaults to 1, but the live-mode gate also
1650
+ # requires (a) a real \`q402_live_*\` API key and (b) a valid 32-byte
1651
+ # hex private key. Empty values fail both checks, so saving this file
1652
+ # as-is just leaves you in sandbox. Paste real values to go live.
1653
+
1650
1654
 
1651
- # \u2500\u2500\u2500 API key \u2014 uncomment ONE (or both for auto-routing) \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1655
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1656
+ # API KEY \u2014 paste on the right of \`=\` (one OR both for auto-routing)
1657
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1652
1658
  # Free Trial: BNB Chain only, 2,000 sponsored TX
1653
1659
  # Get one at: https://q402.quackai.ai/event
1654
- # Q402_TRIAL_API_KEY=q402_live_...
1660
+ Q402_TRIAL_API_KEY=
1655
1661
 
1656
1662
  # Paid Multichain: all 9 chains, per-chain Gas Tank
1657
1663
  # Get one at: https://q402.quackai.ai/payment
1658
- # Q402_MULTICHAIN_API_KEY=q402_live_...
1664
+ Q402_MULTICHAIN_API_KEY=
1659
1665
 
1660
- # \u2500\u2500\u2500 Your wallet \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1666
+
1667
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1668
+ # WALLET \u2014 paste your private key on the right of \`=\`
1669
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1661
1670
  # Hex EVM private key (0x + 64 hex chars). Signs payments LOCALLY on
1662
1671
  # your machine \u2014 never leaves your device, never sent to any server.
1663
- # Q402_PRIVATE_KEY=0x...
1672
+ Q402_PRIVATE_KEY=
1673
+
1664
1674
 
1665
- # \u2500\u2500\u2500 Live mode switch \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1666
- # 0 = sandbox (test mode, no funds move \u2014 every q402_pay returns a fake hash)
1667
- # 1 = real on-chain payments (live mode)
1668
- # Default is 1: real payments enabled. This is safe because mode only
1669
- # flips to live when BOTH a live API key (q402_live_*) AND a valid
1670
- # 32-byte private key are set above. Until you uncomment + paste both,
1671
- # you stay in sandbox. Change to 0 to force sandbox even with real
1672
- # keys (e.g. for chained testing on a paid plan).
1675
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1676
+ # Live mode switch (safe even at 1 \u2014 see SAFE-BY-DEFAULT note at top)
1677
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1678
+ # 0 = sandbox (test mode, no funds move \u2014 every q402_pay returns a fake hash)
1679
+ # 1 = real on-chain payments
1680
+ # Default 1; flips to live only when the API key + private key above are
1681
+ # both populated. Set to 0 to force sandbox even with real keys in place
1682
+ # (e.g. for chained testing on a paid plan).
1673
1683
  Q402_ENABLE_REAL_PAYMENTS=1
1674
1684
 
1675
- # \u2500\u2500\u2500 Q402 relay endpoint \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1685
+
1686
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1687
+ # Q402 relay endpoint
1688
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1676
1689
  # Default canonical Q402 deployment. Only change for self-hosted.
1677
1690
  Q402_RELAY_BASE_URL=https://q402.quackai.ai/api
1678
1691
 
1679
- # \u2500\u2500\u2500 Optional safety guards \u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500\u2500
1692
+
1693
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1694
+ # Optional safety guards (uncomment + edit to enable)
1695
+ # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1680
1696
  # Max USD per single q402_pay call (default: 5)
1681
1697
  # Q402_MAX_AMOUNT_PER_CALL=5
1682
1698
  #
@@ -1706,9 +1722,13 @@ function mask2(key) {
1706
1722
  }
1707
1723
  function detectPhase() {
1708
1724
  const anyKey = !!(CONFIG.trialApiKey || CONFIG.multichainApiKey || CONFIG.legacyApiKey);
1709
- const allEssentials = anyKey && !!CONFIG.privateKey && CONFIG.realPaymentsRequested && CONFIG.apiKeyKind === "live";
1725
+ const hasValidPrivateKey = isValidPrivateKey(CONFIG.privateKey);
1726
+ if (!Q402_ENV_FILE_PRESENT && !anyKey && !CONFIG.privateKey) {
1727
+ return "first-install";
1728
+ }
1729
+ const allEssentials = anyKey && hasValidPrivateKey && CONFIG.realPaymentsRequested && CONFIG.apiKeyKind === "live";
1710
1730
  if (allEssentials) return "live-check";
1711
- if (anyKey || CONFIG.privateKey || CONFIG.realPaymentsRequested) return "needs-completion";
1731
+ if (anyKey || CONFIG.privateKey || Q402_ENV_FILE_PRESENT) return "needs-completion";
1712
1732
  return "first-install";
1713
1733
  }
1714
1734
  async function verifyOneKey(scope, envVar, apiKey) {
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quackai/q402-mcp",
3
- "version": "0.5.13",
3
+ "version": "0.5.15",
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": [