@blockrun/mcp 0.7.1 → 0.7.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.
Files changed (2) hide show
  1. package/dist/index.js +174 -3
  2. package/package.json +1 -1
package/dist/index.js CHANGED
@@ -942,10 +942,10 @@ $0.001/call.`,
942
942
  body: z8.any().optional().describe("JSON body for POST queries (triggers pmQuery)")
943
943
  }
944
944
  },
945
- async ({ path: path2, params, body }) => {
945
+ async ({ path: path3, params, body }) => {
946
946
  try {
947
947
  const llm = getClient();
948
- const result = body ? await llm.pmQuery(path2, body) : await llm.pm(path2, params);
948
+ const result = body ? await llm.pmQuery(path3, body) : await llm.pm(path3, params);
949
949
  return {
950
950
  content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
951
951
  structuredContent: result
@@ -1042,6 +1042,93 @@ ${lines.join("\n\n")}` }],
1042
1042
  );
1043
1043
  }
1044
1044
 
1045
+ // src/tools/modal.ts
1046
+ import { z as z10 } from "zod";
1047
+ var MODAL_GPU_TYPES = ["T4", "L4", "A10G", "A100", "A100-80GB", "H100"];
1048
+ function registerModalTool(server) {
1049
+ server.registerTool(
1050
+ "blockrun_modal",
1051
+ {
1052
+ description: `Run isolated code in a BlockRun-hosted Modal sandbox.
1053
+
1054
+ Use this when you need:
1055
+ - a disposable remote container
1056
+ - GPU access
1057
+ - a clean environment that will not affect the local machine
1058
+ - a safer place to run untrusted or heavy code
1059
+
1060
+ Prefer local tools for normal repo work. Modal is best for isolation or remote execution.
1061
+
1062
+ Pricing:
1063
+ - create: $0.01
1064
+ - exec/status/terminate: $0.001 each`,
1065
+ inputSchema: {
1066
+ action: z10.enum(["create", "exec", "status", "terminate"]).describe(
1067
+ "Sandbox action to perform"
1068
+ ),
1069
+ sandbox_id: z10.string().optional().describe("Sandbox ID returned by a previous create"),
1070
+ command: z10.array(z10.string()).optional().describe('Command array for exec, for example ["python", "-c", "print(2+2)"]'),
1071
+ image: z10.string().optional().describe("Container image for create (default: python:3.11)"),
1072
+ timeout: z10.number().optional().describe("Timeout in seconds for create or exec"),
1073
+ cpu: z10.number().optional().describe("CPU cores for create"),
1074
+ memory: z10.number().optional().describe("Memory in MB for create"),
1075
+ gpu: z10.enum(MODAL_GPU_TYPES).optional().describe("Optional GPU type for create"),
1076
+ setup_commands: z10.array(z10.string()).optional().describe("Shell commands to run during sandbox setup")
1077
+ }
1078
+ },
1079
+ async ({ action, sandbox_id, command, image, timeout, cpu, memory, gpu, setup_commands }) => {
1080
+ try {
1081
+ const llm = getClient();
1082
+ const req = llm;
1083
+ let result;
1084
+ switch (action) {
1085
+ case "create":
1086
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/create", {
1087
+ image,
1088
+ timeout,
1089
+ cpu,
1090
+ memory,
1091
+ gpu,
1092
+ setup_commands
1093
+ });
1094
+ break;
1095
+ case "exec":
1096
+ if (!sandbox_id) throw new Error("sandbox_id required for exec action");
1097
+ if (!command?.length) throw new Error("command array required for exec action");
1098
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/exec", {
1099
+ sandbox_id,
1100
+ command,
1101
+ timeout
1102
+ });
1103
+ break;
1104
+ case "status":
1105
+ if (!sandbox_id) throw new Error("sandbox_id required for status action");
1106
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/status", { sandbox_id });
1107
+ break;
1108
+ case "terminate":
1109
+ if (!sandbox_id) throw new Error("sandbox_id required for terminate action");
1110
+ result = await req.requestWithPaymentRaw("/v1/modal/sandbox/terminate", {
1111
+ sandbox_id
1112
+ });
1113
+ break;
1114
+ default:
1115
+ throw new Error(`Unknown action: ${String(action)}`);
1116
+ }
1117
+ return {
1118
+ content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
1119
+ structuredContent: result
1120
+ };
1121
+ } catch (err) {
1122
+ const errMsg = err instanceof Error ? err.message : String(err);
1123
+ return {
1124
+ content: [{ type: "text", text: formatError(errMsg) }],
1125
+ isError: true
1126
+ };
1127
+ }
1128
+ }
1129
+ );
1130
+ }
1131
+
1045
1132
  // src/mcp-handler.ts
