@blockrun/mcp 0.3.0 → 0.4.1
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/dist/index.js +292 -506
- package/package.json +6 -2
package/dist/index.js
CHANGED
|
@@ -4,13 +4,19 @@
|
|
|
4
4
|
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
|
|
5
5
|
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
|
|
6
6
|
import { z } from "zod";
|
|
7
|
-
import { LLMClient } from "@blockrun/llm";
|
|
7
|
+
import { LLMClient, ImageClient } from "@blockrun/llm";
|
|
8
8
|
import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
|
|
9
9
|
import * as fs from "fs";
|
|
10
10
|
import * as path from "path";
|
|
11
11
|
import * as os from "os";
|
|
12
|
+
import QRCode from "qrcode";
|
|
13
|
+
import { Jimp } from "jimp";
|
|
14
|
+
import open from "open";
|
|
12
15
|
var WALLET_DIR = path.join(os.homedir(), ".blockrun");
|
|
13
16
|
var WALLET_FILE = path.join(WALLET_DIR, ".session");
|
|
17
|
+
var QR_FILE = path.join(WALLET_DIR, "qr.png");
|
|
18
|
+
var USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
19
|
+
var BASE_CHAIN_ID = "8453";
|
|
14
20
|
var MODEL_TIERS = {
|
|
15
21
|
fast: ["google/gemini-2.5-flash", "openai/gpt-4o-mini", "deepseek/deepseek-chat"],
|
|
16
22
|
balanced: ["openai/gpt-4o", "anthropic/claude-sonnet-4", "google/gemini-2.5-pro"],
|
|
@@ -21,9 +27,9 @@ var MODEL_TIERS = {
|
|
|
21
27
|
var walletWasCreated = false;
|
|
22
28
|
var walletAddress = null;
|
|
23
29
|
var client = null;
|
|
30
|
+
var imageClient = null;
|
|
24
31
|
var cachedModels = null;
|
|
25
32
|
var sessionBudget = { limit: null, spent: 0, calls: 0 };
|
|
26
|
-
var USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
27
33
|
var BASE_RPC_URLS = [
|
|
28
34
|
"https://mainnet.base.org",
|
|
29
35
|
"https://base.llamarpc.com",
|
|
@@ -69,6 +75,13 @@ function getClient() {
|
|
|
69
75
|
}
|
|
70
76
|
return client;
|
|
71
77
|
}
|
|
78
|
+
function getImageClient() {
|
|
79
|
+
if (!imageClient) {
|
|
80
|
+
const privateKey = getOrCreateWalletKey();
|
|
81
|
+
imageClient = new ImageClient({ privateKey });
|
|
82
|
+
}
|
|
83
|
+
return imageClient;
|
|
84
|
+
}
|
|
72
85
|
function getWalletInfo() {
|
|
73
86
|
const llm = getClient();
|
|
74
87
|
const address = llm.getWalletAddress();
|
|
@@ -78,65 +91,9 @@ function getWalletInfo() {
|
|
|
78
91
|
chainId: 8453,
|
|
79
92
|
currency: "USDC",
|
|
80
93
|
isNew: walletWasCreated,
|
|
81
|
-
basescanUrl: `https://basescan.org/address/${address}
|
|
82
|
-
fundingOptions: {
|
|
83
|
-
coinbase: "Send USDC, select 'Base' network",
|
|
84
|
-
bridge: "https://bridge.base.org",
|
|
85
|
-
buy: "https://www.coinbase.com/onramp"
|
|
86
|
-
}
|
|
94
|
+
basescanUrl: `https://basescan.org/address/${address}`
|
|
87
95
|
};
|
|
88
96
|
}
|
|
89
|
-
function getWalletSetupInstructions() {
|
|
90
|
-
if (!walletAddress) {
|
|
91
|
-
getClient();
|
|
92
|
-
}
|
|
93
|
-
return `
|
|
94
|
-
================================================================================
|
|
95
|
-
BLOCKRUN WALLET SETUP
|
|
96
|
-
================================================================================
|
|
97
|
-
|
|
98
|
-
Your wallet address: ${walletAddress}
|
|
99
|
-
|
|
100
|
-
To use BlockRun AI models, you need USDC on Base network.
|
|
101
|
-
|
|
102
|
-
HOW TO FUND YOUR WALLET:
|
|
103
|
-
------------------------
|
|
104
|
-
|
|
105
|
-
Option 1: Transfer from Coinbase
|
|
106
|
-
1. Open Coinbase app or website
|
|
107
|
-
2. Go to Send/Receive
|
|
108
|
-
3. Select USDC
|
|
109
|
-
4. Choose "Base" network (important!)
|
|
110
|
-
5. Paste address: ${walletAddress}
|
|
111
|
-
6. Send any amount ($5 is enough to start)
|
|
112
|
-
|
|
113
|
-
Option 2: Bridge from other chains
|
|
114
|
-
1. Go to https://bridge.base.org
|
|
115
|
-
2. Connect your existing wallet
|
|
116
|
-
3. Bridge USDC to Base
|
|
117
|
-
4. Send to: ${walletAddress}
|
|
118
|
-
|
|
119
|
-
Option 3: Buy directly
|
|
120
|
-
1. Go to https://www.coinbase.com/onramp
|
|
121
|
-
2. Buy USDC on Base network
|
|
122
|
-
3. Send to: ${walletAddress}
|
|
123
|
-
|
|
124
|
-
VERIFY YOUR BALANCE:
|
|
125
|
-
https://basescan.org/address/${walletAddress}
|
|
126
|
-
|
|
127
|
-
PRICING (pay only for what you use):
|
|
128
|
-
- GPT-4o: ~$0.005 per request
|
|
129
|
-
- Claude Sonnet: ~$0.003 per request
|
|
130
|
-
- Gemini Flash: ~$0.0001 per request
|
|
131
|
-
- Full pricing: https://blockrun.ai/pricing
|
|
132
|
-
|
|
133
|
-
SECURITY NOTE:
|
|
134
|
-
Your private key is stored at: ~/.blockrun/.session
|
|
135
|
-
This key NEVER leaves your machine - only used for signing payments locally.
|
|
136
|
-
|
|
137
|
-
================================================================================
|
|
138
|
-
`;
|
|
139
|
-
}
|
|
140
97
|
async function getUsdcBalance(address) {
|
|
141
98
|
const data = {
|
|
142
99
|
jsonrpc: "2.0",
|
|
@@ -164,6 +121,42 @@ async function getUsdcBalance(address) {
|
|
|
164
121
|
}
|
|
165
122
|
return null;
|
|
166
123
|
}
|
|
124
|
+
function getEip681Uri(address, amountUsdc = 1) {
|
|
125
|
+
const amountWei = Math.floor(amountUsdc * 1e6);
|
|
126
|
+
return `ethereum:${USDC_ADDRESS}@${BASE_CHAIN_ID}/transfer?address=${address}&uint256=${amountWei}`;
|
|
127
|
+
}
|
|
128
|
+
async function generateQrPng(address) {
|
|
129
|
+
const eip681Uri = getEip681Uri(address);
|
|
130
|
+
const qrBuffer = await QRCode.toBuffer(eip681Uri, {
|
|
131
|
+
type: "png",
|
|
132
|
+
width: 400,
|
|
133
|
+
margin: 2,
|
|
134
|
+
errorCorrectionLevel: "H",
|
|
135
|
+
color: { dark: "#000000", light: "#FFFFFF" }
|
|
136
|
+
});
|
|
137
|
+
const qrImage = await Jimp.read(qrBuffer);
|
|
138
|
+
try {
|
|
139
|
+
const logoUrl = "https://avatars.githubusercontent.com/u/108554348?s=200&v=4";
|
|
140
|
+
const logo = await Jimp.read(logoUrl);
|
|
141
|
+
const logoSize = Math.floor(qrImage.width * 0.2);
|
|
142
|
+
logo.resize({ w: logoSize, h: logoSize });
|
|
143
|
+
const x = Math.floor((qrImage.width - logoSize) / 2);
|
|
144
|
+
const y = Math.floor((qrImage.height - logoSize) / 2);
|
|
145
|
+
qrImage.composite(logo, x, y);
|
|
146
|
+
} catch {
|
|
147
|
+
}
|
|
148
|
+
if (!fs.existsSync(WALLET_DIR)) {
|
|
149
|
+
fs.mkdirSync(WALLET_DIR, { recursive: true, mode: 448 });
|
|
150
|
+
}
|
|
151
|
+
await qrImage.write(QR_FILE);
|
|
152
|
+
return QR_FILE;
|
|
153
|
+
}
|
|
154
|
+
async function openQrInViewer(qrPath) {
|
|
155
|
+
try {
|
|
156
|
+
await open(qrPath);
|
|
157
|
+
} catch {
|
|
158
|
+
}
|
|
159
|
+
}
|
|
167
160
|
function recordSpending(cost) {
|
|
168
161
|
sessionBudget.spent += cost;
|
|
169
162
|
sessionBudget.calls += 1;
|
|
@@ -175,90 +168,219 @@ function checkBudget() {
|
|
|
175
168
|
const remaining = sessionBudget.limit - sessionBudget.spent;
|
|
176
169
|
return { allowed: remaining > 0, remaining };
|
|
177
170
|
}
|
|
171
|
+
function formatError(message) {
|
|
172
|
+
const isPaymentError = message.toLowerCase().includes("payment") || message.toLowerCase().includes("402") || message.toLowerCase().includes("balance") || message.toLowerCase().includes("insufficient");
|
|
173
|
+
let errorText = `Error: ${message}`;
|
|
174
|
+
if (isPaymentError) {
|
|
175
|
+
errorText += `
|
|
176
|
+
|
|
177
|
+
This error usually means your wallet needs funding.
|
|
178
|
+
Run blockrun_wallet with action: "setup" to get funding instructions.
|
|
179
|
+
|
|
180
|
+
Quick fix: Send USDC to your wallet on Base network.`;
|
|
181
|
+
}
|
|
182
|
+
return errorText;
|
|
183
|
+
}
|
|
178
184
|
var server = new McpServer({
|
|
179
185
|
name: "blockrun-mcp",
|
|
180
|
-
version: "0.
|
|
186
|
+
version: "0.4.0"
|
|
181
187
|
});
|
|
182
188
|
server.registerTool(
|
|
183
|
-
"
|
|
189
|
+
"blockrun_wallet",
|
|
184
190
|
{
|
|
185
|
-
description: `
|
|
186
|
-
Pay-per-request with x402 micropayments - no API keys needed.
|
|
187
|
-
|
|
188
|
-
Popular models:
|
|
189
|
-
- openai/gpt-5.2: Most capable OpenAI model
|
|
190
|
-
- anthropic/claude-opus-4: Best for complex reasoning
|
|
191
|
-
- anthropic/claude-sonnet-4: Fast & capable (recommended)
|
|
192
|
-
- google/gemini-2.5-pro: Great for long context
|
|
193
|
-
- deepseek/deepseek-chat: Very affordable
|
|
191
|
+
description: `Manage your BlockRun wallet - check status, get funding instructions, open QR code, or manage session budget.
|
|
194
192
|
|
|
195
|
-
|
|
193
|
+
Actions:
|
|
194
|
+
- status: Show wallet address, balance, and basescan link (default)
|
|
195
|
+
- setup: Full funding instructions with QR code
|
|
196
|
+
- qr: Generate and open QR code for easy funding
|
|
197
|
+
- budget: Manage session spending limit
|
|
198
|
+
|
|
199
|
+
Examples:
|
|
200
|
+
blockrun_wallet() -> status + balance
|
|
201
|
+
blockrun_wallet({ action: "setup" }) -> funding instructions + QR
|
|
202
|
+
blockrun_wallet({ action: "qr" }) -> open QR code
|
|
203
|
+
blockrun_wallet({ action: "budget", budget_action: "set", budget_amount: 1.00 })`,
|
|
196
204
|
inputSchema: {
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
max_tokens: z.number().optional().default(1024).describe("Maximum tokens in response"),
|
|
201
|
-
temperature: z.number().optional().default(1).describe("Creativity level 0-2")
|
|
205
|
+
action: z.enum(["status", "setup", "qr", "budget"]).optional().default("status").describe("What to do"),
|
|
206
|
+
budget_action: z.enum(["set", "check", "clear"]).optional().describe("Budget action (for action='budget')"),
|
|
207
|
+
budget_amount: z.number().optional().describe("Budget limit in USD (for budget_action='set')")
|
|
202
208
|
}
|
|
203
209
|
},
|
|
204
|
-
async ({
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
210
|
+
async ({ action, budget_action, budget_amount }) => {
|
|
211
|
+
const info = getWalletInfo();
|
|
212
|
+
const address = info.address;
|
|
213
|
+
if (action === "budget") {
|
|
214
|
+
const budgetAct = budget_action || "check";
|
|
215
|
+
if (budgetAct === "set") {
|
|
216
|
+
if (budget_amount === void 0 || budget_amount <= 0) {
|
|
217
|
+
return {
|
|
218
|
+
content: [{ type: "text", text: "Error: Provide a positive budget_amount (e.g., 1.00 for $1.00)" }],
|
|
219
|
+
isError: true
|
|
220
|
+
};
|
|
221
|
+
}
|
|
222
|
+
sessionBudget.limit = budget_amount;
|
|
223
|
+
} else if (budgetAct === "clear") {
|
|
224
|
+
sessionBudget.limit = null;
|
|
225
|
+
}
|
|
226
|
+
const remaining = sessionBudget.limit !== null ? sessionBudget.limit - sessionBudget.spent : null;
|
|
227
|
+
const limitStr = sessionBudget.limit !== null ? `$${sessionBudget.limit.toFixed(2)}` : "Unlimited";
|
|
228
|
+
const remainingStr = remaining !== null ? `$${remaining.toFixed(4)}` : "N/A";
|
|
215
229
|
return {
|
|
216
|
-
content: [{ type: "text", text:
|
|
217
|
-
|
|
230
|
+
content: [{ type: "text", text: `Session Budget: ${limitStr} | Spent: $${sessionBudget.spent.toFixed(4)} | Calls: ${sessionBudget.calls} | Remaining: ${remainingStr}${budgetAct === "set" ? ` | Set to $${budget_amount?.toFixed(2)}` : ""}${budgetAct === "clear" ? " | Limit removed" : ""}` }],
|
|
231
|
+
structuredContent: {
|
|
232
|
+
limit: sessionBudget.limit,
|
|
233
|
+
spent: sessionBudget.spent,
|
|
234
|
+
calls: sessionBudget.calls,
|
|
235
|
+
remaining
|
|
236
|
+
}
|
|
218
237
|
};
|
|
219
238
|
}
|
|
239
|
+
if (action === "qr") {
|
|
240
|
+
try {
|
|
241
|
+
const qrPath = await generateQrPng(address);
|
|
242
|
+
await openQrInViewer(qrPath);
|
|
243
|
+
return {
|
|
244
|
+
content: [{ type: "text", text: `QR code opened! Scan with MetaMask to send USDC on Base.
|
|
245
|
+
|
|
246
|
+
Address: ${address}
|
|
247
|
+
QR saved: ${qrPath}` }]
|
|
248
|
+
};
|
|
249
|
+
} catch (err) {
|
|
250
|
+
return {
|
|
251
|
+
content: [{ type: "text", text: `Failed to generate QR: ${err}` }],
|
|
252
|
+
isError: true
|
|
253
|
+
};
|
|
254
|
+
}
|
|
255
|
+
}
|
|
256
|
+
if (action === "setup") {
|
|
257
|
+
let qrMessage = "";
|
|
258
|
+
try {
|
|
259
|
+
const qrPath = await generateQrPng(address);
|
|
260
|
+
await openQrInViewer(qrPath);
|
|
261
|
+
qrMessage = `
|
|
262
|
+
QR code opened for scanning! (${qrPath})`;
|
|
263
|
+
} catch {
|
|
264
|
+
qrMessage = "\n(QR generation failed - use address above)";
|
|
265
|
+
}
|
|
266
|
+
const text2 = `
|
|
267
|
+
================================================================================
|
|
268
|
+
BLOCKRUN WALLET SETUP
|
|
269
|
+
================================================================================
|
|
270
|
+
|
|
271
|
+
Your wallet address: ${address}
|
|
272
|
+
${qrMessage}
|
|
273
|
+
|
|
274
|
+
HOW TO FUND YOUR WALLET:
|
|
275
|
+
------------------------
|
|
276
|
+
|
|
277
|
+
Option 1: Transfer from Coinbase
|
|
278
|
+
1. Open Coinbase app or website
|
|
279
|
+
2. Go to Send/Receive -> Select USDC
|
|
280
|
+
3. Choose "Base" network (important!)
|
|
281
|
+
4. Paste: ${address}
|
|
282
|
+
5. Send $1-5 to start
|
|
283
|
+
|
|
284
|
+
Option 2: Bridge from other chains
|
|
285
|
+
https://bridge.base.org -> Bridge USDC to Base -> Send to address above
|
|
286
|
+
|
|
287
|
+
Option 3: Buy directly
|
|
288
|
+
https://www.coinbase.com/onramp -> Buy USDC on Base -> Send to address above
|
|
289
|
+
|
|
290
|
+
VERIFY BALANCE: https://basescan.org/address/${address}
|
|
291
|
+
|
|
292
|
+
PRICING (pay per use):
|
|
293
|
+
- GPT-4o: ~$0.005/request | Claude Sonnet: ~$0.003/request
|
|
294
|
+
- Gemini Flash: ~$0.0001/request | Full pricing: https://blockrun.ai/pricing
|
|
295
|
+
|
|
296
|
+
SECURITY: Private key stored at ~/.blockrun/.session (never leaves your machine)
|
|
297
|
+
================================================================================`;
|
|
298
|
+
return { content: [{ type: "text", text: text2 }] };
|
|
299
|
+
}
|
|
300
|
+
const balance = await getUsdcBalance(address);
|
|
301
|
+
const balanceStr = balance !== null ? `$${balance.toFixed(6)} USDC` : "Unable to fetch";
|
|
302
|
+
const lowBalance = balance !== null && balance < 1;
|
|
303
|
+
const text = `Wallet: ${address}
|
|
304
|
+
Balance: ${balanceStr}${lowBalance ? " (low - add funds)" : ""}
|
|
305
|
+
Network: Base | View: ${info.basescanUrl}
|
|
306
|
+
${info.isNew ? "\nNEW WALLET - Run with action: 'setup' for funding instructions" : ""}`;
|
|
307
|
+
return {
|
|
308
|
+
content: [{ type: "text", text }],
|
|
309
|
+
structuredContent: {
|
|
310
|
+
address: info.address,
|
|
311
|
+
balance,
|
|
312
|
+
network: info.network,
|
|
313
|
+
chainId: info.chainId,
|
|
314
|
+
isNew: info.isNew,
|
|
315
|
+
basescanUrl: info.basescanUrl
|
|
316
|
+
}
|
|
317
|
+
};
|
|
220
318
|
}
|
|
221
319
|
);
|
|
222
320
|
server.registerTool(
|
|
223
|
-
"
|
|
321
|
+
"blockrun_chat",
|
|
224
322
|
{
|
|
225
|
-
description: `
|
|
323
|
+
description: `Chat with AI models via BlockRun. Supports 30+ models with pay-per-request micropayments.
|
|
226
324
|
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
- powerful: Best quality (GPT-5.2, Claude Opus 4, o3)
|
|
231
|
-
- cheap: Lowest cost (Gemini Flash, DeepSeek)
|
|
232
|
-
- reasoning: Complex logic (o3, o1, DeepSeek Reasoner)
|
|
325
|
+
Two ways to use:
|
|
326
|
+
1. Specify a model directly: model: "openai/gpt-4o"
|
|
327
|
+
2. Use smart routing: mode: "fast" | "balanced" | "powerful" | "cheap" | "reasoning"
|
|
233
328
|
|
|
234
|
-
|
|
329
|
+
Popular models:
|
|
330
|
+
- openai/gpt-5.2, openai/gpt-4o, openai/gpt-4o-mini
|
|
331
|
+
- anthropic/claude-opus-4, anthropic/claude-sonnet-4
|
|
332
|
+
- google/gemini-2.5-pro, google/gemini-2.5-flash
|
|
333
|
+
- deepseek/deepseek-chat (very affordable)
|
|
334
|
+
|
|
335
|
+
Smart routing modes:
|
|
336
|
+
- fast: Gemini Flash, GPT-4o-mini (quickest)
|
|
337
|
+
- balanced: GPT-4o, Claude Sonnet (good default)
|
|
338
|
+
- powerful: GPT-5.2, Claude Opus 4 (best quality)
|
|
339
|
+
- cheap: DeepSeek, Gemini Flash (lowest cost)
|
|
340
|
+
- reasoning: o3, o1 (complex logic)
|
|
341
|
+
|
|
342
|
+
Use blockrun_models to see all available models with pricing.`,
|
|
235
343
|
inputSchema: {
|
|
236
|
-
mode: z.enum(["fast", "balanced", "powerful", "cheap", "reasoning"]).describe("Routing mode"),
|
|
237
344
|
message: z.string().describe("Your message to the AI"),
|
|
345
|
+
model: z.string().optional().describe("Specific model ID (e.g., 'openai/gpt-4o')"),
|
|
346
|
+
mode: z.enum(["fast", "balanced", "powerful", "cheap", "reasoning"]).optional().describe("Smart routing mode (ignored if model specified)"),
|
|
238
347
|
system: z.string().optional().describe("Optional system prompt"),
|
|
239
|
-
max_tokens: z.number().optional().default(1024).describe("
|
|
240
|
-
|
|
241
|
-
outputSchema: {
|
|
242
|
-
model_used: z.string().describe("The model that was used"),
|
|
243
|
-
response: z.string().describe("The AI response")
|
|
348
|
+
max_tokens: z.number().optional().default(1024).describe("Max tokens in response"),
|
|
349
|
+
temperature: z.number().optional().default(1).describe("Creativity 0-2")
|
|
244
350
|
}
|
|
245
351
|
},
|
|
246
|
-
async ({
|
|
247
|
-
const
|
|
248
|
-
|
|
249
|
-
for (const model of models) {
|
|
352
|
+
async ({ message, model, mode, system, max_tokens, temperature }) => {
|
|
353
|
+
const llm = getClient();
|
|
354
|
+
if (model) {
|
|
250
355
|
try {
|
|
251
|
-
const llm = getClient();
|
|
252
356
|
const response = await llm.chat(model, message, {
|
|
357
|
+
system,
|
|
358
|
+
maxTokens: max_tokens,
|
|
359
|
+
temperature
|
|
360
|
+
});
|
|
361
|
+
return { content: [{ type: "text", text: response }] };
|
|
362
|
+
} catch (error) {
|
|
363
|
+
const errorMessage2 = error instanceof Error ? error.message : String(error);
|
|
364
|
+
return {
|
|
365
|
+
content: [{ type: "text", text: formatError(errorMessage2) }],
|
|
366
|
+
isError: true
|
|
367
|
+
};
|
|
368
|
+
}
|
|
369
|
+
}
|
|
370
|
+
const routingMode = mode || "balanced";
|
|
371
|
+
const models = MODEL_TIERS[routingMode];
|
|
372
|
+
let lastError = null;
|
|
373
|
+
for (const m of models) {
|
|
374
|
+
try {
|
|
375
|
+
const response = await llm.chat(m, message, {
|
|
253
376
|
system,
|
|
254
377
|
maxTokens: max_tokens
|
|
255
378
|
});
|
|
256
|
-
const result = { model_used: model, response };
|
|
257
379
|
return {
|
|
258
|
-
content: [{ type: "text", text: `[
|
|
380
|
+
content: [{ type: "text", text: `[${m}]
|
|
259
381
|
|
|
260
382
|
${response}` }],
|
|
261
|
-
structuredContent:
|
|
383
|
+
structuredContent: { model_used: m, response }
|
|
262
384
|
};
|
|
263
385
|
} catch (error) {
|
|
264
386
|
lastError = error;
|
|
@@ -275,19 +397,10 @@ ${response}` }],
|
|
|
275
397
|
server.registerTool(
|
|
276
398
|
"blockrun_models",
|
|
277
399
|
{
|
|
278
|
-
description: "List
|
|
400
|
+
description: "List available AI models with pricing. Use to discover models and compare costs.",
|
|
279
401
|
inputSchema: {
|
|
280
402
|
category: z.enum(["all", "chat", "reasoning", "image", "embedding"]).optional().default("all").describe("Filter by category"),
|
|
281
|
-
provider: z.string().optional().describe("Filter by provider (e.g., 'openai', 'anthropic'
|
|
282
|
-
},
|
|
283
|
-
outputSchema: {
|
|
284
|
-
count: z.number().describe("Number of models returned"),
|
|
285
|
-
models: z.array(z.object({
|
|
286
|
-
id: z.string(),
|
|
287
|
-
name: z.string().optional(),
|
|
288
|
-
inputPrice: z.number().optional(),
|
|
289
|
-
outputPrice: z.number().optional()
|
|
290
|
-
})).describe("List of available models")
|
|
403
|
+
provider: z.string().optional().describe("Filter by provider (e.g., 'openai', 'anthropic')")
|
|
291
404
|
}
|
|
292
405
|
},
|
|
293
406
|
async ({ category, provider }) => {
|
|
@@ -305,13 +418,9 @@ server.registerTool(
|
|
|
305
418
|
}
|
|
306
419
|
if (category && category !== "all") {
|
|
307
420
|
if (category === "image") {
|
|
308
|
-
models = models.filter(
|
|
309
|
-
(m) => m.id.includes("dall-e") || m.id.includes("flux") || m.id.includes("banana")
|
|
310
|
-
);
|
|
421
|
+
models = models.filter((m) => m.id.includes("dall-e") || m.id.includes("flux") || m.id.includes("banana"));
|
|
311
422
|
} else if (category === "reasoning") {
|
|
312
|
-
models = models.filter(
|
|
313
|
-
(m) => m.id.includes("/o1") || m.id.includes("/o3") || m.id.includes("reasoner")
|
|
314
|
-
);
|
|
423
|
+
models = models.filter((m) => m.id.includes("/o1") || m.id.includes("/o3") || m.id.includes("reasoner"));
|
|
315
424
|
} else if (category === "embedding") {
|
|
316
425
|
models = models.filter((m) => m.id.includes("embed"));
|
|
317
426
|
}
|
|
@@ -320,219 +429,92 @@ server.registerTool(
|
|
|
320
429
|
const input = m.inputPrice ? `$${m.inputPrice}/M in` : "";
|
|
321
430
|
const output = m.outputPrice ? `$${m.outputPrice}/M out` : "";
|
|
322
431
|
const pricing = [input, output].filter(Boolean).join(", ");
|
|
323
|
-
return `- ${m.id}
|
|
432
|
+
return `- ${m.id}${pricing ? ` (${pricing})` : ""}`;
|
|
324
433
|
});
|
|
325
|
-
const structuredModels = models.map((m) => ({
|
|
326
|
-
id: m.id,
|
|
327
|
-
name: m.name,
|
|
328
|
-
inputPrice: m.inputPrice,
|
|
329
|
-
outputPrice: m.outputPrice
|
|
330
|
-
}));
|
|
331
434
|
return {
|
|
332
|
-
content: [{ type: "text", text: `
|
|
333
|
-
|
|
435
|
+
content: [{ type: "text", text: `Models (${models.length}):
|
|
334
436
|
${lines.join("\n")}` }],
|
|
335
|
-
structuredContent: { count: models.length, models
|
|
437
|
+
structuredContent: { count: models.length, models }
|
|
336
438
|
};
|
|
337
439
|
}
|
|
338
440
|
);
|
|
339
441
|
server.registerTool(
|
|
340
442
|
"blockrun_image",
|
|
341
443
|
{
|
|
342
|
-
description: `Generate images
|
|
343
|
-
|
|
344
|
-
Models:
|
|
345
|
-
- openai/dall-e-3: High quality, creative ($0.04-0.08/image)
|
|
346
|
-
- together/flux-schnell: Fast generation ($0.02/image)
|
|
347
|
-
- google/nano-banana: Experimental Google model`,
|
|
444
|
+
description: `Generate images. Models: openai/dall-e-3 ($0.04-0.08), together/flux-schnell ($0.02), google/nano-banana`,
|
|
348
445
|
inputSchema: {
|
|
349
|
-
prompt: z.string().describe("
|
|
350
|
-
model: z.enum(["openai/dall-e-3", "together/flux-schnell", "google/nano-banana"]).optional().default("openai/dall-e-3")
|
|
351
|
-
size: z.enum(["1024x1024", "1792x1024", "1024x1792"]).optional().default("1024x1024")
|
|
352
|
-
quality: z.enum(["standard", "hd"]).optional().default("standard")
|
|
353
|
-
},
|
|
354
|
-
outputSchema: {
|
|
355
|
-
url: z.string().describe("URL of the generated image"),
|
|
356
|
-
prompt: z.string().describe("The prompt used"),
|
|
357
|
-
model: z.string().describe("The model used")
|
|
446
|
+
prompt: z.string().describe("Image description"),
|
|
447
|
+
model: z.enum(["openai/dall-e-3", "together/flux-schnell", "google/nano-banana"]).optional().default("openai/dall-e-3"),
|
|
448
|
+
size: z.enum(["1024x1024", "1792x1024", "1024x1792"]).optional().default("1024x1024"),
|
|
449
|
+
quality: z.enum(["standard", "hd"]).optional().default("standard")
|
|
358
450
|
}
|
|
359
451
|
},
|
|
360
452
|
async ({ prompt, model, size, quality }) => {
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
return {
|
|
376
|
-
content: [{ type: "text", text: `Image generation requires payment. Please ensure your wallet has USDC on Base.
|
|
377
|
-
|
|
378
|
-
To generate "${prompt}" with ${model}, the approximate cost is $0.04-0.08 per image.` }],
|
|
379
|
-
isError: true
|
|
380
|
-
};
|
|
381
|
-
}
|
|
382
|
-
if (!response.ok) {
|
|
453
|
+
try {
|
|
454
|
+
const imgClient = getImageClient();
|
|
455
|
+
const response = await imgClient.generate(prompt, {
|
|
456
|
+
model,
|
|
457
|
+
size,
|
|
458
|
+
quality
|
|
459
|
+
});
|
|
460
|
+
const imageUrl = response.data?.[0]?.url;
|
|
461
|
+
if (!imageUrl) {
|
|
462
|
+
return {
|
|
463
|
+
content: [{ type: "text", text: formatError("No image URL in response") }],
|
|
464
|
+
isError: true
|
|
465
|
+
};
|
|
466
|
+
}
|
|
383
467
|
return {
|
|
384
|
-
content: [{ type: "text", text:
|
|
385
|
-
|
|
468
|
+
content: [{ type: "text", text: `Image: ${imageUrl}
|
|
469
|
+
Prompt: ${prompt}
|
|
470
|
+
Model: ${model}` }],
|
|
471
|
+
structuredContent: { url: imageUrl, prompt, model }
|
|
386
472
|
};
|
|
387
|
-
}
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
473
|
+
} catch (err) {
|
|
474
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
475
|
+
if (errMsg.includes("balance") || errMsg.includes("payment") || errMsg.includes("402")) {
|
|
476
|
+
return {
|
|
477
|
+
content: [{ type: "text", text: `Image generation requires payment. Run blockrun_wallet with action: "setup" for funding instructions.
|
|
478
|
+
Error: ${errMsg}` }],
|
|
479
|
+
isError: true
|
|
480
|
+
};
|
|
481
|
+
}
|
|
391
482
|
return {
|
|
392
|
-
content: [{ type: "text", text: formatError(
|
|
483
|
+
content: [{ type: "text", text: formatError(`Image generation failed: ${errMsg}`) }],
|
|
393
484
|
isError: true
|
|
394
485
|
};
|
|
395
486
|
}
|
|
396
|
-
return {
|
|
397
|
-
content: [{ type: "text", text: `Image generated successfully!
|
|
398
|
-
|
|
399
|
-
URL: ${imageUrl}
|
|
400
|
-
|
|
401
|
-
Prompt: ${prompt}
|
|
402
|
-
Model: ${model}` }],
|
|
403
|
-
structuredContent: { url: imageUrl, prompt, model }
|
|
404
|
-
};
|
|
405
|
-
}
|
|
406
|
-
);
|
|
407
|
-
server.registerTool(
|
|
408
|
-
"blockrun_wallet",
|
|
409
|
-
{
|
|
410
|
-
description: "Get information about your BlockRun wallet address. Shows address, network, and quick funding options.",
|
|
411
|
-
inputSchema: {},
|
|
412
|
-
outputSchema: {
|
|
413
|
-
address: z.string().describe("Wallet address"),
|
|
414
|
-
network: z.string().describe("Network name"),
|
|
415
|
-
chainId: z.number().describe("Chain ID"),
|
|
416
|
-
currency: z.string().describe("Currency"),
|
|
417
|
-
isNew: z.boolean().describe("Whether this is a newly created wallet"),
|
|
418
|
-
basescanUrl: z.string().describe("Link to view on Basescan")
|
|
419
|
-
}
|
|
420
|
-
},
|
|
421
|
-
async () => {
|
|
422
|
-
const info = getWalletInfo();
|
|
423
|
-
const isNewWallet = info.isNew;
|
|
424
|
-
let text = `BlockRun Wallet Information
|
|
425
|
-
============================
|
|
426
|
-
|
|
427
|
-
Address: ${info.address}
|
|
428
|
-
Network: ${info.network} (Chain ID: ${info.chainId})
|
|
429
|
-
Currency: ${info.currency}
|
|
430
|
-
|
|
431
|
-
View on Basescan: ${info.basescanUrl}
|
|
432
|
-
`;
|
|
433
|
-
if (isNewWallet) {
|
|
434
|
-
text += `
|
|
435
|
-
STATUS: NEW WALLET - NEEDS FUNDING
|
|
436
|
-
${getWalletSetupInstructions()}`;
|
|
437
|
-
} else {
|
|
438
|
-
text += `
|
|
439
|
-
HOW TO ADD FUNDS:
|
|
440
|
-
-----------------
|
|
441
|
-
Send USDC to the address above on Base network.
|
|
442
|
-
|
|
443
|
-
Quick options:
|
|
444
|
-
1. From Coinbase: ${info.fundingOptions.coinbase}
|
|
445
|
-
2. Bridge: ${info.fundingOptions.bridge}
|
|
446
|
-
3. Buy: ${info.fundingOptions.buy}
|
|
447
|
-
|
|
448
|
-
Full instructions: Run blockrun_setup tool
|
|
449
|
-
`;
|
|
450
|
-
}
|
|
451
|
-
return {
|
|
452
|
-
content: [{ type: "text", text }],
|
|
453
|
-
structuredContent: {
|
|
454
|
-
address: info.address,
|
|
455
|
-
network: info.network,
|
|
456
|
-
chainId: info.chainId,
|
|
457
|
-
currency: info.currency,
|
|
458
|
-
isNew: info.isNew,
|
|
459
|
-
basescanUrl: info.basescanUrl
|
|
460
|
-
}
|
|
461
|
-
};
|
|
462
|
-
}
|
|
463
|
-
);
|
|
464
|
-
server.registerTool(
|
|
465
|
-
"blockrun_setup",
|
|
466
|
-
{
|
|
467
|
-
description: `Get detailed wallet setup and funding instructions. Use this for first-time setup or if you need help adding funds to your wallet.
|
|
468
|
-
|
|
469
|
-
Returns:
|
|
470
|
-
- Your wallet address
|
|
471
|
-
- Step-by-step funding instructions (Coinbase, bridge, direct purchase)
|
|
472
|
-
- Pricing information
|
|
473
|
-
- Security details`,
|
|
474
|
-
inputSchema: {}
|
|
475
|
-
},
|
|
476
|
-
async () => {
|
|
477
|
-
getClient();
|
|
478
|
-
return { content: [{ type: "text", text: getWalletSetupInstructions() }] };
|
|
479
487
|
}
|
|
480
488
|
);
|
|
481
489
|
server.registerTool(
|
|
482
490
|
"blockrun_twitter",
|
|
483
491
|
{
|
|
484
|
-
description: `Search real-time X/Twitter
|
|
485
|
-
|
|
486
|
-
Use this tool for:
|
|
487
|
-
- Checking what people are saying about a topic
|
|
488
|
-
- Finding recent tweets from specific accounts
|
|
489
|
-
- Getting trending discussions
|
|
490
|
-
- Real-time news and events
|
|
491
|
-
|
|
492
|
-
Example queries:
|
|
493
|
-
- "what is @elonmusk posting about today"
|
|
494
|
-
- "trending AI news"
|
|
495
|
-
- "reactions to [event]"`,
|
|
492
|
+
description: `Search real-time X/Twitter via Grok. Use for trending topics, @handles, breaking news.`,
|
|
496
493
|
inputSchema: {
|
|
497
|
-
query: z.string().describe("Search query
|
|
498
|
-
max_results: z.number().optional().default(10).describe("
|
|
499
|
-
},
|
|
500
|
-
outputSchema: {
|
|
501
|
-
query: z.string(),
|
|
502
|
-
model: z.string(),
|
|
503
|
-
response: z.string()
|
|
494
|
+
query: z.string().describe("Search query (can include @handles, topics)"),
|
|
495
|
+
max_results: z.number().optional().default(10).describe("Max results (1-25)")
|
|
504
496
|
}
|
|
505
497
|
},
|
|
506
498
|
async ({ query, max_results }) => {
|
|
507
499
|
const budget = checkBudget();
|
|
508
500
|
if (!budget.allowed) {
|
|
509
501
|
return {
|
|
510
|
-
content: [{ type: "text", text: `Budget limit reached
|
|
511
|
-
|
|
512
|
-
Use blockrun_budget to check or adjust your budget.` }],
|
|
502
|
+
content: [{ type: "text", text: `Budget limit reached ($${sessionBudget.spent.toFixed(4)} of $${sessionBudget.limit?.toFixed(2)}). Use blockrun_wallet with action: "budget" to adjust.` }],
|
|
513
503
|
isError: true
|
|
514
504
|
};
|
|
515
505
|
}
|
|
516
506
|
try {
|
|
517
507
|
const llm = getClient();
|
|
518
|
-
const
|
|
519
|
-
|
|
520
|
-
When searching, focus on:
|
|
521
|
-
- Recent and relevant posts
|
|
522
|
-
- Key accounts and verified sources
|
|
523
|
-
- Engagement metrics when relevant
|
|
524
|
-
Format your response clearly with sources when available.
|
|
525
|
-
Max results requested: ${max_results}`;
|
|
526
|
-
const response = await llm.chat(model, query, {
|
|
527
|
-
system,
|
|
508
|
+
const response = await llm.chat("xai/grok-3", query, {
|
|
509
|
+
system: `Real-time X/Twitter search. Focus on recent posts, key accounts, engagement. Max results: ${max_results}`,
|
|
528
510
|
search: true
|
|
529
511
|
});
|
|
530
512
|
recordSpending(2e-3);
|
|
531
513
|
return {
|
|
532
|
-
content: [{ type: "text", text: `[X/Twitter
|
|
514
|
+
content: [{ type: "text", text: `[X/Twitter via Grok]
|
|
533
515
|
|
|
534
516
|
${response}` }],
|
|
535
|
-
structuredContent: { query, model, response }
|
|
517
|
+
structuredContent: { query, model: "xai/grok-3", response }
|
|
536
518
|
};
|
|
537
519
|
} catch (error) {
|
|
538
520
|
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
@@ -543,141 +525,22 @@ ${response}` }],
|
|
|
543
525
|
}
|
|
544
526
|
}
|
|
545
527
|
);
|
|
546
|
-
server.registerTool(
|
|
547
|
-
"blockrun_balance",
|
|
548
|
-
{
|
|
549
|
-
description: `Check your on-chain USDC balance on Base network.
|
|
550
|
-
|
|
551
|
-
Returns:
|
|
552
|
-
- Current USDC balance
|
|
553
|
-
- Wallet address
|
|
554
|
-
- Link to view on Basescan
|
|
555
|
-
|
|
556
|
-
Use this to see how much funding you have available for BlockRun API calls.`,
|
|
557
|
-
inputSchema: {},
|
|
558
|
-
outputSchema: {
|
|
559
|
-
address: z.string(),
|
|
560
|
-
balance: z.number().nullable(),
|
|
561
|
-
network: z.string(),
|
|
562
|
-
basescanUrl: z.string()
|
|
563
|
-
}
|
|
564
|
-
},
|
|
565
|
-
async () => {
|
|
566
|
-
const llm = getClient();
|
|
567
|
-
const address = llm.getWalletAddress();
|
|
568
|
-
const balance = await getUsdcBalance(address);
|
|
569
|
-
const balanceStr = balance !== null ? `$${balance.toFixed(6)} USDC` : "Unable to fetch (try again)";
|
|
570
|
-
const text = `BlockRun Wallet Balance
|
|
571
|
-
=======================
|
|
572
|
-
|
|
573
|
-
Address: ${address}
|
|
574
|
-
Balance: ${balanceStr}
|
|
575
|
-
Network: Base (Chain ID: 8453)
|
|
576
|
-
|
|
577
|
-
View on Basescan: https://basescan.org/address/${address}
|
|
578
|
-
|
|
579
|
-
${balance !== null && balance < 1 ? "\u26A0\uFE0F Low balance. Consider adding funds to continue using BlockRun." : ""}`;
|
|
580
|
-
return {
|
|
581
|
-
content: [{ type: "text", text }],
|
|
582
|
-
structuredContent: {
|
|
583
|
-
address,
|
|
584
|
-
balance,
|
|
585
|
-
network: "Base",
|
|
586
|
-
basescanUrl: `https://basescan.org/address/${address}`
|
|
587
|
-
}
|
|
588
|
-
};
|
|
589
|
-
}
|
|
590
|
-
);
|
|
591
|
-
server.registerTool(
|
|
592
|
-
"blockrun_budget",
|
|
593
|
-
{
|
|
594
|
-
description: `Manage your session spending budget.
|
|
595
|
-
|
|
596
|
-
Actions:
|
|
597
|
-
- check: View current spending and budget status
|
|
598
|
-
- set: Set a spending limit (e.g., $1.00)
|
|
599
|
-
- clear: Remove spending limit (unlimited)
|
|
600
|
-
|
|
601
|
-
Use this to control how much you spend per session on BlockRun API calls.`,
|
|
602
|
-
inputSchema: {
|
|
603
|
-
action: z.enum(["check", "set", "clear"]).describe("Budget action to perform"),
|
|
604
|
-
amount: z.number().optional().describe("Budget limit in USD (required for 'set' action)")
|
|
605
|
-
},
|
|
606
|
-
outputSchema: {
|
|
607
|
-
limit: z.number().nullable(),
|
|
608
|
-
spent: z.number(),
|
|
609
|
-
calls: z.number(),
|
|
610
|
-
remaining: z.number().nullable()
|
|
611
|
-
}
|
|
612
|
-
},
|
|
613
|
-
async ({ action, amount }) => {
|
|
614
|
-
switch (action) {
|
|
615
|
-
case "set":
|
|
616
|
-
if (amount === void 0 || amount <= 0) {
|
|
617
|
-
return {
|
|
618
|
-
content: [{ type: "text", text: "Error: Please provide a positive amount for the budget limit (e.g., amount: 1.00 for $1.00)" }],
|
|
619
|
-
isError: true
|
|
620
|
-
};
|
|
621
|
-
}
|
|
622
|
-
sessionBudget.limit = amount;
|
|
623
|
-
break;
|
|
624
|
-
case "clear":
|
|
625
|
-
sessionBudget.limit = null;
|
|
626
|
-
break;
|
|
627
|
-
case "check":
|
|
628
|
-
default:
|
|
629
|
-
break;
|
|
630
|
-
}
|
|
631
|
-
const remaining = sessionBudget.limit !== null ? sessionBudget.limit - sessionBudget.spent : null;
|
|
632
|
-
const limitStr = sessionBudget.limit !== null ? `$${sessionBudget.limit.toFixed(2)}` : "Unlimited";
|
|
633
|
-
const remainingStr = remaining !== null ? `$${remaining.toFixed(4)}` : "N/A";
|
|
634
|
-
const text = `BlockRun Session Budget
|
|
635
|
-
=======================
|
|
636
|
-
|
|
637
|
-
Limit: ${limitStr}
|
|
638
|
-
Spent: $${sessionBudget.spent.toFixed(4)}
|
|
639
|
-
Calls: ${sessionBudget.calls}
|
|
640
|
-
Remaining: ${remainingStr}
|
|
641
|
-
|
|
642
|
-
${action === "set" ? `\u2705 Budget set to $${amount?.toFixed(2)}` : ""}
|
|
643
|
-
${action === "clear" ? "\u2705 Budget limit removed (unlimited spending)" : ""}
|
|
644
|
-
${remaining !== null && remaining < 0.01 ? "\u26A0\uFE0F Budget nearly exhausted!" : ""}`;
|
|
645
|
-
return {
|
|
646
|
-
content: [{ type: "text", text }],
|
|
647
|
-
structuredContent: {
|
|
648
|
-
limit: sessionBudget.limit,
|
|
649
|
-
spent: sessionBudget.spent,
|
|
650
|
-
calls: sessionBudget.calls,
|
|
651
|
-
remaining
|
|
652
|
-
}
|
|
653
|
-
};
|
|
654
|
-
}
|
|
655
|
-
);
|
|
656
528
|
server.registerResource(
|
|
657
529
|
"wallet",
|
|
658
530
|
"blockrun://wallet",
|
|
659
|
-
{
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
uri: "blockrun://wallet",
|
|
668
|
-
mimeType: "application/json",
|
|
669
|
-
text: JSON.stringify(info, null, 2)
|
|
670
|
-
}]
|
|
671
|
-
};
|
|
672
|
-
}
|
|
531
|
+
{ description: "Wallet address and status", mimeType: "application/json" },
|
|
532
|
+
async () => ({
|
|
533
|
+
contents: [{
|
|
534
|
+
uri: "blockrun://wallet",
|
|
535
|
+
mimeType: "application/json",
|
|
536
|
+
text: JSON.stringify(getWalletInfo(), null, 2)
|
|
537
|
+
}]
|
|
538
|
+
})
|
|
673
539
|
);
|
|
674
540
|
server.registerResource(
|
|
675
541
|
"models",
|
|
676
542
|
"blockrun://models",
|
|
677
|
-
{
|
|
678
|
-
description: "List of all available AI models with pricing",
|
|
679
|
-
mimeType: "application/json"
|
|
680
|
-
},
|
|
543
|
+
{ description: "Available AI models with pricing", mimeType: "application/json" },
|
|
681
544
|
async () => {
|
|
682
545
|
const llm = getClient();
|
|
683
546
|
if (!cachedModels) {
|
|
@@ -695,87 +558,10 @@ server.registerResource(
|
|
|
695
558
|
};
|
|
696
559
|
}
|
|
697
560
|
);
|
|
698
|
-
server.registerPrompt(
|
|
699
|
-
"quick_chat",
|
|
700
|
-
{
|
|
701
|
-
description: "Start a quick chat with a recommended model",
|
|
702
|
-
argsSchema: {
|
|
703
|
-
message: z.string().describe("Your message"),
|
|
704
|
-
style: z.enum(["concise", "detailed", "creative"]).optional().default("concise").describe("Response style")
|
|
705
|
-
}
|
|
706
|
-
},
|
|
707
|
-
async ({ message, style }) => {
|
|
708
|
-
const systemPrompts = {
|
|
709
|
-
concise: "Be concise and direct. Give short, focused answers.",
|
|
710
|
-
detailed: "Provide thorough, comprehensive answers with examples.",
|
|
711
|
-
creative: "Be creative and imaginative in your responses."
|
|
712
|
-
};
|
|
713
|
-
return {
|
|
714
|
-
messages: [
|
|
715
|
-
{
|
|
716
|
-
role: "user",
|
|
717
|
-
content: {
|
|
718
|
-
type: "text",
|
|
719
|
-
text: `[System: ${systemPrompts[style || "concise"]}]
|
|
720
|
-
|
|
721
|
-
${message}`
|
|
722
|
-
}
|
|
723
|
-
}
|
|
724
|
-
]
|
|
725
|
-
};
|
|
726
|
-
}
|
|
727
|
-
);
|
|
728
|
-
server.registerPrompt(
|
|
729
|
-
"code_review",
|
|
730
|
-
{
|
|
731
|
-
description: "Get a code review from a powerful model",
|
|
732
|
-
argsSchema: {
|
|
733
|
-
code: z.string().describe("The code to review"),
|
|
734
|
-
language: z.string().optional().describe("Programming language"),
|
|
735
|
-
focus: z.enum(["bugs", "performance", "style", "all"]).optional().default("all").describe("What to focus on")
|
|
736
|
-
}
|
|
737
|
-
},
|
|
738
|
-
async ({ code, language, focus }) => {
|
|
739
|
-
const focusInstructions = {
|
|
740
|
-
bugs: "Focus on potential bugs, errors, and edge cases.",
|
|
741
|
-
performance: "Focus on performance issues and optimization opportunities.",
|
|
742
|
-
style: "Focus on code style, readability, and best practices.",
|
|
743
|
-
all: "Review for bugs, performance, and style."
|
|
744
|
-
};
|
|
745
|
-
return {
|
|
746
|
-
messages: [
|
|
747
|
-
{
|
|
748
|
-
role: "user",
|
|
749
|
-
content: {
|
|
750
|
-
type: "text",
|
|
751
|
-
text: `Please review this ${language || ""} code. ${focusInstructions[focus || "all"]}
|
|
752
|
-
|
|
753
|
-
\`\`\`${language || ""}
|
|
754
|
-
${code}
|
|
755
|
-
\`\`\``
|
|
756
|
-
}
|
|
757
|
-
}
|
|
758
|
-
]
|
|
759
|
-
};
|
|
760
|
-
}
|
|
761
|
-
);
|
|
762
|
-
function formatError(message) {
|
|
763
|
-
const isPaymentError = message.toLowerCase().includes("payment") || message.toLowerCase().includes("402") || message.toLowerCase().includes("balance") || message.toLowerCase().includes("insufficient");
|
|
764
|
-
let errorText = `Error: ${message}`;
|
|
765
|
-
if (isPaymentError) {
|
|
766
|
-
errorText += `
|
|
767
|
-
|
|
768
|
-
This error usually means your wallet needs funding.
|
|
769
|
-
Run the blockrun_setup tool to get your wallet address and funding instructions.
|
|
770
|
-
|
|
771
|
-
Quick fix: Send USDC to your wallet on Base network.`;
|
|
772
|
-
}
|
|
773
|
-
return errorText;
|
|
774
|
-
}
|
|
775
561
|
async function main() {
|
|
776
562
|
const transport = new StdioServerTransport();
|
|
777
563
|
await server.connect(transport);
|
|
778
|
-
console.error("BlockRun MCP Server started (v0.
|
|
564
|
+
console.error("BlockRun MCP Server started (v0.4.0)");
|
|
779
565
|
}
|
|
780
566
|
main().catch((error) => {
|
|
781
567
|
console.error("Fatal error:", error);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.4.1",
|
|
4
4
|
"mcpName": "io.github.BlockRunAI/blockrun-mcp",
|
|
5
5
|
"description": "BlockRun MCP Server - Access 30+ AI models via x402 micropayments. No API keys needed.",
|
|
6
6
|
"type": "module",
|
|
@@ -44,13 +44,17 @@
|
|
|
44
44
|
"url": "https://github.com/blockrunai/blockrun-mcp/issues"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@blockrun/llm": "^0.
|
|
47
|
+
"@blockrun/llm": "^0.2.0",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
|
+
"jimp": "^1.6.0",
|
|
50
|
+
"open": "^11.0.0",
|
|
51
|
+
"qrcode": "^1.5.4",
|
|
49
52
|
"viem": "^2.21.0",
|
|
50
53
|
"zod": "^4.3.5"
|
|
51
54
|
},
|
|
52
55
|
"devDependencies": {
|
|
53
56
|
"@types/node": "^20.0.0",
|
|
57
|
+
"@types/qrcode": "^1.5.6",
|
|
54
58
|
"tsup": "^8.0.0",
|
|
55
59
|
"tsx": "^4.0.0",
|
|
56
60
|
"typescript": "^5.0.0"
|