@quackai/q402-mcp 0.5.15 → 0.5.17

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 +14 -15
  2. package/dist/index.js +21 -8
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -91,8 +91,8 @@ Q402_ENABLE_REAL_PAYMENTS=1
91
91
  # Default Q402 deployment. Only change for self-hosted.
92
92
  Q402_RELAY_BASE_URL=https://q402.quackai.ai/api
93
93
 
94
- # Optional safety guards:
95
- # Q402_MAX_AMOUNT_PER_CALL=5
94
+ # Safety guards (max-amount ships uncommented at $200; lower for tighter caps):
95
+ Q402_MAX_AMOUNT_PER_CALL=200
96
96
  # Q402_ALLOWED_RECIPIENTS=0xabc...,0xdef...
97
97
  ```
98
98
 
@@ -165,10 +165,10 @@ Then export the values in `~/.zshrc` / `~/.bashrc`. See the [Codex config refere
165
165
 
166
166
  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.
167
167
 
168
- 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 `1`. Uncomment the lines you need and paste real values in your editor; the live-mode gate only flips once a real key + valid PK are present, so saving the template as-is stays in sandbox. Change the flag to `0` if you want to force sandbox even with real keys (e.g. for chained testing on a paid plan):
168
+ 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` — the three secret lines ship empty (no `#` to remove, just paste the value on the right of `=`) and the live flag defaults to `1`. The live-mode gate only flips once a real key + valid 32-byte PK are populated, so saving the template as-is stays in sandbox automatically. Change the flag to `0` if you want to force sandbox even with real keys (e.g. for chained testing on a paid plan):
169
169
 
170
170
  ```bash
171
- # Two-key model — uncomment ONE (or both for auto-routing).
171
+ # Two-key model — fill ONE (or both for auto-routing).
172
172
  # Auto-routing (same for q402_pay AND q402_batch_pay):
173
173
  # chain="bnb" + Q402_TRIAL_API_KEY set → Trial (free sponsored)
174
174
  # anything else → Multichain (paid 9-chain)
@@ -176,20 +176,19 @@ To enable real on-chain transactions, the resolved API key must be live (`q402_l
176
176
  # status="ambiguous" instead of executing — agent asks user to pick.
177
177
  # Override per call with keyScope: "auto" | "trial" | "multichain".
178
178
 
179
- # Q402_TRIAL_API_KEY=q402_live_... # BNB-only sponsored Trial key (from /event)
180
- # Q402_MULTICHAIN_API_KEY=q402_live_... # paid 9-chain key (per-chain Gas Tank)
179
+ Q402_TRIAL_API_KEY= # BNB-only sponsored Trial key (from /event)
180
+ Q402_MULTICHAIN_API_KEY= # paid 9-chain key (per-chain Gas Tank)
181
181
 
182
- # Q402_PRIVATE_KEY=0x... # signer for the payer EOA (32-byte hex)
182
+ Q402_PRIVATE_KEY= # signer for the payer EOA (0x + 64 hex chars)
183
183
 
184
184
  # Live mode switch:
185
185
  # 0 = sandbox (test mode, no funds move — every q402_pay returns a fake hash)
186
186
  # 1 = real on-chain payments (live mode)
187
- # Default is 1: real payments enabled. Safe because mode only flips
188
- # to live when BOTH a live API key (q402_live_*) AND a valid 32-byte
189
- # private key are set above. Placeholders ("0x...") are rejected by
190
- # the live-mode gate, so partial setups stay in sandbox with a hint.
191
- # Change to 0 to force sandbox even with real keys (e.g. for chained
192
- # testing on a paid plan).
187
+ # Default 1: real payments enabled. Safe because mode only flips to live
188
+ # when BOTH a live API key (q402_live_*) AND a valid 32-byte private
189
+ # key are populated above. Empty values fail the gate, so partial setups
190
+ # stay in sandbox with a hint. Change to 0 to force sandbox even with
191
+ # real keys (e.g. for chained testing on a paid plan).
193
192
  Q402_ENABLE_REAL_PAYMENTS=1
