@blockrun/mcp 0.9.1 → 0.11.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 (3) hide show
  1. package/README.md +31 -6
  2. package/dist/index.js +89 -42
  3. package/package.json +2 -2
package/README.md CHANGED
@@ -45,6 +45,27 @@ After BlockRun, it can. Each query costs fractions of a cent, billed from a loca
45
45
 
46
46
  ---
47
47
 
48
+ ## Showcase
49
+
50
+ Posters generated through `blockrun_image` with `openai/gpt-image-2`. Each is a single API call routed through BlockRun, paid in USDC on Base.
51
+
52
+ ### Latest — GPT-5.5 now live on BlockRun
53
+
54
+ <p align="center">
55
+ <img src="assets/posters/gpt-5-5-launch.png" width="640" alt="gpt-5.5 — now live on BlockRun. Pay per call. No subscription. No keys.">
56
+ </p>
57
+
58
+ ### Gallery
59
+
60
+ | | | |
61
+ |:---:|:---:|:---:|
62
+ | <img src="assets/posters/cornell-2026-popular-booth.png" width="280" alt="Thank you, Cornell — BlockRun at the Cornell Blockchain Conference 2026, packed booth"> | <img src="assets/posters/cornell-2026.png" width="280" alt="Thank you, Cornell — BlockRun at the Cornell Blockchain Conference 2026"> | <img src="skills/image-prompting/example-100t-poster.jpg" width="280" alt="100 Trillion Tokens served — synthwave milestone poster"> |
63
+ | **Cornell Blockchain Conference 2026** — packed booth recap | **Cornell Blockchain Conference 2026** — quiet variant | **100 Trillion Tokens** — milestone synthwave poster |
64
+
65
+ Prompts and a worked example for these are in [`skills/image-prompting/SKILL.md`](skills/image-prompting/SKILL.md).
66
+
67
+ ---
68
+
48
69
  ## Install
49
70
 
50
71
  **Claude Code (recommended)**
@@ -83,7 +104,7 @@ Run `blockrun_wallet` to see your address. Send USDC on Base.
83
104
  | Coinbase | Send → USDC → Base network → paste address |
