@blockrun/mcp 0.7.1 → 0.8.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (2) hide show
  1. package/dist/index.js +341 -31
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -597,15 +597,17 @@ Actions:
597
597
  - edit: Transform an existing image using img2img
598
598
 
599
599
  Generation models:
600
- - zai/cogview-4 ($0.02) \u2014 Zhipu CogView-4, photorealistic, great for detailed scenes
600
+ - zai/cogview-4 ($0.015) \u2014 Zhipu CogView-4, photorealistic, great for detailed scenes
601
+ - xai/grok-imagine-image ($0.02) \u2014 xAI Grok Imagine, stylized, fast
602
+ - xai/grok-imagine-image-pro ($0.07) \u2014 xAI Grok Imagine Pro, higher quality
603
+ - openai/gpt-image-1 ($0.02-0.04) \u2014 GPT native image generation
601
604
  - openai/dall-e-3 ($0.04-0.08) \u2014 High quality, prompt adherence
602
- - together/flux-schnell ($0.02) \u2014 Fast, stylized
603
- - google/nano-banana \u2014 Google image model
605
+ - google/nano-banana ($0.05) \u2014 Google image model
604
606
  Edit models: openai/gpt-image-1 (default for edits)`,
605
607
  inputSchema: {
606
608
  prompt: z4.string().describe("Image description or edit instructions"),
607
609
  action: z4.enum(["generate", "edit"]).optional().default("generate").describe("generate: create from text; edit: transform existing image"),
608
- model: z4.enum(["zai/cogview-4", "openai/dall-e-3", "together/flux-schnell", "google/nano-banana", "openai/gpt-image-1"]).optional().describe("Model to use (default: dall-e-3 for generate, gpt-image-1 for edit). zai/cogview-4 is Zhipu's photorealistic model."),
610
+ model: z4.enum(["zai/cogview-4", "openai/dall-e-3", "together/flux-schnell", "google/nano-banana", "openai/gpt-image-1", "xai/grok-imagine-image", "xai/grok-imagine-image-pro"]).optional().describe("Model to use (default: dall-e-3 for generate, gpt-image-1 for edit). xai/grok-imagine-image is stylized and fast; xai/grok-imagine-image-pro is higher quality."),
609
611
  image: z4.string().optional().describe("Source image for edit action: base64-encoded image or URL"),
610
612
  size: z4.enum(["1024x1024", "1792x1024", "1024x1792"]).optional().default("1024x1024"),
611
613
  quality: z4.enum(["standard", "hd"]).optional().default("standard")
@@ -804,8 +806,144 @@ async function fetchWithTimeout(url, options, timeoutMs) {
804
806
  }
805
807
  }
806
808
 
807
- // src/tools/search.ts
809
+ // src/tools/video.ts
808
810
  import { z as z6 } from "zod";
811
+ import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
812
+ import {
813
+ createPaymentPayload as createPaymentPayload2,
814
+ parsePaymentRequired as parsePaymentRequired2,
815
+ extractPaymentDetails as extractPaymentDetails2
816
+ } from "@blockrun/llm";
817
+ var BLOCKRUN_API2 = "https://blockrun.ai/api";
818
+ var VIDEO_TIMEOUT = 3e5;
819
+ function registerVideoTool(server) {
820
+ server.registerTool(
821
+ "blockrun_video",
822
+ {
823
+ description: `Generate short AI videos via BlockRun x402.
824
+
825
+ Uses xAI's Grok Imagine Video to turn a text prompt (and optional seed image) into an 8-second MP4 clip. The call blocks until the video is ready (30-120s typical).
826
+
827
+ Model: xai/grok-imagine-video ($0.05/sec, 8s default -> $0.42/clip)
828
+
829
+ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GCS so URLs don't expire).`,
830
+ inputSchema: {
831
+ prompt: z6.string().describe("Text description of the video to generate. E.g. 'a red apple slowly spinning on a wooden table', 'a hummingbird hovering near a red flower, ultra slow motion'"),
832
+ image_url: z6.string().url().optional().describe("Optional seed image URL for image-to-video generation"),
833
+ duration_seconds: z6.number().int().min(1).max(60).optional().describe("Duration to bill for (defaults to the model's 8s default). Must not exceed max_duration_seconds."),
834
+ model: z6.enum(["xai/grok-imagine-video"]).optional().default("xai/grok-imagine-video").describe("Video model to use")
835
+ }
836
+ },
837
+ async ({ prompt, image_url, duration_seconds, model }) => {
838
+ try {
839
+ const privateKey = getOrCreateWalletKey();
840
+ const account = privateKeyToAccount2(privateKey);
841
+ const url = `${BLOCKRUN_API2}/v1/videos/generations`;
842
+ const body = { model, prompt };
843
+ if (image_url) body.image_url = image_url;
844
+ if (duration_seconds !== void 0) body.duration_seconds = duration_seconds;
845
+ const resp402 = await fetchWithTimeout2(url, {
846
+ method: "POST",
847
+ headers: { "Content-Type": "application/json" },
848
+ body: JSON.stringify(body)
849
+ }, 15e3);
850
+ if (resp402.status !== 402) {
851
+ const data2 = await resp402.json();
852
+ throw new Error(`Expected 402, got ${resp402.status}: ${JSON.stringify(data2)}`);
853
+ }
854
+ const prHeader = resp402.headers.get("payment-required") || resp402.headers.get("PAYMENT-REQUIRED");
855
+ if (!prHeader) throw new Error("No PAYMENT-REQUIRED header in 402 response");
856
+ const paymentRequired = parsePaymentRequired2(prHeader);
857
+ const details = extractPaymentDetails2(paymentRequired);
858
+ const paymentPayload = await createPaymentPayload2(
859
+ privateKey,
860
+ account.address,
861
+ details.recipient,
862
+ details.amount,
863
+ details.network || "eip155:8453",
864
+ {
865
+ resourceUrl: details.resource?.url || url,
866
+ resourceDescription: details.resource?.description || "BlockRun Video Generation",
867
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
868
+ extra: details.extra
869
+ }
870
+ );
871
+ const resp = await fetchWithTimeout2(url, {
872
+ method: "POST",
873
+ headers: {
874
+ "Content-Type": "application/json",
875
+ "PAYMENT-SIGNATURE": paymentPayload
876
+ },
877
+ body: JSON.stringify(body)
878
+ }, VIDEO_TIMEOUT);
879
+ if (resp.status === 402) {
880
+ throw new Error("Payment rejected. Check your wallet balance.");
881
+ }
882
+ if (!resp.ok) {
883
+ const errBody = await resp.json().catch(() => ({ error: "Request failed" }));
884
+ throw new Error(`API error ${resp.status}: ${JSON.stringify(errBody)}`);
885
+ }
886
+ const data = await resp.json();
887
+ const clip = data.data?.[0];
888
+ if (!clip?.url) throw new Error("No video URL in response");
889
+ const txHash = resp.headers.get("X-Payment-Receipt") || resp.headers.get("x-payment-receipt");
890
+ const lines = [
891
+ `\u{1F3AC} Video ready!`,
892
+ `URL: ${clip.url}`,
893
+ `Duration: ${clip.duration_seconds ? `${clip.duration_seconds}s` : "8s"}`,
894
+ `Model: ${data.model || model}`,
895
+ ...clip.backed_up ? [`Backed up to BlockRun storage (URL is permanent)`] : clip.source_url ? [`Source URL: ${clip.source_url}`] : [],
896
+ ...clip.request_id ? [`Request ID: ${clip.request_id}`] : [],
897
+ ...txHash ? [`Tx: ${txHash}`] : []
898
+ ];
899
+ return {
900
+ content: [{ type: "text", text: lines.join("\n") }],
901
+ structuredContent: {
902
+ url: clip.url,
903
+ ...clip.source_url ? { source_url: clip.source_url } : {},
904
+ duration_seconds: clip.duration_seconds,
905
+ model: data.model || model,
906
+ ...clip.request_id ? { request_id: clip.request_id } : {},
907
+ ...clip.backed_up !== void 0 ? { backed_up: clip.backed_up } : {},
908
+ ...txHash ? { txHash } : {}
909
+ }
910
+ };
911
+ } catch (err) {
912
+ const errMsg = err instanceof Error ? err.message : String(err);
913
+ if (errMsg.includes("balance") || errMsg.includes("payment") || errMsg.includes("402") || errMsg.includes("rejected")) {
914
+ return {
915
+ content: [{ type: "text", text: `Video generation requires payment. Run blockrun_wallet with action: "setup" for funding instructions.
916
+ Error: ${errMsg}` }],
917
+ isError: true
918
+ };
919
+ }
920
+ if (errMsg.includes("abort") || errMsg.includes("timeout") || errMsg.includes("Timeout") || errMsg.includes("timed out")) {
921
+ return {
922
+ content: [{ type: "text", text: `Video generation timed out. xAI's async job didn't complete in time \u2014 please try again.
923
+ Error: ${errMsg}` }],
924
+ isError: true
925
+ };
926
+ }
927
+ return {
928
+ content: [{ type: "text", text: formatError(`Video generation failed: ${errMsg}`) }],
929
+ isError: true
930
+ };
931
+ }
932
+ }
933
+ );
934
+ }
935
+ async function fetchWithTimeout2(url, options, timeoutMs) {
936
+ const controller = new AbortController();
937
+ const id = setTimeout(() => controller.abort(), timeoutMs);
938
+ try {
939
+ return await fetch(url, { ...options, signal: controller.signal });
940
+ } finally {
941
+ clearTimeout(id);
942
+ }
943
+ }
944
+
945
+ // src/tools/search.ts
946
+ import { z as z7 } from "zod";
809
947
  function registerSearchTool(server) {
810
948
  server.registerTool(
811
949
  "blockrun_search",
@@ -817,11 +955,11 @@ Pricing: ~$0.01/search
817
955
 
818
956
  Returns a summary with cited sources.`,
819
957
  inputSchema: {
820
- query: z6.string().describe("Search query"),
821
- sources: z6.array(z6.enum(["web", "x", "news"])).optional().describe("Sources to search (default: web + x + news)"),
822
- max_results: z6.number().optional().default(10).describe("Max results per source (1-20)"),
823
- from_date: z6.string().optional().describe("Start date filter (YYYY-MM-DD)"),
824
- to_date: z6.string().optional().describe("End date filter (YYYY-MM-DD)")
958
+ query: z7.string().describe("Search query"),
959
+ sources: z7.array(z7.enum(["web", "x", "news"])).optional().describe("Sources to search (default: web + x + news)"),
960
+ max_results: z7.number().optional().default(10).describe("Max results per source (1-20)"),
961
+ from_date: z7.string().optional().describe("Start date filter (YYYY-MM-DD)"),
962
+ to_date: z7.string().optional().describe("End date filter (YYYY-MM-DD)")
825
963
  }
826
964
  },
827
965
  async ({ query, sources, max_results, from_date, to_date }) => {
@@ -849,7 +987,7 @@ Returns a summary with cited sources.`,
849
987
  }
850
988
 
851
989
  // src/tools/exa.ts
852
- import { z as z7 } from "zod";
990
+ import { z as z8 } from "zod";
853
991
  function registerExaTool(server) {
854
992
  server.registerTool(
855
993
  "blockrun_exa",
@@ -862,14 +1000,14 @@ Actions:
862
1000
  - contents: Fetch full Markdown text from URLs, ready for LLM context ($0.002/URL)
863
1001
  - similar: Find pages semantically similar to a given URL ($0.01/call)`,
864
1002
  inputSchema: {
865
- action: z7.enum(["search", "answer", "contents", "similar"]).describe("Action to perform"),
866
- query: z7.string().optional().describe("Natural language query (for search/answer)"),
867
- url: z7.string().optional().describe("Reference URL to find similar pages (for similar action)"),
868
- urls: z7.array(z7.string()).optional().describe("URLs to fetch content from (for contents action, up to 100)"),
869
- num_results: z7.number().optional().describe("Number of results to return (default: 10)"),
870
- category: z7.string().optional().describe("Category filter: 'news', 'research paper', 'company', 'tweet', 'github', 'pdf'"),
871
- include_domains: z7.array(z7.string()).optional().describe("Only search within these domains"),
872
- exclude_domains: z7.array(z7.string()).optional().describe("Exclude these domains from results")
1003
+ action: z8.enum(["search", "answer", "contents", "similar"]).describe("Action to perform"),
1004
+ query: z8.string().optional().describe("Natural language query (for search/answer)"),
1005
+ url: z8.string().optional().describe("Reference URL to find similar pages (for similar action)"),
1006
+ urls: z8.array(z8.string()).optional().describe("URLs to fetch content from (for contents action, up to 100)"),
1007
+ num_results: z8.number().optional().describe("Number of results to return (default: 10)"),
1008
+ category: z8.string().optional().describe("Category filter: 'news', 'research paper', 'company', 'tweet', 'github', 'pdf'"),
1009
+ include_domains: z8.array(z8.string()).optional().describe("Only search within these domains"),
1010
+ exclude_domains: z8.array(z8.string()).optional().describe("Exclude these domains from results")
873
1011
  }
874
1012
  },
875
1013
  async ({ action, query, url, urls, num_results, category, include_domains, exclude_domains }) => {
@@ -922,7 +1060,7 @@ Actions:
922
1060
  }
923
1061
 
924
1062
  // src/tools/markets.ts
925
- import { z as z8 } from "zod";
1063
+ import { z as z9 } from "zod";
926
1064
  function registerMarketsTool(server) {
927
1065
  server.registerTool(
928
1066
  "blockrun_markets",
@@ -937,15 +1075,15 @@ Example paths:
937
1075
 
938
1076
  $0.001/call.`,
939
1077
  inputSchema: {
940
- path: z8.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14'"),
941
- params: z8.record(z8.string(), z8.string()).optional().describe("Query parameters for GET requests"),
942
- body: z8.any().optional().describe("JSON body for POST queries (triggers pmQuery)")
1078
+ path: z9.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14'"),
1079
+ params: z9.record(z9.string(), z9.string()).optional().describe("Query parameters for GET requests"),
1080
+ body: z9.any().optional().describe("JSON body for POST queries (triggers pmQuery)")
943
1081
  }
944
1082
  },
945
- async ({ path: path2, params, body }) => {
1083
+ async ({ path: path3, params, body }) => {
946
1084
  try {
947
1085
  const llm = getClient();
948
- const result = body ? await llm.pmQuery(path2, body) : await llm.pm(path2, params);
1086
+ const result = body ? await llm.pmQuery(path3, body) : await llm.pm(path3, params);
949
1087
  return {
950
1088
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
951
1089
  structuredContent: result
@@ -962,7 +1100,7 @@ $0.001/call.`,
962
1100
  }
963
1101
 
964
1102
  // src/tools/dex.ts
965
- import { z as z9 } from "zod";
1103
+ import { z as z10 } from "zod";
966
1104
  function registerDexTool(server) {
967
1105
  server.registerTool(
968
1106
  "blockrun_dex",
@@ -979,10 +1117,10 @@ Examples:
979
1117
  blockrun_dex({ token: "So11...xxx" }) -> Get specific token data
980
1118
  blockrun_dex({ symbol: "PEPE" }) -> Search by symbol`,
981
1119
  inputSchema: {
982
- query: z9.string().optional().describe("Search query (token name, symbol, or address)"),
983
- token: z9.string().optional().describe("Token address for direct lookup"),
984
- symbol: z9.string().optional().describe("Token symbol to search"),
985
- chain: z9.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
1120
+ query: z10.string().optional().describe("Search query (token name, symbol, or address)"),
1121
+ token: z10.string().optional().describe("Token address for direct lookup"),
1122
+ symbol: z10.string().optional().describe("Token symbol to search"),
1123
+ chain: z10.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
986
1124
  }
987
1125
  },
988
1126
  async ({ query, token, symbol, chain }) => {
@@ -1042,6 +1180,93 @@ ${lines.join("\n\n")}` }],
1042
1180
  );
1043
1181
  }
1044
1182
 
1183
+ // src/tools/modal.ts
1184
+ import { z as z11 } from "zod";
1185
+ var MODAL_GPU_TYPES = ["T4", "L4", "A10G", "A100", "A100-80GB", "H100"];
1186
+ function registerModalTool(server) {
1187
+ server.registerTool(
1188
+ "blockrun_modal",
1189
+ {
1190
+ description: `Run isolated code in a BlockRun-hosted Modal sandbox.
1191
+
1192
+ Use this when you need:
1193
+ - a disposable remote container
1194
+ - GPU access
1195
+ - a clean environment that will not affect the local machine
1196
+ - a safer place to run untrusted or heavy code
1197
+
1198
+ Prefer local tools for normal repo work. Modal is best for isolation or remote execution.
1199
+
1200
+ Pricing:
1201
+ - create: $0.01
1202
+ - exec/status/terminate: $0.001 each`,
1203
+ inputSchema: {
1204
+ action: z11.enum(["create", "exec", "status", "terminate"]).describe(
1205
+ "Sandbox action to perform"
1206
+ ),
1207
+ sandbox_id: z11.string().optional().describe("Sandbox ID returned by a previous create"),
1208
+ command: z11.array(z11.string()).optional().describe('Command array for exec, for example ["python", "-c", "print(2+2)"]'),
1209
+ image: z11.string().optional().describe("Container image for create (default: python:3.11)"),
1210
+ timeout: z11.number().optional().describe("Timeout in seconds for create or exec"),
1211
+ cpu: z11.number().optional().describe("CPU cores for create"),
1212
+ memory: z11.number().optional().describe("Memory in MB for create"),
1213
+ gpu: z11.enum(MODAL_GPU_TYPES).optional().describe("Optional GPU type for create"),
1214
+ setup_commands: z11.array(z11.string()).optional().describe("Shell commands to run during sandbox setup")
1215
+ }
1216
+ },
1217
+ async ({ action, sandbox_id, command, image, timeout, cpu, memory, gpu, setup_commands }) => {
1218
+ try {
1219
+ const llm = getClient();
1220
+ const req = llm;
1221
+ let result;
1222
+ switch (action) {
1223
+ case "create":
1224
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/create", {
1225
+ image,
1226
+ timeout,
1227
+ cpu,
1228
+ memory,
1229
+ gpu,
1230
+ setup_commands
1231
+ });
1232
+ break;
1233
+ case "exec":
1234
+ if (!sandbox_id) throw new Error("sandbox_id required for exec action");
1235
+ if (!command?.length) throw new Error("command array required for exec action");
1236
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/exec", {
1237
+ sandbox_id,
1238
+ command,
1239
+ timeout
1240
+ });
1241
+ break;
1242
+ case "status":
1243
+ if (!sandbox_id) throw new Error("sandbox_id required for status action");
1244
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/status", { sandbox_id });
1245
+ break;
1246
+ case "terminate":
1247
+ if (!sandbox_id) throw new Error("sandbox_id required for terminate action");
1248
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/terminate", {
1249
+ sandbox_id
1250
+ });
1251
+ break;
1252
+ default:
1253
+ throw new Error(`Unknown action: ${String(action)}`);
1254
+ }
1255
+ return {
1256
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1257
+ structuredContent: result
1258
+ };
1259
+ } catch (err) {
1260
+ const errMsg = err instanceof Error ? err.message : String(err);
1261
+ return {
1262
+ content: [{ type: "text", text: formatError(errMsg) }],
1263
+ isError: true
1264
+ };
1265
+ }
1266
+ }
1267
+ );
1268
+ }
1269
+
1045
1270
  // src/mcp-handler.ts
1046
1271
  function initializeMcpServer(server) {
1047
1272
  const budget = { limit: null, spent: 0, calls: 0, agents: /* @__PURE__ */ new Map() };
@@ -1051,10 +1276,12 @@ function initializeMcpServer(server) {
1051
1276
  registerModelsTool(server, modelCache);
1052
1277
  registerImageTool(server);
1053
1278
  registerMusicTool(server);
1279
+ registerVideoTool(server);
1054
1280
  registerSearchTool(server);
1055
1281
  registerExaTool(server);
1056
1282
  registerMarketsTool(server);
1057
1283
  registerDexTool(server);
1284
+ registerModalTool(server);
1058
1285
  server.registerResource(
1059
1286
  "wallet",
1060
1287
  "blockrun://wallet",
@@ -1090,8 +1317,90 @@ function initializeMcpServer(server) {
1090
1317
  );
1091
1318
  }
1092
1319
 
1320
+ // src/utils/key-leak-scanner.ts
1321
+ import fs2 from "fs";
1322
+ import path2 from "path";
1323
+ import os2 from "os";
1324
+ function looksLikeRawPrivateKey(value) {
1325
+ if (typeof value !== "string") return false;
1326
+ if (/^0x[0-9a-fA-F]{64}$/.test(value)) return true;
1327
+ if (value.length >= 80 && value.length <= 100 && /^[1-9A-HJ-NP-Za-km-z]+$/.test(value)) return true;
1328
+ return false;
1329
+ }
1330
+ function walk(obj, file, jsonPath, out) {
1331
+ if (obj === null || typeof obj !== "object") return;
1332
+ if (Array.isArray(obj)) {
1333
+ obj.forEach((v, i) => walk(v, file, `${jsonPath}[${i}]`, out));
1334
+ return;
1335
+ }
1336
+ for (const [k, v] of Object.entries(obj)) {
1337
+ const next = jsonPath ? `${jsonPath}.${k}` : k;
1338
+ if (/wallet[-_ ]?key|private[-_ ]?key|secret/i.test(k) && looksLikeRawPrivateKey(v)) {
1339
+ out.push({ file, path: next });
1340
+ } else if (looksLikeRawPrivateKey(v)) {
1341
+ out.push({ file, path: next });
1342
+ }
1343
+ walk(v, file, next, out);
1344
+ }
1345
+ }
1346
+ function scanFile(file) {
1347
+ try {
1348
+ if (!fs2.existsSync(file)) return [];
1349
+ const raw = fs2.readFileSync(file, "utf-8");
1350
+ const data = JSON.parse(raw);
1351
+ const out = [];
1352
+ walk(data, file, "", out);
1353
+ return out;
1354
+ } catch {
1355
+ return [];
1356
+ }
1357
+ }
1358
+ function warnOnLeakedKeys() {
1359
+ const home = os2.homedir();
1360
+ const candidates = [
1361
+ path2.join(home, ".claude.json"),
1362
+ path2.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
1363
+ path2.join(home, ".config", "claude", "claude_desktop_config.json"),
1364
+ path2.join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json")
1365
+ ];
1366
+ const findings = [];
1367
+ for (const f of candidates) findings.push(...scanFile(f));
1368
+ if (findings.length === 0) return false;
1369
+ const bar = "\u2550".repeat(72);
1370
+ console.error("");
1371
+ console.error(`\x1B[31m${bar}`);
1372
+ console.error(" \u{1F6A8} WALLET PRIVATE KEY DETECTED IN CONFIG FILE");
1373
+ console.error(bar + "\x1B[0m");
1374
+ console.error("");
1375
+ console.error(" Your config contains what looks like a raw wallet private key.");
1376
+ console.error(" Private keys should NEVER be stored in these files \u2014 they get");
1377
+ console.error(" backed up to iCloud / Dropbox / Time Machine, synced across");
1378
+ console.error(" machines, and readable by anything that can read your config.");
1379
+ console.error("");
1380
+ console.error(" Found in:");
1381
+ for (const f of findings) {
1382
+ console.error(` \xB7 ${f.file}`);
1383
+ console.error(` at: ${f.path}`);
1384
+ }
1385
+ console.error("");
1386
+ console.error(" RECOMMENDED ACTIONS:");
1387
+ console.error(" 1. Treat this key as compromised. Rotate your wallet:");
1388
+ console.error(" - Create a new wallet");
1389
+ console.error(" - Transfer remaining USDC to the new address");
1390
+ console.error(" - Retire the old key");
1391
+ console.error(" 2. Remove the X-Wallet-Key entries from your config.");
1392
+ console.error(" 3. Reconnect using the local package (signs locally, key");
1393
+ console.error(" never leaves your machine):");
1394
+ console.error(" claude mcp remove blockrun");
1395
+ console.error(" claude mcp add blockrun npx -y @blockrun/mcp@latest");
1396
+ console.error("");
1397
+ console.error(" Details: https://github.com/BlockRunAI/blockrun-mcp-server/issues/1");
1398
+ console.error("");
1399
+ return true;
1400
+ }
1401
+
1093
1402
  // src/index.ts
1094
- var VERSION = "0.6.8";
1403
+ var VERSION = "0.7.2";
1095
1404
  async function checkForUpdate() {
1096
1405
  try {
1097
1406
  const resp = await fetch("https://registry.npmjs.org/@blockrun/mcp/latest", {
@@ -1106,6 +1415,7 @@ async function checkForUpdate() {
1106
1415
  }
1107
1416
  }
1108
1417
  async function main() {
1418
+ warnOnLeakedKeys();
1109
1419
  const server = new McpServer({
1110
1420
  name: "blockrun-mcp",
1111
1421
  version: VERSION
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/mcp",
3
- "version": "0.7.1",
3
+ "version": "0.8.0",
4
4
  "mcpName": "io.github.BlockRunAI/blockrun-mcp",
5
5
  "description": "BlockRun MCP Server - Give your AI agent web search, deep research, prediction markets, crypto data, X/Twitter intelligence. Paid via x402 micropayments.",
6
6
  "type": "module",