194
193
  ```
195
194
 
@@ -203,7 +202,7 @@ Two additional guards run before every payment regardless of mode:
203
202
 
204
203
  | Env var | Default | Effect |
205
204
  |---|---|---|
206
- | `Q402_MAX_AMOUNT_PER_CALL` | `5` | Reject any single call where `amount > N` USD-equivalent. |
205
+ | `Q402_MAX_AMOUNT_PER_CALL` | `200` | Reject any single call where `amount > N` USD-equivalent. |
207
206
  | `Q402_ALLOWED_RECIPIENTS` | (empty = off) | Comma-separated address allowlist. When set, all other recipients are rejected. |
208
207
 
209
208
  Combined with the `confirm: true` argument the tool requires, this means the model needs (a) explicit user OK in chat, (b) amount ≤ cap, (c) recipient on allowlist if one exists, (d) all three live-mode env vars set, before a single wei moves.
@@ -218,7 +217,7 @@ Combined with the `confirm: true` argument the tool requires, this means the mod
218
217
  | `Q402_MULTICHAIN_API_KEY` | live-pay (9-chain) | Paid 9-chain key. Get one at https://q402.quackai.ai/payment. Auto-routed for non-BNB chains AND for BNB when no Trial key is set. Cap: 20 recipients per batch. |
219
218
  | `Q402_PRIVATE_KEY` | live-pay | Signer for the payer EOA. **Never share. Never paste in chat.** |
220
219
  | `Q402_ENABLE_REAL_PAYMENTS` | live-pay | Set to `1` to opt in. Any other value (or unset) → sandbox. |
221
- | `Q402_MAX_AMOUNT_PER_CALL` | optional | USD-equivalent cap. Defaults to `5`. |
220
+ | `Q402_MAX_AMOUNT_PER_CALL` | optional | USD-equivalent cap. Defaults to `200`. Lower for tighter agent blast-radius. |
222
221
  | `Q402_ALLOWED_RECIPIENTS` | optional | Comma-separated lowercase addresses. Defaults to no allowlist. |
223
222
  | `Q402_RELAY_BASE_URL` | optional | Defaults to `https://q402.quackai.ai/api`. Override for self-hosted Q402. |
224
223
 
package/dist/index.js CHANGED
@@ -96,7 +96,7 @@ var Q402_ENV_FILE_KEYS_ALL = Object.freeze(
96
96
  new Set(Object.keys(FILE_ENV))
97
97
  );
98
98
  var DEFAULT_RELAY_BASE = "https://q402.quackai.ai/api";
