@blockrun/mcp 0.13.0 → 0.14.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/README.md +127 -1
- package/dist/index.js +360 -42
- package/package.json +3 -2
package/README.md
CHANGED
|
@@ -66,6 +66,14 @@ Prompts and a worked example for these are in [`skills/image-prompting/SKILL.md`
|
|
|
66
66
|
|
|
67
67
|
---
|
|
68
68
|
|
|
69
|
+
## Prerequisites
|
|
70
|
+
|
|
71
|
+
- **Node.js ≥ 18** (`node -v`)
|
|
72
|
+
- **~$5 USDC** on Base or Solana (the server auto-creates a wallet on first run; see [Fund your wallet](#fund-your-wallet))
|
|
73
|
+
- **An MCP client**: Claude Code, Claude Desktop, Cursor, Windsurf, or ChatGPT Desktop
|
|
74
|
+
|
|
75
|
+
---
|
|
76
|
+
|
|
69
77
|
## Install
|
|
70
78
|
|
|
71
79
|
**Claude Code (recommended)**
|
|
@@ -93,6 +101,23 @@ ensures `-y` is passed to `npx`, not parsed by `claude mcp add`.
|
|
|
93
101
|
claude mcp add blockrun -s user --transport http https://mcp.blockrun.ai/mcp
|
|
94
102
|
```
|
|
95
103
|
|
|
104
|
+
**Cursor** — add to `~/.cursor/mcp.json` (macOS / Linux) or `%APPDATA%\Cursor\mcp.json` (Windows):
|
|
105
|
+
```json
|
|
106
|
+
{
|
|
107
|
+
"mcpServers": {
|
|
108
|
+
"blockrun": {
|
|
109
|
+
"command": "npx",
|
|
110
|
+
"args": ["-y", "@blockrun/mcp@latest"]
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
**Windsurf** — same JSON, in:
|
|
117
|
+
- macOS: `~/.codeium/windsurf/mcp_config.json`
|
|
118
|
+
- Linux: `~/.config/.codeium/windsurf/mcp_config.json`
|
|
119
|
+
- Windows: `%APPDATA%\Codeium\windsurf\mcp_config.json`
|
|
120
|
+
|
|
96
121
|
---
|
|
97
122
|
|
|
98
123
|
## Fund your wallet
|
|
@@ -118,6 +143,7 @@ $5 covers ~5,000 market queries, ~500 Exa searches, ~250 image generations, or ~
|
|
|
118
143
|
| `blockrun_music` | MiniMax music generation | per track |
|
|
119
144
|
| `blockrun_price` | Pyth-backed realtime + OHLC — crypto / FX / commodity (free), 12 stock markets (paid) | free or $0.001/call |
|
|
120
145
|
| `blockrun_markets` | Polymarket (markets, candles, trades, orderbooks, leaderboards, smart-wallet PnL/clusters, UMA oracle), Kalshi, Limitless, Opinion, Predict.Fun, dFlow, Binance Futures, cross-platform match + search | $0.001–0.005/query |
|
|
146
|
+
| `blockrun_surf` | Surf (asksurf.ai) — 84 endpoints: CEX market data, on-chain SQL (13 chains, 80+ ClickHouse tables), 100M+ labeled wallets, Polymarket + Kalshi side-by-side, social mindshare, news, search, Surf-1.5 chat with citations | $0.001–0.02/call |
|
|
121
147
|
| `blockrun_x` | X/Twitter — profiles, tweets, followers, mentions, search (AttentionVC) | per call |
|
|
122
148
|
| `blockrun_exa` | Neural web search (Exa) — research, competitors, papers, URL content | $0.01/query |
|
|
123
149
|
| `blockrun_search` | Grok Live Search — web + news with citations | ~$0.025 per source |
|
|
@@ -127,12 +153,56 @@ $5 covers ~5,000 market queries, ~500 Exa searches, ~250 image generations, or ~
|
|
|
127
153
|
|
|
128
154
|
---
|
|
129
155
|
|
|
156
|
+
## Tips for effective LLMs
|
|
157
|
+
|
|
158
|
+
> **For LLMs and agents invoking BlockRun MCP tools:**
|
|
159
|
+
|
|
160
|
+
- **CRITICAL: When any `blockrun_*` tool returns a payment / balance / 402 error, call `blockrun_wallet` *first* to check status, then `action:"setup"` to get funding instructions.** Don't retry the failing tool blindly — the wallet is empty.
|
|
161
|
+
- **CRITICAL: For `blockrun_markets` and `blockrun_surf`, the 80+ endpoint catalog lives in `skills/markets/SKILL.md` and `skills/surf/SKILL.md`, NOT in the tool description.** Browse the skill before guessing endpoint paths.
|
|
162
|
+
- **CRITICAL: `blockrun_chat routing:"smart"` (ClawRouter) only works on Base wallets.** On Solana, pass `mode:` or `model:` to pick a model directly.
|
|
163
|
+
- **CRITICAL: `blockrun_music` and `blockrun_video` are payment-on-completion async.** Failures or client-side timeouts do NOT charge. Don't retry-loop them — they may take 60–180s.
|
|
164
|
+
- **CRITICAL: Before spawning child agents, allocate per-agent budget:** `blockrun_wallet action:"delegate" agent_id:"X" agent_limit:1.00`. Pass `agent_id:"X"` to every downstream `blockrun_*` call — the child is auto-blocked when the budget hits zero.
|
|
165
|
+
- **Free tier first for drafts**: `blockrun_chat mode:"free"` (NVIDIA), `blockrun_dex`, `blockrun_price` (crypto / FX / commodity), and `blockrun_models` are all $0. Use them to scaffold before paying for premium models.
|
|
166
|
+
|
|
167
|
+
---
|
|
168
|
+
|
|
169
|
+
## Key Use Cases
|
|
170
|
+
|
|
171
|
+
What kinds of questions can Claude (or any LLM agent) answer once BlockRun MCP is installed:
|
|
172
|
+
|
|
173
|
+
1. **Price reads / market data**
|
|
174
|
+
> *"What's BTC trading at? Compare with last week's average."* → `blockrun_price` (free) or `blockrun_surf` path:`market/price`
|
|
175
|
+
|
|
176
|
+
2. **Prediction-market consensus**
|
|
177
|
+
> *"What's Polymarket's odds for the next Fed rate decision?"* → `blockrun_markets` path:`polymarket/events` + filter
|
|
178
|
+
|
|
179
|
+
3. **On-chain forensics**
|
|
180
|
+
> *"This wallet (0xabc...) — what's it labeled as? What does it hold? When did it whale up?"* → `blockrun_surf` paths:`wallet/labels/batch`, `wallet/detail`, `wallet/net-worth`
|
|
181
|
+
|
|
182
|
+
4. **Cited research with sources**
|
|
183
|
+
> *"Find the 5 most-cited papers on speculative decoding from the last 90 days. Summarize the dominant approach."* → `blockrun_exa` action:`search` then `contents`
|
|
184
|
+
|
|
185
|
+
5. **Image generation with on-image text**
|
|
186
|
+
> *"Generate a poster announcing GPT-5.5 on BlockRun, retro-futuristic, with the headline 'NOW LIVE'."* → `blockrun_image` + the `image-prompting` skill 5-section framework
|
|
187
|
+
|
|
188
|
+
6. **Voice phone-out**
|
|
189
|
+
> *"Call +1-415-555-... and confirm the appointment on Friday at 3pm."* → `blockrun_phone` action:`voice_call`, then poll `voice_status`
|
|
190
|
+
|
|
191
|
+
7. **Multi-agent research with budget cap**
|
|
192
|
+
> *"Spawn 3 research agents on competing L1 narratives. Cap each at $0.50."* → `blockrun_wallet delegate × 3` → children call `blockrun_chat` + `blockrun_exa` with their `agent_id`
|
|
193
|
+
|
|
194
|
+
8. **Cross-chain SQL forensics**
|
|
195
|
+
> *"Top 10 tokens by DEX volume on Base in the last 24h."* → `blockrun_surf` path:`onchain/sql`, body: `{ sql: "SELECT..." }`
|
|
196
|
+
|
|
197
|
+
---
|
|
198
|
+
|
|
130
199
|
## Why not just use the APIs directly?
|
|
131
200
|
|
|
132
201
|
| | Direct APIs | BlockRun |
|
|
133
202
|
|---|---|---|
|
|
134
203
|
| Exa | Sign up, $20/mo minimum | $0.01/call, no subscription |
|
|
135
204
|
| Polymarket | Undocumented, rate-limited | $0.001/call, clean JSON |
|
|
205
|
+
| Surf (asksurf.ai) | Account + monthly plan | $0.001/call, no Surf account, 84 endpoints |
|
|
136
206
|
| Twitter/X API | $100–$5000/month | $0.03/page, no approval |
|
|
137
207
|
| Multiple sources | 4 accounts, 4 API keys, 4 billing pages | 1 wallet |
|
|
138
208
|
|
|
@@ -140,15 +210,71 @@ One wallet. All sources. No dashboards.
|
|
|
140
210
|
|
|
141
211
|
---
|
|
142
212
|
|
|
213
|
+
## When NOT to use BlockRun MCP
|
|
214
|
+
|
|
215
|
+
BlockRun shines when you want **unified billing + many sources + LLM-readable errors**. It is not the right fit for:
|
|
216
|
+
|
|
217
|
+
- **High-volume single-API workloads (≥10k calls/day to one source).** Direct subscriptions amortize better past the break-even point — Polymarket's free public API plus your own caching beats $0.001 × 10k/day if you don't need cross-source aggregation.
|
|
218
|
+
- **Compliance-sensitive flows that need a fiat invoice / audit trail.** BlockRun settles in USDC; receipts are on-chain (Basescan / Solscan) but are not tax invoices. For enterprise procurement, contract directly with the upstream provider.
|
|
219
|
+
- **Latency-critical sub-100ms reads.** Each x402 call adds ~200–500ms of payment-signing + settlement overhead vs. a direct authenticated request. For HFT-style flows, run your own infra.
|
|
220
|
+
- **You only need one source forever.** If you'll only ever call Polymarket, or only ever Exa, save the indirection — sign up upstream and skip the wallet.
|
|
221
|
+
|
|
222
|
+
Use BlockRun when you want pay-per-call for *exploration*, *aggregation*, or *agent-driven* workloads where you can't predict which source you'll reach for next.
|
|
223
|
+
|
|
224
|
+
---
|
|
225
|
+
|
|
143
226
|
## Multi-agent budget delegation
|
|
144
227
|
|
|
145
228
|
Delegate a spending budget to a child agent with `agent_id`. The child is auto-blocked when the budget runs out — useful for autonomous agents that shouldn't run up unbounded costs.
|
|
146
229
|
|
|
147
230
|
---
|
|
148
231
|
|
|
232
|
+
## Troubleshooting
|
|
233
|
+
|
|
234
|
+
- **`Insufficient balance` / HTTP 402 after retry** → Run `blockrun_wallet action:"setup"`. Send USDC on Base (or Solana — see [Environment Variables](#environment-variables)).
|
|
235
|
+
- **`Smart routing (ClawRouter) is not available on Solana`** → Pass `model:` or `mode:` explicitly to `blockrun_chat`, or switch back to Base by unsetting `SOLANA_WALLET_KEY` and removing `~/.blockrun/.solana-session`.
|
|
236
|
+
- **`claude mcp list` doesn't show `blockrun`** → Check `node -v` (must be ≥18). Clear the npx cache: `rm -rf ~/.npm/_npx`. Re-run the install command from above.
|
|
237
|
+
- **`fetch failed` / timeout when checking wallet balance** → Base RPC transient outage. The tool already falls through 3 public RPCs; retry after 30s. Persistent failures usually = local proxy / firewall blocking outbound RPC.
|
|
238
|
+
- **`ENOENT: ~/.blockrun/.session`** → Expected on first run. The server auto-creates the wallet; check stderr for the `WALLET_CREATED` line confirming the address.
|
|
239
|
+
- **`Video generation timed out` (5-min cap)** → Upstream Seedance / xAI queue congestion. **No charge** (payment-on-completion). Retry, or pick a faster model (`bytedance/seedance-1.5-pro`).
|
|
240
|
+
- **`Music generation timed out` (200s cap)** → Same pattern. **No charge**. Retry; if it persists, the upstream model is rate-limited — try off-peak.
|
|
241
|
+
|
|
242
|
+
---
|
|
243
|
+
|
|
244
|
+
## Environment Variables
|
|
245
|
+
|
|
246
|
+
| Variable / File | Default | Effect |
|
|
247
|
+
|---|---|---|
|
|
248
|
+
| `~/.blockrun/.session` | auto-created on first run | EVM private key (0x...). File exists → use Base. |
|
|
249
|
+
| `~/.blockrun/.solana-session` | not created | Solana private key. File exists → switch to Solana. |
|
|
250
|
+
| `SOLANA_WALLET_KEY` | unset | Env-var override of `.solana-session`. Set → use Solana. |
|
|
251
|
+
|
|
252
|
+
Chain selection priority (see `src/utils/wallet.ts:24`):
|
|
253
|
+
|
|
254
|
+
1. `SOLANA_WALLET_KEY` env var present → Solana
|
|
255
|
+
2. `~/.blockrun/.solana-session` exists → Solana
|
|
256
|
+
3. Otherwise → Base (`~/.blockrun/.session` auto-created)
|
|
257
|
+
|
|
258
|
+
**Switching chains:**
|
|
259
|
+
|
|
260
|
+
- Base → Solana: `export SOLANA_WALLET_KEY=...`, or `echo "<secret>" > ~/.blockrun/.solana-session`
|
|
261
|
+
- Solana → Base: `unset SOLANA_WALLET_KEY && rm ~/.blockrun/.solana-session` (the existing `.session` is reused, so it's the same Base wallet)
|
|
262
|
+
|
|
263
|
+
The server also runs a non-blocking npm registry check at startup and prints an `Update available` notice to stderr when a newer `@blockrun/mcp` version exists. Upgrade by re-running the install command — no manual `npm update` needed.
|
|
264
|
+
|
|
265
|
+
---
|
|
266
|
+
|
|
149
267
|
## How it works
|
|
150
268
|
|
|
151
|
-
Pay-per-call via [x402](https://x402.org) micropayments in USDC
|
|
269
|
+
Pay-per-call via [x402](https://x402.org) micropayments in USDC. Your wallet lives at `~/.blockrun/.session` (Base) or `~/.blockrun/.solana-session` (Solana). The private key never leaves your machine.
|
|
270
|
+
|
|
271
|
+
---
|
|
272
|
+
|
|
273
|
+
## Contributing
|
|
274
|
+
|
|
275
|
+
PRs welcome. See [CONTRIBUTING.md](./CONTRIBUTING.md) for setup, the tool-vs-skill design rule, and how to add a new partner API.
|
|
276
|
+
|
|
277
|
+
Issues: [github.com/blockrunai/blockrun-mcp/issues](https://github.com/blockrunai/blockrun-mcp/issues)
|
|
152
278
|
|
|
153
279
|
---
|
|
154
280
|
|
package/dist/index.js
CHANGED
|
@@ -6,38 +6,58 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
|
6
6
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
7
7
|
|
|
8
8
|
// src/utils/wallet.ts
|
|
9
|
+
import fs from "fs";
|
|
9
10
|
import {
|
|
10
11
|
LLMClient,
|
|
11
12
|
ImageClient,
|
|
12
13
|
PriceClient,
|
|
14
|
+
SolanaLLMClient,
|
|
13
15
|
getOrCreateWallet,
|
|
16
|
+
loadSolanaWallet,
|
|
14
17
|
getPaymentLinks,
|
|
15
18
|
formatWalletCreatedMessage,
|
|
16
|
-
formatNeedsFundingMessage
|
|
19
|
+
formatNeedsFundingMessage,
|
|
20
|
+
SOLANA_WALLET_FILE_PATH
|
|
17
21
|
} from "@blockrun/llm";
|
|
18
|
-
var
|
|
22
|
+
var _evmClient = null;
|
|
19
23
|
var _imageClient = null;
|
|
20
24
|
var _priceClient = null;
|
|
21
|
-
var
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
25
|
+
var _evmWalletInfo = null;
|
|
26
|
+
var _solanaClient = null;
|
|
27
|
+
function getChain() {
|
|
28
|
+
if (process.env.SOLANA_WALLET_KEY) return "solana";
|
|
29
|
+
try {
|
|
30
|
+
if (fs.existsSync(SOLANA_WALLET_FILE_PATH)) return "solana";
|
|
31
|
+
} catch {
|
|
32
|
+
}
|
|
33
|
+
return "base";
|
|
34
|
+
}
|
|
35
|
+
function ensureEvmWallet() {
|
|
36
|
+
if (!_evmWalletInfo) {
|
|
37
|
+
_evmWalletInfo = getOrCreateWallet();
|
|
38
|
+
if (_evmWalletInfo.isNew) {
|
|
39
|
+
console.error(formatWalletCreatedMessage(_evmWalletInfo.address));
|
|
27
40
|
}
|
|
28
41
|
}
|
|
29
|
-
return
|
|
42
|
+
return _evmWalletInfo;
|
|
30
43
|
}
|
|
31
44
|
function getOrCreateWalletKey() {
|
|
32
|
-
const info =
|
|
45
|
+
const info = ensureEvmWallet();
|
|
33
46
|
return info.privateKey;
|
|
34
47
|
}
|
|
35
48
|
function getClient() {
|
|
36
|
-
if (
|
|
49
|
+
if (getChain() === "solana") {
|
|
50
|
+
if (!_solanaClient) {
|
|
51
|
+
const privateKey = process.env.SOLANA_WALLET_KEY || loadSolanaWallet() || void 0;
|
|
52
|
+
_solanaClient = new SolanaLLMClient(privateKey ? { privateKey } : void 0);
|
|
53
|
+
}
|
|
54
|
+
return _solanaClient;
|
|
55
|
+
}
|
|
56
|
+
if (!_evmClient) {
|
|
37
57
|
const privateKey = getOrCreateWalletKey();
|
|
38
|
-
|
|
58
|
+
_evmClient = new LLMClient({ privateKey });
|
|
39
59
|
}
|
|
40
|
-
return
|
|
60
|
+
return _evmClient;
|
|
41
61
|
}
|
|
42
62
|
function getImageClient() {
|
|
43
63
|
if (!_imageClient) {
|
|
@@ -53,8 +73,21 @@ function getPriceClient() {
|
|
|
53
73
|
}
|
|
54
74
|
return _priceClient;
|
|
55
75
|
}
|
|
56
|
-
function getWalletInfo() {
|
|
57
|
-
|
|
76
|
+
async function getWalletInfo() {
|
|
77
|
+
if (getChain() === "solana") {
|
|
78
|
+
const client = getClient();
|
|
79
|
+
const address = await client.getWalletAddress();
|
|
80
|
+
return {
|
|
81
|
+
address,
|
|
82
|
+
network: "Solana",
|
|
83
|
+
chainId: null,
|
|
84
|
+
currency: "USDC",
|
|
85
|
+
isNew: false,
|
|
86
|
+
explorerUrl: `https://solscan.io/account/${address}`,
|
|
87
|
+
fundingUrl: "https://sol.blockrun.ai"
|
|
88
|
+
};
|
|
89
|
+
}
|
|
90
|
+
const info = ensureEvmWallet();
|
|
58
91
|
const links = getPaymentLinks(info.address);
|
|
59
92
|
return {
|
|
60
93
|
address: info.address,
|
|
@@ -62,11 +95,19 @@ function getWalletInfo() {
|
|
|
62
95
|
chainId: 8453,
|
|
63
96
|
currency: "USDC",
|
|
64
97
|
isNew: info.isNew,
|
|
65
|
-
|
|
98
|
+
explorerUrl: links.basescan,
|
|
66
99
|
fundingUrl: links.blockrun
|
|
67
100
|
};
|
|
68
101
|
}
|
|
69
102
|
async function getUsdcBalance(address) {
|
|
103
|
+
if (getChain() === "solana") {
|
|
104
|
+
try {
|
|
105
|
+
const client = getClient();
|
|
106
|
+
return await client.getBalance();
|
|
107
|
+
} catch {
|
|
108
|
+
return null;
|
|
109
|
+
}
|
|
110
|
+
}
|
|
70
111
|
const USDC_ADDRESS2 = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
71
112
|
const BASE_RPC_URLS = [
|
|
72
113
|
"https://mainnet.base.org",
|
|
@@ -101,7 +142,8 @@ import { z } from "zod";
|
|
|
101
142
|
// src/utils/qr.ts
|
|
102
143
|
import QRCode from "qrcode";
|
|
103
144
|
import open from "open";
|
|
104
|
-
import * as
|
|
145
|
+
import * as fs2 from "fs";
|
|
146
|
+
import sharp from "sharp";
|
|
105
147
|
|
|
106
148
|
// src/utils/constants.ts
|
|
107
149
|
import * as path from "path";
|
|
@@ -123,22 +165,53 @@ var MODEL_TIERS = {
|
|
|
123
165
|
};
|
|
124
166
|
|
|
125
167
|
// src/utils/qr.ts
|
|
168
|
+
var SOLANA_USDC_MINT = "EPjFWdd5AufqSSqeM2qN1xzybapC8G4wEGGkZwyTDt1v";
|
|
126
169
|
function getEip681Uri(address, amountUsdc = 1) {
|
|
127
170
|
const amountWei = Math.floor(amountUsdc * 1e6);
|
|
128
171
|
return `ethereum:${USDC_ADDRESS}@${BASE_CHAIN_ID}/transfer?address=${address}&uint256=${amountWei}`;
|
|
129
172
|
}
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
173
|
+
function getSolanaPayUri(address, amountUsdc = 1) {
|
|
174
|
+
return `solana:${address}?spl-token=${SOLANA_USDC_MINT}&amount=${amountUsdc}&label=BlockRun`;
|
|
175
|
+
}
|
|
176
|
+
function buildSolanaLogoSvg(size) {
|
|
177
|
+
const half = size / 2;
|
|
178
|
+
return `<svg width="${size}" height="${size}" xmlns="http://www.w3.org/2000/svg">
|
|
179
|
+
<defs>
|
|
180
|
+
<linearGradient id="g" x1="0%" y1="0%" x2="100%" y2="100%">
|
|
181
|
+
<stop offset="0%" style="stop-color:#9945FF"/>
|
|
182
|
+
<stop offset="100%" style="stop-color:#14F195"/>
|
|
183
|
+
</linearGradient>
|
|
184
|
+
<clipPath id="c"><circle cx="${half}" cy="${half}" r="${half}"/></clipPath>
|
|
185
|
+
</defs>
|
|
186
|
+
<circle cx="${half}" cy="${half}" r="${half}" fill="url(#g)" clip-path="url(#c)"/>
|
|
187
|
+
<text x="${half}" y="${half + 14}" font-size="40" font-weight="bold" fill="white"
|
|
188
|
+
font-family="Arial,sans-serif" text-anchor="middle">\u25CE</text>
|
|
189
|
+
</svg>`;
|
|
190
|
+
}
|
|
191
|
+
async function overlayLogo(qrBuf, chain, qrSize) {
|
|
192
|
+
if (chain !== "solana") return qrBuf;
|
|
193
|
+
const logoSize = Math.round(qrSize * 0.18);
|
|
194
|
+
const pad = Math.round(logoSize * 0.08);
|
|
195
|
+
const logoBuf = await sharp(Buffer.from(buildSolanaLogoSvg(logoSize))).resize(logoSize, logoSize).extend({ top: pad, bottom: pad, left: pad, right: pad, background: { r: 255, g: 255, b: 255, alpha: 1 } }).toBuffer();
|
|
196
|
+
const totalSize = logoSize + pad * 2;
|
|
197
|
+
const offset = Math.round((qrSize - totalSize) / 2);
|
|
198
|
+
return sharp(qrBuf).composite([{ input: logoBuf, left: offset, top: offset }]).toBuffer();
|
|
199
|
+
}
|
|
200
|
+
async function generateQrPng(address, chain = "base") {
|
|
201
|
+
const uri = chain === "solana" ? getSolanaPayUri(address) : getEip681Uri(address);
|
|
202
|
+
const qrSize = 400;
|
|
203
|
+
if (!fs2.existsSync(WALLET_DIR)) {
|
|
204
|
+
fs2.mkdirSync(WALLET_DIR, { recursive: true, mode: 448 });
|
|
134
205
|
}
|
|
135
|
-
await QRCode.
|
|
206
|
+
const qrBuf = await QRCode.toBuffer(uri, {
|
|
136
207
|
type: "png",
|
|
137
|
-
width:
|
|
208
|
+
width: qrSize,
|
|
138
209
|
margin: 2,
|
|
139
210
|
errorCorrectionLevel: "H",
|
|
140
211
|
color: { dark: "#000000", light: "#FFFFFF" }
|
|
141
212
|
});
|
|
213
|
+
const finalBuf = await overlayLogo(qrBuf, chain, qrSize);
|
|
214
|
+
fs2.writeFileSync(QR_FILE, finalBuf);
|
|
142
215
|
return QR_FILE;
|
|
143
216
|
}
|
|
144
217
|
async function openQrInViewer(qrPath) {
|
|
@@ -157,15 +230,17 @@ function formatError(message) {
|
|
|
157
230
|
if (isServerError) {
|
|
158
231
|
errorText += `
|
|
159
232
|
|
|
160
|
-
This is a temporary API issue. The
|
|
233
|
+
This is a temporary API issue. The API may be experiencing problems.
|
|
161
234
|
Try again in a few minutes, or use a different model (e.g., openai/gpt-4o).`;
|
|
162
235
|
} else if (isPaymentError) {
|
|
236
|
+
const chain = getChain();
|
|
237
|
+
const network = chain === "solana" ? "Solana" : "Base";
|
|
163
238
|
errorText += `
|
|
164
239
|
|
|
165
240
|
This error usually means your wallet needs funding.
|
|
166
241
|
Run blockrun_wallet with action: "setup" to get funding instructions.
|
|
167
242
|
|
|
168
|
-
Quick fix: Send USDC to your wallet on
|
|
243
|
+
Quick fix: Send USDC to your wallet on ${network} network.`;
|
|
169
244
|
}
|
|
170
245
|
return errorText;
|
|
171
246
|
}
|
|
@@ -210,8 +285,9 @@ Do NOT call this for actual AI queries \u2014 use blockrun_chat for that.`,
|
|
|
210
285
|
}
|
|
211
286
|
},
|
|
212
287
|
async ({ action, budget_action, budget_amount, agent_id, agent_limit }) => {
|
|
213
|
-
const info = getWalletInfo();
|
|
288
|
+
const info = await getWalletInfo();
|
|
214
289
|
const address = info.address;
|
|
290
|
+
const chain = getChain();
|
|
215
291
|
if (action === "budget") {
|
|
216
292
|
const budgetAct = budget_action || "check";
|
|
217
293
|
if (budgetAct === "set") {
|
|
@@ -289,10 +365,11 @@ Pass agent_id: "${agent_id}" in any blockrun_* tool call to track and enforce th
|
|
|
289
365
|
}
|
|
290
366
|
if (action === "qr") {
|
|
291
367
|
try {
|
|
292
|
-
const qrPath = await generateQrPng(address);
|
|
368
|
+
const qrPath = await generateQrPng(address, chain);
|
|
293
369
|
await openQrInViewer(qrPath);
|
|
370
|
+
const scanNote = chain === "solana" ? "Scan with a Solana wallet (Phantom, Solflare) to send USDC on Solana." : "Scan with MetaMask to send USDC on Base.";
|
|
294
371
|
return {
|
|
295
|
-
content: [{ type: "text", text: `QR code opened!
|
|
372
|
+
content: [{ type: "text", text: `QR code opened! ${scanNote}
|
|
296
373
|
|
|
297
374
|
Address: ${address}
|
|
298
375
|
QR saved: ${qrPath}` }]
|
|
@@ -307,14 +384,46 @@ QR saved: ${qrPath}` }]
|
|
|
307
384
|
if (action === "setup") {
|
|
308
385
|
let qrMessage = "";
|
|
309
386
|
try {
|
|
310
|
-
const qrPath = await generateQrPng(address);
|
|
387
|
+
const qrPath = await generateQrPng(address, chain);
|
|
311
388
|
await openQrInViewer(qrPath);
|
|
312
389
|
qrMessage = `
|
|
313
390
|
QR code opened for scanning! (${qrPath})`;
|
|
314
391
|
} catch {
|
|
315
392
|
qrMessage = "\n(QR generation failed - use address above)";
|
|
316
393
|
}
|
|
317
|
-
const text2 = `
|
|
394
|
+
const text2 = chain === "solana" ? `
|
|
395
|
+
================================================================================
|
|
396
|
+
BLOCKRUN WALLET SETUP (SOLANA)
|
|
397
|
+
================================================================================
|
|
398
|
+
|
|
399
|
+
Your Solana wallet address: ${address}
|
|
400
|
+
${qrMessage}
|
|
401
|
+
|
|
402
|
+
HOW TO FUND YOUR WALLET:
|
|
403
|
+
------------------------
|
|
404
|
+
|
|
405
|
+
Option 1: Transfer from Coinbase
|
|
406
|
+
1. Open Coinbase app or website
|
|
407
|
+
2. Go to Send/Receive \u2192 Select USDC
|
|
408
|
+
3. Choose "Solana" network (important!)
|
|
409
|
+
4. Paste: ${address}
|
|
410
|
+
5. Send $1-5 to start
|
|
411
|
+
|
|
412
|
+
Option 2: Transfer from any Solana wallet (Phantom, Solflare, Backpack)
|
|
413
|
+
- Send USDC (SPL) to: ${address}
|
|
414
|
+
- Make sure to use Solana network, not EVM
|
|
415
|
+
|
|
416
|
+
Option 3: Bridge from other chains
|
|
417
|
+
https://portalbridge.com \u2192 Bridge USDC to Solana \u2192 Send to address above
|
|
418
|
+
|
|
419
|
+
VERIFY BALANCE: https://solscan.io/account/${address}
|
|
420
|
+
|
|
421
|
+
PRICING (pay per use):
|
|
422
|
+
- GPT-4o: ~$0.005/request | Claude Sonnet: ~$0.003/request
|
|
423
|
+
- Gemini Flash: ~$0.0001/request | Full pricing: https://blockrun.ai/pricing
|
|
424
|
+
|
|
425
|
+
SECURITY: Private key stored at ~/.blockrun/.solana-session (never leaves your machine)
|
|
426
|
+
================================================================================` : `
|
|
318
427
|
================================================================================
|
|
319
428
|
BLOCKRUN WALLET SETUP
|
|
320
429
|
================================================================================
|
|
@@ -351,9 +460,10 @@ SECURITY: Private key stored at ~/.blockrun/.session (never leaves your machine)
|
|
|
351
460
|
const balance = await getUsdcBalance(address);
|
|
352
461
|
const balanceStr = balance !== null ? `$${balance.toFixed(6)} USDC` : "Unable to fetch";
|
|
353
462
|
const lowBalance = balance !== null && balance < 1;
|
|
463
|
+
const explorerLabel = chain === "solana" ? "Solscan" : "Basescan";
|
|
354
464
|
const text = `Wallet: ${address}
|
|
355
465
|
Balance: ${balanceStr}${lowBalance ? " (low - add funds)" : ""}
|
|
356
|
-
Network:
|
|
466
|
+
Network: ${info.network} | View: ${info.explorerUrl}
|
|
357
467
|
${info.isNew ? "\nNEW WALLET - Run with action: 'setup' for funding instructions" : ""}`;
|
|
358
468
|
return {
|
|
359
469
|
content: [{ type: "text", text }],
|
|
@@ -363,7 +473,8 @@ ${info.isNew ? "\nNEW WALLET - Run with action: 'setup' for funding instructions
|
|
|
363
473
|
network: info.network,
|
|
364
474
|
chainId: info.chainId,
|
|
365
475
|
isNew: info.isNew,
|
|
366
|
-
|
|
476
|
+
explorerUrl: info.explorerUrl,
|
|
477
|
+
explorerLabel
|
|
367
478
|
}
|
|
368
479
|
};
|
|
369
480
|
}
|
|
@@ -372,6 +483,7 @@ ${info.isNew ? "\nNEW WALLET - Run with action: 'setup' for funding instructions
|
|
|
372
483
|
|
|
373
484
|
// src/tools/chat.ts
|
|
374
485
|
import { z as z2 } from "zod";
|
|
486
|
+
import { LLMClient as LLMClient2 } from "@blockrun/llm";
|
|
375
487
|
|
|
376
488
|
// src/utils/budget.ts
|
|
377
489
|
function checkBudget(budget, agentId) {
|
|
@@ -448,6 +560,12 @@ Run blockrun_models to see all 41+ models with pricing.`,
|
|
|
448
560
|
};
|
|
449
561
|
}
|
|
450
562
|
if (routing === "smart") {
|
|
563
|
+
if (!(llm instanceof LLMClient2)) {
|
|
564
|
+
return {
|
|
565
|
+
content: [{ type: "text", text: "Smart routing (ClawRouter) is not available on Solana. Use a specific model or mode instead." }],
|
|
566
|
+
isError: true
|
|
567
|
+
};
|
|
568
|
+
}
|
|
451
569
|
try {
|
|
452
570
|
const result = await llm.smartChat(message, {
|
|
453
571
|
system,
|
|
@@ -1594,6 +1712,201 @@ Pricing:
|
|
|
1594
1712
|
);
|
|
1595
1713
|
}
|
|
1596
1714
|
|
|
1715
|
+
// src/tools/phone.ts
|
|
1716
|
+
import { z as z14 } from "zod";
|
|
1717
|
+
function registerPhoneTool(server) {
|
|
1718
|
+
server.registerTool(
|
|
1719
|
+
"blockrun_phone",
|
|
1720
|
+
{
|
|
1721
|
+
description: `Phone number intelligence, provisioning, and outbound AI voice calls via BlockRun x402.
|
|
1722
|
+
|
|
1723
|
+
Pricing:
|
|
1724
|
+
- lookup: $0.01 \u2014 carrier + line type for any number
|
|
1725
|
+
- lookup_fraud: $0.05 \u2014 + SIM swap / call forwarding signals
|
|
1726
|
+
- numbers_buy: $5.00 \u2014 provision a US/CA number for 30 days
|
|
1727
|
+
- numbers_renew: $5.00 \u2014 extend lease 30 days
|
|
1728
|
+
- numbers_list: $0.001 \u2014 list your wallet-owned numbers
|
|
1729
|
+
- numbers_release: free \u2014 release number back to pool
|
|
1730
|
+
- voice_call: $0.54 flat \u2014 outbound AI voice call via Bland.ai (up to 5 min default)
|
|
1731
|
+
- voice_status: free \u2014 poll call status + transcript
|
|
1732
|
+
|
|
1733
|
+
Voice call flow:
|
|
1734
|
+
1. blockrun_phone action:"voice_call" to:"+1..." task:"Confirm appointment for..."
|
|
1735
|
+
2. Returns call_id immediately (call runs async)
|
|
1736
|
+
3. blockrun_phone action:"voice_status" call_id:"..." to poll until completed
|
|
1737
|
+
|
|
1738
|
+
Voice presets: nat, josh, maya, june, paige, derek, florian
|
|
1739
|
+
Phone numbers use E.164 format: +14155552671`,
|
|
1740
|
+
inputSchema: {
|
|
1741
|
+
action: z14.enum([
|
|
1742
|
+
"lookup",
|
|
1743
|
+
"lookup_fraud",
|
|
1744
|
+
"numbers_buy",
|
|
1745
|
+
"numbers_renew",
|
|
1746
|
+
"numbers_list",
|
|
1747
|
+
"numbers_release",
|
|
1748
|
+
"voice_call",
|
|
1749
|
+
"voice_status"
|
|
1750
|
+
]).describe("Action to perform"),
|
|
1751
|
+
phone_number: z14.string().optional().describe("E.164 phone number, e.g. +14155552671 (required for lookup, lookup_fraud, numbers_renew, numbers_release)"),
|
|
1752
|
+
country: z14.string().optional().describe("Country for numbers_buy: US or CA (default: US)"),
|
|
1753
|
+
area_code: z14.string().optional().describe("Preferred 3-digit area code for numbers_buy (best effort)"),
|
|
1754
|
+
to: z14.string().optional().describe("Destination E.164 number (required for voice_call)"),
|
|
1755
|
+
task: z14.string().optional().describe("What the AI should do on the call, 10\u20134000 chars (required for voice_call)"),
|
|
1756
|
+
from: z14.string().optional().describe("Your provisioned BlockRun caller ID number (optional for voice_call)"),
|
|
1757
|
+
voice: z14.enum(["nat", "josh", "maya", "june", "paige", "derek", "florian"]).optional().describe("AI voice preset"),
|
|
1758
|
+
max_duration: z14.number().min(1).max(30).optional().describe("Max call duration in minutes (1\u201330, default: 5)"),
|
|
1759
|
+
language: z14.string().optional().describe("Language code, e.g. en-US (default: en-US)"),
|
|
1760
|
+
first_sentence: z14.string().optional().describe("Custom opening line for the AI agent"),
|
|
1761
|
+
wait_for_greeting: z14.boolean().optional().describe("Wait for recipient to speak before AI starts"),
|
|
1762
|
+
call_id: z14.string().optional().describe("Call ID from voice_call response (required for voice_status)")
|
|
1763
|
+
}
|
|
1764
|
+
},
|
|
1765
|
+
async ({
|
|
1766
|
+
action,
|
|
1767
|
+
phone_number,
|
|
1768
|
+
country,
|
|
1769
|
+
area_code,
|
|
1770
|
+
to,
|
|
1771
|
+
task,
|
|
1772
|
+
from,
|
|
1773
|
+
voice,
|
|
1774
|
+
max_duration,
|
|
1775
|
+
language,
|
|
1776
|
+
first_sentence,
|
|
1777
|
+
wait_for_greeting,
|
|
1778
|
+
call_id
|
|
1779
|
+
}) => {
|
|
1780
|
+
const client = getClient();
|
|
1781
|
+
const req = client;
|
|
1782
|
+
const chain = getChain();
|
|
1783
|
+
try {
|
|
1784
|
+
let result;
|
|
1785
|
+
switch (action) {
|
|
1786
|
+
case "lookup": {
|
|
1787
|
+
if (!phone_number) return { content: [{ type: "text", text: "phone_number required (E.164)" }], isError: true };
|
|
1788
|
+
result = await req.requestWithPaymentRaw("/v1/phone/lookup", { phoneNumber: phone_number });
|
|
1789
|
+
break;
|
|
1790
|
+
}
|
|
1791
|
+
case "lookup_fraud": {
|
|
1792
|
+
if (!phone_number) return { content: [{ type: "text", text: "phone_number required (E.164)" }], isError: true };
|
|
1793
|
+
result = await req.requestWithPaymentRaw("/v1/phone/lookup/fraud", { phoneNumber: phone_number });
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
case "numbers_buy": {
|
|
1797
|
+
const body = {};
|
|
1798
|
+
if (country) body.country = country;
|
|
1799
|
+
if (area_code) body.areaCode = area_code;
|
|
1800
|
+
result = await req.requestWithPaymentRaw("/v1/phone/numbers/buy", body);
|
|
1801
|
+
break;
|
|
1802
|
+
}
|
|
1803
|
+
case "numbers_renew": {
|
|
1804
|
+
if (!phone_number) return { content: [{ type: "text", text: "phone_number required (E.164)" }], isError: true };
|
|
1805
|
+
result = await req.requestWithPaymentRaw("/v1/phone/numbers/renew", { phoneNumber: phone_number });
|
|
1806
|
+
break;
|
|
1807
|
+
}
|
|
1808
|
+
case "numbers_list": {
|
|
1809
|
+
result = await req.requestWithPaymentRaw("/v1/phone/numbers/list", {});
|
|
1810
|
+
break;
|
|
1811
|
+
}
|
|
1812
|
+
case "numbers_release": {
|
|
1813
|
+
if (!phone_number) return { content: [{ type: "text", text: "phone_number required (E.164)" }], isError: true };
|
|
1814
|
+
result = await req.requestWithPaymentRaw("/v1/phone/numbers/release", { phoneNumber: phone_number });
|
|
1815
|
+
break;
|
|
1816
|
+
}
|
|
1817
|
+
case "voice_call": {
|
|
1818
|
+
if (!to) return { content: [{ type: "text", text: "to (destination phone number) required" }], isError: true };
|
|
1819
|
+
if (!task) return { content: [{ type: "text", text: "task required (what the AI should do on the call)" }], isError: true };
|
|
1820
|
+
const body = { to, task };
|
|
1821
|
+
if (from) body.from = from;
|
|
1822
|
+
if (voice) body.voice = voice;
|
|
1823
|
+
if (max_duration !== void 0) body.max_duration = max_duration;
|
|
1824
|
+
if (language) body.language = language;
|
|
1825
|
+
if (first_sentence) body.first_sentence = first_sentence;
|
|
1826
|
+
if (wait_for_greeting !== void 0) body.wait_for_greeting = wait_for_greeting;
|
|
1827
|
+
result = await req.requestWithPaymentRaw("/v1/voice/call", body);
|
|
1828
|
+
break;
|
|
1829
|
+
}
|
|
1830
|
+
case "voice_status": {
|
|
1831
|
+
if (!call_id) return { content: [{ type: "text", text: "call_id required" }], isError: true };
|
|
1832
|
+
const apiBase = chain === "solana" ? "https://sol.blockrun.ai/api" : "https://blockrun.ai/api";
|
|
1833
|
+
const resp = await fetch(`${apiBase}/v1/voice/call/${encodeURIComponent(call_id)}`, {
|
|
1834
|
+
signal: AbortSignal.timeout(15e3)
|
|
1835
|
+
});
|
|
1836
|
+
if (!resp.ok) {
|
|
1837
|
+
const err = await resp.text().catch(() => resp.statusText);
|
|
1838
|
+
return { content: [{ type: "text", text: formatError(`voice_status ${resp.status}: ${err}`) }], isError: true };
|
|
1839
|
+
}
|
|
1840
|
+
result = await resp.json();
|
|
1841
|
+
break;
|
|
1842
|
+
}
|
|
1843
|
+
}
|
|
1844
|
+
const text = typeof result === "object" ? JSON.stringify(result, null, 2) : String(result);
|
|
1845
|
+
return {
|
|
1846
|
+
content: [{ type: "text", text }],
|
|
1847
|
+
structuredContent: result
|
|
1848
|
+
};
|
|
1849
|
+
} catch (err) {
|
|
1850
|
+
return { content: [{ type: "text", text: formatError(err instanceof Error ? err.message : String(err)) }], isError: true };
|
|
1851
|
+
}
|
|
1852
|
+
}
|
|
1853
|
+
);
|
|
1854
|
+
}
|
|
1855
|
+
|
|
1856
|
+
// src/tools/surf.ts
|
|
1857
|
+
import { z as z15 } from "zod";
|
|
1858
|
+
function registerSurfTool(server) {
|
|
1859
|
+
server.registerTool(
|
|
1860
|
+
"blockrun_surf",
|
|
1861
|
+
{
|
|
1862
|
+
description: `Unified crypto data via Surf (asksurf.ai) \u2014 84 endpoints, one API.
|
|
1863
|
+
|
|
1864
|
+
Coverage: CEX market data (16 exchanges), on-chain SQL across 13 chains, 100M+ labeled wallets, prediction markets (Polymarket + Kalshi), social mindshare / CT intelligence, news, unified search, and Surf-1.5 chat with citations.
|
|
1865
|
+
|
|
1866
|
+
Pricing (settled in USDC to Surf's Base treasury):
|
|
1867
|
+
- Tier 1 $0.001 \u2014 prices, rankings, lists, news, profiles, simple reads
|
|
1868
|
+
- Tier 2 $0.005 \u2014 order books, candles, search, wallet detail, social aggregates
|
|
1869
|
+
- Tier 3 $0.020 \u2014 raw on-chain SQL, structured queries, surf-1.5 chat
|
|
1870
|
+
|
|
1871
|
+
Common paths (full 84-endpoint catalog in the surf skill):
|
|
1872
|
+
- market/price?symbol=BTC (T1)
|
|
1873
|
+
- exchange/price?pair=BTC-USDT (T1)
|
|
1874
|
+
- prediction-market/polymarket/ranking (T1)
|
|
1875
|
+
- search/web?q=ethereum+pectra+upgrade (T2)
|
|
1876
|
+
- wallet/detail?address=0x... (T2)
|
|
1877
|
+
- social/mindshare?q=ethereum&interval=1d (T2)
|
|
1878
|
+
- onchain/sql + body:{ sql: "SELECT ..." } (T3)
|
|
1879
|
+
- chat/completions + body:{ model:"surf/surf-1.5", messages:[]} (T3, $0.02 flat)
|
|
1880
|
+
|
|
1881
|
+
Method is auto-routed: pass 'body' for POST endpoints; otherwise GET with 'params'.
|
|
1882
|
+
Each Surf endpoint pre-validates required params before settling \u2014 you get a 400 (not a charge) if a required field is missing. Browse the full catalog: https://blockrun.ai/marketplace/surf`,
|
|
1883
|
+
inputSchema: {
|
|
1884
|
+
path: z15.string().describe("Endpoint path under /v1/surf/, e.g. 'market/price', 'prediction-market/polymarket/ranking', 'wallet/detail', 'onchain/sql', 'chat/completions'"),
|
|
1885
|
+
params: z15.record(z15.string(), z15.string()).optional().describe("Query parameters for GET endpoints, e.g. { symbol: 'BTC' } or { address: '0x...', chain: 'ethereum' }"),
|
|
1886
|
+
body: z15.any().optional().describe("JSON body for POST endpoints. Provide for: onchain/query, onchain/sql, chat/completions. When set, the call is sent as POST; otherwise GET with params.")
|
|
1887
|
+
}
|
|
1888
|
+
},
|
|
1889
|
+
async ({ path: path3, params, body }) => {
|
|
1890
|
+
try {
|
|
1891
|
+
const client = getClient();
|
|
1892
|
+
const cleanPath = path3.replace(/^\/+/, "").replace(/^v1\/surf\//, "").replace(/^api\/v1\/surf\//, "");
|
|
1893
|
+
const endpoint = `/v1/surf/${cleanPath}`;
|
|
1894
|
+
const result = body !== void 0 ? await client.requestWithPaymentRaw(endpoint, body) : await client.getWithPaymentRaw(endpoint, params);
|
|
1895
|
+
return {
|
|
1896
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1897
|
+
structuredContent: result
|
|
1898
|
+
};
|
|
1899
|
+
} catch (err) {
|
|
1900
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
1901
|
+
return {
|
|
1902
|
+
content: [{ type: "text", text: formatError(errMsg) }],
|
|
1903
|
+
isError: true
|
|
1904
|
+
};
|
|
1905
|
+
}
|
|
1906
|
+
}
|
|
1907
|
+
);
|
|
1908
|
+
}
|
|
1909
|
+
|
|
1597
1910
|
// src/mcp-handler.ts
|
|
1598
1911
|
function initializeMcpServer(server) {
|
|
1599
1912
|
const budget = { limit: null, spent: 0, calls: 0, agents: /* @__PURE__ */ new Map() };
|
|
@@ -1611,17 +1924,22 @@ function initializeMcpServer(server) {
|
|
|
1611
1924
|
registerTwitterTool(server);
|
|
1612
1925
|
registerDexTool(server);
|
|
1613
1926
|
registerModalTool(server);
|
|
1927
|
+
registerPhoneTool(server);
|
|
1928
|
+
registerSurfTool(server);
|
|
1614
1929
|
server.registerResource(
|
|
1615
1930
|
"wallet",
|
|
1616
1931
|
"blockrun://wallet",
|
|
1617
1932
|
{ description: "Wallet address and status", mimeType: "application/json" },
|
|
1618
|
-
async () =>
|
|
1619
|
-
|
|
1620
|
-
|
|
1621
|
-
|
|
1622
|
-
|
|
1623
|
-
|
|
1624
|
-
|
|
1933
|
+
async () => {
|
|
1934
|
+
const info = await getWalletInfo();
|
|
1935
|
+
return {
|
|
1936
|
+
contents: [{
|
|
1937
|
+
uri: "blockrun://wallet",
|
|
1938
|
+
mimeType: "application/json",
|
|
1939
|
+
text: JSON.stringify(info, null, 2)
|
|
1940
|
+
}]
|
|
1941
|
+
};
|
|
1942
|
+
}
|
|
1625
1943
|
);
|
|
1626
1944
|
server.registerResource(
|
|
1627
1945
|
"models",
|
|
@@ -1647,7 +1965,7 @@ function initializeMcpServer(server) {
|
|
|
1647
1965
|
}
|
|
1648
1966
|
|
|
1649
1967
|
// src/utils/key-leak-scanner.ts
|
|
1650
|
-
import
|
|
1968
|
+
import fs3 from "fs";
|
|
1651
1969
|
import path2 from "path";
|
|
1652
1970
|
import os2 from "os";
|
|
1653
1971
|
function looksLikeRawPrivateKey(value) {
|
|
@@ -1674,8 +1992,8 @@ function walk(obj, file, jsonPath, out) {
|
|
|
1674
1992
|
}
|
|
1675
1993
|
function scanFile(file) {
|
|
1676
1994
|
try {
|
|
1677
|
-
if (!
|
|
1678
|
-
const raw =
|
|
1995
|
+
if (!fs3.existsSync(file)) return [];
|
|
1996
|
+
const raw = fs3.readFileSync(file, "utf-8");
|
|
1679
1997
|
const data = JSON.parse(raw);
|
|
1680
1998
|
const out = [];
|
|
1681
1999
|
walk(data, file, "", out);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.14.0",
|
|
4
4
|
"mcpName": "io.github.BlockRunAI/blockrun-mcp",
|
|
5
5
|
"description": "BlockRun MCP Server - Give your AI agent web search, deep research, prediction markets, crypto data, X/Twitter intelligence. Paid via x402 micropayments.",
|
|
6
6
|
"type": "module",
|
|
@@ -14,7 +14,7 @@
|
|
|
14
14
|
"README.md"
|
|
15
15
|
],
|
|
16
16
|
"scripts": {
|
|
17
|
-
"build": "tsup src/index.ts --format esm --dts --clean",
|
|
17
|
+
"build": "tsup src/index.ts --format esm --dts --clean --external sharp",
|
|
18
18
|
"dev": "tsx watch src/index.ts",
|
|
19
19
|
"start": "node dist/index.js",
|
|
20
20
|
"typecheck": "tsc --noEmit",
|
|
@@ -48,6 +48,7 @@
|
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
49
|
"open": "^11.0.0",
|
|
50
50
|
"qrcode": "^1.5.4",
|
|
51
|
+
"sharp": "^0.34.5",
|
|
51
52
|
"viem": "^2.21.0",
|
|
52
53
|
"zod": "^4.3.5"
|
|
53
54
|
},
|