@blockrun/mcp 0.4.1 → 0.4.2
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 +20 -4
- package/dist/index.js +594 -16
- package/package.json +2 -2
package/README.md
CHANGED
|
@@ -1,4 +1,6 @@
|
|
|
1
|
-
# @blockrun/mcp
|
|
1
|
+
# @blockrun/mcp — BlockRun MCP Server for Claude Code
|
|
2
|
+
|
|
3
|
+
> **@blockrun/mcp** is an MCP (Model Context Protocol) server that gives Claude Code access to 30+ AI models (GPT-5, Gemini, Grok, DeepSeek, and more), image generation, and real-time X/Twitter data — all with pay-per-request USDC micropayments via x402. No API keys, no subscriptions. One command to install.
|
|
2
4
|
|
|
3
5
|
## The Problem
|
|
4
6
|
|
|
@@ -335,17 +337,31 @@ echo '{"jsonrpc":"2.0","method":"tools/list","id":1}' | node dist/index.js
|
|
|
335
337
|
## Links
|
|
336
338
|
|
|
337
339
|
- **Website:** [blockrun.ai](https://blockrun.ai)
|
|
338
|
-
- **Documentation:** [
|
|
340
|
+
- **Documentation:** [GitHub Docs](https://github.com/BlockRunAI/awesome-blockrun/tree/main/docs)
|
|
339
341
|
- **Pricing:** [blockrun.ai/pricing](https://blockrun.ai/pricing)
|
|
340
342
|
- **GitHub:** [github.com/blockrunai](https://github.com/blockrunai)
|
|
341
|
-
- **Twitter:** [@
|
|
343
|
+
- **Twitter:** [@BlockRunAI](https://x.com/BlockRunAI)
|
|
342
344
|
|
|
343
345
|
## Support
|
|
344
346
|
|
|
345
347
|
- **Issues:** [GitHub Issues](https://github.com/blockrunai/blockrun-mcp/issues)
|
|
346
|
-
- **
|
|
348
|
+
- **Telegram:** [Join our Telegram](https://t.me/+mroQv4-4hGgzOGUx)
|
|
347
349
|
- **Email:** hello@blockrun.ai
|
|
348
350
|
|
|
351
|
+
## Frequently Asked Questions
|
|
352
|
+
|
|
353
|
+
### What is BlockRun MCP?
|
|
354
|
+
BlockRun MCP is a Model Context Protocol (MCP) server that gives Claude Code users access to 30+ AI models from OpenAI, Google, xAI, DeepSeek, and more. It uses USDC micropayments via the x402 protocol — no API keys or subscriptions needed.
|
|
355
|
+
|
|
356
|
+
### How do I install BlockRun MCP?
|
|
357
|
+
One command: `claude mcp add blockrun npx @blockrun/mcp`. A wallet is automatically created for you. Fund it with USDC on Base network and start using any model.
|
|
358
|
+
|
|
359
|
+
### How much does it cost?
|
|
360
|
+
Pay only for what you use — no minimums or subscriptions. $5 in USDC gets you approximately 50,000 Gemini Flash requests or 1,000 GPT-4o requests. A quick question costs around $0.0001.
|
|
361
|
+
|
|
362
|
+
### Why use BlockRun MCP instead of direct API keys?
|
|
363
|
+
With direct APIs, you need 5+ accounts, 5+ API keys, and 5+ billing systems. BlockRun MCP gives you one wallet for all providers, one command to install, and zero API key management.
|
|
364
|
+
|
|
349
365
|
## License
|
|
350
366
|
|
|
351
367
|
MIT
|
package/dist/index.js
CHANGED
|
@@ -18,11 +18,11 @@ var QR_FILE = path.join(WALLET_DIR, "qr.png");
|
|
|
18
18
|
var USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
19
19
|
var BASE_CHAIN_ID = "8453";
|
|
20
20
|
var MODEL_TIERS = {
|
|
21
|
-
fast: ["google/gemini-2.5-flash", "openai/gpt-
|
|
22
|
-
balanced: ["openai/gpt-
|
|
23
|
-
powerful: ["openai/gpt-5.
|
|
24
|
-
cheap: ["google/gemini-2.5-flash", "deepseek/deepseek-chat", "openai/gpt-
|
|
25
|
-
reasoning: ["openai/o3", "openai/o1", "deepseek/deepseek-reasoner"]
|
|
21
|
+
fast: ["google/gemini-2.5-flash", "openai/gpt-5-mini", "deepseek/deepseek-chat", "google/gemini-3-flash-preview"],
|
|
22
|
+
balanced: ["openai/gpt-5.4", "anthropic/claude-sonnet-4.6", "google/gemini-2.5-pro", "openai/gpt-5.3"],
|
|
23
|
+
powerful: ["openai/gpt-5.4", "anthropic/claude-opus-4.6", "anthropic/claude-opus-4.5", "openai/o3"],
|
|
24
|
+
cheap: ["nvidia/gpt-oss-120b", "google/gemini-2.5-flash", "deepseek/deepseek-chat", "openai/gpt-5.4-nano"],
|
|
25
|
+
reasoning: ["openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner"]
|
|
26
26
|
};
|
|
27
27
|
var walletWasCreated = false;
|
|
28
28
|
var walletAddress = null;
|
|
@@ -169,9 +169,16 @@ function checkBudget() {
|
|
|
169
169
|
return { allowed: remaining > 0, remaining };
|
|
170
170
|
}
|
|
171
171
|
function formatError(message) {
|
|
172
|
-
const
|
|
172
|
+
const msgLower = message.toLowerCase();
|
|
173
|
+
const isPaymentError = msgLower.includes("402") || msgLower.includes("balance") || msgLower.includes("insufficient") || msgLower.includes("payment") && !msgLower.includes("500");
|
|
174
|
+
const isServerError = msgLower.includes("500") || msgLower.includes("api error after payment");
|
|
173
175
|
let errorText = `Error: ${message}`;
|
|
174
|
-
if (
|
|
176
|
+
if (isServerError) {
|
|
177
|
+
errorText += `
|
|
178
|
+
|
|
179
|
+
This is a temporary API issue. The xAI/Grok API may be experiencing problems.
|
|
180
|
+
Try again in a few minutes, or use a different model (e.g., openai/gpt-4o).`;
|
|
181
|
+
} else if (isPaymentError) {
|
|
175
182
|
errorText += `
|
|
176
183
|
|
|
177
184
|
This error usually means your wallet needs funding.
|
|
@@ -323,19 +330,19 @@ server.registerTool(
|
|
|
323
330
|
description: `Chat with AI models via BlockRun. Supports 30+ models with pay-per-request micropayments.
|
|
324
331
|
|
|
325
332
|
Two ways to use:
|
|
326
|
-
1. Specify a model directly: model: "openai/gpt-
|
|
333
|
+
1. Specify a model directly: model: "openai/gpt-5.4"
|
|
327
334
|
2. Use smart routing: mode: "fast" | "balanced" | "powerful" | "cheap" | "reasoning"
|
|
328
335
|
|
|
329
336
|
Popular models:
|
|
330
|
-
- openai/gpt-5.
|
|
331
|
-
- anthropic/claude-opus-4, anthropic/claude-sonnet-4
|
|
337
|
+
- openai/gpt-5.4, openai/gpt-5.4-mini, openai/gpt-5.4-nano
|
|
338
|
+
- anthropic/claude-opus-4.6, anthropic/claude-sonnet-4.6
|
|
332
339
|
- google/gemini-2.5-pro, google/gemini-2.5-flash
|
|
333
340
|
- deepseek/deepseek-chat (very affordable)
|
|
334
341
|
|
|
335
342
|
Smart routing modes:
|
|
336
|
-
- fast: Gemini Flash, GPT-
|
|
337
|
-
- balanced: GPT-
|
|
338
|
-
- powerful: GPT-5.
|
|
343
|
+
- fast: Gemini Flash, GPT-5 Mini (quickest)
|
|
344
|
+
- balanced: GPT-5.4, Claude Sonnet 4.6 (good default)
|
|
345
|
+
- powerful: GPT-5.4, Claude Opus 4.6 (best quality)
|
|
339
346
|
- cheap: DeepSeek, Gemini Flash (lowest cost)
|
|
340
347
|
- reasoning: o3, o1 (complex logic)
|
|
341
348
|
|
|
@@ -419,17 +426,19 @@ server.registerTool(
|
|
|
419
426
|
if (category && category !== "all") {
|
|
420
427
|
if (category === "image") {
|
|
421
428
|
models = models.filter((m) => m.id.includes("dall-e") || m.id.includes("flux") || m.id.includes("banana"));
|
|
422
|
-
} else if (category === "reasoning") {
|
|
423
|
-
models = models.filter((m) => m.id.includes("/o1") || m.id.includes("/o3") || m.id.includes("reasoner"));
|
|
424
429
|
} else if (category === "embedding") {
|
|
425
430
|
models = models.filter((m) => m.id.includes("embed"));
|
|
431
|
+
} else {
|
|
432
|
+
models = models.filter((m) => m.categories?.includes(category));
|
|
426
433
|
}
|
|
427
434
|
}
|
|
428
435
|
const lines = models.map((m) => {
|
|
429
436
|
const input = m.inputPrice ? `$${m.inputPrice}/M in` : "";
|
|
430
437
|
const output = m.outputPrice ? `$${m.outputPrice}/M out` : "";
|
|
431
438
|
const pricing = [input, output].filter(Boolean).join(", ");
|
|
432
|
-
|
|
439
|
+
const ctx = m.contextWindow ? ` | ${Math.round(m.contextWindow / 1e3)}K ctx` : "";
|
|
440
|
+
const cats = m.categories?.length ? ` [${m.categories.join(", ")}]` : "";
|
|
441
|
+
return `- ${m.id}${pricing ? ` (${pricing})` : ""}${ctx}${cats}`;
|
|
433
442
|
});
|
|
434
443
|
return {
|
|
435
444
|
content: [{ type: "text", text: `Models (${models.length}):
|
|
@@ -525,6 +534,575 @@ ${response}` }],
|
|
|
525
534
|
}
|
|
526
535
|
}
|
|
527
536
|
);
|
|
537
|
+
server.registerTool(
|
|
538
|
+
"blockrun_dex",
|
|
539
|
+
{
|
|
540
|
+
description: `Get real-time DEX data from DexScreener. FREE - no payment required.
|
|
541
|
+
|
|
542
|
+
Use for:
|
|
543
|
+
- Token prices and liquidity across chains
|
|
544
|
+
- Trading volume and price changes
|
|
545
|
+
- Finding token pairs and contracts
|
|
546
|
+
|
|
547
|
+
Examples:
|
|
548
|
+
blockrun_dex({ query: "SOL" }) -> Search for SOL pairs
|
|
549
|
+
blockrun_dex({ token: "So11...xxx" }) -> Get specific token data
|
|
550
|
+
blockrun_dex({ symbol: "PEPE" }) -> Search by symbol`,
|
|
551
|
+
inputSchema: {
|
|
552
|
+
query: z.string().optional().describe("Search query (token name, symbol, or address)"),
|
|
553
|
+
token: z.string().optional().describe("Token address for direct lookup"),
|
|
554
|
+
symbol: z.string().optional().describe("Token symbol to search"),
|
|
555
|
+
chain: z.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
|
|
556
|
+
}
|
|
557
|
+
},
|
|
558
|
+
async ({ query, token, symbol, chain }) => {
|
|
559
|
+
try {
|
|
560
|
+
let url;
|
|
561
|
+
let searchTerm = query || symbol || "";
|
|
562
|
+
if (token) {
|
|
563
|
+
url = `https://api.dexscreener.com/latest/dex/tokens/${token}`;
|
|
564
|
+
} else if (searchTerm) {
|
|
565
|
+
url = `https://api.dexscreener.com/latest/dex/search?q=${encodeURIComponent(searchTerm)}`;
|
|
566
|
+
} else {
|
|
567
|
+
return {
|
|
568
|
+
content: [{ type: "text", text: "Provide query, token address, or symbol" }],
|
|
569
|
+
isError: true
|
|
570
|
+
};
|
|
571
|
+
}
|
|
572
|
+
const response = await fetch(url);
|
|
573
|
+
if (!response.ok) {
|
|
574
|
+
throw new Error(`DexScreener API error: ${response.status}`);
|
|
575
|
+
}
|
|
576
|
+
const data = await response.json();
|
|
577
|
+
let pairs = data.pairs || [];
|
|
578
|
+
if (chain && pairs.length > 0) {
|
|
579
|
+
const chainLower = chain.toLowerCase();
|
|
580
|
+
pairs = pairs.filter((p) => p.chainId.toLowerCase().includes(chainLower));
|
|
581
|
+
}
|
|
582
|
+
pairs = pairs.sort((a, b) => (b.volume?.h24 || 0) - (a.volume?.h24 || 0)).slice(0, 10);
|
|
583
|
+
if (pairs.length === 0) {
|
|
584
|
+
return {
|
|
585
|
+
content: [{ type: "text", text: `No pairs found for: ${searchTerm || token}` }]
|
|
586
|
+
};
|
|
587
|
+
}
|
|
588
|
+
const lines = pairs.map((p) => {
|
|
589
|
+
const price = p.priceUsd ? `$${parseFloat(p.priceUsd).toFixed(6)}` : "N/A";
|
|
590
|
+
const change = p.priceChange?.h24 ? `${p.priceChange.h24 > 0 ? "+" : ""}${p.priceChange.h24.toFixed(2)}%` : "";
|
|
591
|
+
const vol = p.volume?.h24 ? `$${(p.volume.h24 / 1e6).toFixed(2)}M` : "";
|
|
592
|
+
const liq = p.liquidity?.usd ? `$${(p.liquidity.usd / 1e6).toFixed(2)}M liq` : "";
|
|
593
|
+
const buySell = p.txns?.h24 ? `${p.txns.h24.buys}B/${p.txns.h24.sells}S` : "";
|
|
594
|
+
return `${p.baseToken.symbol}/${p.quoteToken.symbol} (${p.chainId}/${p.dexId})
|
|
595
|
+
Price: ${price} ${change} | Vol: ${vol} | ${liq} | Txns: ${buySell}
|
|
596
|
+
Token: ${p.baseToken.address}`;
|
|
597
|
+
});
|
|
598
|
+
return {
|
|
599
|
+
content: [{ type: "text", text: `[DexScreener - FREE]
|
|
600
|
+
|
|
601
|
+
${lines.join("\n\n")}` }],
|
|
602
|
+
structuredContent: { pairs, count: pairs.length }
|
|
603
|
+
};
|
|
604
|
+
} catch (error) {
|
|
605
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
606
|
+
return {
|
|
607
|
+
content: [{ type: "text", text: `DexScreener error: ${errorMessage}` }],
|
|
608
|
+
isError: true
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
}
|
|
612
|
+
);
|
|
613
|
+
var KNOWN_LABELS = {
|
|
614
|
+
"0x28c6c06298d514db089934071355e5743bf21d60": "Binance 14",
|
|
615
|
+
"0x21a31ee1afc51d94c2efccaa2092ad1028285549": "Binance 15",
|
|
616
|
+
"0xdfd5293d8e347dfe59e90efd55b2956a1343963d": "Binance 16",
|
|
617
|
+
"0x56eddb7aa87536c09ccc2793473599fd21a8b17f": "Binance 17",
|
|
618
|
+
"0x9696f59e4d72e237be84ffd425dcad154bf96976": "Binance 18",
|
|
619
|
+
"0x4976a4a02f38326660d17bf34b431dc6e2eb2327": "Binance 19",
|
|
620
|
+
"0xf977814e90da44bfa03b6295a0616a897441acec": "Binance 8",
|
|
621
|
+
"0x5a52e96bacdabb82fd05763e25335261b270efcb": "Binance",
|
|
622
|
+
"0x3f5ce5fbfe3e9af3971dd833d26ba9b5c936f0be": "Binance",
|
|
623
|
+
"0xd24400ae8bfebb18ca49be86258a3c749cf46853": "Gemini 2",
|
|
624
|
+
"0x6fc82a5fe25a5cdb58bc74600a40a69c065263f8": "Gemini 3",
|
|
625
|
+
"0x61edcdf5bb737adffe5043706e7c5bb1f1a56eea": "Gemini 4",
|
|
626
|
+
"0x07ee55aa48bb72dcc6e9d78256648910de513eca": "Gemini 5",
|
|
627
|
+
"0xdc76cd25977e0a5ae17155770273ad58648900d3": "Coinbase Prime",
|
|
628
|
+
"0xa9d1e08c7793af67e9d92fe308d5697fb81d3e43": "Coinbase 10",
|
|
629
|
+
"0x503828976d22510aad0201ac7ec88293211d23da": "Coinbase 2",
|
|
630
|
+
"0xddfabcdc4d8ffc6d5beaf154f18b778f892a0740": "Coinbase 3",
|
|
631
|
+
"0x3cd751e6b0078be393132286c442345e5dc49699": "Coinbase 4",
|
|
632
|
+
"0xb5d85cbf7cb3ee0d56b3bb207d5fc4b82f43f511": "Coinbase 5",
|
|
633
|
+
"0xeb2629a2734e272bcc07bda959863f316f4bd4cf": "Coinbase 6",
|
|
634
|
+
"0x02466e547bfdab679fc49e96bbfc62b9747d997c": "Coinbase 8",
|
|
635
|
+
"0xa090e606e30bd747d4e6245a1517ebe430f0057e": "Coinbase",
|
|
636
|
+
"0x8103683202aa8da10536036edef04cdd865c225e": "Kraken 13",
|
|
637
|
+
"0x6cc5f688a315f3dc28a7781717a9a798a59fda7b": "OKX 1",
|
|
638
|
+
"0x236f9f97e0e62388479bf9e5ba4889e46b0273c3": "OKX 2",
|
|
639
|
+
"0x5041ed759dd4afc3a72b8192c143f72f4724081a": "OKX 4",
|
|
640
|
+
"0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88": "MEXC",
|
|
641
|
+
"0x0d0707963952f2fba59dd06f2b425ace40b492fe": "Gate.io",
|
|
642
|
+
"0x1c4b70a3968436b9a0a9cf5205c787eb81bb558c": "Gate.io 3",
|
|
643
|
+
"0xd793281182a0e3e023116004778f45c29fc14f19": "Gate.io 4",
|
|
644
|
+
"0x974caa59e49682cda0ad2bbe82983419a2ecc400": "HTX",
|
|
645
|
+
"0x0211f3cedbef3143223d3acf0e589747933e8527": "HTX 2",
|
|
646
|
+
"0x1062a747393198f70f71ec65a582423dba7e5ab3": "Bybit",
|
|
647
|
+
"0xee5b5b923ffce93a870b3104b7ca09c3db80047a": "Bybit 2",
|
|
648
|
+
"0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503": "Binance: Foundation",
|
|
649
|
+
"0xbe0eb53f46cd790cd13851d5eff43d12404d33e8": "Binance 7",
|
|
650
|
+
"0x40ec5b33f54e0e8a33a975908c5ba1c14e5bbbdf": "Polygon Bridge",
|
|
651
|
+
"0xa3a7b6f88361f48403514059f1f16c8e78d60eec": "Arbitrum Bridge",
|
|
652
|
+
"0x99c9fc46f92e8a1c0dec1b1747d010903e884be1": "Optimism Bridge",
|
|
653
|
+
"0x8315177ab297ba92a06054ce80a67ed4dbd7ed3a": "Arbitrum: Delayed Inbox",
|
|
654
|
+
"0x0000000000000000000000000000000000000000": "Null/Burn Address"
|
|
655
|
+
};
|
|
656
|
+
function getAddressLabel(address) {
|
|
657
|
+
const lower = address.toLowerCase();
|
|
658
|
+
return KNOWN_LABELS[lower] || shortenAddress(address);
|
|
659
|
+
}
|
|
660
|
+
function shortenAddress(address) {
|
|
661
|
+
return `${address.slice(0, 6)}...${address.slice(-4)}`;
|
|
662
|
+
}
|
|
663
|
+
server.registerTool(
|
|
664
|
+
"blockrun_whale",
|
|
665
|
+
{
|
|
666
|
+
description: `Track large ETH transfers (whale movements). Uses BigQuery public data.
|
|
667
|
+
|
|
668
|
+
Shows:
|
|
669
|
+
- Large transfers (100+ ETH)
|
|
670
|
+
- Exchange inflows/outflows
|
|
671
|
+
- Labels for known addresses (Binance, Coinbase, etc.)
|
|
672
|
+
|
|
673
|
+
Note: Requires GOOGLE_APPLICATION_CREDENTIALS env var for BigQuery auth.
|
|
674
|
+
For MVP/demo: Returns simulated data if BigQuery not configured.`,
|
|
675
|
+
inputSchema: {
|
|
676
|
+
hours: z.number().optional().default(24).describe("Hours to look back (default: 24)"),
|
|
677
|
+
min_eth: z.number().optional().default(100).describe("Minimum ETH amount (default: 100)"),
|
|
678
|
+
limit: z.number().optional().default(20).describe("Max results (default: 20)")
|
|
679
|
+
}
|
|
680
|
+
},
|
|
681
|
+
async ({ hours, min_eth, limit }) => {
|
|
682
|
+
const hasGoogleCreds = process.env.GOOGLE_APPLICATION_CREDENTIALS || process.env.GOOGLE_CLOUD_PROJECT || process.env.GCLOUD_PROJECT;
|
|
683
|
+
if (!hasGoogleCreds) {
|
|
684
|
+
const demoData = [
|
|
685
|
+
{ from: "0xf977814e90da44bfa03b6295a0616a897441acec", to: "0x28c6c06298d514db089934071355e5743bf21d60", value: 5e3, time: "2h ago" },
|
|
686
|
+
{ from: "0x503828976d22510aad0201ac7ec88293211d23da", to: "0x47ac0fb4f2d84898e4d9e7b4dab3c24507a6d503", value: 3200, time: "4h ago" },
|
|
687
|
+
{ from: "0x6cc5f688a315f3dc28a7781717a9a798a59fda7b", to: "0xd24400ae8bfebb18ca49be86258a3c749cf46853", value: 2100, time: "6h ago" },
|
|
688
|
+
{ from: "0x1062a747393198f70f71ec65a582423dba7e5ab3", to: "0x99c9fc46f92e8a1c0dec1b1747d010903e884be1", value: 1800, time: "8h ago" },
|
|
689
|
+
{ from: "0x75e89d5979e4f6fba9f97c104c2f0afb3f1dcb88", to: "0xa3a7b6f88361f48403514059f1f16c8e78d60eec", value: 1500, time: "12h ago" }
|
|
690
|
+
];
|
|
691
|
+
const lines = demoData.map((t) => {
|
|
692
|
+
const fromLabel = getAddressLabel(t.from);
|
|
693
|
+
const toLabel = getAddressLabel(t.to);
|
|
694
|
+
return `${t.value.toLocaleString()} ETH | ${fromLabel} \u2192 ${toLabel} | ${t.time}`;
|
|
695
|
+
});
|
|
696
|
+
return {
|
|
697
|
+
content: [{
|
|
698
|
+
type: "text",
|
|
699
|
+
text: `[Whale Tracker - DEMO MODE]
|
|
700
|
+
\u26A0\uFE0F BigQuery not configured. Showing sample data.
|
|
701
|
+
|
|
702
|
+
To enable real data:
|
|
703
|
+
1. Create GCP project: console.cloud.google.com
|
|
704
|
+
2. Enable BigQuery API
|
|
705
|
+
3. Set GOOGLE_APPLICATION_CREDENTIALS env var
|
|
706
|
+
|
|
707
|
+
Sample whale movements:
|
|
708
|
+
${lines.join("\n")}
|
|
709
|
+
|
|
710
|
+
Total: ${demoData.reduce((s, t) => s + t.value, 0).toLocaleString()} ETH across ${demoData.length} transfers`
|
|
711
|
+
}],
|
|
712
|
+
structuredContent: { demo: true, transfers: demoData }
|
|
713
|
+
};
|
|
714
|
+
}
|
|
715
|
+
return {
|
|
716
|
+
content: [{
|
|
717
|
+
type: "text",
|
|
718
|
+
text: `[Whale Tracker]
|
|
719
|
+
|
|
720
|
+
BigQuery credentials detected. Real-time query:
|
|
721
|
+
- Looking back: ${hours}h
|
|
722
|
+
- Min transfer: ${min_eth} ETH
|
|
723
|
+
- Limit: ${limit} results
|
|
724
|
+
|
|
725
|
+
Query would run:
|
|
726
|
+
SELECT block_timestamp, from_address, to_address, value/1e18 as eth
|
|
727
|
+
FROM \`bigquery-public-data.crypto_ethereum.transactions\`
|
|
728
|
+
WHERE value > ${min_eth} * 1e18
|
|
729
|
+
AND block_timestamp > TIMESTAMP_SUB(CURRENT_TIMESTAMP(), INTERVAL ${hours} HOUR)
|
|
730
|
+
ORDER BY value DESC
|
|
731
|
+
LIMIT ${limit}
|
|
732
|
+
|
|
733
|
+
Note: Full BigQuery integration coming soon.`
|
|
734
|
+
}]
|
|
735
|
+
};
|
|
736
|
+
}
|
|
737
|
+
);
|
|
738
|
+
server.registerTool(
|
|
739
|
+
"blockrun_analyze",
|
|
740
|
+
{
|
|
741
|
+
description: `Comprehensive trading analysis combining multiple data sources.
|
|
742
|
+
|
|
743
|
+
Analyzes:
|
|
744
|
+
- DEX data (price, volume, liquidity) via DexScreener
|
|
745
|
+
- Twitter/X sentiment via Grok
|
|
746
|
+
- Whale movements (if BigQuery configured)
|
|
747
|
+
- AI synthesis of all data
|
|
748
|
+
|
|
749
|
+
Example: blockrun_analyze({ token: "SOL", question: "Should I buy?" })`,
|
|
750
|
+
inputSchema: {
|
|
751
|
+
token: z.string().describe("Token symbol or address to analyze"),
|
|
752
|
+
question: z.string().optional().describe("Specific question (default: general analysis)"),
|
|
753
|
+
include_twitter: z.boolean().optional().default(true).describe("Include Twitter sentiment"),
|
|
754
|
+
include_whale: z.boolean().optional().default(false).describe("Include whale tracking")
|
|
755
|
+
}
|
|
756
|
+
},
|
|
757
|
+
async ({ token, question, include_twitter, include_whale }) => {
|
|
758
|
+
const llm = getClient();
|
|
759
|
+
const analysisPrompt = question || `Provide comprehensive trading analysis for ${token}`;
|
|
760
|
+
let contextData = "";
|
|
761
|
+
try {
|
|
762
|
+
const dexUrl = `https://api.dexscreener.com/latest/dex/search?q=${encodeURIComponent(token)}`;
|
|
763
|
+
const dexResponse = await fetch(dexUrl);
|
|
764
|
+
const dexData = await dexResponse.json();
|
|
765
|
+
if (dexData.pairs && dexData.pairs.length > 0) {
|
|
766
|
+
const topPair = dexData.pairs[0];
|
|
767
|
+
contextData += `
|
|
768
|
+
## DEX Data (DexScreener)
|
|
769
|
+
`;
|
|
770
|
+
contextData += `- Token: ${topPair.baseToken.name} (${topPair.baseToken.symbol})
|
|
771
|
+
`;
|
|
772
|
+
contextData += `- Price: $${parseFloat(topPair.priceUsd).toFixed(6)}
|
|
773
|
+
`;
|
|
774
|
+
contextData += `- 24h Change: ${topPair.priceChange?.h24?.toFixed(2) || "N/A"}%
|
|
775
|
+
`;
|
|
776
|
+
contextData += `- 24h Volume: $${((topPair.volume?.h24 || 0) / 1e6).toFixed(2)}M
|
|
777
|
+
`;
|
|
778
|
+
contextData += `- Liquidity: $${((topPair.liquidity?.usd || 0) / 1e6).toFixed(2)}M
|
|
779
|
+
`;
|
|
780
|
+
contextData += `- FDV: $${((topPair.fdv || 0) / 1e9).toFixed(2)}B
|
|
781
|
+
`;
|
|
782
|
+
contextData += `- Chain: ${topPair.chainId}
|
|
783
|
+
`;
|
|
784
|
+
}
|
|
785
|
+
} catch (err) {
|
|
786
|
+
contextData += `
|
|
787
|
+
## DEX Data: Error fetching
|
|
788
|
+
`;
|
|
789
|
+
}
|
|
790
|
+
if (include_twitter) {
|
|
791
|
+
try {
|
|
792
|
+
const twitterResponse = await llm.chat("xai/grok-3", `What are people saying about ${token} on Twitter/X right now? Focus on: sentiment, key influencers, trending topics, price predictions.`, {
|
|
793
|
+
system: "Real-time X/Twitter search. Provide factual summary of recent posts.",
|
|
794
|
+
search: true
|
|
795
|
+
});
|
|
796
|
+
contextData += `
|
|
797
|
+
## Twitter/X Sentiment (via Grok)
|
|
798
|
+
${twitterResponse}
|
|
799
|
+
`;
|
|
800
|
+
} catch {
|
|
801
|
+
contextData += `
|
|
802
|
+
## Twitter: Unable to fetch
|
|
803
|
+
`;
|
|
804
|
+
}
|
|
805
|
+
}
|
|
806
|
+
if (include_whale) {
|
|
807
|
+
contextData += `
|
|
808
|
+
## Whale Movements
|
|
809
|
+
`;
|
|
810
|
+
contextData += `Note: BigQuery not configured. In production, this would show:
|
|
811
|
+
`;
|
|
812
|
+
contextData += `- Large transfers to/from exchanges
|
|
813
|
+
`;
|
|
814
|
+
contextData += `- Smart money wallet movements
|
|
815
|
+
`;
|
|
816
|
+
contextData += `- Exchange inflow/outflow trends
|
|
817
|
+
`;
|
|
818
|
+
}
|
|
819
|
+
const synthesisPrompt = `You are a crypto trading analyst. Based on the following data, answer: "${analysisPrompt}"
|
|
820
|
+
|
|
821
|
+
${contextData}
|
|
822
|
+
|
|
823
|
+
Provide:
|
|
824
|
+
1. Key findings (bullet points)
|
|
825
|
+
2. Risk assessment (Low/Medium/High)
|
|
826
|
+
3. Trading suggestion (if asked)
|
|
827
|
+
4. What to watch for
|
|
828
|
+
|
|
829
|
+
Be factual and balanced. Don't give financial advice, but provide analysis based on the data.`;
|
|
830
|
+
try {
|
|
831
|
+
const analysis = await llm.chat("openai/gpt-4o", synthesisPrompt, {
|
|
832
|
+
system: "Expert crypto trading analyst. Provide data-driven analysis.",
|
|
833
|
+
maxTokens: 1500
|
|
834
|
+
});
|
|
835
|
+
return {
|
|
836
|
+
content: [{
|
|
837
|
+
type: "text",
|
|
838
|
+
text: `[BlockRun Trading Analysis: ${token}]
|
|
839
|
+
|
|
840
|
+
${analysis}
|
|
841
|
+
|
|
842
|
+
---
|
|
843
|
+
Data sources: DexScreener${include_twitter ? ", Twitter/X (Grok)" : ""}${include_whale ? ", Whale Tracker" : ""}`
|
|
844
|
+
}],
|
|
845
|
+
structuredContent: { token, question: analysisPrompt, analysis }
|
|
846
|
+
};
|
|
847
|
+
} catch (error) {
|
|
848
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
849
|
+
return {
|
|
850
|
+
content: [{ type: "text", text: formatError(errorMessage) }],
|
|
851
|
+
isError: true
|
|
852
|
+
};
|
|
853
|
+
}
|
|
854
|
+
}
|
|
855
|
+
);
|
|
856
|
+
function calculateEMA(prices, period) {
|
|
857
|
+
const k = 2 / (period + 1);
|
|
858
|
+
const ema = [prices[0]];
|
|
859
|
+
for (let i = 1; i < prices.length; i++) {
|
|
860
|
+
ema.push(prices[i] * k + ema[i - 1] * (1 - k));
|
|
861
|
+
}
|
|
862
|
+
return ema;
|
|
863
|
+
}
|
|
864
|
+
function calculateRSI(prices, period = 14) {
|
|
865
|
+
if (prices.length < period + 1) return 50;
|
|
866
|
+
let gains = 0, losses = 0;
|
|
867
|
+
for (let i = prices.length - period; i < prices.length; i++) {
|
|
868
|
+
const change = prices[i] - prices[i - 1];
|
|
869
|
+
if (change > 0) gains += change;
|
|
870
|
+
else losses -= change;
|
|
871
|
+
}
|
|
872
|
+
const avgGain = gains / period;
|
|
873
|
+
const avgLoss = losses / period;
|
|
874
|
+
if (avgLoss === 0) return 100;
|
|
875
|
+
const rs = avgGain / avgLoss;
|
|
876
|
+
return 100 - 100 / (1 + rs);
|
|
877
|
+
}
|
|
878
|
+
function calculateMACD(prices) {
|
|
879
|
+
const ema12 = calculateEMA(prices, 12);
|
|
880
|
+
const ema26 = calculateEMA(prices, 26);
|
|
881
|
+
const macdLine = ema12.map((v, i) => v - ema26[i]);
|
|
882
|
+
const signalLine = calculateEMA(macdLine.slice(-9), 9);
|
|
883
|
+
const macd = macdLine[macdLine.length - 1];
|
|
884
|
+
const signal = signalLine[signalLine.length - 1];
|
|
885
|
+
return { macd, signal, histogram: macd - signal };
|
|
886
|
+
}
|
|
887
|
+
server.registerTool(
|
|
888
|
+
"blockrun_signal",
|
|
889
|
+
{
|
|
890
|
+
description: `Generate trading signals using RSI + MACD + EMA strategy.
|
|
891
|
+
|
|
892
|
+
Strategy (from freqtrade-strategies):
|
|
893
|
+
- BUY when: RSI < 40 (oversold) + MACD > Signal + Price > EMA200
|
|
894
|
+
- SELL when: RSI > 70 (overbought) or take profit/stop loss
|
|
895
|
+
|
|
896
|
+
Returns: BUY / SELL / HOLD signal with confidence level.
|
|
897
|
+
|
|
898
|
+
Example: blockrun_signal({ symbol: "BTCUSDT" })`,
|
|
899
|
+
inputSchema: {
|
|
900
|
+
symbol: z.string().describe("Trading pair (e.g., BTCUSDT, ETHUSDT, SOLUSDT)"),
|
|
901
|
+
timeframe: z.enum(["5m", "15m", "1h", "4h"]).optional().default("1h").describe("Candle timeframe")
|
|
902
|
+
}
|
|
903
|
+
},
|
|
904
|
+
async ({ symbol, timeframe }) => {
|
|
905
|
+
try {
|
|
906
|
+
const url = `https://api.binance.com/api/v3/klines?symbol=${symbol.toUpperCase()}&interval=${timeframe}&limit=250`;
|
|
907
|
+
const response = await fetch(url);
|
|
908
|
+
if (!response.ok) {
|
|
909
|
+
throw new Error(`Binance API error: ${response.status}`);
|
|
910
|
+
}
|
|
911
|
+
const candles = await response.json();
|
|
912
|
+
const closes = candles.map((c) => parseFloat(c[4]));
|
|
913
|
+
const currentPrice = closes[closes.length - 1];
|
|
914
|
+
const rsi = calculateRSI(closes);
|
|
915
|
+
const { macd, signal, histogram } = calculateMACD(closes);
|
|
916
|
+
const ema200 = calculateEMA(closes, 200);
|
|
917
|
+
const currentEMA200 = ema200[ema200.length - 1];
|
|
918
|
+
const ema50 = calculateEMA(closes, 50);
|
|
919
|
+
const currentEMA50 = ema50[ema50.length - 1];
|
|
920
|
+
let signalType = "HOLD";
|
|
921
|
+
let confidence = 0;
|
|
922
|
+
let reasons = [];
|
|
923
|
+
const rsiOversold = rsi < 40;
|
|
924
|
+
const macdBullish = macd > signal;
|
|
925
|
+
const aboveEMA200 = currentPrice > currentEMA200;
|
|
926
|
+
const aboveEMA50 = currentPrice > currentEMA50;
|
|
927
|
+
const rsiOverbought = rsi > 70;
|
|
928
|
+
const macdBearish = macd < signal;
|
|
929
|
+
const belowEMA200 = currentPrice < currentEMA200;
|
|
930
|
+
if (rsiOversold && macdBullish && aboveEMA200) {
|
|
931
|
+
signalType = "BUY";
|
|
932
|
+
confidence = 80;
|
|
933
|
+
reasons.push("RSI oversold (<40)");
|
|
934
|
+
reasons.push("MACD bullish crossover");
|
|
935
|
+
reasons.push("Price above EMA200 (uptrend)");
|
|
936
|
+
if (aboveEMA50) {
|
|
937
|
+
confidence += 10;
|
|
938
|
+
reasons.push("Price above EMA50 (strong)");
|
|
939
|
+
}
|
|
940
|
+
} else if (rsiOverbought || macdBearish && belowEMA200) {
|
|
941
|
+
signalType = "SELL";
|
|
942
|
+
confidence = rsiOverbought ? 75 : 60;
|
|
943
|
+
if (rsiOverbought) reasons.push("RSI overbought (>70)");
|
|
944
|
+
if (macdBearish) reasons.push("MACD bearish");
|
|
945
|
+
if (belowEMA200) reasons.push("Price below EMA200 (downtrend)");
|
|
946
|
+
} else {
|
|
947
|
+
signalType = "HOLD";
|
|
948
|
+
confidence = 50;
|
|
949
|
+
reasons.push("No clear signal");
|
|
950
|
+
if (rsi < 50 && macdBullish) reasons.push("Slight bullish bias");
|
|
951
|
+
if (rsi > 50 && macdBearish) reasons.push("Slight bearish bias");
|
|
952
|
+
}
|
|
953
|
+
const stopLoss = signalType === "BUY" ? currentPrice * 0.9 : null;
|
|
954
|
+
const takeProfit = signalType === "BUY" ? currentPrice * 1.2 : null;
|
|
955
|
+
const result = `[Trading Signal: ${symbol}]
|
|
956
|
+
|
|
957
|
+
Signal: ${signalType} (${confidence}% confidence)
|
|
958
|
+
Price: $${currentPrice.toFixed(2)}
|
|
959
|
+
|
|
960
|
+
Indicators:
|
|
961
|
+
- RSI (14): ${rsi.toFixed(1)} ${rsi < 30 ? "\u{1F7E2} Oversold" : rsi > 70 ? "\u{1F534} Overbought" : "\u26AA Neutral"}
|
|
962
|
+
- MACD: ${macd.toFixed(4)} | Signal: ${signal.toFixed(4)} | ${histogram > 0 ? "\u{1F7E2} Bullish" : "\u{1F534} Bearish"}
|
|
963
|
+
- EMA 50: $${currentEMA50.toFixed(2)} ${currentPrice > currentEMA50 ? "\u{1F7E2} Above" : "\u{1F534} Below"}
|
|
964
|
+
- EMA 200: $${currentEMA200.toFixed(2)} ${currentPrice > currentEMA200 ? "\u{1F7E2} Above" : "\u{1F534} Below"}
|
|
965
|
+
|
|
966
|
+
Reasons:
|
|
967
|
+
${reasons.map((r) => `\u2022 ${r}`).join("\n")}
|
|
968
|
+
${signalType === "BUY" ? `
|
|
969
|
+
Suggested:
|
|
970
|
+
\u2022 Stop Loss: $${stopLoss?.toFixed(2)} (-10%)
|
|
971
|
+
\u2022 Take Profit: $${takeProfit?.toFixed(2)} (+20%)` : ""}
|
|
972
|
+
|
|
973
|
+
Strategy: RSI + MACD + EMA (freqtrade-strategies)
|
|
974
|
+
Timeframe: ${timeframe}`;
|
|
975
|
+
return {
|
|
976
|
+
content: [{ type: "text", text: result }],
|
|
977
|
+
structuredContent: {
|
|
978
|
+
symbol,
|
|
979
|
+
signal: signalType,
|
|
980
|
+
confidence,
|
|
981
|
+
price: currentPrice,
|
|
982
|
+
indicators: { rsi, macd, signal, ema50: currentEMA50, ema200: currentEMA200 },
|
|
983
|
+
stopLoss,
|
|
984
|
+
takeProfit,
|
|
985
|
+
reasons
|
|
986
|
+
}
|
|
987
|
+
};
|
|
988
|
+
} catch (error) {
|
|
989
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
990
|
+
return {
|
|
991
|
+
content: [{ type: "text", text: `Signal error: ${errorMessage}` }],
|
|
992
|
+
isError: true
|
|
993
|
+
};
|
|
994
|
+
}
|
|
995
|
+
}
|
|
996
|
+
);
|
|
997
|
+
var ZERO_X_API = "https://api.0x.org/swap/v1";
|
|
998
|
+
var BASE_CHAIN_ID_NUM = 8453;
|
|
999
|
+
var BASE_TOKENS = {
|
|
1000
|
+
"ETH": "0xEeeeeEeeeEeEeeEeEeEeeEEEeeeeEeeeeeeeEEeE",
|
|
1001
|
+
"WETH": "0x4200000000000000000000000000000000000006",
|
|
1002
|
+
"USDC": "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913",
|
|
1003
|
+
"USDbC": "0xd9aAEc86B65D86f6A7B5B1b0c42FFA531710b6CA",
|
|
1004
|
+
"DAI": "0x50c5725949A6F0c72E6C4a641F24049A917DB0Cb",
|
|
1005
|
+
"cbETH": "0x2Ae3F1Ec7F1F5012CFEab0185bfc7aa3cf0DEc22"
|
|
1006
|
+
};
|
|
1007
|
+
server.registerTool(
|
|
1008
|
+
"blockrun_swap",
|
|
1009
|
+
{
|
|
1010
|
+
description: `Execute token swaps on Base network using 0x aggregator.
|
|
1011
|
+
|
|
1012
|
+
\u26A0\uFE0F REAL MONEY - requires user confirmation before execution.
|
|
1013
|
+
|
|
1014
|
+
Example: blockrun_swap({ from: "USDC", to: "ETH", amount: 10 })`,
|
|
1015
|
+
inputSchema: {
|
|
1016
|
+
from: z.string().describe("Token to sell (USDC, ETH, WETH, etc.)"),
|
|
1017
|
+
to: z.string().describe("Token to buy"),
|
|
1018
|
+
amount: z.number().describe("Amount in 'from' token"),
|
|
1019
|
+
slippage: z.number().optional().default(0.5).describe("Max slippage % (default 0.5)"),
|
|
1020
|
+
execute: z.boolean().optional().default(false).describe("Set true to execute (requires confirmation)")
|
|
1021
|
+
}
|
|
1022
|
+
},
|
|
1023
|
+
async ({ from, to, amount, slippage, execute }) => {
|
|
1024
|
+
const fromUpper = from.toUpperCase();
|
|
1025
|
+
const toUpper = to.toUpperCase();
|
|
1026
|
+
const fromToken = BASE_TOKENS[fromUpper];
|
|
1027
|
+
const toToken = BASE_TOKENS[toUpper];
|
|
1028
|
+
if (!fromToken) {
|
|
1029
|
+
return {
|
|
1030
|
+
content: [{ type: "text", text: `Unknown token: ${from}. Supported: ${Object.keys(BASE_TOKENS).join(", ")}` }],
|
|
1031
|
+
isError: true
|
|
1032
|
+
};
|
|
1033
|
+
}
|
|
1034
|
+
if (!toToken) {
|
|
1035
|
+
return {
|
|
1036
|
+
content: [{ type: "text", text: `Unknown token: ${to}. Supported: ${Object.keys(BASE_TOKENS).join(", ")}` }],
|
|
1037
|
+
isError: true
|
|
1038
|
+
};
|
|
1039
|
+
}
|
|
1040
|
+
const decimals = fromUpper === "USDC" || fromUpper === "USDbC" ? 6 : 18;
|
|
1041
|
+
const amountWei = BigInt(Math.floor(amount * 10 ** decimals));
|
|
1042
|
+
try {
|
|
1043
|
+
const quoteUrl = `${ZERO_X_API}/quote?` + new URLSearchParams({
|
|
1044
|
+
sellToken: fromToken,
|
|
1045
|
+
buyToken: toToken,
|
|
1046
|
+
sellAmount: amountWei.toString(),
|
|
1047
|
+
slippagePercentage: (slippage / 100).toString(),
|
|
1048
|
+
chainId: BASE_CHAIN_ID_NUM.toString()
|
|
1049
|
+
});
|
|
1050
|
+
const estimatedOutput = fromUpper === "USDC" ? amount / 3300 : amount * 3300;
|
|
1051
|
+
const quoteResult = `[Swap Quote: ${fromUpper} \u2192 ${toUpper}]
|
|
1052
|
+
|
|
1053
|
+
Sell: ${amount} ${fromUpper}
|
|
1054
|
+
Buy (est): ~${estimatedOutput.toFixed(6)} ${toUpper}
|
|
1055
|
+
Slippage: ${slippage}%
|
|
1056
|
+
Network: Base
|
|
1057
|
+
|
|
1058
|
+
${execute ? "\u26A0\uFE0F EXECUTION REQUESTED" : "\u{1F4A1} Set execute: true to swap"}
|
|
1059
|
+
|
|
1060
|
+
Note: Full 0x integration requires API key.
|
|
1061
|
+
For demo, this shows the quote flow.
|
|
1062
|
+
|
|
1063
|
+
To execute:
|
|
1064
|
+
1. User confirms the swap
|
|
1065
|
+
2. Wallet signs transaction
|
|
1066
|
+
3. Swap executes on-chain
|
|
1067
|
+
4. Returns tx hash`;
|
|
1068
|
+
if (execute) {
|
|
1069
|
+
return {
|
|
1070
|
+
content: [{
|
|
1071
|
+
type: "text",
|
|
1072
|
+
text: `\u26A0\uFE0F SWAP EXECUTION DISABLED FOR SAFETY
|
|
1073
|
+
|
|
1074
|
+
To enable real swaps:
|
|
1075
|
+
1. Add 0x API key
|
|
1076
|
+
2. Implement transaction signing
|
|
1077
|
+
3. Add confirmation flow
|
|
1078
|
+
|
|
1079
|
+
This is a demo. The swap would:
|
|
1080
|
+
\u2022 Sell ${amount} ${fromUpper}
|
|
1081
|
+
\u2022 Buy ~${estimatedOutput.toFixed(6)} ${toUpper}
|
|
1082
|
+
\u2022 Gas: ~$0.01 on Base`
|
|
1083
|
+
}]
|
|
1084
|
+
};
|
|
1085
|
+
}
|
|
1086
|
+
return {
|
|
1087
|
+
content: [{ type: "text", text: quoteResult }],
|
|
1088
|
+
structuredContent: {
|
|
1089
|
+
from: fromUpper,
|
|
1090
|
+
to: toUpper,
|
|
1091
|
+
sellAmount: amount,
|
|
1092
|
+
buyAmount: estimatedOutput,
|
|
1093
|
+
slippage,
|
|
1094
|
+
execute: false
|
|
1095
|
+
}
|
|
1096
|
+
};
|
|
1097
|
+
} catch (error) {
|
|
1098
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
1099
|
+
return {
|
|
1100
|
+
content: [{ type: "text", text: `Swap error: ${errorMessage}` }],
|
|
1101
|
+
isError: true
|
|
1102
|
+
};
|
|
1103
|
+
}
|
|
1104
|
+
}
|
|
1105
|
+
);
|
|
528
1106
|
server.registerResource(
|
|
529
1107
|
"wallet",
|
|
530
1108
|
"blockrun://wallet",
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/mcp",
|
|
3
|
-
"version": "0.4.
|
|
3
|
+
"version": "0.4.2",
|
|
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,7 +44,7 @@
|
|
|
44
44
|
"url": "https://github.com/blockrunai/blockrun-mcp/issues"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@blockrun/llm": "^
|
|
47
|
+
"@blockrun/llm": "^1.4.3",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
49
|
"jimp": "^1.6.0",
|
|
50
50
|
"open": "^11.0.0",
|