99
- var DEFAULT_MAX_AMOUNT = 5;
99
+ var DEFAULT_MAX_AMOUNT = 200;
100
100
  function classifyApiKey(k) {
101
101
  if (!k) return "missing";
102
102
  if (k.startsWith("q402_live_")) return "live";
@@ -121,7 +121,8 @@ function loadConfig() {
121
121
  const apiKeyKind = classifyApiKey(apiKey);
122
122
  const privateKey = ENV.Q402_PRIVATE_KEY ?? null;
123
123
  const realPaymentsRequested = ENV.Q402_ENABLE_REAL_PAYMENTS === "1";
124
- const live = realPaymentsRequested && apiKeyKind === "live" && typeof privateKey === "string" && privateKey.length > 0;
124
+ const anyLiveKey = classifyApiKey(trialApiKey) === "live" || classifyApiKey(multichainApiKey) === "live" || classifyApiKey(legacyApiKey) === "live";
125
+ const live = realPaymentsRequested && anyLiveKey && typeof privateKey === "string" && privateKey.length > 0;
125
126
  return {
126
127
  trialApiKey,
127
128
  multichainApiKey,
@@ -187,7 +188,7 @@ var isValidPrivateKey = (s) => typeof s === "string" && PRIVATE_KEY_RE.test(s);
187
188
 
188
189
  // src/version.ts
189
190
  var PACKAGE_NAME = "@quackai/q402-mcp";
190
- var PACKAGE_VERSION = "0.5.15";
191
+ var PACKAGE_VERSION = "0.5.17";
191
192
 
192
193
  // src/tools/quote.ts
193
194
  import { z } from "zod";
@@ -1043,6 +1044,16 @@ async function runBatchPay(input) {
1043
1044
  }
1044
1045
  }
1045
1046
  const scopeRequest = input.keyScope ?? "auto";
1047
+ if (scopeRequest === "trial" && input.recipients.length > RECIPIENT_LIMIT_TRIAL) {
1048
+ guardsApplied.push("trial_cap_exceeded");
1049
+ return {
1050
+ mode: "none",
1051
+ status: "trial_cap_exceeded",
1052
+ guardsApplied,
1053
+ senderWallet,
1054
+ setupHint: `keyScope="trial" caps at ${RECIPIENT_LIMIT_TRIAL} recipients per call (BNB-only sponsored). Your batch has ${input.recipients.length}. Either trim to the first ${RECIPIENT_LIMIT_TRIAL} recipients and re-invoke with keyScope="trial", or send the full batch on the paid Multichain key by re-invoking with keyScope="multichain" (charges the paid pool + Gas Tank, up to ${RECIPIENT_LIMIT_PAID} per call).`
1055
+ };
1056
+ }
1046
1057
  if (scopeRequest === "auto" && input.chain === "bnb" && CONFIG.trialApiKey && input.recipients.length > RECIPIENT_LIMIT_TRIAL) {
1047
1058
  const overflow = input.recipients.length - RECIPIENT_LIMIT_TRIAL;
1048
1059
  guardsApplied.push("batch_cap_ambiguous");
@@ -1691,11 +1702,12 @@ Q402_RELAY_BASE_URL=https://q402.quackai.ai/api
1691
1702
 
1692
1703
 
1693
1704
  # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\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)
1705
+ # Safety guards
1695
1706
  # \u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501\u2501
1696
- # Max USD per single q402_pay call (default: 5)
1697
- # Q402_MAX_AMOUNT_PER_CALL=5
1698
- #
1707
+ # Max USD per single q402_pay call. Any request above this is rejected
1708
+ # before signing. Lower this if you want a tighter agent blast-radius.
1709
+ Q402_MAX_AMOUNT_PER_CALL=200
1710
+
1699
1711
  # Comma-separated lowercase recipient allowlist (unset = any address OK)
1700
1712
  # Q402_ALLOWED_RECIPIENTS=0xabc...,0xdef...
1701
1713
  `;
@@ -1722,11 +1734,12 @@ function mask2(key) {
1722
1734
  }
1723
1735
  function detectPhase() {
1724
1736
  const anyKey = !!(CONFIG.trialApiKey || CONFIG.multichainApiKey || CONFIG.legacyApiKey);
1737
+ const anyLiveKey = classifyApiKey(CONFIG.trialApiKey) === "live" || classifyApiKey(CONFIG.multichainApiKey) === "live" || classifyApiKey(CONFIG.legacyApiKey) === "live";
1725
1738
  const hasValidPrivateKey = isValidPrivateKey(CONFIG.privateKey);
1726
1739
  if (!Q402_ENV_FILE_PRESENT && !anyKey && !CONFIG.privateKey) {
1727
1740
  return "first-install";
1728
1741
  }
1729
- const allEssentials = anyKey && hasValidPrivateKey && CONFIG.realPaymentsRequested && CONFIG.apiKeyKind === "live";
1742
+ const allEssentials = anyKey && hasValidPrivateKey && CONFIG.realPaymentsRequested && anyLiveKey;
1730
1743
  if (allEssentials) return "live-check";
1731
1744
  if (anyKey || CONFIG.privateKey || Q402_ENV_FILE_PRESENT) return "needs-completion";
1732
1745
  return "first-install";
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quackai/q402-mcp",
3
- "version": "0.5.15",
3
+ "version": "0.5.17",
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": [