@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.
- package/README.md +29 -16
- package/dist/index.js +29 -7
- 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
|
-
|
|
57
|
-
|
|
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
|
-
|
|
61
|
-
|
|
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
|
|
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 —
|
|
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
|
-
|
|
150
|
-
|
|
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`
|
|
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.
|
|
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 {
|
|
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.
|
|
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,
|
|
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.
|
|
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": [
|