@blockrun/mcp 0.18.0 → 0.19.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 +8 -4
  2. package/dist/index.js +337 -86
  3. package/package.json +1 -1
package/README.md CHANGED
@@ -142,6 +142,7 @@ $5 covers ~5,000 market queries, ~500 Exa searches, ~250 image generations, or ~
142
142
  | `blockrun_video` | xAI Grok Imagine Video + ByteDance Seedance 1.5/2.0/2.0-fast (720p + audio defaults); RealFace asset → real-person video | $0.05–0.298/sec |
143
143
  | `blockrun_realface` | Enroll a real person (phone liveness → `ta_xxxx` asset) for Seedance 2.0 real-person video | free; $0.01 to enroll |
144
144
  | `blockrun_music` | MiniMax music generation | per track |
145
+ | `blockrun_speech` | ElevenLabs text-to-speech (Flash/Turbo/Multilingual/v3, 8 voice aliases) + cinematic sound effects; free voice listing | $0.05–0.10/1k chars; $0.0525/effect |
145
146
  | `blockrun_price` | Pyth-backed realtime + OHLC — crypto / FX / commodity (free), 12 stock markets (paid) | free or $0.001/call |
146
147
  | `blockrun_markets` | Polymarket (markets, candles, trades, orderbooks, leaderboards, smart-wallet PnL/clusters, UMA oracle), Kalshi, Limitless, Opinion, Predict.Fun, dFlow, Binance Futures, cross-platform match + search | $0.001–0.005/query |
147
148
  | `blockrun_surf` | Surf (asksurf.ai) — 84 endpoints: CEX market data, on-chain SQL (13 chains, 80+ ClickHouse tables), 100M+ labeled wallets, Polymarket + Kalshi side-by-side, social mindshare, news, search, Surf-1.5 chat with citations | $0.001–0.02/call |
@@ -185,13 +186,16 @@ What kinds of questions can Claude (or any LLM agent) answer once BlockRun MCP i
185
186
  5. **Image generation with on-image text**
186
187
  > *"Generate a poster announcing GPT-5.5 on BlockRun, retro-futuristic, with the headline 'NOW LIVE'."* → `blockrun_image` + the `image-prompting` skill 5-section framework
187
188
 
188
- 6. **Voice phone-out**
189
+ 6. **Give your agent a voice**
190
+ > *"Use blockrun to speak this with the sarah voice."* → `blockrun_speech` action:`speak` (default), `input` + `voice` → hosted MP3 URL. Sound effects via action:`sound_effect`
191
+
192
+ 7. **Voice phone-out**
189
193
  > *"Call +1-415-555-... and confirm the appointment on Friday at 3pm."* → `blockrun_phone` path:`voice/call`, body: `{ to, task, from }` (provision `from` first via `phone/numbers/buy`), then poll `voice/call/{call_id}`
190
194
 
191
- 7. **Multi-agent research with budget cap**
195
+ 8. **Multi-agent research with budget cap**
192
196
  > *"Spawn 3 research agents on competing L1 narratives. Cap each at $0.50."* → `blockrun_wallet delegate × 3` → children call `blockrun_chat` + `blockrun_exa` with their `agent_id`
193
197
 
194
- 8. **Cross-chain SQL forensics**
198
+ 9. **Cross-chain SQL forensics**
195
199
  > *"Top 10 tokens by DEX volume on Base in the last 24h."* → `blockrun_surf` path:`onchain/sql`, body: `{ sql: "SELECT..." }`
196
200
 
197
201
  ---
@@ -261,7 +265,7 @@ Chain selection priority (see `src/utils/wallet.ts`):
261
265
  - Base → Solana: `echo solana > ~/.blockrun/.chain`, then set `SOLANA_WALLET_KEY` or create `~/.blockrun/.solana-session`
262
266
  - Solana → Base: `echo base > ~/.blockrun/.chain` (the existing `.session` is reused, so it's the same Base wallet)
263
267
 
264
- Some media and paid market-data tools still settle on Base only: `blockrun_image`, `blockrun_music`, `blockrun_video`, and paid stock `blockrun_price` calls. In Solana mode they fail before creating or charging a Base wallet.
268
+ Some media and paid market-data tools still settle on Base only: `blockrun_image`, `blockrun_music`, `blockrun_speech`, `blockrun_video`, and paid stock `blockrun_price` calls. In Solana mode they fail before creating or charging a Base wallet.
265
269
 
266
270
  The server also runs a non-blocking npm registry check at startup and prints an `Update available` notice to stderr when a newer `@blockrun/mcp` version exists. Upgrade by re-running the install command — no manual `npm update` needed.
267
271
 
package/dist/index.js CHANGED
@@ -229,7 +229,7 @@ var USDC_ADDRESS = "0x833589fCD6eDb6E08f4c7C32D4f71b54bdA02913";
229
229
  var BASE_CHAIN_ID = "8453";
230
230
  var MODEL_TIERS = {
231
231
  fast: ["google/gemini-3.5-flash", "google/gemini-2.5-flash", "google/gemini-3.1-flash-lite", "openai/gpt-5-mini", "deepseek/deepseek-chat", "google/gemini-3-flash-preview"],
232
- 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"],
232
+ balanced: ["openai/gpt-5.5", "anthropic/claude-sonnet-4.6", "google/gemini-3.1-pro", "moonshot/kimi-k2.6", "openai/gpt-5.3", "openai/gpt-5.4"],
233
233
  powerful: ["anthropic/claude-opus-4.8", "openai/gpt-5.4-pro", "anthropic/claude-opus-4.7", "anthropic/claude-opus-4.6", "openai/o3", "openai/gpt-5.4"],
234
234
  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"],
235
235
  reasoning: ["anthropic/claude-opus-4.8", "openai/o3", "openai/o1", "openai/o3-mini", "deepseek/deepseek-reasoner", "moonshot/kimi-k2.6", "openai/gpt-5.3-codex"],
@@ -874,7 +874,7 @@ Run blockrun_models to see all 41+ models with pricing.`,
874
874
  z2.object({ type: z2.literal("image_url"), image_url: z2.object({ url: z2.string().describe("https URL or data:<mime>;base64,<...> URI") }) })
875
875
  ]))
876
876
  ]).describe("Plain text, or an array of parts for multimodal input (text + image_url). Images are honored on the native anthropic/claude-* path.")
877
- })).optional().describe("Conversation history for multi-turn context. When provided, 'message' is appended as the final user turn. Use with explicit 'model' param (defaults to 'openai/gpt-5.4' if not specified). Note: if you include a role:'system' entry in messages[], do not also pass the system param to avoid duplicate system messages.")
877
+ })).optional().describe("Conversation history for multi-turn context. When provided, 'message' is appended as the final user turn. Use with explicit 'model' param (defaults to 'openai/gpt-5.5' if not specified). Note: if you include a role:'system' entry in messages[], do not also pass the system param to avoid duplicate system messages.")
878
878
  }
879
879
  },
880
880
  async ({ message, model, mode, routing, routing_profile, system, max_tokens, temperature, response_format, stop, thinking, agent_id, messages }) => {
@@ -945,7 +945,7 @@ ${result.response}` }],
945
945
  }
