Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 18 additions & 7 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -2,16 +2,27 @@

All notable changes to BlockRun MCP will be documented in this file.

## Unreleased
## 0.42.0

**`blockrun_video` now pays on Solana as well as Base.** The Solana route uses
the gateway's payment-on-completion async flow: the job POST is never retried,
the gateway's payment-on-completion async flow: the job POST is issued once,
idempotent poll GETs tolerate transient disconnects, and their SVM transaction
is refreshed before its recent blockhash expires. A reactive re-challenge is
pinned to the original amount and recipient, so a slow Seedance 2.5 render can
finish inside a 15-minute polling budget without silently repricing or sending
a payment signature to another origin. Image-to-video also rejects the
Solana-incompatible `image_url` + `aspect_ratio` combination before payment.
is re-signed with a fresh blockhash every 20s so a slow Seedance 2.5 render can
settle at the end of a true 15-minute total budget (quote + submit + polling,
every request clamped to what is left). A gateway re-challenge is pinned to
the original amount, recipient and fee payer, and a payment signature is never
sent to a poll URL off the gateway's origin. A settle-failure 402 is read from
its `PAYMENT-RESPONSE` reason: a funding problem surfaces as one, while the
gateway's documented stale-blockhash re-sign path is bounded and, if
exhausted, reports the still-claimable job id instead of "out of funds". A
poll answer carrying a settlement receipt is booked before its body is
validated, so a truncated payload cannot erase a real charge from the ledger.

**`BLOCKRUN_KEYCHAIN=strict` broke Solana image payments.** `blockrun_image`
on Solana (and the new video route) loaded the wallet through the SDK's
file-only loader, which cannot see a key that strict mode has moved into the
OS keychain, so a funded wallet was reported as missing. Both now resolve the
key the same way `blockrun_wallet` does.

## 0.41.1

Expand Down
4 changes: 2 additions & 2 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@blockrun/mcp",
"version": "0.41.1",
"version": "0.42.0",
"mcpName": "io.github.BlockRunAI/blockrun-mcp",
"description": "BlockRun MCP Server - Give your AI agent web search, deep research, prediction markets, and crypto data. Paid via x402 micropayments.",
"type": "module",
Expand Down
2 changes: 1 addition & 1 deletion skills/gentech-blockrun/SKILL.md
Original file line number Diff line number Diff line change
Expand Up @@ -292,7 +292,7 @@ blockrun_wallet(action="chain", chain="base")
blockrun_wallet(action="chain")
```

**Note:** Base is required for music, speech, video, and realface. Solana works for price, wallet, dex, rpc, surf, etc.
**Note:** Base is required for music, speech, and realface. Image and video pay on either chain. Solana works for price, wallet, dex, rpc, surf, etc.

---

Expand Down
23 changes: 11 additions & 12 deletions src/tools/video.ts
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,13 @@ export const VIDEO_POLL_TIMEOUT_MS = 90_000;
// margin above is asserted against the value the request actually sends,
// rather than a literal restated in the test.
export const VIDEO_PAYMENT_AUTH_SECONDS = 600;
// Solana has no EIP-3009 validBefore to stay inside: utils/solana-402.ts
// re-signs the SPL transaction with a fresh blockhash throughout polling, so
// the budget is bounded by the tool's patience, not an authorization. Measured
// from the helper's entry (quote + submit + polling), so the description's
// "15 min Solana hard cap" is a true total, not a polling budget that starts
// after a submit of unbounded length.
export const SOLANA_VIDEO_TOTAL_BUDGET_MS = 900_000;

// Video pricing mirrors the gateway's calculateVideoPrice() + addTransactionFee()
// (blockrun/src/lib/models.ts). Two regimes:
Expand Down Expand Up @@ -435,27 +442,19 @@ Returns a permanent blockrun-hosted MP4 URL (the gateway mirrors the asset to GC
// Loaded only on the Solana branch so Base-only test harnesses and
// installations never need to initialize SVM payment dependencies.
const { solanaPaidAsyncPost } = await import("../utils/solana-402.js");
// The Solana gateway derives image-to-video geometry from the seed
// image. Supplying aspect_ratio as well is rejected before quoting;
// fail locally so a caller gets the actionable form of that error.
if (image_url && aspect_ratio) {
return {
content: [{ type: "text", text: formatError("Solana image-to-video derives its frame shape from image_url; do not also pass aspect_ratio.") }],
isError: true,
};
}

const { data, paidUsd, txHash } = await solanaPaidAsyncPost(
"/v1/videos/generations",
body,
300_000,
{
pollBudgetMs: 900_000,
pollBudgetMs: SOLANA_VIDEO_TOTAL_BUDGET_MS,
onQuote: (quotedUsd) => {
if (quotedUsd === null || quotedUsd <= estimatedCost) return;
gate?.release();
gate = reserveBudget(budget, agent_id, quotedUsd);
if (!gate.allowed) throw new Error(`${gate.reason}. No payment was signed.`);
// Phrased so formatError's uncharged guard suppresses its
// "fund your wallet" footer — the remedy is the budget, not USDC.
if (!gate.allowed) throw new Error(`${gate.reason}. Use blockrun_wallet action:"report" to see usage or action:"delegate" to increase agent budget. No charge was made.`);
},
},
);
Expand Down
8 changes: 4 additions & 4 deletions src/tools/wallet.ts
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,9 @@ To pay on Solana (no env vars, no file editing, no restart):
1. action:"chain" chain:"solana" → provisions + activates the Solana wallet
2. action:"setup" → Solana address + funding QR (send USDC SPL on Solana)
Switch back with action:"chain" chain:"base". Base-only — these ignore Solana and
need Base: blockrun_music, blockrun_speech, blockrun_video, blockrun_realface,
need Base: blockrun_music, blockrun_speech, blockrun_realface,
paid blockrun_price, blockrun_chat routing:"smart", and native Anthropic
(claude-*). blockrun_image pays on either chain.
(claude-*). blockrun_image and blockrun_video pay on either chain.

Actions:
- status (default): Both wallet addresses + USDC balances, active chain, session spending
Expand Down Expand Up @@ -166,8 +166,8 @@ Do NOT call this for actual AI queries — use blockrun_chat for that.`,
Base: ${both.base.address}
Solana: ${both.solana.address}

All blockrun_* calls now pay on ${active}. Note: price, video, music, speech
and RealFace are Base-only — switch back with chain:"base" for those.`;
All blockrun_* calls now pay on ${active}. Note: price, music, speech and
RealFace are Base-only — switch back with chain:"base" for those.`;
return {
content: [{ type: "text", text }],
structuredContent: {
Expand Down
Loading
Loading