@quackai/q402-mcp 0.4.2 → 0.4.4

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 +212 -193
  2. package/dist/index.js +135 -34
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -1,193 +1,212 @@
1
- # @quackai/q402-mcp
2
-
3
- > MCP server for Q402 — gasless USDC, USDT, and RLUSD payments across 8 EVM chains, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.
4
-
5
- [![npm](https://img.shields.io/npm/v/@quackai/q402-mcp.svg)](https://www.npmjs.com/package/@quackai/q402-mcp)
6
- [![license](https://img.shields.io/npm/l/@quackai/q402-mcp.svg)](./LICENSE)
7
-
8
- > **🎟️ Free trial available (2026-05-19 → 2026-06-30)** — 2,000 gasless transactions on BNB Chain (USDC + USDT), 30-day window, no card. One wallet signature: <https://q402.quackai.ai>.
9
- >
10
- > **Trial-scope policy:** API keys minted under the free-trial program (`plan: "trial"`) are restricted to BNB Chain with USDC/USDT — server-side enforcement, returns `403 TRIAL_BNB_ONLY` otherwise. **Paid API keys see the full 8-chain matrix at all times.**
11
-
12
- Claude can now reason about stablecoin payments end to end — quote a transfer across 8 chains, pick the cheapest route, and (optionally) settle the transaction over [Q402](https://q402.quackai.ai)'s EIP-7702 relayer infrastructure. The recipient receives the full amount; the sender pays $0 in gas.
13
-
14
- ---
15
-
16
- ## Quick start
17
-
18
- The server speaks stdio MCP, so any MCP-compatible client can use it. The two paths verified end-to-end today are **Claude (Desktop / Code)** and **OpenAI Codex CLI**.
19
-
20
- ### Claude Desktop / Claude Code
21
-
22
- ```bash
23
- claude mcp add q402 -- npx -y @quackai/q402-mcp
24
- ```
25
-
26
- Or edit `claude_desktop_config.json` directly:
27
-
28
- ```json
29
- {
30
- "mcpServers": {
31
- "q402": {
32
- "command": "npx",
33
- "args": ["-y", "@quackai/q402-mcp"]
34
- }
35
- }
36
- }
37
- ```
38
-
39
- Restart Claude Desktop and ask:
40
-
41
- > *"Compare gas costs to send 50 USDC to vitalik.eth across all 8 Q402 chains."*
42
-
43
- ### OpenAI Codex CLI
44
-
45
- Three install paths — pick the one that matches your workflow.
46
-
47
- **(a) Codex plugin marketplace** (recommended — bundles the MCP config so users don't write TOML):
48
-
49
- ```bash
50
- codex plugin marketplace add bitgett/q402-mcp
51
- codex /plugins # browse and install "q402"
52
- ```
53
-
54
- This repo carries a Codex plugin manifest at [`.codex-plugin/plugin.json`](./.codex-plugin/plugin.json) and a marketplace catalog at [`.agents/plugins/marketplace.json`](./.agents/plugins/marketplace.json), so any signed-in Codex user can register it as a marketplace source and install with one click.
55
-
56
- **(b) Single MCP server via `codex mcp add`** (no plugin wrapper — just register the stdio server):
57
-
58
- ```bash
59
- codex mcp add q402 -- npx -y @quackai/q402-mcp
60
- ```
61
-
62
- **(c) Direct `~/.codex/config.toml` edit** (`.codex/config.toml` for per-project scope):
63
-
64
- ```toml
65
- [mcp_servers.q402]
66
- command = "npx"
67
- args = ["-y", "@quackai/q402-mcp"]
68
- startup_timeout_sec = 20.0
69
- ```
70
-
71
- To enable real on-chain payments, pass the three live-mode env vars **explicitly** under `env` — Codex does **not** forward host env vars by default:
72
-
73
- ```toml
74
- [mcp_servers.q402]
75
- command = "npx"
76
- args = ["-y", "@quackai/q402-mcp"]
77
- startup_timeout_sec = 20.0
78
- env = {
79
- Q402_API_KEY = "q402_live_...",
80
- Q402_PRIVATE_KEY = "0xabc...",
81
- Q402_ENABLE_REAL_PAYMENTS = "1",
82
- Q402_MAX_AMOUNT_PER_CALL = "5",
83
- }
84
- ```
85
-
86
- > If you'd rather not inline secrets in `config.toml`, use the `env_vars` allow-list form to forward specific names from your shell environment instead see the [Codex config reference](https://developers.openai.com/codex/config-reference) for the full schema.
87
-
88
- Then run `codex` and ask the same kind of question. The first call may take a few seconds while `npx` warms its cache; subsequent calls are instant.
89
-
90
- ### Any other MCP client
91
-
92
- The server has no client-specific code. If your client speaks stdio MCP, point it at `npx -y @quackai/q402-mcp` and the five tools listed below will appear.
93
-
94
- ---
95
-
96
- > `Q402_RELAY_BASE_URL` overrides the relay endpoint. Set it explicitly when running against a self-hosted Q402 deployment or a non-canonical environment.
97
-
98
- ---
99
-
100
- ## Tools exposed
101
-
102
- | Tool | Auth | Purpose |
103
- |---|---|---|
104
- | `q402_quote` | none | Compare gas cost and supported tokens across chains. Read-only. |
105
- | `q402_balance` | API key | Verify the API key and report its plan tier + remaining quota credits (live vs sandbox). |
106
- | `q402_pay` | API key + private key + flag | Send a gasless payment to a single recipient. **Sandbox by default** — see [Sandbox vs live mode](#sandbox-vs-live-mode). |
107
- | `q402_batch_pay` | API key + private key + flag | Send a gasless payment to **multiple** recipients in one call on a single chain × token. Trial keys: 5 rows max. Paid keys: 20 rows max. **Supported chains: avax, bnb, eth, mantle, injective, monad** (default EIP-7702 mode). xlayer + stable are NOT batchable — use `q402_pay` in a loop for those. Same sandbox gating as `q402_pay`. **Rate-limit note:** the inner `/api/relay` budget (30/min per key) is consumed per row, so a paid 20-row batch leaves ~10 inner slots for the next minute. |
108
- | `q402_receipt` | none | Look up a Trust Receipt by `rct_…` id and locally verify its ECDSA signature against the relayer EOA. Returns the public settlement record + a `verified` boolean. *receiptId-only today; tx-hash lookup reserved for a future release.* |
109
-
110
- `q402_pay` and `q402_batch_pay` follow a "confirm in chat first" contract: the tool description instructs the model to never call it without explicit user approval of the recipient address(es), amount(s), chain, and token. For batch calls the user must approve the **full batch**, not the individual rows.
111
-
112
- `q402_receipt` is the natural follow-up: after `q402_pay` returns a `receiptUrl`, hand the agent the `rct_…` id and ask *"verify this receipt"* — the tool re-runs the same canonical-JSON + EIP-191 recovery the receipt page does in the browser, so the verification doesn't depend on trusting any UI. Example prompts that work today:
113
-
114
- > *"Pay 0.10 USDT on BNB to vitalik.eth, then verify the receipt."*
115
- > *"Is `rct_afa5f50bc49a65ebba3b28ab` a real Q402 receipt? Verify the signature."*
116
-
117
- > Per-chain gas tank balances and full transaction history live in the [dashboard](https://q402.quackai.ai/dashboard) — those endpoints require a wallet signature, not a bare API key, so the MCP server points the agent there instead of exposing them.
118
-
119
- ---
120
-
121
- ## Sandbox vs live mode
122
-
123
- By default the MCP server operates in **sandbox mode**: `q402_pay` returns a deterministic-looking fake transaction hash, no funds move, no gas-tank credit is consumed. That makes it safe to plug into any MCP client without worrying about an LLM hallucinating a payment.
124
-
125
- To enable real on-chain transactions, **all three** environment variables must be set:
126
-
127
- ```bash
128
- Q402_API_KEY=q402_live_... # live-tier key from /dashboard
129
- Q402_PRIVATE_KEY=0xabc... # signer for the payer EOA
130
- Q402_ENABLE_REAL_PAYMENTS=1 # explicit opt-in
131
- ```
132
-
133
- Anything missing automatic sandbox fallback with a hint pointing at what to set.
134
-
135
- ### Hard caps
136
-
137
- Two additional guards run before every payment regardless of mode:
138
-
139
- | Env var | Default | Effect |
140
- |---|---|---|
141
- | `Q402_MAX_AMOUNT_PER_CALL` | `5` | Reject any single call where `amount > N` USD-equivalent. |
142
- | `Q402_ALLOWED_RECIPIENTS` | (empty = off) | Comma-separated address allowlist. When set, all other recipients are rejected. |
143
-
144
- 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.
145
-
146
- ---
147
-
148
- ## Configuration reference
149
-
150
- | Env var | Required for | Notes |
151
- |---|---|---|
152
- | `Q402_API_KEY` | balance, live-pay | Issue at https://q402.quackai.ai/dashboard. `q402_test_*` keys keep sandbox on. |
153
- | `Q402_PRIVATE_KEY` | live-pay | Signer for the payer EOA. **Never share. Never paste in chat.** |
154
- | `Q402_ENABLE_REAL_PAYMENTS` | live-pay | Set to `1` to opt in. Any other value (or unset) → sandbox. |
155
- | `Q402_MAX_AMOUNT_PER_CALL` | optional | USD-equivalent cap. Defaults to `5`. |
156
- | `Q402_ALLOWED_RECIPIENTS` | optional | Comma-separated lowercase addresses. Defaults to no allowlist. |
157
- | `Q402_RELAY_BASE_URL` | optional | Defaults to `https://q402.quackai.ai/api`. Override for self-hosted Q402. |
158
-
159
- ---
160
-
161
- ## Supported chains
162
-
163
- | Chain | Chain ID | Token(s) | Notes |
164
- |---|---|---|---|
165
- | BNB Chain | 56 | USDC, USDT | |
166
- | Ethereum | 1 | USDC, USDT, **RLUSD** | L1 — gas is volatile, quote is a snapshot. RLUSD (Ripple USD, NY DFS regulated, decimals 18) Ethereum-only. |
167
- | Avalanche C-Chain | 43114 | USDC, USDT | |
168
- | X Layer | 196 | USDC, USDT | |
169
- | Stable | 988 | USDT0 (USDC and USDT both alias) | Gas paid in USDT0. |
170
- | Mantle | 5000 | USDC, USDT0 | LayerZero OFT USDT0 since 2025-11-27. |
171
- | Injective EVM | 1776 | USDT only | Native USDC via Circle CCTP announced for Q2 2026. |
172
- | Monad | 143 | USDC, USDT0 | Native Circle USDC (CCTP V2) + USDT0 (LayerZero OFT). |
173
-
174
- ---
175
-
176
- ## Why this exists
177
-
178
- x402 standardised "402 Payment Required" semantics for AI agents but the official Coinbase facilitator only covers a few chains and assumes ERC-3009 token support — which excludes BNB USDT, Mantle USDT0, Injective USDT, and the chains where most stablecoin volume actually lives.
179
-
180
- Q402 implements the same payer experience (single signature, $0 gas, instant settlement) on all 8 of those chains using EIP-7702 delegated execution, which works with any ERC-20. This MCP server makes that infrastructure addressable from Claude itself.
181
-
182
- If you want to dig into how the wire protocol differs from x402, see [Q402 docs](https://q402.quackai.ai/docs).
183
-
184
- ---
185
-
186
- ## Repository
187
-
188
- Source code: https://github.com/bitgett/q402-mcp
189
- Issues / requests: https://github.com/bitgett/q402-mcp/issues
190
-
191
- ## License
192
-
193
- Apache-2.0 — see [LICENSE](./LICENSE).
1
+ # @quackai/q402-mcp
2
+
3
+ > MCP server for Q402 — gasless USDC, USDT, and RLUSD payments across 8 EVM chains, callable from Claude (Desktop / Code), OpenAI Codex CLI, and any other Model Context Protocol client.
4
+
5
+ [![npm](https://img.shields.io/npm/v/@quackai/q402-mcp.svg)](https://www.npmjs.com/package/@quackai/q402-mcp)
6
+ [![license](https://img.shields.io/npm/l/@quackai/q402-mcp.svg)](./LICENSE)
7
+
8
+ > **🎟️ Free trial available (2026-05-19 → 2026-06-30)** — 2,000 gasless transactions on BNB Chain (USDC + USDT), 30-day window, no card. One wallet signature: <https://q402.quackai.ai>.
9
+ >
10
+ > **Trial-scope policy:** API keys minted under the free-trial program (`plan: "trial"`) are restricted to BNB Chain with USDC/USDT — server-side enforcement, returns `403 TRIAL_BNB_ONLY` otherwise. **Paid API keys see the full 8-chain matrix at all times.**
11
+
12
+ Claude can now reason about stablecoin payments end to end — quote a transfer across 8 chains, pick the cheapest route, and (optionally) settle the transaction over [Q402](https://q402.quackai.ai)'s EIP-7702 relayer infrastructure. The recipient receives the full amount; the sender pays $0 in gas.
13
+
14
+ ---
15
+
16
+ ## Quick start
17
+
18
+ The server speaks stdio MCP, so any MCP-compatible client can use it. The two paths verified end-to-end today are **Claude (Desktop / Code)** and **OpenAI Codex CLI**.
19
+
20
+ ### Claude Desktop / Claude Code
21
+
22
+ ```bash
23
+ claude mcp add q402 -- npx -y @quackai/q402-mcp
24
+ ```
25
+
26
+ Or edit `claude_desktop_config.json` directly:
27
+
28
+ ```json
29
+ {
30
+ "mcpServers": {
31
+ "q402": {
32
+ "command": "npx",
33
+ "args": ["-y", "@quackai/q402-mcp"]
34
+ }
35
+ }
36
+ }
37
+ ```
38
+
39
+ Restart Claude Desktop and ask:
40
+
41
+ > *"Compare gas costs to send 50 USDC to vitalik.eth across all 8 Q402 chains."*
42
+
43
+ ### OpenAI Codex CLI
44
+
45
+ Three install paths — pick the one that matches your workflow.
46
+
47
+ **(a) Codex plugin marketplace** (recommended — bundles the MCP config so users don't write TOML):
48
+
49
+ ```bash
50
+ codex plugin marketplace add bitgett/q402-mcp
51
+ codex /plugins # browse and install "q402"
52
+ ```
53
+
54
+ This repo carries a Codex plugin manifest at [`.codex-plugin/plugin.json`](./.codex-plugin/plugin.json) and a marketplace catalog at [`.agents/plugins/marketplace.json`](./.agents/plugins/marketplace.json), so any signed-in Codex user can register it as a marketplace source and install with one click.
55
+
56
+ **(b) Single MCP server via `codex mcp add`** (no plugin wrapper — just register the stdio server):
57
+
58
+ ```bash
59
+ codex mcp add q402 -- npx -y @quackai/q402-mcp
60
+ ```
61
+
62
+ **(c) Direct `~/.codex/config.toml` edit** (`.codex/config.toml` for per-project scope):
63
+
64
+ ```toml
65
+ [mcp_servers.q402]
66
+ command = "npx"
67
+ args = ["-y", "@quackai/q402-mcp"]
68
+ startup_timeout_sec = 20.0
69
+ ```
70
+
71
+ To enable real on-chain payments, pass the three live-mode env vars **explicitly** under `env` — Codex does **not** forward host env vars by default:
72
+
73
+ ```toml
74
+ [mcp_servers.q402]
75
+ command = "npx"
76
+ args = ["-y", "@quackai/q402-mcp"]
77
+ startup_timeout_sec = 20.0
78
+ env = {
79
+ # Two-key model (v0.4.4+): set whichever applies — both is best.
80
+ # The server auto-routes by chain: BNB → trial key, else multichain key.
81
+ # Both keys use the same q402_live_ prefix — the env var name is what
82
+ # carries the scope, not the key string. Get the values from the
83
+ # dashboard (each key has its own copy button per view).
84
+ Q402_TRIAL_API_KEY = "q402_live_...", # BNB-only sponsored (from /event)
85
+ Q402_MULTICHAIN_API_KEY = "q402_live_...", # paid 8-chain (from /payment)
86
+ # Legacy fallbackused if neither scoped key above is set.
87
+ Q402_API_KEY = "q402_live_...",
88
+ Q402_PRIVATE_KEY = "0xabc...",
89
+ Q402_ENABLE_REAL_PAYMENTS = "1",
90
+ Q402_MAX_AMOUNT_PER_CALL = "5",
91
+ }
92
+ ```
93
+
94
+ > If you'd rather not inline secrets in `config.toml`, use the `env_vars` allow-list form to forward specific names from your shell environment instead — see the [Codex config reference](https://developers.openai.com/codex/config-reference) for the full schema.
95
+
96
+ Then run `codex` and ask the same kind of question. The first call may take a few seconds while `npx` warms its cache; subsequent calls are instant.
97
+
98
+ ### Any other MCP client
99
+
100
+ The server has no client-specific code. If your client speaks stdio MCP, point it at `npx -y @quackai/q402-mcp` and the five tools listed below will appear.
101
+
102
+ ---
103
+
104
+ > `Q402_RELAY_BASE_URL` overrides the relay endpoint. Set it explicitly when running against a self-hosted Q402 deployment or a non-canonical environment.
105
+
106
+ ---
107
+
108
+ ## Tools exposed
109
+
110
+ | Tool | Auth | Purpose |
111
+ |---|---|---|
112
+ | `q402_quote` | none | Compare gas cost and supported tokens across chains. Read-only. |
113
+ | `q402_balance` | API key | Verify the API key and report its plan tier + remaining quota credits (live vs sandbox). |
114
+ | `q402_pay` | API key + private key + flag | Send a gasless payment to a single recipient. **Sandbox by default** — see [Sandbox vs live mode](#sandbox-vs-live-mode). |
115
+ | `q402_batch_pay` | API key + private key + flag | Send a gasless payment to **multiple** recipients in one call on a single chain × token. Trial keys: 5 rows max. Paid keys: 20 rows max. **Supported chains: avax, bnb, eth, mantle, injective, monad** (default EIP-7702 mode). xlayer + stable are NOT batchable — use `q402_pay` in a loop for those. Same sandbox gating as `q402_pay`. **Rate-limit note:** the inner `/api/relay` budget (30/min per key) is consumed per row, so a paid 20-row batch leaves ~10 inner slots for the next minute. |
116
+ | `q402_receipt` | none | Look up a Trust Receipt by `rct_…` id and locally verify its ECDSA signature against the relayer EOA. Returns the public settlement record + a `verified` boolean. *receiptId-only today; tx-hash lookup reserved for a future release.* |
117
+
118
+ `q402_pay` and `q402_batch_pay` follow a "confirm in chat first" contract: the tool description instructs the model to never call it without explicit user approval of the recipient address(es), amount(s), chain, and token. For batch calls the user must approve the **full batch**, not the individual rows.
119
+
120
+ `q402_receipt` is the natural follow-up: after `q402_pay` returns a `receiptUrl`, hand the agent the `rct_…` id and ask *"verify this receipt"* — the tool re-runs the same canonical-JSON + EIP-191 recovery the receipt page does in the browser, so the verification doesn't depend on trusting any UI. Example prompts that work today:
121
+
122
+ > *"Pay 0.10 USDT on BNB to vitalik.eth, then verify the receipt."*
123
+ > *"Is `rct_afa5f50bc49a65ebba3b28ab` a real Q402 receipt? Verify the signature."*
124
+
125
+ > Per-chain gas tank balances and full transaction history live in the [dashboard](https://q402.quackai.ai/dashboard) — those endpoints require a wallet signature, not a bare API key, so the MCP server points the agent there instead of exposing them.
126
+
127
+ ---
128
+
129
+ ## Sandbox vs live mode
130
+
131
+ By default the MCP server operates in **sandbox mode**: `q402_pay` returns a deterministic-looking fake transaction hash, no funds move, no gas-tank credit is consumed. That makes it safe to plug into any MCP client without worrying about an LLM hallucinating a payment.
132
+
133
+ 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`:
134
+
135
+ ```bash
136
+ # Two-key model (v0.4.4+) — set whichever applies. Both is best.
137
+ # Auto-routing: chain="bnb" trial key (if set), otherwise multichain key.
138
+ # Override per call with keyScope: "auto" | "trial" | "multichain".
139
+ Q402_TRIAL_API_KEY=q402_live_... # BNB-only sponsored Trial key (from /event)
140
+ Q402_MULTICHAIN_API_KEY=q402_live_... # paid 8-chain key (per-chain Gas Tank)
141
+
142
+ # Legacy fallback. Used for both scopes when the two above are unset
143
+ # pre-v0.4.4 users keep working without any config change.
144
+ Q402_API_KEY=q402_live_...
145
+
146
+ Q402_PRIVATE_KEY=0xabc... # signer for the payer EOA
147
+ Q402_ENABLE_REAL_PAYMENTS=1 # explicit opt-in
148
+ ```
149
+
150
+ Anything missing for the resolved scope automatic sandbox fallback with a hint pointing at what to set.
151
+
152
+ ### Hard caps
153
+
154
+ Two additional guards run before every payment regardless of mode:
155
+
156
+ | Env var | Default | Effect |
157
+ |---|---|---|
158
+ | `Q402_MAX_AMOUNT_PER_CALL` | `5` | Reject any single call where `amount > N` USD-equivalent. |
159
+ | `Q402_ALLOWED_RECIPIENTS` | (empty = off) | Comma-separated address allowlist. When set, all other recipients are rejected. |
160
+
161
+ 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.
162
+
163
+ ---
164
+
165
+ ## Configuration reference
166
+
167
+ | Env var | Required for | Notes |
168
+ |---|---|---|
169
+ | `Q402_TRIAL_API_KEY` | live-pay (BNB) | BNB-only sponsored Trial key. Free at https://q402.quackai.ai/event. Used automatically for `chain="bnb"` when set. |
170
+ | `Q402_MULTICHAIN_API_KEY` | live-pay (8-chain) | Paid 8-chain key. Get one at https://q402.quackai.ai/payment. Used for all non-BNB chains and for BNB when no Trial key is set. |
171
+ | `Q402_API_KEY` | legacy fallback | Pre-v0.4.4 single-env path. Used for both scopes when the two above are unset. Keep set if you only have one key. |
172
+ | `Q402_PRIVATE_KEY` | live-pay | Signer for the payer EOA. **Never share. Never paste in chat.** |
173
+ | `Q402_ENABLE_REAL_PAYMENTS` | live-pay | Set to `1` to opt in. Any other value (or unset) → sandbox. |
174
+ | `Q402_MAX_AMOUNT_PER_CALL` | optional | USD-equivalent cap. Defaults to `5`. |
175
+ | `Q402_ALLOWED_RECIPIENTS` | optional | Comma-separated lowercase addresses. Defaults to no allowlist. |
176
+ | `Q402_RELAY_BASE_URL` | optional | Defaults to `https://q402.quackai.ai/api`. Override for self-hosted Q402. |
177
+
178
+ ---
179
+
180
+ ## Supported chains
181
+
182
+ | Chain | Chain ID | Token(s) | Notes |
183
+ |---|---|---|---|
184
+ | BNB Chain | 56 | USDC, USDT | |
185
+ | Ethereum | 1 | USDC, USDT, **RLUSD** | L1 — gas is volatile, quote is a snapshot. RLUSD (Ripple USD, NY DFS regulated, decimals 18) Ethereum-only. |
186
+ | Avalanche C-Chain | 43114 | USDC, USDT | |
187
+ | X Layer | 196 | USDC, USDT | |
188
+ | Stable | 988 | USDT0 (USDC and USDT both alias) | Gas paid in USDT0. |
189
+ | Mantle | 5000 | USDC, USDT0 | LayerZero OFT USDT0 since 2025-11-27. |
190
+ | Injective EVM | 1776 | USDT only | Native USDC via Circle CCTP announced for Q2 2026. |
191
+ | Monad | 143 | USDC, USDT0 | Native Circle USDC (CCTP V2) + USDT0 (LayerZero OFT). |
192
+
193
+ ---
194
+
195
+ ## Why this exists
196
+
197
+ x402 standardised "402 Payment Required" semantics for AI agents but the official Coinbase facilitator only covers a few chains and assumes ERC-3009 token support — which excludes BNB USDT, Mantle USDT0, Injective USDT, and the chains where most stablecoin volume actually lives.
198
+
199
+ Q402 implements the same payer experience (single signature, $0 gas, instant settlement) on all 8 of those chains using EIP-7702 delegated execution, which works with any ERC-20. This MCP server makes that infrastructure addressable from Claude itself.
200
+
201
+ If you want to dig into how the wire protocol differs from x402, see [Q402 docs](https://q402.quackai.ai/docs).
202
+
203
+ ---
204
+
205
+ ## Repository
206
+
207
+ Source code: https://github.com/bitgett/q402-mcp
208
+ Issues / requests: https://github.com/bitgett/q402-mcp/issues
209
+
210
+ ## License
211
+
212
+ Apache-2.0 — see [LICENSE](./LICENSE).
package/dist/index.js CHANGED
@@ -29,12 +29,18 @@ function parseMaxAmount(raw) {
29
29
  return n;
30
30
  }
31
31
  function loadConfig() {
32
- const apiKey = process.env.Q402_API_KEY ?? null;
32
+ const trialApiKey = process.env.Q402_TRIAL_API_KEY ?? null;
33
+ const multichainApiKey = process.env.Q402_MULTICHAIN_API_KEY ?? null;
34
+ const legacyApiKey = process.env.Q402_API_KEY ?? null;
35
+ const apiKey = multichainApiKey ?? trialApiKey ?? legacyApiKey;
33
36
  const apiKeyKind = classifyApiKey(apiKey);
34
37
  const privateKey = process.env.Q402_PRIVATE_KEY ?? null;
35
38
  const realPaymentsRequested = process.env.Q402_ENABLE_REAL_PAYMENTS === "1";
36
39
  const live = realPaymentsRequested && apiKeyKind === "live" && typeof privateKey === "string" && privateKey.length > 0;
37
40
  return {
41
+ trialApiKey,
42
+ multichainApiKey,
43
+ legacyApiKey,
38
44
  apiKey,
39
45
  apiKeyKind,
40
46
  privateKey,
@@ -46,6 +52,51 @@ function loadConfig() {
46
52
  };
47
53
  }
48
54
  var CONFIG = loadConfig();
55
+ function resolveApiKey(chain, scope = "auto", intent = "single") {
56
+ const effectiveScope = scope === "auto" ? (
57
+ // Smart routing: batches default to multichain (trial cap=5 would
58
+ // silently fail any 6+ recipient batch). Single payments default to
59
+ // trial on BNB when a trial key is set, so the free sponsored
60
+ // allotment gets used naturally.
61
+ intent === "batch" ? "multichain" : chain === "bnb" && CONFIG.trialApiKey ? "trial" : "multichain"
62
+ ) : scope;
63
+ if (effectiveScope === "trial") {
64
+ if (chain !== "bnb") {
65
+ return {
66
+ apiKey: null,
67
+ scope: "trial",
68
+ fromLegacyFallback: false,
69
+ sandboxReason: `keyScope="trial" requested but chain="${chain}" \u2014 Trial keys support BNB Chain only. Drop keyScope (or set keyScope="multichain") to use the paid Multichain key on ${chain}.`
70
+ };
71
+ }
72
+ const key2 = CONFIG.trialApiKey ?? CONFIG.legacyApiKey;
73
+ if (!key2) {
74
+ return {
75
+ apiKey: null,
76
+ scope: "trial",
77
+ fromLegacyFallback: false,
78
+ sandboxReason: "keyScope='trial' requested but neither Q402_TRIAL_API_KEY nor Q402_API_KEY is set. Get a free Trial key at https://q402.quackai.ai/event."
79
+ };
80
+ }
81
+ return { apiKey: key2, scope: "trial", fromLegacyFallback: !CONFIG.trialApiKey };
82
+ }
83
+ const key = CONFIG.multichainApiKey ?? CONFIG.legacyApiKey;
84
+ if (!key) {
85
+ return {
86
+ apiKey: null,
87
+ scope: "multichain",
88
+ fromLegacyFallback: false,
89
+ sandboxReason: (scope === "multichain" ? "keyScope='multichain' requested but neither Q402_MULTICHAIN_API_KEY" : `chain="${chain}" routes to the Multichain scope but neither Q402_MULTICHAIN_API_KEY`) + " nor Q402_API_KEY is set. Activate a paid plan at https://q402.quackai.ai/payment to get one."
90
+ };
91
+ }
92
+ return { apiKey: key, scope: "multichain", fromLegacyFallback: !CONFIG.multichainApiKey };
93
+ }
94
+ function isLiveModeFor(resolved) {
95
+ if (!resolved.apiKey) return false;
96
+ if (!CONFIG.realPaymentsRequested) return false;
97
+ if (!CONFIG.privateKey) return false;
98
+ return resolved.apiKey.startsWith("q402_live_");
99
+ }
49
100
 
50
101
  // src/tools/quote.ts
51
102
  import { z } from "zod";
@@ -255,7 +306,7 @@ function runQuote(input) {
255
306
  }
256
307
  var QUOTE_TOOL = {
257
308
  name: "q402_quote",
258
- description: "Compare gas costs and supported tokens across the 8 chains Q402 relays for (avax, bnb, eth, xlayer, stable, mantle, injective, monad). Trial-tier API keys see BNB-only quotes (q402_pay enforces the same scope server-side); paid-tier keys see the full matrix including RLUSD on Ethereum and Injective USDT-only. Read-only \u2014 no API key needed, no funds move. Use this before q402_pay so the user sees what's currently routable on their tier.",
309
+ description: "Compare gas costs and supported tokens across the 8 chains Q402 relays for (avax, bnb, eth, xlayer, stable, mantle, injective, monad). Returns the full chain \xD7 token matrix unconditionally \u2014 this tool does not read any API key, so it can't filter by trial vs multichain scope. When the caller intends to settle with a Trial API Key, treat any non-BNB row as informational only (q402_pay will return 403 TRIAL_BNB_ONLY for those). Includes RLUSD on Ethereum and Injective USDT-only. Read-only \u2014 no API key needed, no funds move. Use this before q402_pay so the user can see what's available and pick a chain.",
259
310
  // Plain JSON schema mirroring the Zod schema above; MCP servers receive parameters as JSON.
260
311
  inputSchema: {
261
312
  type: "object",
@@ -628,6 +679,9 @@ var PayInputSchema = z2.object({
628
679
  token: z2.enum(["USDC", "USDT", "RLUSD"]).describe(
629
680
  "Stablecoin symbol. USDC / USDT supported on most chains (Injective is USDT-only). RLUSD (Ripple USD, NY DFS regulated, decimals 18) is Ethereum-only."
630
681
  ),
682
+ keyScope: z2.enum(["auto", "trial", "multichain"]).optional().describe(
683
+ 'Which API key to use. "auto" (default) picks the Trial key for BNB when Q402_TRIAL_API_KEY is set, and the Multichain key otherwise. "trial" forces the BNB-only sponsored key. "multichain" forces the paid 8-chain key.'
684
+ ),
631
685
  confirm: z2.literal(true).describe(
632
686
  "MUST be true. Prove the user explicitly approved this exact recipient and amount in the conversation right before this tool was called. Setting this to true on behalf of the user without confirmation is a violation of the tool contract."
633
687
  )
@@ -666,18 +720,22 @@ async function runPay(input) {
666
720
  if (CONFIG.allowedRecipients.length > 0) {
667
721
  guardsApplied.push(`recipient_allowlist[${CONFIG.allowedRecipients.length}]`);
668
722
  }
669
- if (CONFIG.mode === "sandbox") {
723
+ const scopeRequest = input.keyScope ?? "auto";
724
+ const resolved = resolveApiKey(input.chain, scopeRequest, "single");
725
+ guardsApplied.push(`scope=${resolved.scope}${resolved.fromLegacyFallback ? "(legacy)" : ""}`);
726
+ const live = isLiveModeFor(resolved);
727
+ if (!live) {
670
728
  const result2 = sandboxPay(chain, {
671
729
  to: input.to,
672
730
  amount: input.amount,
673
731
  token: input.token
674
732
  });
675
733
  guardsApplied.push("mode=sandbox");
676
- const setupHint = describeSandboxReason();
734
+ const setupHint = resolved.sandboxReason ?? describeSandboxReason(resolved.apiKey ?? "");
677
735
  return { result: result2, guardsApplied, setupHint };
678
736
  }
679
737
  const client = new Q402NodeClient({
680
- apiKey: CONFIG.apiKey,
738
+ apiKey: resolved.apiKey,
681
739
  privateKey: CONFIG.privateKey,
682
740
  chain,
683
741
  relayBaseUrl: CONFIG.relayBaseUrl
@@ -690,9 +748,9 @@ async function runPay(input) {
690
748
  guardsApplied.push("mode=live");
691
749
  return { result, guardsApplied };
692
750
  }
693
- function describeSandboxReason() {
751
+ function describeSandboxReason(resolvedKey) {
694
752
  const missing = [];
695
- if (CONFIG.apiKeyKind !== "live") missing.push("Q402_API_KEY (must start with q402_live_)");
753
+ if (!resolvedKey.startsWith("q402_live_")) missing.push("a live API key (must start with q402_live_)");
696
754
  if (!CONFIG.privateKey) missing.push("Q402_PRIVATE_KEY");
697
755
  if (!CONFIG.realPaymentsRequested) missing.push("Q402_ENABLE_REAL_PAYMENTS=1");
698
756
  if (missing.length === 0) return "Sandbox mode active (no env state change needed).";
@@ -700,7 +758,7 @@ function describeSandboxReason() {
700
758
  }
701
759
  var PAY_TOOL = {
702
760
  name: "q402_pay",
703
- description: `Send a gasless USDC, USDT, or RLUSD payment via Q402. Scope depends on the API key tier: trial keys (q402_live_* with plan='trial') are restricted to chain: "bnb" + token "USDC" or "USDT" (server returns TRIAL_BNB_ONLY for anything else). Paid keys can relay across the full 8-chain matrix \u2014 avax, bnb, eth, xlayer, stable, mantle, injective, monad \u2014 with USDC/USDT supported on most chains, RLUSD on Ethereum only, and Injective USDT-only. SANDBOX BY DEFAULT \u2014 no funds move unless Q402_API_KEY (live tier), Q402_PRIVATE_KEY, and Q402_ENABLE_REAL_PAYMENTS=1 are all set. The recipient receives the full amount; the sender pays $0 in gas. ALWAYS get explicit user confirmation of the exact recipient address, amount, chain, and token in conversation immediately before calling this tool.`,
761
+ description: "Send a gasless USDC, USDT, or RLUSD payment via Q402. Uses Q402_TRIAL_API_KEY (BNB-only sponsored Trial) when chain='bnb' and the Trial env is set, and Q402_MULTICHAIN_API_KEY (paid 8-chain) otherwise. 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 \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. ALWAYS get explicit user confirmation of the exact recipient address, amount, chain, and token in conversation immediately before calling this tool.",
704
762
  inputSchema: {
705
763
  type: "object",
706
764
  properties: {
@@ -722,6 +780,11 @@ var PAY_TOOL = {
722
780
  enum: ["USDC", "USDT", "RLUSD"],
723
781
  description: "Stablecoin to send. USDC / USDT supported on most chains; Injective is USDT-only. RLUSD (Ripple USD, NY DFS regulated, decimals 18) is Ethereum-only."
724
782
  },
783
+ keyScope: {
784
+ type: "string",
785
+ enum: ["auto", "trial", "multichain"],
786
+ description: 'Which API key to use. "auto" (default) picks Trial for BNB when Q402_TRIAL_API_KEY is set, Multichain otherwise. "trial" forces the BNB-only sponsored key. "multichain" forces the paid 8-chain key.'
787
+ },
725
788
  confirm: {
726
789
  type: "boolean",
727
790
  const: true,
@@ -752,6 +815,9 @@ var BatchPayInputSchema = z3.object({
752
815
  ).min(1, "recipients must contain at least one row").max(CLIENT_RECIPIENT_CAP, `recipients cannot exceed ${CLIENT_RECIPIENT_CAP} (server enforces tighter cap by key scope)`).describe(
753
816
  `Array of {to, amount} pairs. All recipients share the same chain and token. Trial keys: max ${RECIPIENT_LIMIT_TRIAL} rows. Paid keys: max ${RECIPIENT_LIMIT_PAID} rows.`
754
817
  ),
818
+ keyScope: z3.enum(["auto", "trial", "multichain"]).optional().describe(
819
+ 'Which API key to use. "auto" (default) picks Trial for BNB when Q402_TRIAL_API_KEY is set, Multichain otherwise. Trial forces the BNB-only sponsored key; "multichain" forces the paid 8-chain key.'
820
+ ),
755
821
  confirm: z3.literal(true).describe(
756
822
  "MUST be true. The user must have explicitly approved this exact set of recipients, amounts, chain, and token in the conversation right before this tool was called. Setting confirm=true on behalf of the user without that approval is a violation of the tool contract."
757
823
  )
@@ -796,21 +862,26 @@ async function runBatchPay(input) {
796
862
  if (CONFIG.allowedRecipients.length > 0) {
797
863
  guardsApplied.push(`recipient_allowlist[${CONFIG.allowedRecipients.length}]`);
798
864
  }
799
- if (CONFIG.mode === "sandbox") {
865
+ const scopeRequest = input.keyScope ?? "auto";
866
+ const resolved = resolveApiKey(input.chain, scopeRequest, "batch");
867
+ guardsApplied.push(`scope=${resolved.scope}${resolved.fromLegacyFallback ? "(legacy)" : ""}`);
868
+ const live = isLiveModeFor(resolved);
869
+ if (!live) {
800
870
  const sandboxResults = input.recipients.map(
801
871
  (r) => sandboxPay(chain, { to: r.to, amount: r.amount, token: input.token })
802
872
  );
803
873
  guardsApplied.push("mode=sandbox");
874
+ const reason = resolved.sandboxReason ?? describeSandboxReason2(resolved.apiKey ?? "");
804
875
  return {
805
876
  mode: "sandbox",
806
877
  status: "sandbox",
807
- result: { sandbox: sandboxResults, reason: describeSandboxReason2() },
878
+ result: { sandbox: sandboxResults, reason },
808
879
  guardsApplied,
809
- setupHint: describeSandboxReason2()
880
+ setupHint: reason
810
881
  };
811
882
  }
812
883
  const client = new Q402NodeClient({
813
- apiKey: CONFIG.apiKey,
884
+ apiKey: resolved.apiKey,
814
885
  privateKey: CONFIG.privateKey,
815
886
  chain,
816
887
  relayBaseUrl: CONFIG.relayBaseUrl
@@ -849,9 +920,9 @@ async function runBatchPay(input) {
849
920
  throw err;
850
921
  }
851
922
  }
852
- function describeSandboxReason2() {
923
+ function describeSandboxReason2(resolvedKey) {
853
924
  const missing = [];
854
- if (CONFIG.apiKeyKind !== "live") missing.push("Q402_API_KEY (must start with q402_live_)");
925
+ if (!resolvedKey.startsWith("q402_live_")) missing.push("a live API key (must start with q402_live_)");
855
926
  if (!CONFIG.privateKey) missing.push("Q402_PRIVATE_KEY");
856
927
  if (!CONFIG.realPaymentsRequested) missing.push("Q402_ENABLE_REAL_PAYMENTS=1");
857
928
  if (missing.length === 0) return "Sandbox mode active (no env state change needed).";
@@ -859,7 +930,7 @@ function describeSandboxReason2() {
859
930
  }
860
931
  var BATCH_PAY_TOOL = {
861
932
  name: "q402_batch_pay",
862
- description: `Send gasless payments to MULTIPLE recipients on a single chain \xD7 token in one call. Trial keys (q402_live_* with plan='trial'): max ${RECIPIENT_LIMIT_TRIAL} recipients per call, BNB Chain + USDC/USDT only. Paid keys: max ${RECIPIENT_LIMIT_PAID} recipients per call across 6 EIP-7702 default chains (avax, bnb, eth, mantle, injective, monad). xlayer + stable are NOT batchable \u2014 use q402_pay in a loop. SANDBOX BY DEFAULT \u2014 real on-chain TX only when Q402_API_KEY (live), Q402_PRIVATE_KEY, and Q402_ENABLE_REAL_PAYMENTS=1 are all set. Every recipient receives the full amount; the sender pays $0 in gas for the entire batch. 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.`,
933
+ description: `Send gasless payments to MULTIPLE recipients on a single chain \xD7 token in one call. Uses Q402_TRIAL_API_KEY for chain='bnb' when the Trial env is set, Q402_MULTICHAIN_API_KEY otherwise. Set keyScope='trial' or 'multichain' to force one. Trial keys: max ${RECIPIENT_LIMIT_TRIAL} recipients per call, BNB Chain + USDC/USDT only. Multichain keys: max ${RECIPIENT_LIMIT_PAID} recipients per call across 6 EIP-7702 default chains (avax, bnb, eth, mantle, injective, monad). xlayer + stable are NOT batchable \u2014 use q402_pay in a loop. 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. 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.`,
863
934
  inputSchema: {
864
935
  type: "object",
865
936
  properties: {
@@ -897,6 +968,11 @@ var BATCH_PAY_TOOL = {
897
968
  additionalProperties: false
898
969
  }
899
970
  },
971
+ keyScope: {
972
+ type: "string",
973
+ enum: ["auto", "trial", "multichain"],
974
+ description: 'Which API key to use. "auto" (default) picks Trial for BNB when Q402_TRIAL_API_KEY is set, Multichain otherwise.'
975
+ },
900
976
  confirm: {
901
977
  type: "boolean",
902
978
  const: true,
@@ -915,39 +991,64 @@ function mask(key) {
915
991
  if (!key || key.length < 12) return null;
916
992
  return `${key.slice(0, 12)}\u2026${key.slice(-4)}`;
917
993
  }
918
- async function runBalance() {
919
- if (CONFIG.apiKeyKind === "missing") {
920
- return {
921
- apiKeyKind: "missing",
922
- apiKeyMasked: null,
923
- dashboardUrl: "https://q402.quackai.ai/dashboard",
924
- setupHint: "Set Q402_API_KEY to a key issued at https://q402.quackai.ai/dashboard. Test-tier keys (q402_test_*) work too \u2014 they show sandbox quota."
925
- };
926
- }
994
+ async function verifyOne(apiKey) {
927
995
  const resp = await fetch(`${CONFIG.relayBaseUrl}/keys/verify`, {
928
996
  method: "POST",
929
997
  headers: { "Content-Type": "application/json" },
930
- body: JSON.stringify({ apiKey: CONFIG.apiKey })
998
+ body: JSON.stringify({ apiKey })
931
999
  });
932
- const verifyJson = resp.ok ? await resp.json() : { error: `HTTP ${resp.status}` };
1000
+ return resp.ok ? await resp.json() : { error: `HTTP ${resp.status}` };
1001
+ }
1002
+ function extractTrial(verifyJson) {
933
1003
  const v = verifyJson;
934
- const trialMeta = v && v.isTrial && typeof v.trialExpiresAt === "string" ? {
1004
+ if (!v || !v.isTrial || typeof v.trialExpiresAt !== "string") return void 0;
1005
+ return {
935
1006
  daysLeft: typeof v.trialDaysLeft === "number" ? v.trialDaysLeft : 0,
936
1007
  expiresAt: v.trialExpiresAt,
937
1008
  creditsRemaining: typeof v.remainingCredits === "number" ? v.remainingCredits : 0,
938
1009
  signupUrl: "https://q402.quackai.ai"
939
- } : void 0;
1010
+ };
1011
+ }
1012
+ async function runBalance() {
1013
+ const targets = [];
1014
+ if (CONFIG.trialApiKey) targets.push({ scope: "trial", key: CONFIG.trialApiKey });
1015
+ if (CONFIG.multichainApiKey) targets.push({ scope: "multichain", key: CONFIG.multichainApiKey });
1016
+ if (targets.length === 0 && CONFIG.legacyApiKey) {
1017
+ targets.push({ scope: "legacy", key: CONFIG.legacyApiKey });
1018
+ }
1019
+ if (targets.length === 0) {
1020
+ return {
1021
+ apiKeyKind: "missing",
1022
+ apiKeyMasked: null,
1023
+ scopes: [],
1024
+ dashboardUrl: "https://q402.quackai.ai/dashboard",
1025
+ setupHint: "Set Q402_TRIAL_API_KEY (BNB-only sponsored, free at /event) and/or Q402_MULTICHAIN_API_KEY (paid 8-chain from /dashboard). Single-env legacy: Q402_API_KEY also works."
1026
+ };
1027
+ }
1028
+ const scopes = await Promise.all(
1029
+ targets.map(async ({ scope, key }) => {
1030
+ const verify = await verifyOne(key);
1031
+ return {
1032
+ scope,
1033
+ apiKeyMasked: mask(key) ?? key,
1034
+ verify,
1035
+ trial: extractTrial(verify)
1036
+ };
1037
+ })
1038
+ );
1039
+ const primary = scopes.find((s) => s.scope === "multichain") ?? scopes.find((s) => s.scope === "trial") ?? scopes[0];
940
1040
  return {
941
1041
  apiKeyKind: CONFIG.apiKeyKind,
942
- apiKeyMasked: mask(CONFIG.apiKey),
943
- verify: verifyJson,
944
- dashboardUrl: "https://q402.quackai.ai/dashboard",
945
- ...trialMeta ? { trial: trialMeta } : {}
1042
+ apiKeyMasked: primary.apiKeyMasked,
1043
+ verify: primary.verify,
1044
+ trial: primary.trial,
1045
+ scopes,
1046
+ dashboardUrl: "https://q402.quackai.ai/dashboard"
946
1047
  };
947
1048
  }
948
1049
  var BALANCE_TOOL = {
949
1050
  name: "q402_balance",
950
- description: "Verify the configured API key and report its plan tier (live vs sandbox vs trial). Read-only. When the key is on the free trial, returns the days-left and credits-remaining summary so the agent can surface it. Free trial available at https://q402.quackai.ai \u2014 2,000 gasless TX over 30 days, one wallet signature. For per-chain gas tank balances, point the user at https://q402.quackai.ai/dashboard \u2014 those need a wallet signature, not a bare key.",
1051
+ description: "Verify the configured API key(s) and report each one's plan tier (live vs sandbox vs trial). Read-only. When both Q402_TRIAL_API_KEY and Q402_MULTICHAIN_API_KEY are set, returns BOTH summaries so the agent can show the user trial credits AND paid credits in one view. For trial-scoped keys, returns days-left + credits-remaining for the trial allotment. Free trial available at https://q402.quackai.ai/event \u2014 2,000 gasless TX over 30 days. For per-chain gas tank balances, point the user at https://q402.quackai.ai/dashboard \u2014 those need a wallet signature, not a bare key.",
951
1052
  inputSchema: {
952
1053
  type: "object",
953
1054
  properties: {},
@@ -1120,7 +1221,7 @@ var RECEIPT_TOOL = {
1120
1221
 
1121
1222
  // src/index.ts
1122
1223
  var PACKAGE_NAME = "@quackai/q402-mcp";
1123
- var PACKAGE_VERSION = "0.4.2";
1224
+ var PACKAGE_VERSION = "0.4.4";
1124
1225
  function jsonText(value) {
1125
1226
  return { type: "text", text: JSON.stringify(value, null, 2) };
1126
1227
  }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@quackai/q402-mcp",
3
- "version": "0.4.2",
3
+ "version": "0.4.4",
4
4
  "description": "MCP server for Q402 — gasless USDC, USDT, and RLUSD payments across 8 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": [