@blockrun/mcp 0.6.6 → 0.6.8

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 (3) hide show
  1. package/README.md +2 -2
  2. package/dist/index.js +97 -149
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -7,7 +7,7 @@
7
7
  **Real-time data for Claude — markets, research, X/Twitter, crypto. No API keys. Pay per call.**
8
8
 
9
9
  ```bash
10
- claude mcp add blockrun npx @blockrun/mcp
10
+ claude mcp add blockrun npx -y @blockrun/mcp@latest
11
11
  ```
12
12
 
13
13
  Wallet auto-created. Fund with $5 USDC. Ask Claude anything.
@@ -49,7 +49,7 @@ After BlockRun, it can. Each query costs fractions of a cent, billed from a loca
49
49
 
50
50
  **Claude Code (recommended)**
51
51
  ```bash
52
- claude mcp add blockrun npx @blockrun/mcp
52
+ claude mcp add blockrun npx -y @blockrun/mcp@latest
53
53
  ```
54
54
 
55
55
  **Claude Desktop** — add to `claude_desktop_config.json`:
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,7 +771,7 @@ 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",
@@ -852,9 +786,9 @@ Example paths:
852
786
 
853
787
  $0.001/call.`,
854
788
  inputSchema: {
855
- path: z8.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14'"),
856
- params: z8.record(z8.string(), z8.string()).optional().describe("Query parameters for GET requests"),
857
- 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)")
858
792
  }
859
793
  },
860
794
  async ({ path: path2, params, body }) => {
@@ -877,7 +811,7 @@ $0.001/call.`,
877
811
  }
878
812
 
879
813
  // src/tools/dex.ts
880
- import { z as z9 } from "zod";
814
+ import { z as z8 } from "zod";
881
815
  function registerDexTool(server) {
882
816
  server.registerTool(
883
817
  "blockrun_dex",
@@ -894,10 +828,10 @@ Examples:
894
828
  blockrun_dex({ token: "So11...xxx" }) -> Get specific token data
895
829
  blockrun_dex({ symbol: "PEPE" }) -> Search by symbol`,
896
830
  inputSchema: {
897
- query: z9.string().optional().describe("Search query (token name, symbol, or address)"),
898
- token: z9.string().optional().describe("Token address for direct lookup"),
899
- symbol: z9.string().optional().describe("Token symbol to search"),
900
- 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.)")
901
835
  }
902
836
  },
903
837
  async ({ query, token, symbol, chain }) => {
@@ -965,7 +899,6 @@ function initializeMcpServer(server) {
965
899
  registerChatTool(server, budget);
966
900
  registerModelsTool(server, modelCache);
967
901
  registerImageTool(server);
968
- registerTwitterTool(server, budget);
969
902
  registerSearchTool(server);
970
903
  registerExaTool(server);
971
904
  registerMarketsTool(server);
@@ -1006,15 +939,30 @@ function initializeMcpServer(server) {
1006
939
  }
1007
940
 
1008
941
  // src/index.ts
942
+ var VERSION = "0.6.7";
943
+ async function checkForUpdate() {
944
+ try {
945
+ const resp = await fetch("https://registry.npmjs.org/@blockrun/mcp/latest", {
946
+ signal: AbortSignal.timeout(3e3)
947
+ });
948
+ const data = await resp.json();
949
+ if (data.version && data.version !== VERSION) {
950
+ console.error(`[BlockRun] Update available: v${VERSION} \u2192 v${data.version}`);
951
+ console.error(`[BlockRun] Run: claude mcp add blockrun npx -y @blockrun/mcp@latest`);
952
+ }
953
+ } catch {
954
+ }
955
+ }
1009
956
  async function main() {
1010
957
  const server = new McpServer({
1011
958
  name: "blockrun-mcp",
1012
- version: "0.6.5"
959
+ version: VERSION
1013
960
  });
1014
961
  initializeMcpServer(server);
1015
962
  const transport = new StdioServerTransport();
1016
963
  await server.connect(transport);
1017
- console.error("BlockRun MCP Server started (v0.6.5) \u2014 stdio transport");
964
+ console.error(`BlockRun MCP Server started (v${VERSION}) \u2014 stdio transport`);
965
+ checkForUpdate();
1018
966
  }
1019
967
  main().catch((error) => {
1020
968
  console.error("Fatal error:", error);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/mcp",
3
- "version": "0.6.6",
3
+ "version": "0.6.8",
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",