1046
1133
  function initializeMcpServer(server) {
1047
1134
  const budget = { limit: null, spent: 0, calls: 0, agents: /* @__PURE__ */ new Map() };
@@ -1055,6 +1142,7 @@ function initializeMcpServer(server) {
1055
1142
  registerExaTool(server);
1056
1143
  registerMarketsTool(server);
1057
1144
  registerDexTool(server);
1145
+ registerModalTool(server);
1058
1146
  server.registerResource(
1059
1147
  "wallet",
1060
1148
  "blockrun://wallet",
@@ -1090,8 +1178,90 @@ function initializeMcpServer(server) {
1090
1178
  );
1091
1179
  }
1092
1180
 
1181
+ // src/utils/key-leak-scanner.ts
1182
+ import fs2 from "fs";
1183
+ import path2 from "path";
1184
+ import os2 from "os";
1185
+ function looksLikeRawPrivateKey(value) {
1186
+ if (typeof value !== "string") return false;
1187
+ if (/^0x[0-9a-fA-F]{64}$/.test(value)) return true;
1188
+ if (value.length >= 80 && value.length <= 100 && /^[1-9A-HJ-NP-Za-km-z]+$/.test(value)) return true;
1189
+ return false;
1190
+ }
1191
+ function walk(obj, file, jsonPath, out) {
1192
+ if (obj === null || typeof obj !== "object") return;
1193
+ if (Array.isArray(obj)) {
1194
+ obj.forEach((v, i) => walk(v, file, `${jsonPath}[${i}]`, out));
1195
+ return;
1196
+ }
1197
+ for (const [k, v] of Object.entries(obj)) {
1198
+ const next = jsonPath ? `${jsonPath}.${k}` : k;
1199
+ if (/wallet[-_ ]?key|private[-_ ]?key|secret/i.test(k) && looksLikeRawPrivateKey(v)) {
1200
+ out.push({ file, path: next });
1201
+ } else if (looksLikeRawPrivateKey(v)) {
1202
+ out.push({ file, path: next });
1203
+ }
1204
+ walk(v, file, next, out);
1205
+ }
1206
+ }
1207
+ function scanFile(file) {
1208
+ try {
1209
+ if (!fs2.existsSync(file)) return [];
1210
+ const raw = fs2.readFileSync(file, "utf-8");
1211
+ const data = JSON.parse(raw);
1212
+ const out = [];
1213
+ walk(data, file, "", out);
1214
+ return out;
1215
+ } catch {
1216
+ return [];
1217
+ }
1218
+ }
1219
+ function warnOnLeakedKeys() {
1220
+ const home = os2.homedir();
1221
+ const candidates = [
1222
+ path2.join(home, ".claude.json"),
1223
+ path2.join(home, "Library", "Application Support", "Claude", "claude_desktop_config.json"),
1224
+ path2.join(home, ".config", "claude", "claude_desktop_config.json"),
1225
+ path2.join(home, "AppData", "Roaming", "Claude", "claude_desktop_config.json")
1226
+ ];
1227
+ const findings = [];
1228
+ for (const f of candidates) findings.push(...scanFile(f));
1229
+ if (findings.length === 0) return false;
1230
+ const bar = "\u2550".repeat(72);
1231
+ console.error("");
1232
+ console.error(`\x1B[31m${bar}`);
1233
+ console.error(" \u{1F6A8} WALLET PRIVATE KEY DETECTED IN CONFIG FILE");
1234
+ console.error(bar + "\x1B[0m");
1235
+ console.error("");
1236
+ console.error(" Your config contains what looks like a raw wallet private key.");
1237
+ console.error(" Private keys should NEVER be stored in these files \u2014 they get");
1238
+ console.error(" backed up to iCloud / Dropbox / Time Machine, synced across");
1239
+ console.error(" machines, and readable by anything that can read your config.");
1240
+ console.error("");
1241
+ console.error(" Found in:");
1242
+ for (const f of findings) {
1243
+ console.error(` \xB7 ${f.file}`);
1244
+ console.error(` at: ${f.path}`);
1245
+ }
1246
+ console.error("");
1247
+ console.error(" RECOMMENDED ACTIONS:");
1248
+ console.error(" 1. Treat this key as compromised. Rotate your wallet:");
1249
+ console.error(" - Create a new wallet");
1250
+ console.error(" - Transfer remaining USDC to the new address");
1251
+ console.error(" - Retire the old key");
1252
+ console.error(" 2. Remove the X-Wallet-Key entries from your config.");
1253
+ console.error(" 3. Reconnect using the local package (signs locally, key");
1254
+ console.error(" never leaves your machine):");
1255
+ console.error(" claude mcp remove blockrun");
1256
+ console.error(" claude mcp add blockrun npx -y @blockrun/mcp@latest");
1257
+ console.error("");
1258
+ console.error(" Details: https://github.com/BlockRunAI/blockrun-mcp-server/issues/1");
1259
+ console.error("");
1260
+ return true;
1261
+ }
1262
+
1093
1263
  // src/index.ts
1094
- var VERSION = "0.6.8";
1264
+ var VERSION = "0.7.2";
1095
1265
  async function checkForUpdate() {
1096
1266
  try {
1097
1267
  const resp = await fetch("https://registry.npmjs.org/@blockrun/mcp/latest", {
@@ -1106,6 +1276,7 @@ async function checkForUpdate() {
1106
1276
  }
1107
1277
  }
1108
1278
  async function main() {
1279
+ warnOnLeakedKeys();
1109
1280
  const server = new McpServer({
1110
1281
  name: "blockrun-mcp",
1111
1282
  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.7.2",
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",