@blockrun/mcp 0.24.0 → 0.24.1
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 +70 -9
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -20,8 +20,7 @@ import {
|
|
|
20
20
|
loadSolanaWallet,
|
|
21
21
|
getPaymentLinks,
|
|
22
22
|
formatWalletCreatedMessage,
|
|
23
|
-
formatNeedsFundingMessage
|
|
24
|
-
SOLANA_WALLET_FILE_PATH
|
|
23
|
+
formatNeedsFundingMessage
|
|
25
24
|
} from "@blockrun/llm";
|
|
26
25
|
|
|
27
26
|
// src/utils/constants.ts
|
|
@@ -80,7 +79,7 @@ function getChain() {
|
|
|
80
79
|
if (preferred) return preferred;
|
|
81
80
|
if (process.env.SOLANA_WALLET_KEY) return "solana";
|
|
82
81
|
try {
|
|
83
|
-
if (
|
|
82
|
+
if (loadSolanaWallet()) return "solana";
|
|
84
83
|
} catch {
|
|
85
84
|
}
|
|
86
85
|
return "base";
|
|
@@ -154,6 +153,10 @@ function buildClientWithTimeout(timeoutMs) {
|
|
|
154
153
|
const privateKey = getOrCreateWalletKey();
|
|
155
154
|
return new LLMClient({ privateKey, timeout: timeoutMs });
|
|
156
155
|
}
|
|
156
|
+
function buildClient() {
|
|
157
|
+
if (getChain() === "solana") return buildSolanaClient();
|
|
158
|
+
return new LLMClient({ privateKey: getOrCreateWalletKey() });
|
|
159
|
+
}
|
|
157
160
|
function getAnthropicClient() {
|
|
158
161
|
if (!_anthropicClient) {
|
|
159
162
|
const privateKey = getOrCreateWalletKey();
|
|
@@ -230,7 +233,8 @@ async function getBaseUsdcBalance(address) {
|
|
|
230
233
|
const response = await fetch(rpcUrl, {
|
|
231
234
|
method: "POST",
|
|
232
235
|
headers: { "Content-Type": "application/json" },
|
|
233
|
-
body: JSON.stringify(data)
|
|
236
|
+
body: JSON.stringify(data),
|
|
237
|
+
signal: AbortSignal.timeout(8e3)
|
|
234
238
|
});
|
|
235
239
|
const result = await response.json();
|
|
236
240
|
const usd = parseBaseUsdcCallResult(result.result);
|
|
@@ -248,7 +252,7 @@ async function getChainBalance(chain, address) {
|
|
|
248
252
|
// src/utils/model-cache.ts
|
|
249
253
|
var CACHE_TTL_MS = 5 * 60 * 1e3;
|
|
250
254
|
async function loadModels(llm, cache) {
|
|
251
|
-
if (
|
|
255
|
+
if (cache.models === null || cache.models.length === 0) {
|
|
252
256
|
cache.models = llm.listAllModels ? await llm.listAllModels() : await llm.listModels();
|
|
253
257
|
setTimeout(() => {
|
|
254
258
|
cache.models = null;
|
|
@@ -998,7 +1002,7 @@ Run blockrun_models to see all available models with pricing.`,
|
|
|
998
1002
|
}
|
|
999
1003
|
},
|
|
1000
1004
|
async ({ message, model, mode, routing, routing_profile, system, max_tokens, temperature, response_format, stop, thinking, agent_id, messages }) => {
|
|
1001
|
-
const llm =
|
|
1005
|
+
const llm = buildClient();
|
|
1002
1006
|
const responseFormat = response_format ? { type: response_format } : void 0;
|
|
1003
1007
|
const estimatedCost = estimateChatCost(max_tokens, mode, model, routing, routing_profile);
|
|
1004
1008
|
const gate = reserveBudget(budget, agent_id, estimatedCost);
|
|
@@ -1215,6 +1219,41 @@ ${lines.join("\n")}` }],
|
|
|
1215
1219
|
// src/tools/image.ts
|
|
1216
1220
|
import { z as z4 } from "zod";
|
|
1217
1221
|
import { PaymentError } from "@blockrun/llm";
|
|
1222
|
+
|
|
1223
|
+
// src/utils/ssrf.ts
|
|
1224
|
+
function ipv4Blocked(host) {
|
|
1225
|
+
const m = /^(\d{1,3})\.(\d{1,3})\.(\d{1,3})\.(\d{1,3})$/.exec(host);
|
|
1226
|
+
if (!m) return null;
|
|
1227
|
+
const o = m.slice(1).map(Number);
|
|
1228
|
+
if (o.some((n) => n > 255)) return true;
|
|
1229
|
+
const [a, b] = o;
|
|
1230
|
+
return a === 0 || // 0.0.0.0/8
|
|
1231
|
+
a === 127 || // loopback
|
|
1232
|
+
a === 10 || // private
|
|
1233
|
+
a === 172 && b >= 16 && b <= 31 || // private
|
|
1234
|
+
a === 192 && b === 168 || // private
|
|
1235
|
+
a === 169 && b === 254 || // link-local (incl. metadata)
|
|
1236
|
+
a === 100 && b >= 64 && b <= 127;
|
|
1237
|
+
}
|
|
1238
|
+
function isBlockedFetchHost(hostname) {
|
|
1239
|
+
let host = hostname.trim().toLowerCase();
|
|
1240
|
+
if (host.startsWith("[") && host.endsWith("]")) host = host.slice(1, -1);
|
|
1241
|
+
if (!host) return true;
|
|
1242
|
+
if (host === "localhost" || host.endsWith(".localhost")) return true;
|
|
1243
|
+
if (host.endsWith(".internal") || host.endsWith(".local")) return true;
|
|
1244
|
+
const v4 = ipv4Blocked(host);
|
|
1245
|
+
if (v4 !== null) return v4;
|
|
1246
|
+
if (host.includes(":")) {
|
|
1247
|
+
if (host === "::1" || host === "::") return true;
|
|
1248
|
+
if (host.startsWith("fc") || host.startsWith("fd") || host.startsWith("fe80")) return true;
|
|
1249
|
+
const mapped = /^::ffff:(\d{1,3}\.\d{1,3}\.\d{1,3}\.\d{1,3})$/.exec(host);
|
|
1250
|
+
if (mapped) return ipv4Blocked(mapped[1]) === true;
|
|
1251
|
+
return false;
|
|
1252
|
+
}
|
|
1253
|
+
return false;
|
|
1254
|
+
}
|
|
1255
|
+
|
|
1256
|
+
// src/tools/image.ts
|
|
1218
1257
|
import { readFile } from "fs/promises";
|
|
1219
1258
|
var REFERENCE_IMAGE_MAX_BYTES = 4e6;
|
|
1220
1259
|
var IMAGE_EXT_MIME = {
|
|
@@ -1230,7 +1269,22 @@ async function toImageDataUri(ref) {
|
|
|
1230
1269
|
const ctrl = new AbortController();
|
|
1231
1270
|
const timeout = setTimeout(() => ctrl.abort(), 3e4);
|
|
1232
1271
|
try {
|
|
1233
|
-
|
|
1272
|
+
let url = ref;
|
|
1273
|
+
let res;
|
|
1274
|
+
for (let hop = 0; ; hop++) {
|
|
1275
|
+
const host = new URL(url).hostname;
|
|
1276
|
+
if (isBlockedFetchHost(host)) {
|
|
1277
|
+
throw new Error(`refusing to fetch a private/loopback/link-local address: ${host}`);
|
|
1278
|
+
}
|
|
1279
|
+
res = await fetch(url, { signal: ctrl.signal, redirect: "manual" });
|
|
1280
|
+
const location = res.headers.get("location");
|
|
1281
|
+
if (res.status >= 300 && res.status < 400 && location) {
|
|
1282
|
+
if (hop >= 5) throw new Error("too many redirects");
|
|
1283
|
+
url = new URL(location, url).toString();
|
|
1284
|
+
continue;
|
|
1285
|
+
}
|
|
1286
|
+
break;
|
|
1287
|
+
}
|
|
1234
1288
|
if (!res.ok) throw new Error(`fetch failed: ${res.status} ${res.statusText}`);
|
|
1235
1289
|
const mime2 = (res.headers.get("content-type") || "").toLowerCase().split(";")[0].trim();
|
|
1236
1290
|
if (!mime2.startsWith("image/")) throw new Error(`URL returned non-image content-type: ${mime2 || "(none)"}`);
|
|
@@ -2012,6 +2066,13 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
|
|
|
2012
2066
|
const paymentRequired = parsePaymentRequired3(prHeader);
|
|
2013
2067
|
const details = extractPaymentDetails3(paymentRequired);
|
|
2014
2068
|
const settledUsd = amountToUsd(details.amount);
|
|
2069
|
+
if (settledUsd !== null && settledUsd > estimatedCost) {
|
|
2070
|
+
gate?.release();
|
|
2071
|
+
gate = reserveBudget(budget, agent_id, settledUsd);
|
|
2072
|
+
if (!gate.allowed) {
|
|
2073
|
+
return { content: [{ type: "text", text: `${gate.reason}. Use blockrun_wallet action:"report" to see usage or action:"delegate" to increase agent budget.` }], isError: true };
|
|
2074
|
+
}
|
|
2075
|
+
}
|
|
2015
2076
|
const paymentPayload = await createPaymentPayload3(
|
|
2016
2077
|
privateKey,
|
|
2017
2078
|
account.address,
|
|
@@ -2088,7 +2149,7 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
|
|
|
2088
2149
|
const lines = [
|
|
2089
2150
|
`\u{1F3AC} Video ready!`,
|
|
2090
2151
|
`URL: ${completed.url}`,
|
|
2091
|
-
`Duration: ${completed.duration_seconds
|
|
2152
|
+
`Duration: ${completed.duration_seconds ?? billedSeconds}s`,
|
|
2092
2153
|
`Model: ${completed.modelReturned || selectedModel}`,
|
|
2093
2154
|
...completed.backed_up ? [`Backed up to BlockRun storage (URL is permanent)`] : completed.source_url ? [`Source URL: ${completed.source_url}`] : [],
|
|
2094
2155
|
...completed.request_id ? [`Request ID: ${completed.request_id}`] : [],
|
|
@@ -2887,7 +2948,7 @@ Examples:
|
|
|
2887
2948
|
isError: true
|
|
2888
2949
|
};
|
|
2889
2950
|
}
|
|
2890
|
-
const response = await
|
|
2951
|
+
const response = await fetchWithTimeout(url, {}, 8e3);
|
|
2891
2952
|
if (!response.ok) {
|
|
2892
2953
|
throw new Error(`DexScreener API error: ${response.status}`);
|
|
2893
2954
|
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/mcp",
|
|
3
|
-
"version": "0.24.
|
|
3
|
+
"version": "0.24.1",
|
|
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",
|