@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.
- package/README.md +14 -15
- package/dist/index.js +21 -8
- 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
|
-
#
|
|
95
|
-
|
|
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` —
|
|
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 —
|
|
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
|
-
|
|
180
|
-
|
|
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
|
-
|
|
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
|
|
188
|
-
#
|
|
189
|
-
#
|
|
190
|
-
#
|
|
191
|
-
#
|
|
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` | `
|
|
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 `
|
|
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 =
|
|
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
|
|
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.
|
|
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
|
-
#
|
|
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
|
|
1697
|
-
#
|
|
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 &&
|
|
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.
|
|
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": [
|