@blackpool/cli 0.2.0 → 0.3.0

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/AGENTS.md CHANGED
@@ -1,130 +1,472 @@
1
1
  # AGENTS.md — Blackpool for agents
2
2
 
3
- This file tells an AI agent (or a script) how to make private payments with Blackpool through the CLI: install it, give it a wallet, read its JSON output, and handle failures safely. Humans can read it too.
3
+ Private payments for AI agents and scripts. This file is the complete guide to using Blackpool through its CLI: setup, every command, the JSON each one returns, chains and limits, recipes, integration code, errors and safety rules. Start with the quickstart; everything after it is reference.
4
4
 
5
- - Web: https://blackpool.cash · Docs: https://blackpool.cash/docs#agents · This file: https://blackpool.cash/AGENTS.md
6
- - Package: [`@blackpool/cli`](https://www.npmjs.com/package/@blackpool/cli) (Node.js 18+)
7
- - API: `https://api.blackpool.cash` (the CLI talks to it for you)
5
+ | | |
6
+ |---|---|
7
+ | This file | https://blackpool.cash/AGENTS.md (plain markdown, no JavaScript, no bot challenge) |
8
+ | Mirrors | https://raw.githubusercontent.com/blackpooldev/blackpool/main/AGENTS.md · inside the npm package at `node_modules/@blackpool/cli/AGENTS.md` |
9
+ | Index for LLMs | https://blackpool.cash/llms.txt |
10
+ | CLI | [`@blackpool/cli`](https://www.npmjs.com/package/@blackpool/cli) — Node.js 18+ |
11
+ | MCP | local: `npx -y @blackpool/cli@latest mcp` · hosted read-only: `https://mcp.blackpool.cash/mcp` (§9) |
12
+ | API | `https://api.blackpool.cash` (the CLI calls it for you) |
13
+ | Live status | https://blackpool.cash/status |
14
+ | Human docs | https://blackpool.cash/docs |
8
15
 
9
- ## What you can do
16
+ ---
10
17
 
11
- Shield funds into a zero-knowledge pool, then pay, swap or bridge them with no on-chain link back to your wallet. The relayer pays gas for every private operation, so the agent only needs gas for the initial shield.
18
+ ## 1. Quickstart (60 seconds)
12
19
 
13
- | Command | What it does |
14
- |---|---|
15
- | `blackpool address` | Print the agent wallet address (fund this). |
16
- | `blackpool balance [--chain C] [--pool P] [--wait]` | Private and public balances. `--chain all` covers every chain. |
17
- | `blackpool deposit <amount> [--pool P] [--chain C]` | Shield public funds from the agent wallet into the pool (the agent pays this gas). |
18
- | `blackpool send <to> <amount> [--pool P] [--chain C]` | Private payment to any address. No gas needed. |
19
- | `blackpool swap <amount> --from P [--chain C] [--slippage BPS]` | Private same-chain swap (native ⇄ stablecoin), output re-shielded. |
20
- | `blackpool bridge <dest-asset> <recipient> <amount> --from ORIGIN [--no-wait]` | Private cross-chain transfer via NEAR Intents. |
21
- | `blackpool assets` | Bridge destination assets and valid `--from` origins. |
22
- | `blackpool bridges` | Your bridges with status, and where a refund went. |
23
- | `blackpool recover [--to ADDR] [--chain C]` | Sweep anything stranded on one-time burner wallets. Idempotent. |
24
- | `blackpool login` / `logout` / `version` / `latest` / `help` | Housekeeping. |
25
-
26
- ## Chains, pools and minimums
27
-
28
- | `--chain` | Pools (`--pool`) | Minimum shield | Minimum send / swap / bridge |
29
- |---|---|---|---|
30
- | `base` (default) | `eth`, `usdc` | 0.001 ETH · 3 USDC | 0.001 ETH · 2 USDC |
31
- | `robinhood` | `eth`, `usdg` | 0.001 ETH · 3 USDG | 0.001 ETH · 2 USDG |
32
- | `arbitrum` | `eth`, `usdc` | 0.001 ETH · 3 USDC | 0.001 ETH · 2 USDC |
33
- | `bsc` | `bnb`, `usdc`, `usdt` | 0.02 BNB · 3 USDC/USDT | 0.001 BNB · 2 USDC/USDT |
34
-
35
- When `--pool` is omitted, `deposit` and `send` use the chain's stablecoin pool. Fees: a flat fee (0.00015 ETH, 0.001 BNB, 1 USDC on Base, 0.5 on the other stablecoin pools) plus 0.35%, taken inside the proof from the amount you move. A spend must be more than twice its own fee. Bridge origins for `--from`: `base_eth`, `base_usdc`, `hood_eth`, `hood_usdg`, `arb_eth`, `arb_usdc`, `bsc_bnb`, `bsc_usdc`, `bsc_usdt`.
36
-
37
- ## Setup
20
+ ```sh
21
+ npm i -g @blackpool/cli # or prefix every command with: npx -y @blackpool/cli@latest
22
+ export BLACKPOOL_KEY=0x<agent-wallet-private-key> # a dedicated wallet, never your main one
23
+ export BLACKPOOL_JSON=1 # every command prints one JSON object on stdout
24
+
25
+ blackpool address # → {"ok":true,"command":"address","address":"0x…"} fund this address
26
+ blackpool deposit 25 --pool usdc --chain base # shield 25 USDC (the only step that needs gas)
27
+ blackpool send 0xRECIPIENT 10 --pool usdc --chain base # private payment, relayer pays gas
28
+ blackpool balance --chain base --pool usdc # → {"ok":true,…,"balance":"13.965",…}
29
+ ```
30
+
31
+ Agent app with MCP support? Skip the shell entirely: see §9.
32
+
33
+ Paste this into your agent's instructions:
34
+
35
+ ```text
36
+ You can make private crypto payments with the `blackpool` CLI (env BLACKPOOL_KEY and BLACKPOOL_JSON=1 are set).
37
+ Always run commands with a timeout of at least 300 seconds and parse the single JSON object on stdout.
38
+ "ok": true means it succeeded; "ok": false has an "error" to report. Exit code 2 means you called it wrong.
39
+ Before paying, check `blackpool balance --chain <c> --pool <p>`; if it is short, `blackpool deposit` first.
40
+ Never retry deposit, send, swap or bridge after a timeout or unclear failure: run `blackpool balance --wait`
41
+ (or `blackpool bridges`) to see whether it landed. `blackpool recover --chain all` is always safe to run.
42
+ Never print, log or share BLACKPOOL_KEY or anything in ~/.blackpool.
43
+ Full guide: https://blackpool.cash/AGENTS.md
44
+ ```
45
+
46
+ ---
47
+
48
+ ## 2. How Blackpool works (what an agent needs to know)
49
+
50
+ - **Public balance**: tokens in the agent wallet, visible to everyone.
51
+ - **Private balance**: notes inside a Blackpool pool contract. Spending them reveals neither the agent wallet nor which deposit paid.
52
+ - **Shield (`deposit`)**: moves public funds into the pool. Signed and paid (gas) by the agent wallet. This is the only on-chain action that shows your wallet.
53
+ - **Private operations (`send`, `swap`, `bridge`)**: a zero-knowledge proof spends notes; the Blackpool relayer submits the transaction and pays gas. The fee is taken from the amount moved, so the agent needs no gas for these.
54
+ - **Burner**: a one-time wallet used inside `swap` and `bridge`. Its key is encrypted locally and registered before it is funded, so `recover` can always sweep it.
55
+ - **Account**: derived from one signature of the Blackpool sign-in message by the agent wallet. Same key → same private account on every chain and machine.
56
+ - **Pools are per chain and per asset.** A private USDC balance on Base cannot pay on Arbitrum; use `bridge` to move between chains.
57
+
58
+ ---
59
+
60
+ ## 3. Setup
61
+
62
+ ### 3.1 Install
38
63
 
39
64
  ```sh
40
- npm i -g @blackpool/cli # or prefix every command with: npx -y @blackpool/cli
41
- export BLACKPOOL_KEY=0x... # private key of a DEDICATED agent wallet (see Security)
42
- export BLACKPOOL_JSON=1 # machine-readable output (same as --json on every command)
43
- blackpool address # fund this address with gas + the asset to shield
65
+ npm i -g @blackpool/cli # global: `blackpool …`
66
+ npx -y @blackpool/cli@latest … # no install, always the newest version
67
+ blackpool version --json # {"ok":true,"command":"version","version":"0.2.0"}
68
+ blackpool latest --json # {"ok":true,"command":"latest","installed":"0.2.0","latest":"0.2.0","upToDate":true}
44
69
  ```
45
70
 
46
- `BLACKPOOL_KEY` replaces the interactive `blackpool login`. The first command that needs your private account signs the Blackpool sign-in message with that key and caches the signature in `~/.blackpool/config.json` (mode 600). Set `BLACKPOOL_HOME` to keep that cache somewhere else, for example a writable directory inside a sandbox.
71
+ ### 3.2 Configure
47
72
 
48
- | Variable | Purpose |
49
- |---|---|
50
- | `BLACKPOOL_KEY` | Agent wallet private key (0x-hex). Never sent anywhere. |
51
- | `BLACKPOOL_JSON` | `1` = JSON output for every command. |
52
- | `BLACKPOOL_HOME` | Config directory (default `~/.blackpool`). |
53
- | `BLACKPOOL_API` | API base URL (default `https://api.blackpool.cash`). |
73
+ | Variable | Required | Purpose |
74
+ |---|---|---|
75
+ | `BLACKPOOL_KEY` | yes (or run `blackpool login` once) | Agent wallet private key, 0x-prefixed hex. Used only locally. |
76
+ | `BLACKPOOL_JSON` | recommended | `1` = JSON output for every command (same as adding `--json`). |
77
+ | `BLACKPOOL_HOME` | no | Config/cache directory, default `~/.blackpool`. Set it when the home directory is read-only (containers, serverless). |
78
+ | `BLACKPOOL_API` | no | API base URL, default `https://api.blackpool.cash`. |
54
79
 
55
- ## JSON output
80
+ Without `BLACKPOOL_KEY`, `blackpool login` asks for the key with a hidden prompt and stores it at `~/.blackpool/wallet.json` (mode 600). Agents should prefer the environment variable.
56
81
 
57
- With `--json` (or `BLACKPOOL_JSON=1`) stdout carries exactly one JSON object and nothing else; progress messages go to stderr. Parse stdout, log stderr if you want.
82
+ ### 3.3 Fund
58
83
 
59
84
  ```sh
60
- $ blackpool send 0x9f3c...41e7 5 --pool usdc --chain base --json
61
- {"ok":true,"command":"send","chain":"base","pool":"usdc","recipient":"0x9f3c...41e7","amount":5,
62
- "received":"3.9825","fee":"1.0175","symbol":"USDC","txHash":"0x...","explorer":"https://basescan.org/tx/0x..."}
85
+ blackpool address --json
86
+ ```
87
+
88
+ Send the agent wallet the asset you want to shield **plus a little native gas for the deposit** (a deposit uses about 1.3M gas; keep at least 0.0005 ETH on Robinhood Chain, Base or Arbitrum, or 0.002 BNB on BNB Chain). After shielding, private operations need no gas.
89
+
90
+ ### 3.4 Verify
63
91
 
64
- $ blackpool send 0x9f3c...41e7 0.5 --json
65
- {"ok":false,"command":"send","error":"minimum send is 2 USDC on Base — smaller amounts are mostly consumed by the withdraw fee (flat + 0.35%)"}
92
+ ```sh
93
+ blackpool balance --chain all --json
66
94
  ```
67
95
 
68
- | Command | Fields on success |
69
- |---|---|
70
- | `address`, `login` | `address` |
71
- | `balance` | `balances[]`: `{chain, address, private: {SYM: {balance, utxos}}, public: {SYM: amount}}`; with `--pool`: `{chain, pool, balance, symbol, utxos}` |
72
- | `deposit` | `chain, pool, amount, symbol, txHash, explorer, privateBalance` |
73
- | `send` | `chain, pool, recipient, amount, received, fee, symbol, txHash, explorer` |
74
- | `swap` | `chain, from, amount, received, symbol, txHash, explorer, privateBalance` |
75
- | `bridge` | `from, to, recipient, amount, amountOutQuote, withdrawTx, depositAddress, statusUrl, status` (`SUBMITTED`, `SUCCESS` or `PENDING`) |
76
- | `bridges` | `bridges[]` with `status`, `amountIn`, `fromAsset`, `toAsset`, `recipient`, `refundTo`, `statusUrl` |
77
- | `assets` | `assets{}`, `origins[]` |
78
- | `recover` | `recovered[]`: `{chain, to, swept, dust, errors}` |
96
+ ---
97
+
98
+ ## 4. Command reference
99
+
100
+ Every command accepts `--json`. Amounts are plain decimals in token units (`10`, `0.25`), never wei. `--chain` defaults to `base`. Typical durations assume a healthy network.
101
+
102
+ ### `address`
103
+ Print the agent wallet address.
104
+ ```json
105
+ {"ok":true,"command":"address","address":"0x70997970C51812dc3A010C7d01b50e0d17dc79C8"}
106
+ ```
107
+
108
+ ### `balance [--chain C|all] [--pool P] [--wait]`
109
+ Private and public balances. `--pool` limits the answer to one private pool. `--wait` polls up to 30 s while a private balance reads 0 (use right after a deposit). ~5–15 s.
110
+ ```json
111
+ {"ok":true,"command":"balance","chain":"base","pool":"usdc","address":"0x…","balance":"13.965","symbol":"USDC","utxos":2}
112
+ ```
113
+ Without `--pool` (one entry per chain; `--chain all` returns four):
114
+ ```json
115
+ {"ok":true,"command":"balance","balances":[{"chain":"base","address":"0x…",
116
+ "private":{"ETH":{"balance":"0.0","utxos":0},"USDC":{"balance":"13.965","utxos":2}},
117
+ "public":{"ETH":"0.00412","USDC":"0.0"}}]}
118
+ ```
119
+ A value of `"?"` with an `error` field means that read failed; retry.
79
120
 
80
- Balances and amounts are decimal strings in token units, never wei.
121
+ ### `deposit <amount> [--pool P] [--chain C]`
122
+ Shield public funds from the agent wallet. ERC-20 pools approve first (one extra transaction). Needs native gas. ~30–90 s.
123
+ ```json
124
+ {"ok":true,"command":"deposit","chain":"base","pool":"usdc","amount":25,"symbol":"USDC",
125
+ "txHash":"0x…","explorer":"https://basescan.org/tx/0x…","privateBalance":"25.0"}
126
+ ```
127
+ `--pool` defaults to the chain's stablecoin pool.
128
+
129
+ ### `send <recipient> <amount> [--pool P] [--chain C]`
130
+ Private payment to any EVM address on the same chain. `amount` is what leaves your private balance; the recipient gets `amount − fee`. No gas needed. ~30–90 s.
131
+ ```json
132
+ {"ok":true,"command":"send","chain":"base","pool":"usdc","recipient":"0x…","amount":10,
133
+ "received":"8.965","fee":"1.035","symbol":"USDC","txHash":"0x…","explorer":"https://basescan.org/tx/0x…"}
134
+ ```
135
+ To make the recipient receive exactly `X`, send `(X + flat) / (1 − 0.0035)` rounded up (see §5).
136
+
137
+ ### `swap <amount> --from P [--chain C] [--slippage BPS]`
138
+ Private same-chain swap between the chain's native pool and stablecoin pool; the output is re-shielded into your private balance. `--slippage` in basis points, default `100` (1%). No gas needed. ~1–3 min.
139
+ ```json
140
+ {"ok":true,"command":"swap","chain":"base","from":"eth","amount":0.01,"received":"22.41",
141
+ "symbol":"USDC","txHash":"0x…","explorer":"https://basescan.org/tx/0x…","privateBalance":"36.375"}
142
+ ```
143
+ If it fails half-way, funds sit on a registered burner: run `recover`.
144
+
145
+ ### `bridge <dest-asset> <recipient> <amount> --from ORIGIN [--no-wait]`
146
+ Private cross-chain transfer through NEAR Intents. `ORIGIN` is the private pool paying (default `base_usdc`); `dest-asset` and recipient format are listed in §6. The withdrawal is private; the solver delivers on the destination chain. Without `--no-wait` the CLI polls up to 6 minutes.
147
+ ```json
148
+ {"ok":true,"command":"bridge","from":"base_usdc","to":"sol_usdc","recipient":"7Gx4…kQ2m","amount":10,
149
+ "amountOutQuote":"8.91","withdrawTx":"0x…","depositAddress":"0x…","statusUrl":"https://…",
150
+ "status":"SUCCESS","amountOut":"8.93"}
151
+ ```
152
+ `status` is `SUBMITTED` (with `--no-wait`), `SUCCESS`, or `PENDING` (still running after 6 min; check `bridges` later). A refund or failure returns `"ok":false`; refunds go to the agent wallet on the origin chain.
153
+
154
+ ### `bridges`
155
+ Every bridge this account has made, newest first, from the server-side intent registry (works from any machine).
156
+ ```json
157
+ {"ok":true,"command":"bridges","bridges":[{"status":"SUCCESS","createdAt":1793760000000,"amountIn":"10",
158
+ "fromAsset":"base_usdc","toAsset":"sol_usdc","recipient":"7Gx4…","refundTo":"0x…","originChain":"base",
159
+ "amountOutQuote":"8.91","amountOut":"8.93","destTx":"…","depositAddress":"0x…","statusUrl":"https://…"}]}
160
+ ```
161
+
162
+ ### `assets`
163
+ Bridge destinations and valid origins, live from the API.
164
+ ```json
165
+ {"ok":true,"command":"assets","assets":{"sol_usdc":{"chain":"sol","symbol":"USDC","decimals":6,"isNative":false},"…":{}},
166
+ "origins":["base_eth","base_usdc","hood_eth","hood_usdg","arb_eth","arb_usdc","bsc_bnb","bsc_usdc","bsc_usdt"]}
167
+ ```
168
+
169
+ ### `recover [--to ADDR] [--chain C|all]`
170
+ Sweep every registered burner that still holds funds to the agent wallet (or `--to`). Idempotent and safe at any time. Swept funds land in the **public** wallet; `deposit` again to make them private. ~10–60 s per chain.
171
+ ```json
172
+ {"ok":true,"command":"recover","recovered":[{"chain":"base","to":"0x…","swept":1,"dust":0,"errors":0}]}
173
+ ```
174
+
175
+ ### `login`, `logout`, `version`, `latest`, `help`
176
+ `login` signs and caches the sign-in message (`{"address"}`); `logout` deletes the stored key and cache (`{"removed":[…]}`); `version` and `latest` report versions.
177
+
178
+ ---
179
+
180
+ ## 5. Chains, pools, fees and minimums
81
181
 
82
- **Exit codes:** `0` success · `1` the operation failed or was refused (including amounts under a minimum) · `2` usage error (unknown command or missing arguments). On `1` and `2` the JSON has `"ok": false` and an `error` message written for a human.
182
+ | `--chain` | Pools (`--pool`) | Min deposit | Min send / swap / bridge | Flat fee | DEX for `swap` |
183
+ |---|---|---|---|---|---|
184
+ | `base` (default) | `eth`, `usdc` | 0.001 ETH · 3 USDC | 0.001 ETH · 2 USDC | 0.00015 ETH · 1 USDC | Uniswap V3 |
185
+ | `robinhood` | `eth`, `usdg` | 0.001 ETH · 3 USDG | 0.001 ETH · 2 USDG | 0.00015 ETH · 0.5 USDG | Uniswap V3 |
186
+ | `arbitrum` | `eth`, `usdc` | 0.001 ETH · 3 USDC | 0.001 ETH · 2 USDC | 0.00015 ETH · 0.5 USDC | Uniswap V3 |
187
+ | `bsc` | `bnb`, `usdc`, `usdt` | 0.02 BNB · 3 USDC/USDT | 0.001 BNB · 2 USDC/USDT | 0.001 BNB · 0.5 USDC/USDT | PancakeSwap V3 |
83
188
 
84
- ## Recipes
189
+ - **Fee** on every private spend = flat fee + 0.35% of the amount, deducted from the amount. Example: `send 10` USDC on Base → fee 1 + 0.035 = 1.035, recipient gets 8.965.
190
+ - A spend must be more than twice its own fee; the CLI enforces the minimums above before doing anything.
191
+ - Bridges pay the pool fee plus the solver's quote (shown as `amountOutQuote`); each route also has a solver minimum.
192
+ - `deposit` costs only network gas; there is no Blackpool fee on shielding.
85
193
 
86
- Pay someone privately (first time on a chain):
194
+ ---
87
195
 
196
+ ## 6. Bridging
197
+
198
+ **Origins** (`--from`): `base_eth`, `base_usdc`, `hood_eth`, `hood_usdg` (Robinhood Chain), `arb_eth`, `arb_usdc`, `bsc_bnb`, `bsc_usdc`, `bsc_usdt`.
199
+
200
+ **Destinations** (`dest-asset`; run `blackpool assets` for the live list):
201
+
202
+ | Chain | Assets | Recipient format |
203
+ |---|---|---|
204
+ | Base | `base_eth`, `base_weth`, `base_usdc`, `base_cbbtc` | EVM `0x…` |
205
+ | Ethereum | `eth_eth`, `eth_weth`, `eth_usdc`, `eth_usdt`, `eth_cbbtc` | EVM `0x…` |
206
+ | Arbitrum | `arb_eth`, `arb_weth`, `arb_usdc`, `arb_usdt`, `arb_arb` | EVM `0x…` |
207
+ | BNB Chain | `bsc_bnb`, `bsc_usdc`, `bsc_usdt` | EVM `0x…` |
208
+ | Robinhood Chain | `hood_eth`, `hood_usdg`, `hood_weth`, `hood_usde` | EVM `0x…` |
209
+ | Solana | `sol_sol`, `sol_usdc`, `sol_usdt` | base58 public key |
210
+
211
+ **Statuses** in `bridges`: `PENDING_DEPOSIT` → `PROCESSING` → `SUCCESS`, or `REFUNDED` / `FAILED` (refund to `refundTo` on `originChain`). Treat any other value as in progress.
212
+
213
+ ---
214
+
215
+ ## 7. Recipes
216
+
217
+ ### Pay an invoice exactly
88
218
  ```sh
89
- blackpool deposit 25 --pool usdc --chain base --json # shield once
90
- blackpool send 0xRECIPIENT 10 --pool usdc --chain base --json
219
+ # recipient must receive 50 USDC on Base: amount = (50 + 1) / 0.9965 = 51.18 (round up)
220
+ blackpool balance --chain base --pool usdc --json # need >= 51.18
221
+ blackpool send 0xVENDOR 51.18 --pool usdc --chain base --json
91
222
  ```
92
223
 
93
- Check before paying:
224
+ ### Top up only when needed
225
+ ```sh
226
+ bal=$(blackpool balance --chain base --pool usdc --json | jq -r .balance)
227
+ if (( $(echo "$bal < 51.18" | bc -l) )); then blackpool deposit 100 --pool usdc --chain base --json; fi
228
+ ```
94
229
 
230
+ ### Batch payouts (sequential, never parallel)
95
231
  ```sh
96
- blackpool balance --chain base --pool usdc --json # .balance must cover amount
232
+ while IFS=, read -r to amount; do
233
+ out=$(blackpool send "$to" "$amount" --pool usdc --chain base --json)
234
+ echo "$out" >> payouts.jsonl
235
+ [ "$(echo "$out" | jq -r .ok)" = "true" ] || { echo "stopped at $to"; break; }
236
+ done < payouts.csv
97
237
  ```
238
+ Run payouts one at a time: each spend consumes notes and returns change, so parallel sends from one account can race.
98
239
 
99
- Bridge to Solana and return as soon as the solver has the funds:
240
+ ### Swap ETH to USDC privately
241
+ ```sh
242
+ blackpool swap 0.01 --from eth --chain base --json
243
+ ```
100
244
 
245
+ ### Bridge to Solana without blocking
101
246
  ```sh
102
247
  blackpool bridge sol_usdc <solana-address> 10 --from base_usdc --no-wait --json
103
- blackpool bridges --json # poll status later
248
+ blackpool bridges --json | jq '.bridges[0].status' # poll every minute until SUCCESS / REFUNDED
104
249
  ```
105
250
 
106
- After any swap or bridge that failed half-way:
251
+ ### Move private funds to another chain (rebalance)
252
+ ```sh
253
+ blackpool bridge arb_usdc $(blackpool address --json | jq -r .address) 20 --from base_usdc --json
254
+ blackpool deposit 19 --pool usdc --chain arbitrum --json # re-shield on the destination chain
255
+ ```
256
+ The bridge delivers to a public address; shield it again on the destination chain.
107
257
 
258
+ ### Health check before a run
108
259
  ```sh
109
- blackpool recover --chain all --json # safe to run any time
260
+ curl -s https://api.blackpool.cash/api/health | jq '.ok, .relayerGas'
110
261
  ```
111
262
 
112
- ## Failure handling
263
+ ### Clean up after an interrupted swap or bridge
264
+ ```sh
265
+ blackpool recover --chain all --json
266
+ ```
267
+
268
+ ---
269
+
270
+ ## 8. Integration code
271
+
272
+ ### Node.js
273
+ ```js
274
+ import { execFile } from 'node:child_process'
275
+
276
+ export function blackpool(args, { timeoutMs = 300_000 } = {}) {
277
+ return new Promise((resolve) => {
278
+ execFile('blackpool', [...args, '--json'], { timeout: timeoutMs, env: process.env }, (err, stdout, stderr) => {
279
+ try { resolve(JSON.parse(stdout.trim().split('\n').pop())) }
280
+ catch { resolve({ ok: false, error: (stderr || err?.message || 'no output').trim(), exitCode: err?.code }) }
281
+ })
282
+ })
283
+ }
284
+
285
+ const r = await blackpool(['send', '0xRECIPIENT', '10', '--pool', 'usdc', '--chain', 'base'])
286
+ if (!r.ok) throw new Error(r.error)
287
+ console.log(r.txHash, r.received)
288
+ ```
289
+
290
+ ### Python
291
+ ```python
292
+ import json, subprocess
293
+
294
+ def blackpool(*args, timeout=300):
295
+ p = subprocess.run(["blackpool", *args, "--json"], capture_output=True, text=True, timeout=timeout)
296
+ try:
297
+ return json.loads(p.stdout.strip().splitlines()[-1])
298
+ except (ValueError, IndexError):
299
+ return {"ok": False, "error": (p.stderr or "no output").strip(), "exitCode": p.returncode}
300
+
301
+ r = blackpool("balance", "--chain", "base", "--pool", "usdc")
302
+ print(r["balance"] if r["ok"] else r["error"])
303
+ ```
304
+
305
+ ### Tool definition for function-calling models
306
+ Expose one tool and let the model choose the command:
307
+ ```json
308
+ {
309
+ "name": "blackpool",
310
+ "description": "Private crypto payments via the Blackpool CLI. Commands: address; balance [--chain base|robinhood|arbitrum|bsc|all] [--pool P] [--wait]; deposit <amount> [--pool P] [--chain C]; send <recipient> <amount> [--pool P] [--chain C]; swap <amount> --from P [--chain C]; bridge <dest-asset> <recipient> <amount> --from ORIGIN [--no-wait]; bridges; assets; recover [--chain C|all]. Returns one JSON object; ok=false carries an error. Never retry deposit/send/swap/bridge without checking balance first.",
311
+ "input_schema": {
312
+ "type": "object",
313
+ "properties": {
314
+ "args": { "type": "array", "items": { "type": "string" }, "description": "CLI arguments, e.g. [\"send\",\"0xabc…\",\"10\",\"--pool\",\"usdc\",\"--chain\",\"base\"]" }
315
+ },
316
+ "required": ["args"]
317
+ }
318
+ }
319
+ ```
320
+ Implement it with the Node or Python wrapper above. Allow-list the first argument to the commands you want the agent to use (for example only `balance`, `send` and `bridges`).
113
321
 
114
- - **Read-only commands** (`address`, `balance`, `assets`, `bridges`, `version`) are safe to retry.
115
- - **`recover` is idempotent**: run it again whenever a swap or bridge did not finish.
116
- - **Do not blindly retry `deposit`, `send`, `swap` or `bridge`.** A timeout can mean the transaction landed. Check `balance --wait` (or `bridges`) first, then decide.
117
- - A balance that reads `0` right after a deposit is usually the indexer catching up: use `balance --wait`.
118
- - `rate limited, slow down`: the API allows 40 relay operations and 600 reads per minute per IP. The CLI waits for the `retryAfter` the API sends and retries up to four times; space out bulk payments.
119
- - Proof generation takes about 30–90 seconds per private operation. Use a command timeout of at least 3 minutes (bridges without `--no-wait` can poll up to 6 minutes).
322
+ ---
323
+
324
+ ## 9. MCP servers
325
+
326
+ Blackpool speaks the [Model Context Protocol](https://modelcontextprotocol.io), so any MCP-capable agent app can call it as tools instead of running shell commands. There are two servers.
327
+
328
+ ### 9.1 Local server — payments (recommended)
329
+
330
+ Runs on your machine over stdio and exposes the full CLI as tools. The wallet key stays in the local environment; tools behave exactly like the commands in §4 and return the same JSON.
331
+
332
+ Add it to your app's MCP configuration (most apps use this `mcpServers` format):
333
+
334
+ ```json
335
+ {
336
+ "mcpServers": {
337
+ "blackpool": {
338
+ "command": "npx",
339
+ "args": ["-y", "@blackpool/cli@latest", "mcp"],
340
+ "env": {
341
+ "BLACKPOOL_KEY": "0x<agent-wallet-private-key>",
342
+ "BLACKPOOL_MCP_TOOLS": "balance,send,bridges"
343
+ }
344
+ }
345
+ }
346
+ }
347
+ ```
348
+
349
+ With the CLI installed globally, `"command": "blackpool", "args": ["mcp"]` works too. Apps that add servers from a terminal take the same command: `npx -y @blackpool/cli@latest mcp`.
350
+
351
+ | Tool | Does | Annotations |
352
+ |---|---|---|
353
+ | `blackpool_address` | Agent wallet address | read-only |
354
+ | `blackpool_balance` | Private and public balances (`chain`, `pool`, `wait`) | read-only |
355
+ | `blackpool_deposit` | Shield from the agent wallet (`amount`, `pool`, `chain`) | destructive |
356
+ | `blackpool_send` | Private payment (`recipient`, `amount`, `pool`, `chain`) | destructive |
357
+ | `blackpool_swap` | Private same-chain swap (`amount`, `from`, `chain`, `slippage_bps`) | destructive |
358
+ | `blackpool_bridge` | Private bridge (`dest_asset`, `recipient`, `amount`, `from`, `wait`) | destructive |
359
+ | `blackpool_bridges` | Bridge history and status | read-only |
360
+ | `blackpool_assets` | Bridge destinations and origins | read-only |
361
+ | `blackpool_recover` | Sweep stranded burners (`chain`, `to`) | idempotent |
362
+
363
+ Resource: `blackpool://agents.md` (this guide).
364
+
365
+ Limit what the agent can do from the configuration:
366
+
367
+ | Variable | Effect |
368
+ |---|---|
369
+ | `BLACKPOOL_MCP_TOOLS` | Comma-separated allow-list, e.g. `balance,send,bridges` (with or without the `blackpool_` prefix). |
370
+ | `BLACKPOOL_MCP_READONLY` | `1` exposes only read-only tools. |
371
+ | `BLACKPOOL_HOME`, `BLACKPOOL_API` | Same as for the CLI (§3.2). |
372
+
373
+ Every tool result carries the command's JSON as `structuredContent` (and as text); `isError` is `true` when `"ok": false`. The retry rules in §12 apply unchanged: never repeat `blackpool_deposit`, `blackpool_send`, `blackpool_swap` or `blackpool_bridge` after a timeout without checking `blackpool_balance` or `blackpool_bridges` first.
374
+
375
+ ### 9.2 Hosted server — read-only
376
+
377
+ `https://mcp.blackpool.cash/mcp` (Streamable HTTP, no sign-in). It never receives keys or signatures, so it can only answer questions:
378
+
379
+ | Tool | Returns |
380
+ |---|---|
381
+ | `blackpool_status` | API health, configured pools, relayer gas per chain |
382
+ | `blackpool_fees` | Flat fee, rate, minimum deposit and minimum spend per pool |
383
+ | `blackpool_assets` | Bridge destinations and origins |
384
+ | `blackpool_bridge_quote` | Indicative quote (`from`, `to`, `amount`) |
385
+ | `blackpool_analytics` | Shielded notes per chain and pool, relayer fees |
386
+ | `blackpool_guide` | This guide, whole or one section (`section`) |
387
+
388
+ ```json
389
+ {
390
+ "mcpServers": {
391
+ "blackpool-info": { "url": "https://mcp.blackpool.cash/mcp" }
392
+ }
393
+ }
394
+ ```
395
+
396
+ Use the hosted server to plan (fees, routes, quotes, status) and the local server to act.
397
+
398
+ ---
399
+
400
+ ## 10. Output contract
401
+
402
+ - stdout: exactly one line of JSON when `--json` (or `BLACKPOOL_JSON=1`) is set. stderr: human progress, safe to log.
403
+ - Envelope: `{"ok": true, "command": "<name>", …fields}` or `{"ok": false, "command": "<name>", "error": "<message>"}`.
404
+ - Amounts are decimal strings in token units (some input echoes such as `amount` are numbers). Tx hashes are 0x-hex; `explorer` is a full URL.
405
+ - Exit codes: `0` success · `1` failed or refused (including amounts under a minimum) · `2` usage (unknown command, missing arguments).
406
+ - New fields may be added; existing fields are not renamed or removed without a major version bump.
407
+
408
+ ---
409
+
410
+ ## 11. Errors and what to do
411
+
412
+ | `error` contains | Meaning | Do |
413
+ |---|---|---|
414
+ | `no wallet configured` | `BLACKPOOL_KEY` missing | Set it, or run `blackpool login`. |
415
+ | `invalid BLACKPOOL_KEY` / `stored key invalid` | Not a 0x hex private key | Fix the key. |
416
+ | `minimum deposit is …` / `minimum send is …` | Below the floor in §5 | Use a larger amount. |
417
+ | `unknown pool` / `unknown chain` / `unknown --from` | Wrong flag value | Use the values in §5–6. |
418
+ | `invalid recipient address` | Wrong format for the destination | EVM `0x…` (40 hex) or Solana base58. |
419
+ | `insufficient` / `no unspent` | Private balance too low | `balance`, then `deposit` more. |
420
+ | `Withdrawal must be more than twice the fee` | Amount too close to the fee | Larger amount. |
421
+ | `Amount is too low for this route. Minimum is …` | Solver minimum for that bridge | Bridge at least that much. |
422
+ | `rate limited, slow down` | API limit (40 relay ops, 600 reads per minute per IP) | The CLI already waited and retried 4 times; pause a minute. |
423
+ | `simulation failed: …` | The relayer refused a transaction that would revert | Do not retry blindly; check `balance`; report the message. |
424
+ | `reshield would revert` / `swap output … too small` | Swap stopped after funds reached the burner | `recover`, then `deposit` again. |
425
+ | `solver refunded` / `bridge failed at solver` | Bridge did not complete | Refund goes to the agent wallet on the origin chain; `deposit` to re-shield. |
426
+ | `timeout` / `fetch failed` / `HTTP 5xx` | Network or service problem | Check https://blackpool.cash/status; for write commands check `balance --wait` before retrying. |
427
+
428
+ ---
429
+
430
+ ## 12. Retries, timeouts, concurrency
431
+
432
+ - Safe to retry any time: `address`, `balance`, `assets`, `bridges`, `recover`, `version`, `latest`.
433
+ - **Not** safe to retry blindly: `deposit`, `send`, `swap`, `bridge`. A timeout can mean the transaction landed. Check `balance --wait` (or `bridges`) and only retry if the balance is unchanged.
434
+ - Give every command a timeout of **at least 300 s** (`bridge` without `--no-wait` up to 420 s).
435
+ - One write command at a time per account. Reads can run in parallel.
436
+ - A private balance can read `0` for a few seconds after a deposit while the indexer catches up; use `--wait`.
437
+
438
+ ---
439
+
440
+ ## 13. Security
441
+
442
+ - **Dedicated agent wallet.** Fund it with only what the agent may spend. Never use a personal or treasury key.
443
+ - `BLACKPOOL_KEY` never leaves the machine: it signs the sign-in message, deposit approvals and deposit transactions locally.
444
+ - The **sign-in signature** is cached in `$BLACKPOOL_HOME/config.json` (mode 600) and sent to the API with each private operation. The API derives the private account keys from it in memory to build the proof and does not store or log it, but anyone holding that signature can spend the agent's private balance. Treat the config directory like a key: never print it, never put it in prompts, logs or tool output.
445
+ - Only sign the Blackpool sign-in message on blackpool.cash or with this CLI.
446
+ - The contracts and circuits have not been externally audited; pools are upgradeable by an admin key during launch. Size balances accordingly. Details: https://blackpool.cash/docs#security
447
+ - Report vulnerabilities privately to hello@blackpool.cash (subject `security`).
448
+
449
+ ---
450
+
451
+ ## 14. Direct API (advanced)
452
+
453
+ The CLI is the supported interface. If you cannot run Node.js, the same operations are HTTP calls on `https://api.blackpool.cash` (`/api/balance`, `/api/deposit/prepare`, `/api/withdraw`, `/api/swap-private`, `/api/bridge/quote`, `/api/swap`, `/api/intents`, `/api/burner/*`, `/api/health`), all JSON. They need the sign-in signature (EIP-191 signature of the exact message below by the agent wallet), and deposits need you to sign and broadcast the returned transaction yourself. Endpoint list: https://blackpool.cash/docs#api
454
+
455
+ ```text
456
+ Welcome to Blackpool
457
+
458
+ Sign this message to access your private account.
459
+
460
+ - Gas free: no transaction is sent
461
+ - Your private account keys are derived from this signature
462
+ - It cannot move funds from your wallet or approve spending
463
+ - Only sign it on blackpool.cash or the Blackpool CLI
464
+ ```
120
465
 
121
- ## Security
466
+ The message is part of the account: a different message, even by one character, is a different (empty) account.
122
467
 
123
- - **Use a dedicated agent wallet** holding only what the agent should spend. Never give an agent your main wallet key.
124
- - `BLACKPOOL_KEY` stays on the machine. It is used to sign the sign-in message and the deposit transaction locally; it is never sent to the API.
125
- - The sign-in signature **is** sent to the API with each private operation: the API derives your private account keys from it in memory to build the proof, and does not store or log it. Anyone holding that signature can spend the agent's private balance, so protect `~/.blackpool/config.json` like a key, and never paste the signature into prompts, logs or tool output.
126
- - Pools and circuits have not been externally audited yet. Keep balances proportional to that risk. Details: https://blackpool.cash/docs#security
468
+ ---
127
469
 
128
- ## Working on this repository
470
+ ## 15. Working on this repository
129
471
 
130
- If you are a coding agent editing this repository rather than using the CLI: the CLI lives in `cli/`, contracts in `contracts/`, circuits in `circuits/`, the web app in `frontend/`. Run `npx hardhat test` before changing contracts or core libraries, keep the sign-in message, note format and circuit byte-identical across `src/`, `cli/` and `frontend/` (changing any of them changes every account), and never commit keys, `.env` files or credentials.
472
+ If you are a coding agent editing this repository rather than using the CLI: the CLI lives in `cli/`, contracts in `contracts/`, circuits in `circuits/`, client libraries in `src/`, the web app in `frontend/`. Run `npx hardhat test` before changing contracts or core libraries. Keep the sign-in message, note format and circuit byte-identical across `src/`, `cli/` and `frontend/` (changing any of them changes every account). Never commit keys, `.env` files or credentials. Keep this file in sync with `cli/bin/blackpool.js` when commands or output fields change.
package/README.md CHANGED
@@ -131,3 +131,13 @@ Docs: [docs.blackpool.cash/agents](https://docs.blackpool.cash/agents) · Site:
131
131
  ## Agents
132
132
 
133
133
  Run any command with `--json` (or `BLACKPOOL_JSON=1`) to get one JSON object on stdout and progress on stderr; exit codes are `0` ok, `1` failed, `2` usage. [`AGENTS.md`](AGENTS.md) (also at https://blackpool.cash/AGENTS.md) documents every command's fields, minimums, retry rules and security notes.
134
+
135
+ ## MCP
136
+
137
+ `blackpool mcp` runs a Model Context Protocol server over stdio, so agent apps can call Blackpool as tools:
138
+
139
+ ```json
140
+ { "mcpServers": { "blackpool": { "command": "npx", "args": ["-y", "@blackpool/cli@latest", "mcp"], "env": { "BLACKPOOL_KEY": "0x..." } } } }
141
+ ```
142
+
143
+ `BLACKPOOL_MCP_TOOLS=balance,send` limits the tools, `BLACKPOOL_MCP_READONLY=1` exposes read-only ones. A hosted read-only server (status, fees, assets, quotes, analytics, guide) runs at `https://mcp.blackpool.cash/mcp`. Details: [AGENTS.md §9](AGENTS.md).
package/bin/blackpool.js CHANGED
@@ -1358,6 +1358,7 @@ ${color('Commands:', c.bold)}
1358
1358
  Run 'blackpool assets' for the destination list.
1359
1359
  assets List supported bridge destination assets.
1360
1360
  bridges List your cross-chain bridges + status (where a refund went).
1361
+ mcp Run a Model Context Protocol server over stdio (agent apps).
1361
1362
  recover [--to <addr>] [--chain] Sweep any stranded funds from burner wallets.
1362
1363
  --chain all sweeps every chain in one run (idempotent).
1363
1364
  version Print CLI version.
@@ -1418,6 +1419,7 @@ async function main() {
1418
1419
  case 'login': await cmdLogin(); break
1419
1420
  case 'logout': await cmdLogout(); break
1420
1421
  case 'address': await cmdAddress(); break
1422
+ case 'mcp': await require('./mcp.js').serve(); process.exit(0)
1421
1423
  case 'balance': await cmdBalance(args); break
1422
1424
  case 'deposit': await cmdDeposit(args); break
1423
1425
  case 'send': await cmdSend(args); break
package/bin/mcp.js ADDED
@@ -0,0 +1,184 @@
1
+ 'use strict'
2
+ /**
3
+ * blackpool mcp — Model Context Protocol server over stdio (newline-delimited JSON-RPC 2.0).
4
+ *
5
+ * Every tool runs the CLI itself as a child process with --json, so tools behave exactly like the commands documented
6
+ * in AGENTS.md, and a failing command can never take the server down. The wallet key stays in this process's
7
+ * environment (BLACKPOOL_KEY) and is only passed to the child; nothing is written to stdout except protocol messages.
8
+ *
9
+ * Optional environment:
10
+ * BLACKPOOL_MCP_TOOLS comma-separated allow-list of tool names (default: all)
11
+ * BLACKPOOL_MCP_READONLY 1 = expose read-only tools only
12
+ */
13
+ const path = require('path')
14
+ const fs = require('fs')
15
+ const { spawn } = require('child_process')
16
+
17
+ const VERSION = require('../package.json').version
18
+ const CLI = path.join(__dirname, 'blackpool.js')
19
+ const SUPPORTED = ['2025-06-18', '2025-03-26', '2024-11-05']
20
+
21
+ const CHAIN = { type: 'string', enum: ['base', 'robinhood', 'arbitrum', 'bsc'], description: 'Chain. Default base.' }
22
+ const POOL = { type: 'string', description: 'Pool on that chain: eth|usdc (base, arbitrum), eth|usdg (robinhood), bnb|usdc|usdt (bsc). Default: the chain\'s stablecoin pool.' }
23
+ const AMOUNT = { type: 'string', pattern: '^[0-9]+(\\.[0-9]+)?$', description: 'Decimal amount in token units, e.g. "10" or "0.25". Never wei.' }
24
+
25
+ const TOOLS = [
26
+ {
27
+ name: 'blackpool_address', title: 'Wallet address', readOnly: true,
28
+ description: 'Address of the agent wallet. Fund it with the asset to shield plus a little native gas for deposits.',
29
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
30
+ args: () => ['address'],
31
+ },
32
+ {
33
+ name: 'blackpool_balance', title: 'Balances', readOnly: true,
34
+ description: 'Private (shielded) and public balances. With pool: one private pool only. wait=true polls up to 30 s while a private balance reads 0 (right after a deposit).',
35
+ inputSchema: { type: 'object', properties: { chain: { ...CHAIN, enum: [...CHAIN.enum, 'all'], description: 'Chain, or "all". Default base.' }, pool: POOL, wait: { type: 'boolean' } }, additionalProperties: false },
36
+ args: (a) => ['balance', ...opt('--chain', a.chain), ...opt('--pool', a.pool), ...(a.wait ? ['--wait'] : [])],
37
+ },
38
+ {
39
+ name: 'blackpool_deposit', title: 'Shield (deposit)', destructive: true,
40
+ description: 'Shield public funds from the agent wallet into the private pool. Signs and pays gas from the agent wallet. Takes 30-90 s. Do not retry after a timeout without checking blackpool_balance.',
41
+ inputSchema: { type: 'object', properties: { amount: AMOUNT, pool: POOL, chain: CHAIN }, required: ['amount'], additionalProperties: false },
42
+ args: (a) => ['deposit', String(a.amount), ...opt('--pool', a.pool), ...opt('--chain', a.chain)],
43
+ },
44
+ {
45
+ name: 'blackpool_send', title: 'Private send', destructive: true,
46
+ description: 'Pay any EVM address privately from the private balance. The relayer pays gas; the recipient receives amount minus the fee (flat + 0.35%). Takes 30-90 s. Never retry after a timeout without checking blackpool_balance.',
47
+ inputSchema: { type: 'object', properties: { recipient: { type: 'string', pattern: '^0x[0-9a-fA-F]{40}$' }, amount: AMOUNT, pool: POOL, chain: CHAIN }, required: ['recipient', 'amount'], additionalProperties: false },
48
+ args: (a) => ['send', a.recipient, String(a.amount), ...opt('--pool', a.pool), ...opt('--chain', a.chain)],
49
+ },
50
+ {
51
+ name: 'blackpool_swap', title: 'Private swap', destructive: true,
52
+ description: 'Private same-chain swap between the native pool and the stablecoin pool; output is re-shielded. If it stops half-way, run blackpool_recover.',
53
+ inputSchema: { type: 'object', properties: { amount: AMOUNT, from: { type: 'string', description: 'Pool to swap from (eth|usdc on base/arbitrum, eth|usdg on robinhood, bnb|usdt on bsc).' }, chain: CHAIN, slippage_bps: { type: 'integer', minimum: 10, maximum: 500 } }, required: ['amount', 'from'], additionalProperties: false },
54
+ args: (a) => ['swap', String(a.amount), '--from', a.from, ...opt('--chain', a.chain), ...opt('--slippage', a.slippage_bps)],
55
+ },
56
+ {
57
+ name: 'blackpool_bridge', title: 'Private bridge', destructive: true,
58
+ description: 'Private cross-chain transfer via NEAR Intents. from = origin private pool (base_eth, base_usdc, hood_eth, hood_usdg, arb_eth, arb_usdc, bsc_bnb, bsc_usdc, bsc_usdt); dest_asset from blackpool_assets; recipient is EVM 0x… or a Solana address for sol_*. Returns once submitted unless wait=true.',
59
+ inputSchema: { type: 'object', properties: { dest_asset: { type: 'string' }, recipient: { type: 'string' }, amount: AMOUNT, from: { type: 'string' }, wait: { type: 'boolean', description: 'Poll delivery up to 6 minutes. Default false.' } }, required: ['dest_asset', 'recipient', 'amount', 'from'], additionalProperties: false },
60
+ args: (a) => ['bridge', a.dest_asset, a.recipient, String(a.amount), '--from', a.from, ...(a.wait ? [] : ['--no-wait'])],
61
+ },
62
+ {
63
+ name: 'blackpool_bridges', title: 'Bridge history', readOnly: true,
64
+ description: 'Every bridge of this account with status (PENDING_DEPOSIT, PROCESSING, SUCCESS, REFUNDED, FAILED) and refund address.',
65
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
66
+ args: () => ['bridges'],
67
+ },
68
+ {
69
+ name: 'blackpool_assets', title: 'Bridge assets', readOnly: true,
70
+ description: 'Bridge destination assets and valid origins.',
71
+ inputSchema: { type: 'object', properties: {}, additionalProperties: false },
72
+ args: () => ['assets'],
73
+ },
74
+ {
75
+ name: 'blackpool_recover', title: 'Recover', idempotent: true,
76
+ description: 'Sweep every registered burner that still holds funds back to the agent wallet (or to). Safe to run any time.',
77
+ inputSchema: { type: 'object', properties: { chain: { ...CHAIN, enum: [...CHAIN.enum, 'all'] }, to: { type: 'string', pattern: '^0x[0-9a-fA-F]{40}$' } }, additionalProperties: false },
78
+ args: (a) => ['recover', ...opt('--chain', a.chain || 'all'), ...opt('--to', a.to)],
79
+ },
80
+ ]
81
+
82
+ function opt(flag, v) { return v === undefined || v === null || v === '' ? [] : [flag, String(v)] }
83
+
84
+ function enabledTools() {
85
+ let list = TOOLS
86
+ if (process.env.BLACKPOOL_MCP_READONLY === '1') list = list.filter(t => t.readOnly)
87
+ const allow = (process.env.BLACKPOOL_MCP_TOOLS || '').split(',').map(s => s.trim()).filter(Boolean)
88
+ if (allow.length) list = list.filter(t => allow.includes(t.name) || allow.includes(t.name.replace(/^blackpool_/, '')))
89
+ return list
90
+ }
91
+
92
+ function describe(t) {
93
+ return {
94
+ name: t.name, title: t.title, description: t.description, inputSchema: t.inputSchema,
95
+ annotations: { title: t.title, readOnlyHint: !!t.readOnly, destructiveHint: !!t.destructive, idempotentHint: !!(t.readOnly || t.idempotent), openWorldHint: true },
96
+ }
97
+ }
98
+
99
+ function runCli(args, timeoutMs) {
100
+ return new Promise((resolve) => {
101
+ const child = spawn(process.execPath, [CLI, ...args, '--json'], { env: { ...process.env, BLACKPOOL_JSON: '1' }, stdio: ['ignore', 'pipe', 'pipe'] })
102
+ let out = '', err = ''
103
+ const timer = setTimeout(() => { child.kill('SIGTERM') }, timeoutMs)
104
+ child.stdout.on('data', d => { out += d })
105
+ child.stderr.on('data', d => { err += d; if (err.length > 20000) err = err.slice(-20000) })
106
+ child.on('close', (code) => {
107
+ clearTimeout(timer)
108
+ const last = out.trim().split('\n').pop() || ''
109
+ try { resolve({ code, json: JSON.parse(last) }) }
110
+ catch { resolve({ code, json: { ok: false, error: code === null ? `timed out after ${Math.round(timeoutMs / 1000)} s; check balance before retrying` : (err.trim().split('\n').pop() || 'no output') } }) }
111
+ })
112
+ })
113
+ }
114
+
115
+ function agentsDoc() {
116
+ try { return fs.readFileSync(path.join(__dirname, '..', 'AGENTS.md'), 'utf8') } catch { return 'See https://blackpool.cash/AGENTS.md' }
117
+ }
118
+
119
+ async function handle(msg) {
120
+ const { id, method, params = {} } = msg
121
+ switch (method) {
122
+ case 'initialize': {
123
+ const v = SUPPORTED.includes(params.protocolVersion) ? params.protocolVersion : SUPPORTED[0]
124
+ return {
125
+ protocolVersion: v,
126
+ capabilities: { tools: { listChanged: false }, resources: { listChanged: false } },
127
+ serverInfo: { name: 'blackpool', title: 'Blackpool', version: VERSION },
128
+ instructions: 'Private payments with zero-knowledge pools. Check blackpool_balance before paying; deposit first if the private balance is short. Never retry deposit, send, swap or bridge after a timeout without checking blackpool_balance or blackpool_bridges. blackpool_recover is always safe. Full guide: resource blackpool://agents.md.',
129
+ }
130
+ }
131
+ case 'ping': return {}
132
+ case 'tools/list': return { tools: enabledTools().map(describe) }
133
+ case 'tools/call': {
134
+ const t = enabledTools().find(x => x.name === params.name)
135
+ if (!t) throw rpcError(-32602, `unknown tool: ${params.name}`)
136
+ const a = params.arguments || {}
137
+ for (const r of t.inputSchema.required || []) if (a[r] === undefined || a[r] === '') return toolResult({ ok: false, error: `missing argument: ${r}` })
138
+ const { json } = await runCli(t.args(a), t.name === 'blackpool_bridge' && a.wait ? 450_000 : 330_000)
139
+ return toolResult(json)
140
+ }
141
+ case 'resources/list':
142
+ return { resources: [{ uri: 'blackpool://agents.md', name: 'AGENTS.md', title: 'Blackpool guide for agents', mimeType: 'text/markdown', description: 'Setup, commands, JSON fields, limits, recipes, errors and security rules.' }] }
143
+ case 'resources/read':
144
+ if (params.uri !== 'blackpool://agents.md') throw rpcError(-32002, 'resource not found')
145
+ return { contents: [{ uri: params.uri, mimeType: 'text/markdown', text: agentsDoc() }] }
146
+ case 'resources/templates/list': return { resourceTemplates: [] }
147
+ case 'prompts/list': return { prompts: [] }
148
+ default: throw rpcError(-32601, `method not found: ${method}`)
149
+ }
150
+ }
151
+
152
+ function toolResult(json) {
153
+ return { content: [{ type: 'text', text: JSON.stringify(json) }], structuredContent: json, isError: json.ok === false }
154
+ }
155
+ function rpcError(code, message) { return Object.assign(new Error(message), { rpc: { code, message } }) }
156
+
157
+ function serve() {
158
+ return new Promise((resolve) => {
159
+ const send = (obj) => process.stdout.write(JSON.stringify(obj) + '\n')
160
+ let buf = ''
161
+ process.stdin.setEncoding('utf8')
162
+ process.stdin.on('data', (chunk) => {
163
+ buf += chunk
164
+ let i
165
+ while ((i = buf.indexOf('\n')) >= 0) {
166
+ const line = buf.slice(0, i).trim(); buf = buf.slice(i + 1)
167
+ if (!line) continue
168
+ let msg
169
+ try { msg = JSON.parse(line) } catch { send({ jsonrpc: '2.0', id: null, error: { code: -32700, message: 'parse error' } }); continue }
170
+ const batch = Array.isArray(msg) ? msg : [msg]
171
+ for (const m of batch) {
172
+ if (m.id === undefined || m.id === null) continue // notification (initialized, cancelled, …)
173
+ handle(m).then(
174
+ (result) => send({ jsonrpc: '2.0', id: m.id, result }),
175
+ (e) => send({ jsonrpc: '2.0', id: m.id, error: e.rpc || { code: -32603, message: String(e.message || e) } }),
176
+ )
177
+ }
178
+ }
179
+ })
180
+ process.stdin.on('end', resolve)
181
+ })
182
+ }
183
+
184
+ module.exports = { serve, TOOLS }
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blackpool/cli",
3
- "version": "0.2.0",
3
+ "version": "0.3.0",
4
4
  "description": "Private payments for onchain agents. CLI for BLACKPOOL - shield, send, swap, bridge with zero-knowledge privacy on Base, Robinhood Chain, Arbitrum, and BNB Smart Chain.",
5
5
  "bin": {
6
6
  "blackpool": "bin/blackpool.js"
@@ -18,6 +18,8 @@
18
18
  "ethers": "^5.7.2"
19
19
  },
20
20
  "keywords": [
21
+ "mcp",
22
+ "model-context-protocol",
21
23
  "agents",
22
24
  "arbitrum",
23
25
  "base",