946
946
  }
947
947
  if (messages && messages.length > 0) {
948
- const targetModel = model || MODEL_TIERS[mode ?? "balanced"]?.[0] || "openai/gpt-5.4";
948
+ const targetModel = model || MODEL_TIERS[mode ?? "balanced"]?.[0] || "openai/gpt-5.5";
949
949
  const fullMessages = [
950
950
  ...system ? [{ role: "system", content: system }] : [],
951
951
  ...messages,
@@ -1367,7 +1367,7 @@ async function fetchWithTimeout(url, options, timeoutMs) {
1367
1367
  }
1368
1368
  }
1369
1369
 
1370
- // src/tools/video.ts
1370
+ // src/tools/speech.ts
1371
1371
  import { z as z6 } from "zod";
1372
1372
  import { privateKeyToAccount as privateKeyToAccount2 } from "viem/accounts";
1373
1373
  import {
@@ -1376,6 +1376,256 @@ import {
1376
1376
  extractPaymentDetails as extractPaymentDetails2
1377
1377
  } from "@blockrun/llm";
1378
1378
  var BLOCKRUN_API2 = "https://blockrun.ai/api";
1379
+ var SPEECH_TIMEOUT = 6e4;
1380
+ var VOICES_TIMEOUT = 15e3;
1381
+ var MARGIN = 1.05;
1382
+ var MIN_PAYMENT_USD = 1e-3;
1383
+ var SOUND_EFFECT_COST = 0.05 * MARGIN;
1384
+ var SPEECH_MODELS = {
1385
+ "elevenlabs/flash-v2.5": { pricePer1kChars: 0.05, maxInputChars: 4e4 },
1386
+ "elevenlabs/turbo-v2.5": { pricePer1kChars: 0.05, maxInputChars: 4e4 },
1387
+ "elevenlabs/multilingual-v2": { pricePer1kChars: 0.1, maxInputChars: 1e4 },
1388
+ "elevenlabs/v3": { pricePer1kChars: 0.1, maxInputChars: 5e3 }
1389
+ };
1390
+ var VOICE_ALIASES = [
1391
+ { alias: "sarah", description: "Mature, reassuring, confident (default)" },
1392
+ { alias: "george", description: "Warm, captivating storyteller" },
1393
+ { alias: "laura", description: "Enthusiast, quirky" },
1394
+ { alias: "charlie", description: "Deep, confident, energetic" },
1395
+ { alias: "river", description: "Relaxed, neutral, informative" },
1396
+ { alias: "roger", description: "Laid-back, casual, resonant" },
1397
+ { alias: "callum", description: "Husky trickster" },
1398
+ { alias: "harry", description: "Fierce warrior" }
1399
+ ];
1400
+ function speechCost(model, charCount) {
1401
+ const m = SPEECH_MODELS[model];
1402
+ const raw = charCount / 1e3 * (m?.pricePer1kChars ?? 0.05) * MARGIN;
1403
+ return Math.max(raw, MIN_PAYMENT_USD);
1404
+ }
1405
+ function registerSpeechTool(server, budget) {
1406
+ server.registerTool(
1407
+ "blockrun_speech",
1408
+ {
1409
+ description: `ElevenLabs voice via BlockRun x402 \u2014 speak text aloud, generate sound effects, list voices.
1410
+
1411
+ Actions:
1412
+ - speak (default): text-to-speech. E.g. "speak this with the sarah voice". Price = chars/1000 \xD7 rate (min $0.001), quoted before payment.
1413
+ - sound_effect: cinematic sound effects from a text prompt, up to 22s ($0.0525/clip)
1414
+ - voices: list available voices (free)
1415
+
1416
+ Models (speak): elevenlabs/flash-v2.5 ($0.05/1k chars, ~75ms, default), elevenlabs/turbo-v2.5 ($0.05/1k), elevenlabs/multilingual-v2 ($0.10/1k, narration), elevenlabs/v3 ($0.10/1k, most expressive)
1417
+
1418
+ Voice aliases: sarah (default), george, laura, charlie, river, roger, callum, harry \u2014 or any raw ElevenLabs voice_id.
1419
+
1420
+ Returns a hosted audio URL \u2014 download immediately if you need to keep the file.`,
1421
+ inputSchema: {
1422
+ action: z6.enum(["speak", "sound_effect", "voices"]).optional().default("speak").describe("speak: text-to-speech (default). sound_effect: generate a sound effect. voices: list voices (free)."),
1423
+ input: z6.string().optional().describe("speak: text to synthesize. sound_effect: description of the sound, e.g. 'rain on a tin roof, distant thunder' (max 1000 chars)."),
1424
+ voice: z6.string().optional().describe("Voice alias (sarah, george, laura, charlie, river, roger, callum, harry) or raw ElevenLabs voice_id. Default: sarah."),
1425
+ model: z6.enum(["elevenlabs/flash-v2.5", "elevenlabs/turbo-v2.5", "elevenlabs/multilingual-v2", "elevenlabs/v3"]).optional().default("elevenlabs/flash-v2.5").describe("Speech model (speak only)"),
1426
+ response_format: z6.enum(["mp3", "opus", "pcm", "wav"]).optional().default("mp3").describe("Audio format"),
1427
+ speed: z6.number().min(0.7).max(1.2).optional().describe("Playback speed 0.7-1.2 (speak only)"),
1428
+ duration_seconds: z6.number().min(0.5).max(22).optional().describe("Sound effect length in seconds (sound_effect only; default: auto)"),
1429
+ prompt_influence: z6.number().min(0).max(1).optional().describe("How literally to follow the prompt, 0-1 (sound_effect only)"),
1430
+ agent_id: z6.string().optional().describe("Agent identifier for budget tracking and enforcement.")
1431
+ }
1432
+ },
1433
+ async ({ action, input, voice, model, response_format, speed, duration_seconds, prompt_influence, agent_id }) => {
1434
+ try {
1435
+ if (action === "voices") {
1436
+ return await listVoices();
1437
+ }
1438
+ if (getChain() !== "base") {
1439
+ return {
1440
+ content: [{ type: "text", text: formatError("blockrun_speech currently settles on Base only. Switch BlockRun to Base (for example: run blockrun_wallet with action:chain chain:base) and fund the Base wallet with USDC.") }],
1441
+ isError: true
1442
+ };
1443
+ }
1444
+ if (!input?.trim()) {
1445
+ return {
1446
+ content: [{ type: "text", text: formatError(`'input' is required for action:"${action}" \u2014 the text to ${action === "speak" ? "synthesize" : "describe the sound effect"}.`) }],
1447
+ isError: true
1448
+ };
1449
+ }
1450
+ let endpoint;
1451
+ let body;
1452
+ let cost;
1453
+ if (action === "sound_effect") {
1454
+ if (input.length > 1e3) {
1455
+ return {
1456
+ content: [{ type: "text", text: formatError(`Sound effect description too long: ${input.length} characters (max 1000).`) }],
1457
+ isError: true
1458
+ };
1459
+ }
1460
+ endpoint = `${BLOCKRUN_API2}/v1/audio/sound-effects`;
1461
+ body = { model: "elevenlabs/sound-effects", text: input, response_format };
1462
+ if (duration_seconds !== void 0) body.duration_seconds = duration_seconds;
1463
+ if (prompt_influence !== void 0) body.prompt_influence = prompt_influence;
1464
+ cost = SOUND_EFFECT_COST;
1465
+ } else {
1466
+ const modelInfo = SPEECH_MODELS[model] ?? SPEECH_MODELS["elevenlabs/flash-v2.5"];
1467
+ if (input.length > modelInfo.maxInputChars) {
1468
+ return {
1469
+ content: [{ type: "text", text: formatError(`Input too long: ${input.length.toLocaleString("en-US")} characters (max ${modelInfo.maxInputChars.toLocaleString("en-US")} for ${model}). Split the text or use elevenlabs/flash-v2.5 (40k max).`) }],
1470
+ isError: true
1471
+ };
1472
+ }
1473
+ endpoint = `${BLOCKRUN_API2}/v1/audio/speech`;
1474
+ body = { model, input, voice: voice || "sarah", response_format };
1475
+ if (speed !== void 0) body.speed = speed;
1476
+ cost = speechCost(model, input.length);
1477
+ }
1478
+ const budgetCheck = checkBudget(budget, agent_id, cost);
1479
+ if (!budgetCheck.allowed) {
1480
+ return {
1481
+ content: [{ type: "text", text: `${budgetCheck.reason}. Use blockrun_wallet action:"report" to see usage or action:"delegate" to increase agent budget.` }],
1482
+ isError: true
1483
+ };
1484
+ }
1485
+ const privateKey = getOrCreateWalletKey();
1486
+ const account = privateKeyToAccount2(privateKey);
1487
+ const resp402 = await fetchWithTimeout2(endpoint, {
1488
+ method: "POST",
1489
+ headers: { "Content-Type": "application/json" },
1490
+ body: JSON.stringify(body)
1491
+ }, 15e3);
1492
+ if (resp402.status !== 402) {
1493
+ const data2 = await resp402.json().catch(() => ({}));
1494
+ throw new Error(`Expected 402, got ${resp402.status}: ${JSON.stringify(data2)}`);
1495
+ }
1496
+ const prHeader = resp402.headers.get("payment-required") || resp402.headers.get("PAYMENT-REQUIRED");
1497
+ if (!prHeader) throw new Error("No PAYMENT-REQUIRED header in 402 response");
1498
+ const paymentRequired = parsePaymentRequired2(prHeader);
1499
+ const details = extractPaymentDetails2(paymentRequired);
1500
+ const paymentPayload = await createPaymentPayload2(
1501
+ privateKey,
1502
+ account.address,
1503
+ details.recipient,
1504
+ details.amount,
1505
+ details.network || "eip155:8453",
1506
+ {
1507
+ resourceUrl: details.resource?.url || endpoint,
1508
+ resourceDescription: details.resource?.description || "BlockRun Speech",
1509
+ maxTimeoutSeconds: details.maxTimeoutSeconds || 300,
1510
+ extra: details.extra
1511
+ }
1512
+ );
1513
+ const resp = await fetchWithTimeout2(endpoint, {
1514
+ method: "POST",
1515
+ headers: {
1516
+ "Content-Type": "application/json",
1517
+ "PAYMENT-SIGNATURE": paymentPayload
1518
+ },
1519
+ body: JSON.stringify(body)
1520
+ }, SPEECH_TIMEOUT);
1521
+ if (resp.status === 402) {
1522
+ throw new Error("Payment rejected. Check your wallet balance.");
1523
+ }
1524
+ if (!resp.ok) {
1525
+ const errBody = await resp.json().catch(() => ({ error: "Request failed" }));
1526
+ throw new Error(`API error ${resp.status}: ${JSON.stringify(errBody)}`);
1527
+ }
1528
+ const data = await resp.json();
1529
+ const clip = data.data?.[0];
1530
+ if (!clip?.url) throw new Error("No audio URL in response");
1531
+ const txHash = resp.headers.get("X-Payment-Receipt") || resp.headers.get("x-payment-receipt");
1532
+ recordSpending(budget, cost, agent_id);
1533
+ const lines = [
1534
+ action === "sound_effect" ? `\u{1F50A} Sound effect ready!` : `\u{1F5E3}\uFE0F Speech ready!`,
1535
+ `URL: ${clip.url}`,
1536
+ `Format: ${clip.format || response_format}`,
1537
+ ...clip.characters !== void 0 ? [`Characters: ${clip.characters}`] : [],
1538
+ ...clip.duration_seconds !== void 0 ? [`Duration: ${clip.duration_seconds}s`] : [],
1539
+ `Model: ${data.model || (action === "sound_effect" ? "elevenlabs/sound-effects" : model)}`,
1540
+ `Cost: $${cost.toFixed(4)}`,
1541
+ ...txHash ? [`Tx: ${txHash}`] : [],
1542
+ ``,
1543
+ `Note: This URL may expire \u2014 download it now if you need to keep the file.`
1544
+ ];
1545
+ return {
1546
+ content: [{ type: "text", text: lines.join("\n") }],
1547
+ structuredContent: {
1548
+ url: clip.url,
1549
+ format: clip.format || response_format,
1550
+ ...clip.characters !== void 0 ? { characters: clip.characters } : {},
1551
+ ...clip.duration_seconds !== void 0 ? { duration_seconds: clip.duration_seconds } : {},
1552
+ model: data.model || (action === "sound_effect" ? "elevenlabs/sound-effects" : model),
1553
+ cost_usd: cost,
1554
+ ...txHash ? { txHash } : {}
1555
+ }
1556
+ };
1557
+ } catch (err) {
1558
+ const errMsg = err instanceof Error ? err.message : String(err);
1559
+ if (errMsg.includes("balance") || errMsg.includes("payment") || errMsg.includes("402") || errMsg.includes("rejected")) {
1560
+ return {
1561
+ content: [{ type: "text", text: `Speech generation requires payment. Run blockrun_wallet with action: "setup" for funding instructions.
1562
+ Error: ${errMsg}` }],
1563
+ isError: true
1564
+ };
1565
+ }
1566
+ if (errMsg.includes("abort") || errMsg.includes("timeout") || errMsg.includes("Timeout")) {
1567
+ return {
1568
+ content: [{ type: "text", text: `Speech generation timed out \u2014 please try again.
1569
+ Error: ${errMsg}` }],
1570
+ isError: true
1571
+ };
1572
+ }
1573
+ return {
1574
+ content: [{ type: "text", text: formatError(`Speech generation failed: ${errMsg}`) }],
1575
+ isError: true
1576
+ };
1577
+ }
1578
+ }
1579
+ );
1580
+ }
1581
+ async function listVoices() {
1582
+ try {
1583
+ const resp = await fetchWithTimeout2(`${BLOCKRUN_API2}/v1/audio/voices`, { method: "GET" }, VOICES_TIMEOUT);
1584
+ if (resp.ok) {
1585
+ const payload = await resp.json();
1586
+ const voices = payload.data || [];
1587
+ if (voices.length > 0) {
1588
+ const lines2 = voices.map((v) => {
1589
+ const tags = v.labels ? Object.values(v.labels).join(", ") : "";
1590
+ return `- ${v.alias ? `${v.alias} ` : ""}(${v.voice_id})${v.name ? ` \u2014 ${v.name}` : ""}${tags ? ` [${tags}]` : ""}`;
1591
+ });
1592
+ return {
1593
+ content: [{ type: "text", text: `Available voices (pass alias or voice_id as 'voice'):
1594
+ ${lines2.join("\n")}` }],
1595
+ structuredContent: { voices }
1596
+ };
1597
+ }
1598
+ }
1599
+ } catch {
1600
+ }
1601
+ const lines = VOICE_ALIASES.map((v) => `- ${v.alias} \u2014 ${v.description}`);
1602
+ return {
1603
+ content: [{ type: "text", text: `Built-in voice aliases (pass as 'voice'; full catalog temporarily unavailable):
1604
+ ${lines.join("\n")}
1605
+
1606
+ Any raw ElevenLabs voice_id also works.` }],
1607
+ structuredContent: { voices: VOICE_ALIASES }
1608
+ };
1609
+ }
1610
+ async function fetchWithTimeout2(url, options, timeoutMs) {
1611
+ const controller = new AbortController();
1612
+ const id = setTimeout(() => controller.abort(), timeoutMs);
1613
+ try {
1614
+ return await fetch(url, { ...options, signal: controller.signal });
1615
+ } finally {
1616
+ clearTimeout(id);
1617
+ }
1618
+ }
1619
+
1620
+ // src/tools/video.ts
1621
+ import { z as z7 } from "zod";
1622
+ import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
1623
+ import {
1624
+ createPaymentPayload as createPaymentPayload3,
1625
+ parsePaymentRequired as parsePaymentRequired3,
1626
+ extractPaymentDetails as extractPaymentDetails3
1627
+ } from "@blockrun/llm";
1628
+ var BLOCKRUN_API3 = "https://blockrun.ai/api";
1379
1629
  var TOTAL_BUDGET_MS = 3e5;
1380
1630
  var POLL_INTERVAL_MS = 5e3;
1381
1631
  var VIDEO_PRICE_PER_SECOND = {
@@ -1420,12 +1670,12 @@ RealFace: to generate video of a SPECIFIC real person, first enroll them with bl
1420
1670
 
1421
1671
  Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GCS so URLs don't expire).`,
1422
1672
  inputSchema: {
1423
- 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'"),
1424
- image_url: z6.string().url().optional().describe("Optional seed image URL for image-to-video generation"),
1425
- real_face_asset_id: z6.string().regex(/^ta_[A-Za-z0-9]+$/, "token360 asset id like 'ta_xxxx'").optional().describe("BytePlus RealFace asset id (from blockrun_realface enroll/list) to generate video of a specific real person. Seedance 2.0 / 2.0-fast only. Mutually exclusive with image_url."),
1426
- 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)."),
1427
- model: z6.enum(["azure/sora-2", "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"),
1428
- agent_id: z6.string().optional().describe("Agent identifier for budget tracking and enforcement.")
1673
+ prompt: z7.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'"),
1674
+ image_url: z7.string().url().optional().describe("Optional seed image URL for image-to-video generation"),
1675
+ real_face_asset_id: z7.string().regex(/^ta_[A-Za-z0-9]+$/, "token360 asset id like 'ta_xxxx'").optional().describe("BytePlus RealFace asset id (from blockrun_realface enroll/list) to generate video of a specific real person. Seedance 2.0 / 2.0-fast only. Mutually exclusive with image_url."),
1676
+ duration_seconds: z7.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)."),
1677
+ model: z7.enum(["azure/sora-2", "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"),
1678
+ agent_id: z7.string().optional().describe("Agent identifier for budget tracking and enforcement.")
1429
1679
  }
1430
1680
  },
1431
1681
  async ({ prompt, image_url, real_face_asset_id, duration_seconds, model, agent_id }) => {
@@ -1463,13 +1713,13 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
1463
1713
  };
1464
1714
  }
1465
1715
  const privateKey = getOrCreateWalletKey();
1466
- const account = privateKeyToAccount2(privateKey);
1467
- const submitUrl = `${BLOCKRUN_API2}/v1/videos/generations`;
1716
+ const account = privateKeyToAccount3(privateKey);
1717
+ const submitUrl = `${BLOCKRUN_API3}/v1/videos/generations`;
1468
1718
  const body = { model: selectedModel, prompt };
1469
1719
  if (image_url) body.image_url = image_url;
1470
1720
  if (real_face_asset_id) body.real_face_asset_id = real_face_asset_id;
1471
1721
  if (duration_seconds !== void 0) body.duration_seconds = duration_seconds;
1472
- const resp402 = await fetchWithTimeout2(submitUrl, {
1722
+ const resp402 = await fetchWithTimeout3(submitUrl, {
1473
1723
  method: "POST",
1474
1724
  headers: { "Content-Type": "application/json" },
1475
1725
  body: JSON.stringify(body)
@@ -1480,9 +1730,9 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
1480
1730
  }
1481
1731
  const prHeader = resp402.headers.get("payment-required") || resp402.headers.get("PAYMENT-REQUIRED");
1482
1732
  if (!prHeader) throw new Error("No PAYMENT-REQUIRED header in 402 response");
1483
- const paymentRequired = parsePaymentRequired2(prHeader);
1484
- const details = extractPaymentDetails2(paymentRequired);
1485
- const paymentPayload = await createPaymentPayload2(
1733
+ const paymentRequired = parsePaymentRequired3(prHeader);
1734
+ const details = extractPaymentDetails3(paymentRequired);
1735
+ const paymentPayload = await createPaymentPayload3(
1486
1736
  privateKey,
1487
1737
  account.address,
1488
1738
  details.recipient,
@@ -1497,7 +1747,7 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
1497
1747
  extra: details.extra
1498
1748
  }
1499
1749
  );
1500
- const submitResp = await fetchWithTimeout2(submitUrl, {
1750
+ const submitResp = await fetchWithTimeout3(submitUrl, {
1501
1751
  method: "POST",
1502
1752
  headers: {
1503
1753
  "Content-Type": "application/json",
@@ -1516,13 +1766,13 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
1516
1766
  if (!submitData.id || !submitData.poll_url) {
1517
1767
  throw new Error(`Submit response missing id/poll_url: ${JSON.stringify(submitData)}`);
1518
1768
  }
1519
- const pollAbsoluteUrl = submitData.poll_url.startsWith("http") ? submitData.poll_url : `${BLOCKRUN_API2.replace(/\/api$/, "")}${submitData.poll_url}`;
1769
+ const pollAbsoluteUrl = submitData.poll_url.startsWith("http") ? submitData.poll_url : `${BLOCKRUN_API3.replace(/\/api$/, "")}${submitData.poll_url}`;
1520
1770
  const startedAt = Date.now();
1521
1771
  let lastStatus = submitData.status || "queued";
1522
1772
  let completed = null;
1523
1773
  while (Date.now() - startedAt < TOTAL_BUDGET_MS) {
1524
1774
  await new Promise((r) => setTimeout(r, POLL_INTERVAL_MS));
1525
- const pollResp = await fetchWithTimeout2(pollAbsoluteUrl, {
1775
+ const pollResp = await fetchWithTimeout3(pollAbsoluteUrl, {
1526
1776
  method: "GET",
1527
1777
  headers: { "PAYMENT-SIGNATURE": paymentPayload }
1528
1778
  }, 9e4);
@@ -1601,7 +1851,7 @@ Error: ${errMsg}` }],
1601
1851
  }
1602
1852
  );
1603
1853
  }
1604
- async function fetchWithTimeout2(url, options, timeoutMs) {
1854
+ async function fetchWithTimeout3(url, options, timeoutMs) {
1605
1855
  const controller = new AbortController();
1606
1856
  const id = setTimeout(() => controller.abort(), timeoutMs);
1607
1857
  try {
@@ -1612,16 +1862,16 @@ async function fetchWithTimeout2(url, options, timeoutMs) {
1612
1862
  }
1613
1863
 
1614
1864
  // src/tools/realface.ts
1615
- import { z as z7 } from "zod";
1616
- import { privateKeyToAccount as privateKeyToAccount3 } from "viem/accounts";
1865
+ import { z as z8 } from "zod";
1866
+ import { privateKeyToAccount as privateKeyToAccount4 } from "viem/accounts";
1617
1867
  import {
1618
- createPaymentPayload as createPaymentPayload3,
1619
- parsePaymentRequired as parsePaymentRequired3,
1620
- extractPaymentDetails as extractPaymentDetails3
1868
+ createPaymentPayload as createPaymentPayload4,
1869
+ parsePaymentRequired as parsePaymentRequired4,
1870
+ extractPaymentDetails as extractPaymentDetails4
1621
1871
  } from "@blockrun/llm";
1622
- var BLOCKRUN_API3 = "https://blockrun.ai/api";
1872
+ var BLOCKRUN_API4 = "https://blockrun.ai/api";
1623
1873
  var ENROLLMENT_PRICE_USD = 0.01;
1624
- async function fetchWithTimeout3(url, options, timeoutMs) {
1874
+ async function fetchWithTimeout4(url, options, timeoutMs) {
1625
1875
  const controller = new AbortController();
1626
1876
  const id = setTimeout(() => controller.abort(), timeoutMs);
1627
1877
  try {
@@ -1652,11 +1902,11 @@ Typical flow:
1652
1902
 
1653
1903
  Privacy: BlockRun does not store face/liveness data \u2014 only the asset id, name, and the photo URL you supply.`,
1654
1904
  inputSchema: {
1655
- action: z7.enum(["init", "status", "enroll", "list"]).describe("What to do"),
1656
- name: z7.string().min(1).max(64).optional().describe("Display name for the person (required for init and enroll)."),
1657
- group_id: z7.string().regex(/^legacy_rf_\d+$/).optional().describe("Asset-group id from init (required for status and enroll; pass to init to refresh an expired H5 link)."),
1658
- image_url: z7.string().url().optional().describe("Public HTTPS URL to a clear front-facing face photo (JPG/PNG/WEBP, \u226410MB). Required for enroll."),
1659
- agent_id: z7.string().optional().describe("Agent identifier for budget tracking and enforcement (enroll only).")
1905
+ action: z8.enum(["init", "status", "enroll", "list"]).describe("What to do"),
1906
+ name: z8.string().min(1).max(64).optional().describe("Display name for the person (required for init and enroll)."),
1907
+ group_id: z8.string().regex(/^legacy_rf_\d+$/).optional().describe("Asset-group id from init (required for status and enroll; pass to init to refresh an expired H5 link)."),
1908
+ image_url: z8.string().url().optional().describe("Public HTTPS URL to a clear front-facing face photo (JPG/PNG/WEBP, \u226410MB). Required for enroll."),
1909
+ agent_id: z8.string().optional().describe("Agent identifier for budget tracking and enforcement (enroll only).")
1660
1910
  }
1661
1911
  },
1662
1912
  async ({ action, name, group_id, image_url, agent_id }) => {
@@ -1667,7 +1917,7 @@ Privacy: BlockRun does not store face/liveness data \u2014 only the asset id, na
1667
1917
  }
1668
1918
  const body = { name };
1669
1919
  if (group_id) body.groupId = group_id;
1670
- const resp = await fetchWithTimeout3(`${BLOCKRUN_API3}/v1/realface/init`, {
1920
+ const resp = await fetchWithTimeout4(`${BLOCKRUN_API4}/v1/realface/init`, {
1671
1921
  method: "POST",
1672
1922
  headers: { "Content-Type": "application/json" },
1673
1923
  body: JSON.stringify(body)
@@ -1716,7 +1966,7 @@ QR opened for scanning (${qrPath}).`;
1716
1966
  if (!group_id) {
1717
1967
  return { content: [{ type: "text", text: formatError('group_id is required for action:"status".') }], isError: true };
1718
1968
  }
1719
- const resp = await fetchWithTimeout3(`${BLOCKRUN_API3}/v1/realface/status?groupId=${encodeURIComponent(group_id)}`, {
1969
+ const resp = await fetchWithTimeout4(`${BLOCKRUN_API4}/v1/realface/status?groupId=${encodeURIComponent(group_id)}`, {
1720
1970
  method: "GET"
1721
1971
  }, 3e4);
1722
1972
  const data = await resp.json().catch(() => ({}));
@@ -1744,8 +1994,8 @@ QR opened for scanning (${qrPath}).`;
1744
1994
  };
1745
1995
  }
1746
1996
  if (action === "list") {
1747
- const account = privateKeyToAccount3(getOrCreateWalletKey());
1748
- const resp = await fetchWithTimeout3(`${BLOCKRUN_API3}/v1/wallet/${account.address}/realfaces`, {
1997
+ const account = privateKeyToAccount4(getOrCreateWalletKey());
1998
+ const resp = await fetchWithTimeout4(`${BLOCKRUN_API4}/v1/wallet/${account.address}/realfaces`, {
1749
1999
  method: "GET"
1750
2000
  }, 3e4);
1751
2001
  const data = await resp.json().catch(() => ({}));
@@ -1783,10 +2033,10 @@ Enroll one: blockrun_realface action:"init" name:"\u2026".` }],
1783
2033
  return { content: [{ type: "text", text: `${budgetCheck.reason}. Use blockrun_wallet action:"report" to see usage or action:"delegate" to increase agent budget.` }], isError: true };
1784
2034
  }
1785
2035
  const privateKey = getOrCreateWalletKey();
1786
- const account = privateKeyToAccount3(privateKey);
1787
- const enrollUrl = `${BLOCKRUN_API3}/v1/realface/enroll`;
2036
+ const account = privateKeyToAccount4(privateKey);
2037
+ const enrollUrl = `${BLOCKRUN_API4}/v1/realface/enroll`;
1788
2038
  const reqBody = JSON.stringify({ name, image_url, group_id });
1789
- const resp402 = await fetchWithTimeout3(enrollUrl, {
2039
+ const resp402 = await fetchWithTimeout4(enrollUrl, {
1790
2040
  method: "POST",
1791
2041
  headers: { "Content-Type": "application/json" },
1792
2042
  body: reqBody
@@ -1797,9 +2047,9 @@ Enroll one: blockrun_realface action:"init" name:"\u2026".` }],
1797
2047
  }
1798
2048
  const prHeader = resp402.headers.get("payment-required") || resp402.headers.get("PAYMENT-REQUIRED");
1799
2049
  if (!prHeader) throw new Error("No PAYMENT-REQUIRED header in 402 response");
1800
- const paymentRequired = parsePaymentRequired3(prHeader);
1801
- const details = extractPaymentDetails3(paymentRequired);
1802
- const paymentPayload = await createPaymentPayload3(
2050
+ const paymentRequired = parsePaymentRequired4(prHeader);
2051
+ const details = extractPaymentDetails4(paymentRequired);
2052
+ const paymentPayload = await createPaymentPayload4(
1803
2053
  privateKey,
1804
2054
  account.address,
1805
2055
  details.recipient,
@@ -1812,7 +2062,7 @@ Enroll one: blockrun_realface action:"init" name:"\u2026".` }],
1812
2062
  extra: details.extra
1813
2063
  }
1814
2064
  );
1815
- const resp = await fetchWithTimeout3(enrollUrl, {
2065
+ const resp = await fetchWithTimeout4(enrollUrl, {
1816
2066
  method: "POST",
1817
2067
  headers: {
1818
2068
  "Content-Type": "application/json",
@@ -1874,7 +2124,7 @@ Error: ${errMsg}` }],
1874
2124
  }
1875
2125
 
1876
2126
  // src/tools/search.ts
1877
- import { z as z8 } from "zod";
2127
+ import { z as z9 } from "zod";
1878
2128
 
1879
2129
  // src/utils/body.ts
1880
2130
  function coerceBody(body) {
@@ -1910,9 +2160,9 @@ Common shape:
1910
2160
 
1911
2161
  Full request shape + worked examples in the \`search\` skill (\`skills/search/SKILL.md\`).`,
1912
2162
  inputSchema: {
1913
- path: z8.string().optional().default("").describe("Endpoint sub-path under /v1/search/ (default empty = root /v1/search). Reserved for future surfaces."),
1914
- body: z8.any().optional().describe("Request body. At minimum { query: '...' }. Sent as POST."),
1915
- agent_id: z8.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2163
+ path: z9.string().optional().default("").describe("Endpoint sub-path under /v1/search/ (default empty = root /v1/search). Reserved for future surfaces."),
2164
+ body: z9.any().optional().describe("Request body. At minimum { query: '...' }. Sent as POST."),
2165
+ agent_id: z9.string().optional().describe("Agent identifier for budget tracking and enforcement.")
1916
2166
  }
1917
2167
  },
1918
2168
  async ({ path: path5, body, agent_id }) => {
@@ -1943,7 +2193,7 @@ Full request shape + worked examples in the \`search\` skill (\`skills/search/SK
1943
2193
  }
1944
2194
 
1945
2195
  // src/tools/exa.ts
1946
- import { z as z9 } from "zod";
2196
+ import { z as z10 } from "zod";
1947
2197
  function estimateExaCost(path5, body) {
1948
2198
  const cleanPath = path5.replace(/^\/+/, "").replace(/^v1\/exa\//, "");
1949
2199
  if (cleanPath === "contents") {
@@ -1968,9 +2218,9 @@ Categories for search: "news", "research paper", "company", "tweet", "github", "
1968
2218
 
1969
2219
  Full request/response shapes + worked research workflows in the \`exa-research\` skill.`,
1970
2220
  inputSchema: {
1971
- path: z9.string().describe("Endpoint name under /v1/exa/, e.g. 'search', 'answer', 'contents', 'find-similar'"),
1972
- body: z9.any().optional().describe("JSON body for the call. Sent as POST. Required for all four endpoints."),
1973
- agent_id: z9.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2221
+ path: z10.string().describe("Endpoint name under /v1/exa/, e.g. 'search', 'answer', 'contents', 'find-similar'"),
2222
+ body: z10.any().optional().describe("JSON body for the call. Sent as POST. Required for all four endpoints."),
2223
+ agent_id: z10.string().optional().describe("Agent identifier for budget tracking and enforcement.")
1974
2224
  }
1975
2225
  },
1976
2226
  async ({ path: path5, body, agent_id }) => {
@@ -2001,7 +2251,7 @@ Full request/response shapes + worked research workflows in the \`exa-research\`
2001
2251
  }
2002
2252
 
2003
2253
  // src/tools/markets.ts
2004
- import { z as z10 } from "zod";
2254
+ import { z as z11 } from "zod";
2005
2255
  function estimateMarketCost(path5, body) {
2006
2256
  if (body !== void 0) return 5e-3;
2007
2257
  const p = path5.toLowerCase();
@@ -2066,10 +2316,10 @@ CROSS-PLATFORM:
2066
2316
 
2067
2317
  Pass query params via 'params' (GET). Use 'body' only for POST endpoints (e.g. polymarket/wallet/identities).`,
2068
2318
  inputSchema: {
2069
- path: z10.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14', 'polymarket/wallet/0xabc...', 'markets/search'"),
2070
- params: z10.record(z10.string(), z10.string()).optional().describe("Query parameters for GET requests (e.g. { limit: '20', active: 'true' })"),
2071
- body: z10.any().optional().describe("JSON body for POST queries (triggers pmQuery \u2014 most endpoints are GET)"),
2072
- agent_id: z10.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2319
+ path: z11.string().describe("Endpoint path, e.g. 'polymarket/events', 'kalshi/markets/KXBTC-25MAR14', 'polymarket/wallet/0xabc...', 'markets/search'"),
2320
+ params: z11.record(z11.string(), z11.string()).optional().describe("Query parameters for GET requests (e.g. { limit: '20', active: 'true' })"),
2321
+ body: z11.any().optional().describe("JSON body for POST queries (triggers pmQuery \u2014 most endpoints are GET)"),
2322
+ agent_id: z11.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2073
2323
  }
2074
2324
  },
2075
2325
  async ({ path: path5, params, body, agent_id }) => {
@@ -2102,9 +2352,9 @@ Pass query params via 'params' (GET). Use 'body' only for POST endpoints (e.g. p
2102
2352
  }
2103
2353
 
2104
2354
  // src/tools/price.ts
2105
- import { z as z11 } from "zod";
2106
- var CATEGORY = z11.enum(["crypto", "fx", "commodity", "usstock", "stocks"]);
2107
- var MARKET = z11.enum([
2355
+ import { z as z12 } from "zod";
2356
+ var CATEGORY = z12.enum(["crypto", "fx", "commodity", "usstock", "stocks"]);
2357
+ var MARKET = z12.enum([
2108
2358
  "us",
2109
2359
  "hk",
2110
2360
  "jp",
@@ -2118,9 +2368,9 @@ var MARKET = z11.enum([
2118
2368
  "cn",
2119
2369
  "ca"
2120
2370
  ]);
2121
- var RESOLUTION = z11.enum(["1", "5", "15", "60", "240", "D", "W", "M"]);
2122
- var SESSION = z11.enum(["pre", "post", "on"]);
2123
- var ACTION = z11.enum(["price", "history", "list"]);
2371
+ var RESOLUTION = z12.enum(["1", "5", "15", "60", "240", "D", "W", "M"]);
2372
+ var SESSION = z12.enum(["pre", "post", "on"]);
2373
+ var ACTION = z12.enum(["price", "history", "list"]);
2124
2374
  function isPaidPriceCall(action, category) {
2125
2375
  return action !== "list" && (category === "stocks" || category === "usstock");
2126
2376
  }
@@ -2148,15 +2398,15 @@ Examples:
2148
2398
  inputSchema: {
2149
2399
  action: ACTION.describe("Which endpoint to hit: price, history, or list."),
2150
2400
  category: CATEGORY.describe("Market category."),
2151
- symbol: z11.string().optional().describe("Ticker (required for price+history). e.g. BTC-USD, AAPL, EUR-USD."),
2401
+ symbol: z12.string().optional().describe("Ticker (required for price+history). e.g. BTC-USD, AAPL, EUR-USD."),
2152
2402
  market: MARKET.optional().describe("Stock market code \u2014 required when category='stocks'."),
2153
2403
  session: SESSION.optional().describe("Equity session hint (pre/post/on); ignored for non-equity."),
2154
2404
  resolution: RESOLUTION.optional().describe("Bar resolution for history (default D)."),
2155
- from: z11.number().optional().describe("History window start (unix seconds)."),
2156
- to: z11.number().optional().describe("History window end (unix seconds)."),
2157
- query: z11.string().optional().describe("Free-text filter for list."),
2158
- limit: z11.number().int().positive().max(2e3).optional().describe("Max items for list (default 100, max 2000)."),
2159
- agent_id: z11.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2405
+ from: z12.number().optional().describe("History window start (unix seconds)."),
2406
+ to: z12.number().optional().describe("History window end (unix seconds)."),
2407
+ query: z12.string().optional().describe("Free-text filter for list."),
2408
+ limit: z12.number().int().positive().max(2e3).optional().describe("Max items for list (default 100, max 2000)."),
2409
+ agent_id: z12.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2160
2410
  }
2161
2411
  },
2162
2412
  async ({ action, category, symbol, market, session, resolution, from, to, query, limit, agent_id }) => {
@@ -2229,7 +2479,7 @@ Examples:
2229
2479
  }
2230
2480
 
2231
2481
  // src/tools/dex.ts
2232
- import { z as z12 } from "zod";
2482
+ import { z as z13 } from "zod";
2233
2483
  function registerDexTool(server) {
2234
2484
  server.registerTool(
2235
2485
  "blockrun_dex",
@@ -2246,10 +2496,10 @@ Examples:
2246
2496
  blockrun_dex({ token: "So11...xxx" }) -> Get specific token data
2247
2497
  blockrun_dex({ symbol: "PEPE" }) -> Search by symbol`,
2248
2498
  inputSchema: {
2249
- query: z12.string().optional().describe("Search query (token name, symbol, or address)"),
2250
- token: z12.string().optional().describe("Token address for direct lookup"),
2251
- symbol: z12.string().optional().describe("Token symbol to search"),
2252
- chain: z12.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
2499
+ query: z13.string().optional().describe("Search query (token name, symbol, or address)"),
2500
+ token: z13.string().optional().describe("Token address for direct lookup"),
2501
+ symbol: z13.string().optional().describe("Token symbol to search"),
2502
+ chain: z13.string().optional().describe("Filter by chain (ethereum, solana, base, etc.)")
2253
2503
  }
2254
2504
  },
2255
2505
  async ({ query, token, symbol, chain }) => {
@@ -2310,7 +2560,7 @@ ${lines.join("\n\n")}` }],
2310
2560
  }
2311
2561
 
2312
2562
  // src/tools/modal.ts
2313
- import { z as z13 } from "zod";
2563
+ import { z as z14 } from "zod";
2314
2564
  function estimateModalCost(path5) {
2315
2565
  return path5.includes("sandbox/create") ? 0.01 : 1e-3;
2316
2566
  }
@@ -2330,9 +2580,9 @@ Common paths (all POST):
2330
2580
 
2331
2581
  Full action shapes + GPU type details in the \`modal\` skill.`,
2332
2582
  inputSchema: {
2333
- path: z13.string().describe("Endpoint under /v1/modal/, e.g. 'sandbox/create', 'sandbox/exec'"),
2334
- body: z13.any().optional().describe("JSON body. Sent as POST."),
2335
- agent_id: z13.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2583
+ path: z14.string().describe("Endpoint under /v1/modal/, e.g. 'sandbox/create', 'sandbox/exec'"),
2584
+ body: z14.any().optional().describe("JSON body. Sent as POST."),
2585
+ agent_id: z14.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2336
2586
  }
2337
2587
  },
2338
2588
  async ({ path: path5, body, agent_id }) => {
@@ -2363,7 +2613,7 @@ Full action shapes + GPU type details in the \`modal\` skill.`,
2363
2613
  }
2364
2614
 
2365
2615
  // src/tools/phone.ts
2366
- import { z as z14 } from "zod";
2616
+ import { z as z15 } from "zod";
2367
2617
  function estimatePhoneCost(path5, hasBody) {
2368
2618
  if (!hasBody && path5.startsWith("voice/call/")) return 0;
2369
2619
  if (path5 === "phone/numbers/release") return 0;
@@ -2396,9 +2646,9 @@ Voice presets: nat, josh, maya, june, paige, derek, florian. Phone numbers use E
2396
2646
 
2397
2647
  Voice call flow + voice preset details + full body shapes in the \`phone\` skill.`,
2398
2648
  inputSchema: {
2399
- path: z14.string().describe("Endpoint after /v1/. Use 'phone/...' for lookup + number ops, 'voice/call' for outbound AI calls, 'voice/call/{id}' (no body) to poll status."),
2400
- body: z14.any().optional().describe("JSON body. Sent as POST. Omit for the free GET poll (voice/call/{call_id})."),
2401
- agent_id: z14.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2649
+ path: z15.string().describe("Endpoint after /v1/. Use 'phone/...' for lookup + number ops, 'voice/call' for outbound AI calls, 'voice/call/{id}' (no body) to poll status."),
2650
+ body: z15.any().optional().describe("JSON body. Sent as POST. Omit for the free GET poll (voice/call/{call_id})."),
2651
+ agent_id: z15.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2402
2652
  }
2403
2653
  },
2404
2654
  async ({ path: path5, body, agent_id }) => {
@@ -2429,7 +2679,7 @@ Voice call flow + voice preset details + full body shapes in the \`phone\` skill
2429
2679
  }
2430
2680
 
2431
2681
  // src/tools/surf.ts
2432
- import { z as z15 } from "zod";
2682
+ import { z as z16 } from "zod";
2433
2683
  var SURF_T3_PATHS = /* @__PURE__ */ new Set([
2434
2684
  "onchain/sql",
2435
2685
  "onchain/query",
@@ -2493,10 +2743,10 @@ Common paths (full 84-endpoint catalog in the surf skill):
2493
2743
  Method is auto-routed: pass 'body' for POST endpoints; otherwise GET with 'params'.
2494
2744
  Each Surf endpoint pre-validates required params before settling \u2014 you get a 400 (not a charge) if a required field is missing. Browse the full catalog: https://blockrun.ai/marketplace/surf`,
2495
2745
  inputSchema: {
2496
- path: z15.string().describe("Endpoint path under /v1/surf/, e.g. 'market/price', 'prediction-market/polymarket/ranking', 'wallet/detail', 'onchain/sql', 'chat/completions'"),
2497
- params: z15.record(z15.string(), z15.string()).optional().describe("Query parameters for GET endpoints, e.g. { symbol: 'BTC' } or { address: '0x...', chain: 'ethereum' }"),
2498
- body: z15.any().optional().describe("JSON body for POST endpoints. Provide for: onchain/query, onchain/sql, chat/completions. When set, the call is sent as POST; otherwise GET with params."),
2499
- agent_id: z15.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2746
+ path: z16.string().describe("Endpoint path under /v1/surf/, e.g. 'market/price', 'prediction-market/polymarket/ranking', 'wallet/detail', 'onchain/sql', 'chat/completions'"),
2747
+ params: z16.record(z16.string(), z16.string()).optional().describe("Query parameters for GET endpoints, e.g. { symbol: 'BTC' } or { address: '0x...', chain: 'ethereum' }"),
2748
+ body: z16.any().optional().describe("JSON body for POST endpoints. Provide for: onchain/query, onchain/sql, chat/completions. When set, the call is sent as POST; otherwise GET with params."),
2749
+ agent_id: z16.string().optional().describe("Agent identifier for budget tracking and enforcement.")
2500
2750
  }
2501
2751
  },
2502
2752
  async ({ path: path5, params, body, agent_id }) => {
@@ -2538,6 +2788,7 @@ function initializeMcpServer(server) {
2538
2788
  registerModelsTool(server, modelCache);
2539
2789
  registerImageTool(server, budget);
2540
2790
  registerMusicTool(server, budget);
2791
+ registerSpeechTool(server, budget);
2541
2792
  registerVideoTool(server, budget);
2542
2793
  registerRealfaceTool(server, budget);
2543
2794
  registerSearchTool(server, budget);
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@blockrun/mcp",
3
- "version": "0.18.0",
3
+ "version": "0.19.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",