@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.
- package/README.md +212 -193
- package/dist/index.js +135 -34
- 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
|
-
[](https://www.npmjs.com/package/@quackai/q402-mcp)
|
|
6
|
-
[](./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
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
|
168
|
-
|
|
169
|
-
|
|
|
170
|
-
|
|
|
171
|
-
|
|
|
172
|
-
|
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
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
|
+
[](https://www.npmjs.com/package/@quackai/q402-mcp)
|
|
6
|
+
[](./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 fallback — used 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
|
|
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).
|
|
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
|
-
|
|
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:
|
|
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 (
|
|
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:
|
|
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
|
-
|
|
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
|
|
878
|
+
result: { sandbox: sandboxResults, reason },
|
|
808
879
|
guardsApplied,
|
|
809
|
-
setupHint:
|
|
880
|
+
setupHint: reason
|
|
810
881
|
};
|
|
811
882
|
}
|
|
812
883
|
const client = new Q402NodeClient({
|
|
813
|
-
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 (
|
|
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
|
|
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
|
|
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
|
|
998
|
+
body: JSON.stringify({ apiKey })
|
|
931
999
|
});
|
|
932
|
-
|
|
1000
|
+
return resp.ok ? await resp.json() : { error: `HTTP ${resp.status}` };
|
|
1001
|
+
}
|
|
1002
|
+
function extractTrial(verifyJson) {
|
|
933
1003
|
const v = verifyJson;
|
|
934
|
-
|
|
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
|
-
}
|
|
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:
|
|
943
|
-
verify:
|
|
944
|
-
|
|
945
|
-
|
|
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
|
|
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.
|
|
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.
|
|
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": [
|