@blockrun/mcp 0.7.2 → 0.9.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.
- package/dist/index.js +419 -42
- package/package.json +2 -2
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"
|
|
|
8
8
|
import {
|
|
9
9
|
LLMClient,
|
|
10
10
|
ImageClient,
|
|
11
|
+
PriceClient,
|
|
11
12
|
getOrCreateWallet,
|
|
12
13
|
getPaymentLinks,
|
|
13
14
|
formatWalletCreatedMessage,
|
|
@@ -15,6 +16,7 @@ import {
|
|
|
15
16
|
} from "@blockrun/llm";
|
|
16
17
|
var _client = null;
|
|
17
18
|
var _imageClient = null;
|
|
19
|
+
var _priceClient = null;
|
|
18
20
|
var _walletInfo = null;
|
|
19
21
|
function ensureWallet() {
|
|
20
22
|
if (!_walletInfo) {
|
|
@@ -43,6 +45,13 @@ function getImageClient() {
|
|
|
43
45
|
}
|
|
44
46
|
return _imageClient;
|
|
45
47
|
}
|
|
48
|
+
function getPriceClient() {
|
|
49
|
+
if (!_priceClient) {
|
|
50
|
+
const privateKey = getOrCreateWalletKey();
|
|
51
|
+
_priceClient = new PriceClient({ privateKey });
|
|
52
|
+
}
|
|
53
|
+
return _priceClient;
|
|
54
|
+
}
|
|
46
55
|
function getWalletInfo() {
|
|
47
56
|
const info = ensureWallet();
|
|
48
57
|
const links = getPaymentLinks(info.address);
|
|
@@ -103,12 +112,12 @@ var USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
|
|
|
103
112
|
var BASE_CHAIN_ID = "8453";
|
|
104
113
|
var MODEL_TIERS = {
|
|
105
114
|
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.
|
|
115
|
+
balanced: ["openai/gpt-5.4", "anthropic/claude-sonnet-4.6", "google/gemini-2.5-pro", "moonshot/kimi-k2.6", "openai/gpt-5.3", "google/gemini-3.1-pro"],
|
|
116
|
+
powerful: ["openai/gpt-5.4-pro", "anthropic/claude-opus-4.7", "anthropic/claude-opus-4.6", "openai/o3", "openai/gpt-5.4"],
|
|
108
117
|
cheap: ["zai/glm-5", "zai/glm-5-turbo", "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"],
|
|
118
|
+
reasoning: ["openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner", "moonshot/kimi-k2.6", "openai/gpt-5.3-codex"],
|
|
110
119
|
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", "nvidia/glm-4.7"],
|
|
111
|
-
coding: ["zai/glm-5", "openai/gpt-5.3-codex", "nvidia/qwen3-coder-480b", "nvidia/devstral-2-123b", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4"],
|
|
120
|
+
coding: ["zai/glm-5", "openai/gpt-5.3-codex", "moonshot/kimi-k2.6", "nvidia/qwen3-coder-480b", "nvidia/devstral-2-123b", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4"],
|
|
112
121
|
glm: ["zai/glm-5", "zai/glm-5-turbo", "nvidia/glm-4.7"]
|
|
113
122
|
};
|
|
114
123
|
|
|
@@ -597,15 +606,17 @@ Actions:
|
|
|
597
606
|
- edit: Transform an existing image using img2img
|
|
598
607
|
|
|
599
608
|
Generation models:
|
|
600
|
-
- zai/cogview-4 ($0.
|
|
609
|
+
- zai/cogview-4 ($0.015) \u2014 Zhipu CogView-4, photorealistic, great for detailed scenes
|
|
610
|
+
- xai/grok-imagine-image ($0.02) \u2014 xAI Grok Imagine, stylized, fast
|
|
611
|
+
- xai/grok-imagine-image-pro ($0.07) \u2014 xAI Grok Imagine Pro, higher quality
|
|
612
|
+
- openai/gpt-image-1 ($0.02-0.04) \u2014 GPT native image generation
|
|
601
613
|
- openai/dall-e-3 ($0.04-0.08) \u2014 High quality, prompt adherence
|
|
602
|
-
-
|
|
603
|
-
- google/nano-banana \u2014 Google image model
|
|
614
|
+
- google/nano-banana ($0.05) \u2014 Google image model
|
|
604
615
|
Edit models: openai/gpt-image-1 (default for edits)`,
|
|
605
616
|
inputSchema: {
|
|
606
617
|
prompt: z4.string().describe("Image description or edit instructions"),
|
|
607
618
|
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).
|
|
619
|
+
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
620
|
image: z4.string().optional().describe("Source image for edit action: base64-encoded image or URL"),
|
|
610
621
|
size: z4.enum(["1024x1024", "1792x1024", "1024x1792"]).optional().default("1024x1024"),
|
|
611
622
|
quality: z4.enum(["standard", "hd"]).optional().default("standard")
|
|
@@ -804,8 +815,144 @@ async function fetchWithTimeout(url, options, timeoutMs) {
|
|
|
804
815
|
}
|
|
805
816
|
}
|
|
806
817
|
|
|
807
|
-
// src/tools/
|
|
818
|
+
// src/tools/video.ts
|
|
808
819
|
import { z as z6 } from "zod";
|
|
820
|
+
import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
|
|
821
|
+
import {
|
|
822
|
+
createPaymentPayload as createPaymentPayload2,
|
|
823
|
+
parsePaymentRequired as parsePaymentRequired2,
|
|
824
|
+
extractPaymentDetails as extractPaymentDetails2
|
|
825
|
+
} from "@blockrun/llm";
|
|
826
|
+
var BLOCKRUN_API2 = "https://blockrun.ai/api";
|
|
827
|
+
var VIDEO_TIMEOUT = 3e5;
|
|
828
|
+
function registerVideoTool(server) {
|
|
829
|
+
server.registerTool(
|
|
830
|
+
"blockrun_video",
|
|
831
|
+
{
|
|
832
|
+
description: `Generate short AI videos via BlockRun x402.
|
|
833
|
+
|
|
834
|
+
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).
|
|
835
|
+
|
|
836
|
+
Model: xai/grok-imagine-video ($0.05/sec, 8s default -> $0.42/clip)
|
|
837
|
+
|
|
838
|
+
Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GCS so URLs don't expire).`,
|
|
839
|
+
inputSchema: {
|
|
840
|
+
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'"),
|
|
841
|
+
image_url: z6.string().url().optional().describe("Optional seed image URL for image-to-video generation"),
|
|
842
|
+
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."),
|
|
843
|
+
model: z6.enum(["xai/grok-imagine-video"]).optional().default("xai/grok-imagine-video").describe("Video model to use")
|
|
844
|
+
}
|
|
845
|
+
},
|
|
846
|
+
async ({ prompt, image_url, duration_seconds, model }) => {
|
|
847
|
+
try {
|
|
848
|
+
const privateKey = getOrCreateWalletKey();
|
|
849
|
+
const account = privateKeyToAccount2(privateKey);
|
|
850
|
+
const url = `${BLOCKRUN_API2}/v1/videos/generations`;
|
|
851
|
+
const body = { model, prompt };
|
|
852
|
+
if (image_url) body.image_url = image_url;
|
|
853
|
+
if (duration_seconds !== void 0) body.duration_seconds = duration_seconds;
|
|
854
|
+
const resp402 = await fetchWithTimeout2(url, {
|
|
855
|
+
method: "POST",
|
|
856
|
+
headers: { "Content-Type": "application/json" },
|
|
857
|
+
body: JSON.stringify(body)
|
|
858
|
+
}, 15e3);
|
|
859
|
+
if (resp402.status !== 402) {
|
|
860
|
+
const data2 = await resp402.json();
|
|
861
|
+
throw new Error(`Expected 402, got ${resp402.status}: ${JSON.stringify(data2)}`);
|
|
862
|
+
}
|
|
863
|
+
const prHeader = resp402.headers.get("payment-required") || resp402.headers.get("PAYMENT-REQUIRED");
|
|
864
|
+
if (!prHeader) throw new Error("No PAYMENT-REQUIRED header in 402 response");
|
|
865
|
+
const paymentRequired = parsePaymentRequired2(prHeader);
|
|
866
|
+
const details = extractPaymentDetails2(paymentRequired);
|
|
867
|
+
const paymentPayload = await createPaymentPayload2(
|
|
868
|
+
privateKey,
|
|
869
|
+
account.address,
|
|
870
|
+
details.recipient,
|
|
871
|
+
details.amount,
|
|
872
|
+
details.network || "eip155:8453",
|
|
873
|
+
{
|
|
874
|
+
resourceUrl: details.resource?.url || url,
|
|
875
|
+
resourceDescription: details.resource?.description || "BlockRun Video Generation",
|
|
876
|
+
maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
|
|
877
|
+
extra: details.extra
|
|
878
|
+
}
|
|
879
|
+
);
|
|
880
|
+
const resp = await fetchWithTimeout2(url, {
|
|
881
|
+
method: "POST",
|
|
882
|
+
headers: {
|
|
883
|
+
"Content-Type": "application/json",
|
|
884
|
+
"PAYMENT-SIGNATURE": paymentPayload
|
|
885
|
+
},
|
|
886
|
+
body: JSON.stringify(body)
|
|
887
|
+
}, VIDEO_TIMEOUT);
|
|
888
|
+
if (resp.status === 402) {
|
|
889
|
+
throw new Error("Payment rejected. Check your wallet balance.");
|
|
890
|
+
}
|
|
891
|
+
if (!resp.ok) {
|
|
892
|
+
const errBody = await resp.json().catch(() => ({ error: "Request failed" }));
|
|
893
|
+
throw new Error(`API error ${resp.status}: ${JSON.stringify(errBody)}`);
|
|
894
|
+
}
|
|
895
|
+
const data = await resp.json();
|
|
896
|
+
const clip = data.data?.[0];
|
|
897
|
+
if (!clip?.url) throw new Error("No video URL in response");
|
|
898
|
+
const txHash = resp.headers.get("X-Payment-Receipt") || resp.headers.get("x-payment-receipt");
|
|
899
|
+
const lines = [
|
|
900
|
+
`\u{1F3AC} Video ready!`,
|
|
901
|
+
`URL: ${clip.url}`,
|
|
902
|
+
`Duration: ${clip.duration_seconds ? `${clip.duration_seconds}s` : "8s"}`,
|
|
903
|
+
`Model: ${data.model || model}`,
|
|
904
|
+
...clip.backed_up ? [`Backed up to BlockRun storage (URL is permanent)`] : clip.source_url ? [`Source URL: ${clip.source_url}`] : [],
|
|
905
|
+
...clip.request_id ? [`Request ID: ${clip.request_id}`] : [],
|
|
906
|
+
...txHash ? [`Tx: ${txHash}`] : []
|
|
907
|
+
];
|
|
908
|
+
return {
|
|
909
|
+
content: [{ type: "text", text: lines.join("\n") }],
|
|
910
|
+
structuredContent: {
|
|
911
|
+
url: clip.url,
|
|
912
|
+
...clip.source_url ? { source_url: clip.source_url } : {},
|
|
913
|
+
duration_seconds: clip.duration_seconds,
|
|
914
|
+
model: data.model || model,
|
|
915
|
+
...clip.request_id ? { request_id: clip.request_id } : {},
|
|
916
|
+
...clip.backed_up !== void 0 ? { backed_up: clip.backed_up } : {},
|
|
917
|
+
...txHash ? { txHash } : {}
|
|
918
|
+
}
|
|
919
|
+
};
|
|
920
|
+
} catch (err) {
|
|
921
|
+
const errMsg = err instanceof Error ? err.message : String(err);
|
|
922
|
+
if (errMsg.includes("balance") || errMsg.includes("payment") || errMsg.includes("402") || errMsg.includes("rejected")) {
|
|
923
|
+
return {
|
|
924
|
+
content: [{ type: "text", text: `Video generation requires payment. Run blockrun_wallet with action: "setup" for funding instructions.
|
|
925
|
+
Error: ${errMsg}` }],
|
|
926
|
+
isError: true
|
|
927
|
+
};
|
|
928
|
+
}
|
|
929
|
+
if (errMsg.includes("abort") || errMsg.includes("timeout") || errMsg.includes("Timeout") || errMsg.includes("timed out")) {
|
|
930
|
+
return {
|
|
931
|
+
content: [{ type: "text", text: `Video generation timed out. xAI's async job didn't complete in time \u2014 please try again.
|
|
932
|
+
Error: ${errMsg}` }],
|
|
933
|
+
isError: true
|
|
934
|
+
};
|
|
935
|
+
}
|
|
936
|
+
return {
|
|
937
|
+
content: [{ type: "text", text: formatError(`Video generation failed: ${errMsg}`) }],
|
|
938
|
+
isError: true
|
|
939
|
+
};
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
);
|
|
943
|
+
}
|
|
944
|
+
async function fetchWithTimeout2(url, options, timeoutMs) {
|
|
945
|
+
const controller = new AbortController();
|
|
946
|
+
const id = setTimeout(() => controller.abort(), timeoutMs);
|
|
947
|
+
try {
|
|
948
|
+
return await fetch(url, { ...options, signal: controller.signal });
|
|
949
|
+
} finally {
|
|
950
|
+
clearTimeout(id);
|
|
951
|
+
}
|
|
952
|
+
}
|
|
953
|
+
|
|
954
|
+
// src/tools/search.ts
|
|
955
|
+
import { z as z7 } from "zod";
|
|
809
956
|
function registerSearchTool(server) {
|
|
810
957
|
server.registerTool(
|
|
811
958
|
"blockrun_search",
|
|
@@ -817,11 +964,11 @@ Pricing: ~$0.01/search
|
|
|
817
964
|
|
|
818
965
|
Returns a summary with cited sources.`,
|
|
819
966
|
inputSchema: {
|
|
820
|
-
query:
|
|
821
|
-
sources:
|
|
822
|
-
max_results:
|
|
823
|
-
from_date:
|
|
824
|
-
to_date:
|
|
967
|
+
query: z7.string().describe("Search query"),
|
|
968
|
+
sources: z7.array(z7.enum(["web", "x", "news"])).optional().describe("Sources to search (default: web + x + news)"),
|
|
969
|
+
max_results: z7.number().optional().default(10).describe("Max results per source (1-20)"),
|
|
970
|
+
from_date: z7.string().optional().describe("Start date filter (YYYY-MM-DD)"),
|
|
971
|
+
to_date: z7.string().optional().describe("End date filter (YYYY-MM-DD)")
|
|
825
972
|
}
|
|
826
973
|
},
|
|
827
974
|
async ({ query, sources, max_results, from_date, to_date }) => {
|
|
@@ -849,7 +996,7 @@ Returns a summary with cited sources.`,
|
|
|
849
996
|
}
|
|
850
997
|
|
|
851
998
|
// src/tools/exa.ts
|
|
852
|
-
import { z as
|
|
999
|
+
import { z as z8 } from "zod";
|
|
853
1000
|
function registerExaTool(server) {
|
|
854
1001
|
server.registerTool(
|
|
855
1002
|
"blockrun_exa",
|
|
@@ -862,14 +1009,14 @@ Actions:
|
|
|
862
1009
|
- contents: Fetch full Markdown text from URLs, ready for LLM context ($0.002/URL)
|
|
863
1010
|
- similar: Find pages semantically similar to a given URL ($0.01/call)`,
|
|
864
1011
|
inputSchema: {
|
|
865
|
-
action:
|
|
866
|
-
query:
|
|
867
|
-
url:
|
|
868
|
-
urls:
|
|
869
|
-
num_results:
|
|
870
|
-
category:
|
|
871
|
-
include_domains:
|
|
872
|
-
exclude_domains:
|
|
1012
|
+
action: z8.enum(["search", "answer", "contents", "similar"]).describe("Action to perform"),
|
|
1013
|
+
query: z8.string().optional().describe("Natural language query (for search/answer)"),
|
|
1014
|
+
url: z8.string().optional().describe("Reference URL to find similar pages (for similar action)"),
|
|
1015
|
+
urls: z8.array(z8.string()).optional().describe("URLs to fetch content from (for contents action, up to 100)"),
|
|
1016
|
+
num_results: z8.number().optional().describe("Number of results to return (default: 10)"),
|
|
1017
|
+
category: z8.string().optional().describe("Category filter: 'news', 'research paper', 'company', 'tweet', 'github', 'pdf'"),
|
|
1018
|
+
include_domains: z8.array(z8.string()).optional().describe("Only search within these domains"),
|
|
1019
|
+
exclude_domains: z8.array(z8.string()).optional().describe("Exclude these domains from results")
|
|
873
1020
|
}
|
|
874
1021
|
},
|
|
875
1022
|
async ({ action, query, url, urls, num_results, category, include_domains, exclude_domains }) => {
|
|
@@ -922,7 +1069,7 @@ Actions:
|
|
|
922
1069
|
}
|
|
923
1070
|
|
|
924
1071
|
// src/tools/markets.ts
|
|
925
|
-
import { z as
|
|
1072
|
+
import { z as z9 } from "zod";
|
|
926
1073
|
function registerMarketsTool(server) {
|
|
927
1074
|
server.registerTool(
|
|
928
1075
|
"blockrun_markets",
|
|
@@ -937,9 +1084,9 @@ Example paths:
|
|
|
937
1084
|
|
|
938
1085
|
$0.001/call.`,
|
|
939
1086
|
inputSchema: {
|
|
940
|
-
path:
|
|
941
|
-
params:
|
|
942
|
-
body:
|
|
1087
|
+
path: z9.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14'"),
|
|
1088
|
+
params: z9.record(z9.string(), z9.string()).optional().describe("Query parameters for GET requests"),
|
|
1089
|
+
body: z9.any().optional().describe("JSON body for POST queries (triggers pmQuery)")
|
|
943
1090
|
}
|
|
944
1091
|
},
|
|
945
1092
|
async ({ path: path3, params, body }) => {
|
|
@@ -961,8 +1108,235 @@ $0.001/call.`,
|
|
|
961
1108
|
);
|
|
962
1109
|
}
|
|
963
1110
|
|
|
1111
|
+
// src/tools/price.ts
|
|
1112
|
+
import { z as z10 } from "zod";
|
|
1113
|
+
var CATEGORY = z10.enum(["crypto", "fx", "commodity", "usstock", "stocks"]);
|
|
1114
|
+
var MARKET = z10.enum([
|
|
1115
|
+
"us",
|
|
1116
|
+
"hk",
|
|
1117
|
+
"jp",
|
|
1118
|
+
"kr",
|
|
1119
|
+
"gb",
|
|
1120
|
+
"de",
|
|
1121
|
+
"fr",
|
|
1122
|
+
"nl",
|
|
1123
|
+
"ie",
|
|
1124
|
+
"lu",
|
|
1125
|
+
"cn",
|
|
1126
|
+
"ca"
|
|
1127
|
+
]);
|
|
1128
|
+
var RESOLUTION = z10.enum(["1", "5", "15", "60", "240", "D", "W", "M"]);
|
|
1129
|
+
var SESSION = z10.enum(["pre", "post", "on"]);
|
|
1130
|
+
var ACTION = z10.enum(["price", "history", "list"]);
|
|
1131
|
+
function registerPriceTool(server) {
|
|
1132
|
+
server.registerTool(
|
|
1133
|
+
"blockrun_price",
|
|
1134
|
+
{
|
|
1135
|
+
description: `Realtime quotes and OHLC history for crypto, FX, commodities and 12 global stock markets (Pyth-backed).
|
|
1136
|
+
|
|
1137
|
+
- action="price" \u2014 realtime quote for a symbol
|
|
1138
|
+
- action="history" \u2014 OHLC bars between from/to (unix seconds)
|
|
1139
|
+
- action="list" \u2014 discovery: list available symbols (free)
|
|
1140
|
+
|
|
1141
|
+
Pricing:
|
|
1142
|
+
- crypto / fx / commodity: FREE across price, history and list
|
|
1143
|
+
- stocks / usstock: $0.001 per price or history call (list free)
|
|
1144
|
+
|
|
1145
|
+
Stocks markets: us, hk, jp, kr, gb, de, fr, nl, ie, lu, cn, ca (required when category="stocks").
|
|
1146
|
+
|
|
1147
|
+
Examples:
|
|
1148
|
+
- { action: "price", category: "crypto", symbol: "BTC-USD" }
|
|
1149
|
+
- { action: "price", category: "stocks", symbol: "AAPL", market: "us" }
|
|
1150
|
+
- { action: "history", category: "crypto", symbol: "ETH-USD", resolution: "D", from: 1700000000, to: 1710000000 }
|
|
1151
|
+
- { action: "list", category: "crypto", query: "sol" }`,
|
|
1152
|
+
inputSchema: {
|
|
1153
|
+
action: ACTION.describe("Which endpoint to hit: price, history, or list."),
|
|
1154
|
+
category: CATEGORY.describe("Market category."),
|
|
1155
|
+
symbol: z10.string().optional().describe("Ticker (required for price+history). e.g. BTC-USD, AAPL, EUR-USD."),
|
|
1156
|
+
market: MARKET.optional().describe("Stock market code \u2014 required when category='stocks'."),
|
|
1157
|
+
session: SESSION.optional().describe("Equity session hint (pre/post/on); ignored for non-equity."),
|
|
1158
|
+
resolution: RESOLUTION.optional().describe("Bar resolution for history (default D)."),
|
|
1159
|
+
from: z10.number().optional().describe("History window start (unix seconds)."),
|
|
1160
|
+
to: z10.number().optional().describe("History window end (unix seconds)."),
|
|
1161
|
+
query: z10.string().optional().describe("Free-text filter for list."),
|
|
1162
|
+
limit: z10.number().optional().describe("Max items for list (default 100, max 2000).")
|
|
1163
|
+
}
|
|
1164
|
+
},
|
|
1165
|
+
async ({ action, category, symbol, market, session, resolution, from, to, query, limit }) => {
|
|
1166
|
+
try {
|
|
1167
|
+
const priceClient = getPriceClient();
|
|
1168
|
+
if (action === "price") {
|
|
1169
|
+
if (!symbol) throw new Error("symbol is required for action='price'");
|
|
1170
|
+
const result2 = await priceClient.price(category, symbol, {
|
|
1171
|
+
market,
|
|
1172
|
+
session
|
|
1173
|
+
});
|
|
1174
|
+
return {
|
|
1175
|
+
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }],
|
|
1176
|
+
structuredContent: result2
|
|
1177
|
+
};
|
|
1178
|
+
}
|
|
1179
|
+
if (action === "history") {
|
|
1180
|
+
if (!symbol) throw new Error("symbol is required for action='history'");
|
|
1181
|
+
if (!from) throw new Error("from (unix seconds) is required for action='history'");
|
|
1182
|
+
const result2 = await priceClient.history(category, symbol, {
|
|
1183
|
+
market,
|
|
1184
|
+
session,
|
|
1185
|
+
resolution: resolution ?? "D",
|
|
1186
|
+
from,
|
|
1187
|
+
to
|
|
1188
|
+
});
|
|
1189
|
+
return {
|
|
1190
|
+
content: [{ type: "text", text: JSON.stringify(result2, null, 2) }],
|
|
1191
|
+
structuredContent: result2
|
|
1192
|
+
};
|
|
1193
|
+
}
|
|
1194
|
+
const result = await priceClient.listSymbols(category, {
|
|
1195
|
+
market,
|
|
1196
|
+
query,
|
|
1197
|
+
limit
|
|
1198
|
+
});
|
|
1199
|
+
return {
|
|
1200
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1201
|
+
structuredContent: result
|
|
1202
|
+
};
|
|
1203
|
+
} catch (err) {
|
|
1204
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1205
|
+
return {
|
|
1206
|
+
content: [{ type: "text", text: formatError(msg) }],
|
|
1207
|
+
isError: true
|
|
1208
|
+
};
|
|
1209
|
+
}
|
|
1210
|
+
}
|
|
1211
|
+
);
|
|
1212
|
+
}
|
|
1213
|
+
|
|
1214
|
+
// src/tools/twitter.ts
|
|
1215
|
+
import { z as z11 } from "zod";
|
|
1216
|
+
var ACTION2 = z11.enum([
|
|
1217
|
+
"user_lookup",
|
|
1218
|
+
"user_info",
|
|
1219
|
+
"followers",
|
|
1220
|
+
"followings",
|
|
1221
|
+
"verified_followers",
|
|
1222
|
+
"user_tweets",
|
|
1223
|
+
"user_mentions",
|
|
1224
|
+
"tweet_lookup",
|
|
1225
|
+
"tweet_replies",
|
|
1226
|
+
"tweet_thread",
|
|
1227
|
+
"search"
|
|
1228
|
+
]);
|
|
1229
|
+
function registerTwitterTool(server) {
|
|
1230
|
+
server.registerTool(
|
|
1231
|
+
"blockrun_x",
|
|
1232
|
+
{
|
|
1233
|
+
description: `Structured X/Twitter data via AttentionVC partner API.
|
|
1234
|
+
|
|
1235
|
+
Actions:
|
|
1236
|
+
- user_lookup (usernames: string | string[])
|
|
1237
|
+
- user_info (username)
|
|
1238
|
+
- followers (username, cursor?)
|
|
1239
|
+
- followings (username, cursor?)
|
|
1240
|
+
- verified_followers (user_id, cursor?)
|
|
1241
|
+
- user_tweets (username, includeReplies?, cursor?)
|
|
1242
|
+
- user_mentions (username, sinceTime?, untilTime?, cursor?)
|
|
1243
|
+
- tweet_lookup (tweet_ids: string | string[])
|
|
1244
|
+
- tweet_replies (tweet_id, cursor?, queryType?)
|
|
1245
|
+
- tweet_thread (tweet_id, cursor?)
|
|
1246
|
+
- search (query, queryType?, cursor?)
|
|
1247
|
+
|
|
1248
|
+
Paid per request via x402; prices scale with the endpoint (e.g. user_lookup ~ $0.02, followers ~ $0.05/page).`,
|
|
1249
|
+
inputSchema: {
|
|
1250
|
+
action: ACTION2,
|
|
1251
|
+
usernames: z11.union([z11.string(), z11.array(z11.string())]).optional(),
|
|
1252
|
+
username: z11.string().optional(),
|
|
1253
|
+
user_id: z11.string().optional(),
|
|
1254
|
+
tweet_id: z11.string().optional(),
|
|
1255
|
+
tweet_ids: z11.union([z11.string(), z11.array(z11.string())]).optional(),
|
|
1256
|
+
query: z11.string().optional(),
|
|
1257
|
+
queryType: z11.enum(["Latest", "Top", "Default"]).optional(),
|
|
1258
|
+
cursor: z11.string().optional(),
|
|
1259
|
+
sinceTime: z11.string().optional(),
|
|
1260
|
+
untilTime: z11.string().optional(),
|
|
1261
|
+
includeReplies: z11.boolean().optional()
|
|
1262
|
+
}
|
|
1263
|
+
},
|
|
1264
|
+
async (args) => {
|
|
1265
|
+
try {
|
|
1266
|
+
const llm = getClient();
|
|
1267
|
+
const a = args.action;
|
|
1268
|
+
const require2 = (value, name) => {
|
|
1269
|
+
if (value === void 0 || value === null || value === "") {
|
|
1270
|
+
throw new Error(`${name} is required for action='${a}'`);
|
|
1271
|
+
}
|
|
1272
|
+
return value;
|
|
1273
|
+
};
|
|
1274
|
+
let result;
|
|
1275
|
+
switch (a) {
|
|
1276
|
+
case "user_lookup":
|
|
1277
|
+
result = await llm.xUserLookup(require2(args.usernames, "usernames"));
|
|
1278
|
+
break;
|
|
1279
|
+
case "user_info":
|
|
1280
|
+
result = await llm.xUserInfo(require2(args.username, "username"));
|
|
1281
|
+
break;
|
|
1282
|
+
case "followers":
|
|
1283
|
+
result = await llm.xFollowers(require2(args.username, "username"), args.cursor);
|
|
1284
|
+
break;
|
|
1285
|
+
case "followings":
|
|
1286
|
+
result = await llm.xFollowings(require2(args.username, "username"), args.cursor);
|
|
1287
|
+
break;
|
|
1288
|
+
case "verified_followers":
|
|
1289
|
+
result = await llm.xVerifiedFollowers(require2(args.user_id, "user_id"), args.cursor);
|
|
1290
|
+
break;
|
|
1291
|
+
case "user_tweets":
|
|
1292
|
+
result = await llm.xUserTweets(
|
|
1293
|
+
require2(args.username, "username"),
|
|
1294
|
+
args.includeReplies,
|
|
1295
|
+
args.cursor
|
|
1296
|
+
);
|
|
1297
|
+
break;
|
|
1298
|
+
case "user_mentions":
|
|
1299
|
+
result = await llm.xUserMentions(
|
|
1300
|
+
require2(args.username, "username"),
|
|
1301
|
+
args.sinceTime,
|
|
1302
|
+
args.untilTime,
|
|
1303
|
+
args.cursor
|
|
1304
|
+
);
|
|
1305
|
+
break;
|
|
1306
|
+
case "tweet_lookup":
|
|
1307
|
+
result = await llm.xTweetLookup(require2(args.tweet_ids, "tweet_ids"));
|
|
1308
|
+
break;
|
|
1309
|
+
case "tweet_replies":
|
|
1310
|
+
result = await llm.xTweetReplies(
|
|
1311
|
+
require2(args.tweet_id, "tweet_id"),
|
|
1312
|
+
args.queryType,
|
|
1313
|
+
args.cursor
|
|
1314
|
+
);
|
|
1315
|
+
break;
|
|
1316
|
+
case "tweet_thread":
|
|
1317
|
+
result = await llm.xTweetThread(require2(args.tweet_id, "tweet_id"), args.cursor);
|
|
1318
|
+
break;
|
|
1319
|
+
case "search":
|
|
1320
|
+
result = await llm.xSearch(require2(args.query, "query"), args.queryType, args.cursor);
|
|
1321
|
+
break;
|
|
1322
|
+
}
|
|
1323
|
+
return {
|
|
1324
|
+
content: [{ type: "text", text: JSON.stringify(result, null, 2) }],
|
|
1325
|
+
structuredContent: result
|
|
1326
|
+
};
|
|
1327
|
+
} catch (err) {
|
|
1328
|
+
const msg = err instanceof Error ? err.message : String(err);
|
|
1329
|
+
return {
|
|
1330
|
+
content: [{ type: "text", text: formatError(msg) }],
|
|
1331
|
+
isError: true
|
|
1332
|
+
};
|
|
1333
|
+
}
|
|
1334
|
+
}
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
1337
|
+
|
|
964
1338
|
// src/tools/dex.ts
|
|
965
|
-
import { z as
|
|
1339
|
+
import { z as z12 } from "zod";
|
|
966
1340
|
function registerDexTool(server) {
|
|
967
1341
|
server.registerTool(
|
|
968
1342
|
"blockrun_dex",
|
|
@@ -979,10 +1353,10 @@ Examples:
|
|
|
979
1353
|
blockrun_dex({ token: "So11...xxx" }) -> Get specific token data
|
|
980
1354
|
blockrun_dex({ symbol: "PEPE" }) -> Search by symbol`,
|
|
981
1355
|
inputSchema: {
|
|
982
|
-
query:
|
|
983
|
-
token:
|
|
984
|
-
symbol:
|
|
985
|
-
chain:
|
|
1356
|
+
query: z12.string().optional().describe("Search query (token name, symbol, or address)"),
|
|
1357
|
+
token: z12.string().optional().describe("Token address for direct lookup"),
|
|
1358
|
+
symbol: z12.string().optional().describe("Token symbol to search"),
|
|
1359
|
+
chain: z12.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
|
|
986
1360
|
}
|
|
987
1361
|
},
|
|
988
1362
|
async ({ query, token, symbol, chain }) => {
|
|
@@ -1043,7 +1417,7 @@ ${lines.join("\n\n")}` }],
|
|
|
1043
1417
|
}
|
|
1044
1418
|
|
|
1045
1419
|
// src/tools/modal.ts
|
|
1046
|
-
import { z as
|
|
1420
|
+
import { z as z13 } from "zod";
|
|
1047
1421
|
var MODAL_GPU_TYPES = ["T4", "L4", "A10G", "A100", "A100-80GB", "H100"];
|
|
1048
1422
|
function registerModalTool(server) {
|
|
1049
1423
|
server.registerTool(
|
|
@@ -1063,17 +1437,17 @@ Pricing:
|
|
|
1063
1437
|
- create: $0.01
|
|
1064
1438
|
- exec/status/terminate: $0.001 each`,
|
|
1065
1439
|
inputSchema: {
|
|
1066
|
-
action:
|
|
1440
|
+
action: z13.enum(["create", "exec", "status", "terminate"]).describe(
|
|
1067
1441
|
"Sandbox action to perform"
|
|
1068
1442
|
),
|
|
1069
|
-
sandbox_id:
|
|
1070
|
-
command:
|
|
1071
|
-
image:
|
|
1072
|
-
timeout:
|
|
1073
|
-
cpu:
|
|
1074
|
-
memory:
|
|
1075
|
-
gpu:
|
|
1076
|
-
setup_commands:
|
|
1443
|
+
sandbox_id: z13.string().optional().describe("Sandbox ID returned by a previous create"),
|
|
1444
|
+
command: z13.array(z13.string()).optional().describe('Command array for exec, for example ["python", "-c", "print(2+2)"]'),
|
|
1445
|
+
image: z13.string().optional().describe("Container image for create (default: python:3.11)"),
|
|
1446
|
+
timeout: z13.number().optional().describe("Timeout in seconds for create or exec"),
|
|
1447
|
+
cpu: z13.number().optional().describe("CPU cores for create"),
|
|
1448
|
+
memory: z13.number().optional().describe("Memory in MB for create"),
|
|
1449
|
+
gpu: z13.enum(MODAL_GPU_TYPES).optional().describe("Optional GPU type for create"),
|
|
1450
|
+
setup_commands: z13.array(z13.string()).optional().describe("Shell commands to run during sandbox setup")
|
|
1077
1451
|
}
|
|
1078
1452
|
},
|
|
1079
1453
|
async ({ action, sandbox_id, command, image, timeout, cpu, memory, gpu, setup_commands }) => {
|
|
@@ -1138,9 +1512,12 @@ function initializeMcpServer(server) {
|
|
|
1138
1512
|
registerModelsTool(server, modelCache);
|
|
1139
1513
|
registerImageTool(server);
|
|
1140
1514
|
registerMusicTool(server);
|
|
1515
|
+
registerVideoTool(server);
|
|
1141
1516
|
registerSearchTool(server);
|
|
1142
1517
|
registerExaTool(server);
|
|
1143
1518
|
registerMarketsTool(server);
|
|
1519
|
+
registerPriceTool(server);
|
|
1520
|
+
registerTwitterTool(server);
|
|
1144
1521
|
registerDexTool(server);
|
|
1145
1522
|
registerModalTool(server);
|
|
1146
1523
|
server.registerResource(
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@blockrun/mcp",
|
|
3
|
-
"version": "0.
|
|
3
|
+
"version": "0.9.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",
|
|
@@ -44,7 +44,7 @@
|
|
|
44
44
|
"url": "https://github.com/blockrunai/blockrun-mcp/issues"
|
|
45
45
|
},
|
|
46
46
|
"dependencies": {
|
|
47
|
-
"@blockrun/llm": "^1.
|
|
47
|
+
"@blockrun/llm": "^1.8.0",
|
|
48
48
|
"@modelcontextprotocol/sdk": "^1.0.0",
|
|
49
49
|
"open": "^11.0.0",
|
|
50
50
|
"qrcode": "^1.5.4",
|