@blockrun/mcp 0.6.5 → 0.6.7

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.
Files changed (2) hide show
  1. package/dist/index.js +86 -157
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -5,70 +5,29 @@ import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
5
5
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
6
6
 
7
7
  // src/utils/wallet.ts
8
- import { LLMClient, ImageClient } from "@blockrun/llm";
9
- import { generatePrivateKey, privateKeyToAccount } from "viem/accounts";
10
- import * as fs from "fs";
11
-
12
- // src/utils/constants.ts
13
- import * as path from "path";
14
- import * as os from "os";
15
- var WALLET_DIR = path.join(os.homedir(), ".blockrun");
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";
20
- var BASE_RPC_URLS = [
21
- "https://mainnet.base.org",
22
- "https://base.llamarpc.com",
23
- "https://1rpc.io/base"
24
- ];
25
- var MODEL_TIERS = {
26
- fast: ["google/gemini-2.5-flash", "google/gemini-3.1-flash-lite", "openai/gpt-5-mini", "deepseek/deepseek-chat", "google/gemini-3-flash-preview"],
27
- balanced: ["openai/gpt-5.4", "anthropic/claude-sonnet-4.6", "google/gemini-2.5-pro", "openai/gpt-5.3", "google/gemini-3.1-pro"],
28
- powerful: ["openai/gpt-5.4-pro", "anthropic/claude-opus-4.6", "anthropic/claude-opus-4.5", "openai/o3", "openai/gpt-5.4"],
29
- cheap: ["nvidia/gpt-oss-120b", "nvidia/deepseek-v3.2", "google/gemini-2.5-flash", "deepseek/deepseek-chat", "openai/gpt-5.4-nano"],
30
- reasoning: ["openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner", "openai/gpt-5.3-codex"],
31
- free: ["nvidia/gpt-oss-120b", "nvidia/deepseek-v3.2", "nvidia/nemotron-ultra-253b", "nvidia/nemotron-super-49b", "nvidia/qwen3-coder-480b", "nvidia/llama-4-maverick", "nvidia/gpt-oss-20b"],
32
- coding: ["openai/gpt-5.3-codex", "nvidia/qwen3-coder-480b", "nvidia/devstral-2-123b", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4"]
33
- };
34
-
35
- // src/utils/wallet.ts
36
- var _walletWasCreated = false;
37
- var _walletAddress = null;
8
+ import {
9
+ LLMClient,
10
+ ImageClient,
11
+ getOrCreateWallet,
12
+ getPaymentLinks,
13
+ formatWalletCreatedMessage,
14
+ formatNeedsFundingMessage
15
+ } from "@blockrun/llm";
38
16
  var _client = null;
39
17
  var _imageClient = null;
40
- function getOrCreateWalletKey() {
41
- const envKey = process.env.BLOCKRUN_WALLET_KEY || process.env.BASE_CHAIN_WALLET_KEY;
42
- if (envKey) {
43
- const account2 = privateKeyToAccount(envKey);
44
- _walletAddress = account2.address;
45
- return envKey;
46
- }
47
- if (fs.existsSync(WALLET_FILE)) {
48
- try {
49
- const savedKey = fs.readFileSync(WALLET_FILE, "utf-8").trim();
50
- if (savedKey.startsWith("0x") && savedKey.length === 66) {
51
- const account2 = privateKeyToAccount(savedKey);
52
- _walletAddress = account2.address;
53
- return savedKey;
54
- }
55
- } catch {
56
- }
57
- }
58
- const newKey = generatePrivateKey();
59
- const account = privateKeyToAccount(newKey);
60
- _walletAddress = account.address;
61
- _walletWasCreated = true;
62
- try {
63
- if (!fs.existsSync(WALLET_DIR)) {
64
- fs.mkdirSync(WALLET_DIR, { recursive: true, mode: 448 });
18
+ var _walletInfo = null;
19
+ function ensureWallet() {
20
+ if (!_walletInfo) {
21
+ _walletInfo = getOrCreateWallet();
22
+ if (_walletInfo.isNew) {
23
+ console.error(formatWalletCreatedMessage(_walletInfo.address));
65
24
  }
66
- fs.writeFileSync(WALLET_FILE, newKey, { mode: 384 });
67
- console.error(`[BlockRun] New wallet created and saved to ${WALLET_FILE}`);
68
- } catch (err) {
69
- console.error(`[BlockRun] Warning: Could not save wallet to file: ${err}`);
70
25
  }
71
- return newKey;
26
+ return _walletInfo;
27
+ }
28
+ function getOrCreateWalletKey() {
29
+ const info = ensureWallet();
30
+ return info.privateKey;
72
31
  }
73
32
  function getClient() {
74
33
  if (!_client) {
@@ -85,28 +44,29 @@ function getImageClient() {
85
44
  return _imageClient;
86
45
  }
87
46
  function getWalletInfo() {
88
- const llm = getClient();
89
- const address = llm.getWalletAddress();
47
+ const info = ensureWallet();
48
+ const links = getPaymentLinks(info.address);
90
49
  return {
91
- address,
50
+ address: info.address,
92
51
  network: "Base",
93
52
  chainId: 8453,
94
53
  currency: "USDC",
95
- isNew: _walletWasCreated,
96
- basescanUrl: `https://basescan.org/address/${address}`
54
+ isNew: info.isNew,
55
+ basescanUrl: links.basescan,
56
+ fundingUrl: links.blockrun
97
57
  };
98
58
  }
99
59
  async function getUsdcBalance(address) {
60
+ const USDC_ADDRESS2 = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
61
+ const BASE_RPC_URLS = [
62
+ "https://mainnet.base.org",
63
+ "https://base.llamarpc.com",
64
+ "https://1rpc.io/base"
65
+ ];
100
66
  const data = {
101
67
  jsonrpc: "2.0",
102
68
  method: "eth_call",
103
- params: [
104
- {
105
- to: USDC_ADDRESS,
106
- data: `0x70a08231000000000000000000000000${address.slice(2)}`
107
- },
108
- "latest"
109
- ],
69
+ params: [{ to: USDC_ADDRESS2, data: `0x70a08231000000000000000000000000${address.slice(2)}` }, "latest"],
110
70
  id: 1
111
71
  };
112
72
  for (const rpcUrl of BASE_RPC_URLS) {
@@ -117,9 +77,7 @@ async function getUsdcBalance(address) {
117
77
  body: JSON.stringify(data)
118
78
  });
119
79
  const result = await response.json();
120
- if (result.result) {
121
- return parseInt(result.result, 16) / 1e6;
122
- }
80
+ if (result.result) return parseInt(result.result, 16) / 1e6;
123
81
  } catch {
124
82
  continue;
125
83
  }
@@ -133,15 +91,35 @@ import { z } from "zod";
133
91
  // src/utils/qr.ts
134
92
  import QRCode from "qrcode";
135
93
  import open from "open";
136
- import * as fs2 from "fs";
94
+ import * as fs from "fs";
95
+
96
+ // src/utils/constants.ts
97
+ import * as path from "path";
98
+ import * as os from "os";
99
+ var WALLET_DIR = path.join(os.homedir(), ".blockrun");
100
+ var WALLET_FILE = path.join(WALLET_DIR, ".session");
101
+ var QR_FILE = path.join(WALLET_DIR, "qr.png");
102
+ var USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
103
+ var BASE_CHAIN_ID = "8453";
104
+ var MODEL_TIERS = {
105
+ fast: ["google/gemini-2.5-flash", "google/gemini-3.1-flash-lite", "openai/gpt-5-mini", "deepseek/deepseek-chat", "google/gemini-3-flash-preview"],
106
+ balanced: ["openai/gpt-5.4", "anthropic/claude-sonnet-4.6", "google/gemini-2.5-pro", "openai/gpt-5.3", "google/gemini-3.1-pro"],
107
+ powerful: ["openai/gpt-5.4-pro", "anthropic/claude-opus-4.6", "anthropic/claude-opus-4.5", "openai/o3", "openai/gpt-5.4"],
108
+ cheap: ["nvidia/gpt-oss-120b", "nvidia/deepseek-v3.2", "google/gemini-2.5-flash", "deepseek/deepseek-chat", "openai/gpt-5.4-nano"],
109
+ reasoning: ["openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner", "openai/gpt-5.3-codex"],
110
+ free: ["nvidia/gpt-oss-120b", "nvidia/deepseek-v3.2", "nvidia/nemotron-ultra-253b", "nvidia/nemotron-super-49b", "nvidia/qwen3-coder-480b", "nvidia/llama-4-maverick", "nvidia/gpt-oss-20b"],
111
+ coding: ["openai/gpt-5.3-codex", "nvidia/qwen3-coder-480b", "nvidia/devstral-2-123b", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4"]
112
+ };
113
+
114
+ // src/utils/qr.ts
137
115
  function getEip681Uri(address, amountUsdc = 1) {
138
116
  const amountWei = Math.floor(amountUsdc * 1e6);
139
117
  return `ethereum:${USDC_ADDRESS}@${BASE_CHAIN_ID}/transfer?address=${address}&uint256=${amountWei}`;
140
118
  }
141
119
  async function generateQrPng(address) {
142
120
  const eip681Uri = getEip681Uri(address);
143
- if (!fs2.existsSync(WALLET_DIR)) {
144
- fs2.mkdirSync(WALLET_DIR, { recursive: true, mode: 448 });
121
+ if (!fs.existsSync(WALLET_DIR)) {
122
+ fs.mkdirSync(WALLET_DIR, { recursive: true, mode: 448 });
145
123
  }
146
124
  await QRCode.toFile(QR_FILE, eip681Uri, {
147
125
  type: "png",
@@ -675,52 +653,8 @@ Error: ${errMsg}` }],
675
653
  );
676
654
  }
677
655
 
678
- // src/tools/twitter.ts
679
- import { z as z5 } from "zod";
680
- function registerTwitterTool(server, budget) {
681
- server.registerTool(
682
- "blockrun_twitter",
683
- {
684
- description: `Search real-time X/Twitter via Grok. Use for trending topics, @handles, breaking news.`,
685
- inputSchema: {
686
- query: z5.string().describe("Search query (can include @handles, topics)"),
687
- max_results: z5.number().optional().default(10).describe("Max results (1-25)")
688
- }
689
- },
690
- async ({ query, max_results }) => {
691
- const budgetCheck = checkBudget(budget);
692
- if (!budgetCheck.allowed) {
693
- return {
694
- content: [{ type: "text", text: `Budget limit reached ($${budget.spent.toFixed(4)} of $${budget.limit?.toFixed(2)}). Use blockrun_wallet with action: "budget" to adjust.` }],
695
- isError: true
696
- };
697
- }
698
- try {
699
- const llm = getClient();
700
- const response = await llm.chat("xai/grok-3", query, {
701
- system: `Real-time X/Twitter search. Focus on recent posts, key accounts, engagement. Max results: ${max_results}`,
702
- search: true
703
- });
704
- recordSpending(budget, 2e-3);
705
- return {
706
- content: [{ type: "text", text: `[X/Twitter via Grok]
707
-
708
- ${response}` }],
709
- structuredContent: { query, model: "xai/grok-3", response }
710
- };
711
- } catch (error) {
712
- const errorMessage = error instanceof Error ? error.message : String(error);
713
- return {
714
- content: [{ type: "text", text: formatError(errorMessage) }],
715
- isError: true
716
- };
717
- }
718
- }
719
- );
720
- }
721
-
722
656
  // src/tools/search.ts
723
- import { z as z6 } from "zod";
657
+ import { z as z5 } from "zod";
724
658
  function registerSearchTool(server) {
725
659
  server.registerTool(
726
660
  "blockrun_search",
@@ -732,11 +666,11 @@ Pricing: ~$0.01/search
732
666
 
733
667
  Returns a summary with cited sources.`,
734
668
  inputSchema: {
735
- query: z6.string().describe("Search query"),
736
- sources: z6.array(z6.enum(["web", "x", "news"])).optional().describe("Sources to search (default: web + x + news)"),
737
- max_results: z6.number().optional().default(10).describe("Max results per source (1-20)"),
738
- from_date: z6.string().optional().describe("Start date filter (YYYY-MM-DD)"),
739
- to_date: z6.string().optional().describe("End date filter (YYYY-MM-DD)")
669
+ query: z5.string().describe("Search query"),
670
+ sources: z5.array(z5.enum(["web", "x", "news"])).optional().describe("Sources to search (default: web + x + news)"),
671
+ max_results: z5.number().optional().default(10).describe("Max results per source (1-20)"),
672
+ from_date: z5.string().optional().describe("Start date filter (YYYY-MM-DD)"),
673
+ to_date: z5.string().optional().describe("End date filter (YYYY-MM-DD)")
740
674
  }
741
675
  },
742
676
  async ({ query, sources, max_results, from_date, to_date }) => {
@@ -764,7 +698,7 @@ Returns a summary with cited sources.`,
764
698
  }
765
699
 
766
700
  // src/tools/exa.ts
767
- import { z as z7 } from "zod";
701
+ import { z as z6 } from "zod";
768
702
  function registerExaTool(server) {
769
703
  server.registerTool(
770
704
  "blockrun_exa",
@@ -777,14 +711,14 @@ Actions:
777
711
  - contents: Fetch full Markdown text from URLs, ready for LLM context ($0.002/URL)
778
712
  - similar: Find pages semantically similar to a given URL ($0.01/call)`,
779
713
  inputSchema: {
780
- action: z7.enum(["search", "answer", "contents", "similar"]).describe("Action to perform"),
781
- query: z7.string().optional().describe("Natural language query (for search/answer)"),
782
- url: z7.string().optional().describe("Reference URL to find similar pages (for similar action)"),
783
- urls: z7.array(z7.string()).optional().describe("URLs to fetch content from (for contents action, up to 100)"),
784
- num_results: z7.number().optional().describe("Number of results to return (default: 10)"),
785
- category: z7.string().optional().describe("Category filter: 'news', 'research paper', 'company', 'tweet', 'github', 'pdf'"),
786
- include_domains: z7.array(z7.string()).optional().describe("Only search within these domains"),
787
- exclude_domains: z7.array(z7.string()).optional().describe("Exclude these domains from results")
714
+ action: z6.enum(["search", "answer", "contents", "similar"]).describe("Action to perform"),
715
+ query: z6.string().optional().describe("Natural language query (for search/answer)"),
716
+ url: z6.string().optional().describe("Reference URL to find similar pages (for similar action)"),
717
+ urls: z6.array(z6.string()).optional().describe("URLs to fetch content from (for contents action, up to 100)"),
718
+ num_results: z6.number().optional().describe("Number of results to return (default: 10)"),
719
+ category: z6.string().optional().describe("Category filter: 'news', 'research paper', 'company', 'tweet', 'github', 'pdf'"),
720
+ include_domains: z6.array(z6.string()).optional().describe("Only search within these domains"),
721
+ exclude_domains: z6.array(z6.string()).optional().describe("Exclude these domains from results")
788
722
  }
789
723
  },
790
724
  async ({ action, query, url, urls, num_results, category, include_domains, exclude_domains }) => {
@@ -837,28 +771,24 @@ Actions:
837
771
  }
838
772
 
839
773
  // src/tools/markets.ts
840
- import { z as z8 } from "zod";
774
+ import { z as z7 } from "zod";
841
775
  function registerMarketsTool(server) {
842
776
  server.registerTool(
843
777
  "blockrun_markets",
844
778
  {
845
- description: `Prediction market data via Predexon. Real-time data from Polymarket, Kalshi, dFlow, and Binance Futures.
846
-
847
- Usage:
848
- - GET: blockrun_markets with path + optional params
849
- - POST: blockrun_markets with path + body
779
+ description: `Prediction market data. Real-time from Polymarket, Kalshi.
850
780
 
851
781
  Example paths:
852
782
  - "polymarket/events" \u2014 list active events
853
- - "polymarket/search" + params: { q: "bitcoin" } \u2014 search events
783
+ - "polymarket/markets" \u2014 list markets
784
+ - "kalshi/markets" \u2014 Kalshi markets
854
785
  - "kalshi/markets/KXBTC-25MAR14" \u2014 specific market
855
- - "polymarket/query" + body: { filter: "active", limit: 10 } \u2014 structured query
856
786
 
857
- Pricing: $0.001/GET, $0.005/POST`,
787
+ $0.001/call.`,
858
788
  inputSchema: {
859
- path: z8.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14'"),
860
- params: z8.record(z8.string(), z8.string()).optional().describe("Query parameters for GET requests"),
861
- body: z8.any().optional().describe("JSON body for POST queries (triggers pmQuery)")
789
+ path: z7.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14'"),
790
+ params: z7.record(z7.string(), z7.string()).optional().describe("Query parameters for GET requests"),
791
+ body: z7.any().optional().describe("JSON body for POST queries (triggers pmQuery)")
862
792
  }
863
793
  },
864
794
  async ({ path: path2, params, body }) => {
@@ -881,7 +811,7 @@ Pricing: $0.001/GET, $0.005/POST`,
881
811
  }
882
812
 
883
813
  // src/tools/dex.ts
884
- import { z as z9 } from "zod";
814
+ import { z as z8 } from "zod";
885
815
  function registerDexTool(server) {
886
816
  server.registerTool(
887
817
  "blockrun_dex",
@@ -898,10 +828,10 @@ Examples:
898
828
  blockrun_dex({ token: "So11...xxx" }) -> Get specific token data
899
829
  blockrun_dex({ symbol: "PEPE" }) -> Search by symbol`,
900
830
  inputSchema: {
901
- query: z9.string().optional().describe("Search query (token name, symbol, or address)"),
902
- token: z9.string().optional().describe("Token address for direct lookup"),
903
- symbol: z9.string().optional().describe("Token symbol to search"),
904
- chain: z9.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
831
+ query: z8.string().optional().describe("Search query (token name, symbol, or address)"),
832
+ token: z8.string().optional().describe("Token address for direct lookup"),
833
+ symbol: z8.string().optional().describe("Token symbol to search"),
834
+ chain: z8.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
905
835
  }
906
836
  },
907
837
  async ({ query, token, symbol, chain }) => {
@@ -969,7 +899,6 @@ function initializeMcpServer(server) {
969
899
  registerChatTool(server, budget);
970
900
  registerModelsTool(server, modelCache);
971
901
  registerImageTool(server);
972
- registerTwitterTool(server, budget);
973
902
  registerSearchTool(server);
974
903
  registerExaTool(server);
975
904
  registerMarketsTool(server);
@@ -1013,12 +942,12 @@ function initializeMcpServer(server) {
1013
942
  async function main() {
1014
943
  const server = new McpServer({
1015
944
  name: "blockrun-mcp",
1016
- version: "0.6.4"
945
+ version: "0.6.6"
1017
946
  });
1018
947
  initializeMcpServer(server);
1019
948
  const transport = new StdioServerTransport();
1020
949
  await server.connect(transport);
1021
- console.error("BlockRun MCP Server started (v0.6.4) \u2014 stdio transport");
950
+ console.error("BlockRun MCP Server started (v0.6.6) \u2014 stdio transport");
1022
951
  }
1023
952
  main().catch((error) => {
1024
953
  console.error("Fatal error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/mcp",
3
- "version": "0.6.5",
3
+ "version": "0.6.7",
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",