84
105
  | Bridge from Ethereum | [bridge.base.org](https://bridge.base.org) |
85
106
 
86
- $5 covers ~5,000 market queries, ~500 Exa searches, or ~250 image generations.
107
+ $5 covers ~5,000 market queries, ~500 Exa searches, ~250 image generations, or ~30 Seedance 1.5-pro clips (5s).
87
108
 
88
109
  ---
89
110
 
@@ -91,14 +112,18 @@ $5 covers ~5,000 market queries, ~500 Exa searches, or ~250 image generations.
91
112
 
92
113
  | Tool | Data source | Cost |
93
114
  |------|-------------|------|
115
+ | `blockrun_chat` | 55+ LLMs (GPT, Claude, Gemini, DeepSeek, Kimi K2.6, GLM, NVIDIA free tier, ...) with `mode` tier routing | per token |
116
+ | `blockrun_image` | DALL-E 3, GPT Image 1/2, Grok Imagine, Flux, CogView-4, Nano Banana — generation + editing | $0.015–0.12 |
117
+ | `blockrun_video` | xAI Grok Imagine Video + ByteDance Seedance 1.5/2.0/2.0-fast | $0.03–0.30/sec |
118
+ | `blockrun_music` | MiniMax music generation | per track |
119
+ | `blockrun_price` | Pyth-backed realtime + OHLC — crypto / FX / commodity (free), 12 stock markets (paid) | free or $0.001/call |
94
120
  | `blockrun_markets` | Polymarket, Kalshi, dFlow, Binance Futures | $0.001/query |
121
+ | `blockrun_x` | X/Twitter — profiles, tweets, followers, mentions, search (AttentionVC) | per call |
95
122
  | `blockrun_exa` | Neural web search (Exa) — research, competitors, papers, URL content | $0.01/query |
96
- | `blockrun_search` | Web + news with citations | ~$0.01 |
97
- | `blockrun_twitter` | X/Twitter — profiles, tweets, trends, Grok sentiment | per token |
123
+ | `blockrun_search` | Grok Live Search — web + news with citations | ~$0.025 per source |
98
124
  | `blockrun_dex` | Live DEX prices via DexScreener | free |
99
- | `blockrun_image` | DALL-E 3, Flux image generation + editing | $0.02–0.08 |
100
- | `blockrun_chat` | GPT-4o, Gemini, DeepSeek, 30+ models | per token |
101
- | `blockrun_wallet` | Balance, spending, agent budgets | free |
125
+ | `blockrun_models` | Live catalogue of every LLM/image/video/music model + pricing | free |
126
+ | `blockrun_wallet` | Balance, spending, agent budgets, setup QR | free |
102
127
 
103
128
  ---
104
129
 
package/dist/index.js CHANGED
@@ -117,8 +117,8 @@ var MODEL_TIERS = {
117
117
  powerful: ["openai/gpt-5.4-pro", "anthropic/claude-opus-4.7", "anthropic/claude-opus-4.6", "openai/o3", "openai/gpt-5.4"],
118
118
  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"],
119
119
  reasoning: ["openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner", "moonshot/kimi-k2.6", "openai/gpt-5.3-codex"],
120
- 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"],
121
- 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"],
120
+ free: ["nvidia/qwen3-next-80b-a3b-thinking", "nvidia/mistral-small-4-119b", "nvidia/gpt-oss-120b", "nvidia/deepseek-v3.2", "nvidia/qwen3-coder-480b", "nvidia/llama-4-maverick", "nvidia/gpt-oss-20b", "nvidia/glm-4.7"],
121
+ coding: ["zai/glm-5", "openai/gpt-5.3-codex", "moonshot/kimi-k2.6", "nvidia/qwen3-coder-480b", "anthropic/claude-sonnet-4.6", "openai/gpt-5.4"],
122
122
  glm: ["zai/glm-5", "zai/glm-5-turbo", "nvidia/glm-4.7"]
123
123
  };
124
124
 
@@ -611,13 +611,14 @@ Generation models:
611
611
  - xai/grok-imagine-image ($0.02) \u2014 xAI Grok Imagine, stylized, fast
612
612
  - xai/grok-imagine-image-pro ($0.07) \u2014 xAI Grok Imagine Pro, higher quality
613
613
  - openai/gpt-image-1 ($0.02-0.04) \u2014 GPT native image generation
614
+ - openai/gpt-image-2 ($0.06-0.12) \u2014 ChatGPT Images 2.0, reasoning-driven, multilingual text rendering + character consistency
614
615
  - openai/dall-e-3 ($0.04-0.08) \u2014 High quality, prompt adherence
615
616
  - google/nano-banana ($0.05) \u2014 Google image model
616
- Edit models: openai/gpt-image-1 (default for edits)`,
617
+ Edit models: openai/gpt-image-1, openai/gpt-image-2 (default for edits)`,
617
618
  inputSchema: {
618
619
  prompt: z4.string().describe("Image description or edit instructions"),
619
620
  action: z4.enum(["generate", "edit"]).optional().default("generate").describe("generate: create from text; edit: transform existing image"),
620
- 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."),
621
+ model: z4.enum(["zai/cogview-4", "openai/dall-e-3", "together/flux-schnell", "google/nano-banana", "openai/gpt-image-1", "openai/gpt-image-2", "xai/grok-imagine-image", "xai/grok-imagine-image-pro"]).optional().describe("Model to use (default: dall-e-3 for generate, gpt-image-2 for edit). xai/grok-imagine-image is stylized and fast; xai/grok-imagine-image-pro is higher quality; gpt-image-2 is the newest edit-capable model with stronger instruction following."),
621
622
  image: z4.string().optional().describe("Source image for edit action: base64-encoded image or URL"),
622
623
  size: z4.enum(["1024x1024", "1792x1024", "1024x1792"]).optional().default("1024x1024"),
623
624
  quality: z4.enum(["standard", "hd"]).optional().default("standard")
@@ -635,7 +636,7 @@ Edit models: openai/gpt-image-1 (default for edits)`,
635
636
  };
636
637
  }
637
638
  response = await imgClient.edit(prompt, image, {
638
- model: model || "openai/gpt-image-1",
639
+ model: model || "openai/gpt-image-2",
639
640
  size
640
641
  });
641
642
  } else {
@@ -825,41 +826,46 @@ import {
825
826
  extractPaymentDetails as extractPaymentDetails2
826
827
  } from "@blockrun/llm";
827
828
  var BLOCKRUN_API2 = "https://blockrun.ai/api";
828
- var VIDEO_TIMEOUT = 3e5;
829
+ var TOTAL_BUDGET_MS = 3e5;
830
+ var POLL_INTERVAL_MS = 5e3;
829
831
  function registerVideoTool(server) {
830
832
  server.registerTool(
831
833
  "blockrun_video",
832
834
  {
833
- description: `Generate short AI videos via BlockRun x402.
835
+ description: `Generate short AI videos via BlockRun x402 (async, client-polled).
834
836
 
835
- 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).
837
+ Turns a text prompt (and optional seed image) into a short MP4 clip. The tool submits the job, then polls until the video is ready (typical total wall-time 60-180s; 5 min hard cap). Payment is settled only when upstream returns a finished video \u2014 if the job fails or we give up, you are not charged.
836
838
 
837
- Model: xai/grok-imagine-video ($0.05/sec, 8s default -> $0.42/clip)
839
+ Models:
840
+ - xai/grok-imagine-video ($0.05/sec, 8s default -> $0.42/clip) \u2014 stylized, fast
841
+ - bytedance/seedance-1.5-pro ($0.03/sec, 720p, 5s default up to 10s) \u2014 cheapest
842
+ - bytedance/seedance-2.0-fast ($0.15/sec, ~60-80s gen) \u2014 sweet-spot price/quality
843
+ - bytedance/seedance-2.0 ($0.30/sec, 720p Pro) \u2014 highest quality
838
844
 
839
845
  Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GCS so URLs don't expire).`,
840
846
  inputSchema: {
841
847
  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'"),
842
848
  image_url: z6.string().url().optional().describe("Optional seed image URL for image-to-video generation"),
843
- 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."),
844
- model: z6.enum(["xai/grok-imagine-video"]).optional().default("xai/grok-imagine-video").describe("Video model to use")
849
+ duration_seconds: z6.number().int().min(1).max(60).optional().describe("Duration to bill for (defaults to the model's default \u2014 8s for xAI, 5s for Seedance; Seedance supports up to 10s)."),
850
+ model: z6.enum(["xai/grok-imagine-video", "bytedance/seedance-1.5-pro", "bytedance/seedance-2.0-fast", "bytedance/seedance-2.0"]).optional().default("xai/grok-imagine-video").describe("Video model to use")
845
851
  }
846
852
  },
847
853
  async ({ prompt, image_url, duration_seconds, model }) => {
848
854
  try {
849
855
  const privateKey = getOrCreateWalletKey();
850
856
  const account = privateKeyToAccount2(privateKey);
851
- const url = `${BLOCKRUN_API2}/v1/videos/generations`;
857
+ const submitUrl = `${BLOCKRUN_API2}/v1/videos/generations`;
852
858
  const body = { model, prompt };
853
859
  if (image_url) body.image_url = image_url;
854
860
  if (duration_seconds !== void 0) body.duration_seconds = duration_seconds;
855
- const resp402 = await fetchWithTimeout2(url, {
861
+ const resp402 = await fetchWithTimeout2(submitUrl, {
856
862
  method: "POST",
857
863
  headers: { "Content-Type": "application/json" },
858
864
  body: JSON.stringify(body)
859
865
  }, 15e3);
860
866
  if (resp402.status !== 402) {
861
- const data2 = await resp402.json();
862
- throw new Error(`Expected 402, got ${resp402.status}: ${JSON.stringify(data2)}`);
867
+ const data = await resp402.json();
868
+ throw new Error(`Expected 402, got ${resp402.status}: ${JSON.stringify(data)}`);
863
869
  }
864
870
  const prHeader = resp402.headers.get("payment-required") || resp402.headers.get("PAYMENT-REQUIRED");
865
871
  if (!prHeader) throw new Error("No PAYMENT-REQUIRED header in 402 response");
@@ -872,50 +878,91 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
872
878
  details.amount,
873
879
  details.network || "eip155:8453",
874
880
  {
875
- resourceUrl: details.resource?.url || url,
881
+ resourceUrl: details.resource?.url || submitUrl,
876
882
  resourceDescription: details.resource?.description || "BlockRun Video Generation",
877
- maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
883
+ // Bump to 10 min so the signed authorization stays valid through the
884
+ // async polling window. Default (~5 min) is tight when upstream is slow.
885
+ maxTimeoutSeconds: Math.max(details.maxTimeoutSeconds || 0, 600),
878
886
  extra: details.extra
879
887
  }
880
888
  );
881
- const resp = await fetchWithTimeout2(url, {
889
+ const submitResp = await fetchWithTimeout2(submitUrl, {
882
890
  method: "POST",
883
891
  headers: {
884
892
  "Content-Type": "application/json",
885
893
  "PAYMENT-SIGNATURE": paymentPayload
886
894
  },
887
895
  body: JSON.stringify(body)
888
- }, VIDEO_TIMEOUT);
889
- if (resp.status === 402) {
896
+ }, 3e4);
897
+ if (submitResp.status === 402) {
890
898
  throw new Error("Payment rejected. Check your wallet balance.");
891
899
  }
892
- if (!resp.ok) {
893
- const errBody = await resp.json().catch(() => ({ error: "Request failed" }));
894
- throw new Error(`API error ${resp.status}: ${JSON.stringify(errBody)}`);
900
+ if (!submitResp.ok && submitResp.status !== 202) {
901
+ const errBody = await submitResp.json().catch(() => ({ error: "Submit failed" }));
902
+ throw new Error(`API error ${submitResp.status}: ${JSON.stringify(errBody)}`);
903
+ }
904
+ const submitData = await submitResp.json();
905
+ if (!submitData.id || !submitData.poll_url) {
906
+ throw new Error(`Submit response missing id/poll_url: ${JSON.stringify(submitData)}`);
907
+ }
908
+ const pollAbsoluteUrl = submitData.poll_url.startsWith("http") ? submitData.poll_url : `${BLOCKRUN_API2.replace(/\/api$/, "")}${submitData.poll_url}`;
909
+ const startedAt = Date.now();
910
+ let lastStatus = submitData.status || "queued";
911
+ let completed = null;
912
+ while (Date.now() - startedAt < TOTAL_BUDGET_MS) {
913
+ await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
914
+ const pollResp = await fetchWithTimeout2(pollAbsoluteUrl, {
915
+ method: "GET",
916
+ headers: { "PAYMENT-SIGNATURE": paymentPayload }
917
+ }, 9e4);
918
+ const pollData = await pollResp.json().catch(() => ({}));
919
+ lastStatus = pollData.status || lastStatus;
920
+ if (pollResp.status === 202 && (lastStatus === "queued" || lastStatus === "in_progress")) {
921
+ continue;
922
+ }
923
+ if (lastStatus === "failed") {
924
+ throw new Error(`Upstream generation failed: ${pollData.error || "unknown"}. No payment taken.`);
925
+ }
926
+ if (pollResp.ok && lastStatus === "completed") {
927
+ const clip = pollData.data?.[0];
928
+ if (!clip?.url) throw new Error("Completed poll missing video URL");
929
+ completed = {
930
+ url: clip.url,
931
+ source_url: clip.source_url,
932
+ duration_seconds: clip.duration_seconds,
933
+ request_id: clip.request_id,
934
+ backed_up: clip.backed_up,
935
+ modelReturned: pollData.model,
936
+ txHash: pollResp.headers.get("X-Payment-Receipt") || pollResp.headers.get("x-payment-receipt") || void 0
937
+ };
938
+ break;
939
+ }
940
+ if (!pollResp.ok && pollResp.status !== 202 && pollResp.status !== 504) {
941
+ throw new Error(`Poll error ${pollResp.status}: ${JSON.stringify(pollData)}`);
942
+ }
943
+ }
944
+ if (!completed) {
945
+ throw new Error(`Video generation did not complete within ${Math.round(TOTAL_BUDGET_MS / 1e3)}s (last status: ${lastStatus}). No payment was taken.`);
895
946
  }
896
- const data = await resp.json();
897
- const clip = data.data?.[0];
898
- if (!clip?.url) throw new Error("No video URL in response");
899
- const txHash = resp.headers.get("X-Payment-Receipt") || resp.headers.get("x-payment-receipt");
900
947
  const lines = [
901
948
  `\u{1F3AC} Video ready!`,
902
- `URL: ${clip.url}`,
903
- `Duration: ${clip.duration_seconds ? `${clip.duration_seconds}s` : "8s"}`,
904
- `Model: ${data.model || model}`,
905
- ...clip.backed_up ? [`Backed up to BlockRun storage (URL is permanent)`] : clip.source_url ? [`Source URL: ${clip.source_url}`] : [],
906
- ...clip.request_id ? [`Request ID: ${clip.request_id}`] : [],
907
- ...txHash ? [`Tx: ${txHash}`] : []
949
+ `URL: ${completed.url}`,
950
+ `Duration: ${completed.duration_seconds ? `${completed.duration_seconds}s` : "8s"}`,
951
+ `Model: ${completed.modelReturned || model}`,
952
+ ...completed.backed_up ? [`Backed up to BlockRun storage (URL is permanent)`] : completed.source_url ? [`Source URL: ${completed.source_url}`] : [],
953
+ ...completed.request_id ? [`Request ID: ${completed.request_id}`] : [],
954
+ ...completed.txHash ? [`Tx: ${completed.txHash}`] : []
908
955
  ];
909
956
  return {
910
957
  content: [{ type: "text", text: lines.join("\n") }],
911
958
  structuredContent: {
912
- url: clip.url,
913
- ...clip.source_url ? { source_url: clip.source_url } : {},
914
- duration_seconds: clip.duration_seconds,
915
- model: data.model || model,
916
- ...clip.request_id ? { request_id: clip.request_id } : {},
917
- ...clip.backed_up !== void 0 ? { backed_up: clip.backed_up } : {},
918
- ...txHash ? { txHash } : {}
959
+ url: completed.url,
960
+ ...completed.source_url ? { source_url: completed.source_url } : {},
961
+ duration_seconds: completed.duration_seconds,
962
+ model: completed.modelReturned || model,
963
+ ...completed.request_id ? { request_id: completed.request_id } : {},
964
+ ...completed.backed_up !== void 0 ? { backed_up: completed.backed_up } : {},
965
+ ...completed.txHash ? { txHash: completed.txHash } : {}
919
966
  }
920
967
  };
921
968
  } catch (err) {
@@ -927,9 +974,9 @@ Error: ${errMsg}` }],
927
974
  isError: true
928
975
  };
929
976
  }
930
- if (errMsg.includes("abort") || errMsg.includes("timeout") || errMsg.includes("Timeout") || errMsg.includes("timed out")) {
977
+ if (errMsg.includes("abort") || errMsg.includes("timeout") || errMsg.includes("Timeout") || errMsg.includes("timed out") || errMsg.includes("did not complete within")) {
931
978
  return {
932
- content: [{ type: "text", text: `Video generation timed out. xAI's async job didn't complete in time \u2014 please try again.
979
+ content: [{ type: "text", text: `Video generation timed out. The upstream async job didn't complete in time \u2014 please try again.
933
980
  Error: ${errMsg}` }],
934
981
  isError: true
935
982
  };
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/mcp",
3
- "version": "0.9.1",
3
+ "version": "0.11.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.8.0",
47
+ "@blockrun/llm": "^1.9.0",
48
48
  "@modelcontextprotocol/sdk": "^1.0.0",
49
49
  "open": "^11.0.0",
50
50
  "qrcode": "^1.5.4",