@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.
- package/dist/index.js +174 -3
- 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:
|
|
945
|
+
async ({ path: path3, params, body }) => {
|
|
946
946
|
try {
|
|
947
947
|
const llm = getClient();
|
|
948
|
-
const result = body ? await llm.pmQuery(
|
|
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.
|
|
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.
|
|
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",
|