<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[Arib Khan]]></title><description><![CDATA[Arib Khan]]></description><link>https://aribkhan-revana.hashnode.dev</link><generator>RSS for Node</generator><lastBuildDate>Mon, 02 Nov 2026 14:48:13 GMT</lastBuildDate><atom:link href="https://aribkhan-revana.hashnode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Generating video from Claude and Cursor with one API key (MCP + agent skills)]]></title><description><![CDATA[Disclosure: I work on Revana. The examples use its MCP server and REST API because that's what I know end to end. The patterns (quote first, cap the cost…]]></description><link>https://aribkhan-revana.hashnode.dev/generating-video-from-claude-and-cursor-with-one-api-key-mcp-agent-skills</link><guid isPermaLink="true">https://aribkhan-revana.hashnode.dev/generating-video-from-claude-and-cursor-with-one-api-key-mcp-agent-skills</guid><category><![CDATA[AI]]></category><category><![CDATA[mcp]]></category><category><![CDATA[claude]]></category><category><![CDATA[api]]></category><category><![CDATA[video]]></category><dc:creator><![CDATA[Arib Khan]]></dc:creator><pubDate>Fri, 30 Oct 2026 11:47:40 GMT</pubDate><enclosure url="https://cdn.hashnode.com/uploads/covers/6ae47dde45d80eb88ac2f6a8/6f8216c4-4de0-4bf9-b5f8-9635e21d247e.png" length="0" type="image/jpeg"/><content:encoded><![CDATA[<blockquote>
<p>Disclosure: I work on <a href="https://revana.si">Revana</a>. The examples use its MCP server and REST API because that's what I know end to end. The patterns (quote first, cap the cost, idempotent retries, wait without polling) apply to any paid generation API you give an agent.</p>
</blockquote>
<p>Coding agents are now good at the glue work around media: writing a storyboard, generating a start frame, rendering a clip, trimming it with ffmpeg, and committing the result to a repo. What they lack out of the box is a safe way to <em>spend money</em> on generation. An agent that can call a video model in a retry loop can also burn $50 in a minute.</p>
<p>This post covers two ways to give Claude (Claude Code, Claude Desktop) and Cursor access to video models such as Seedance 2.5, Veo 3.1 and Kling, through a single key:</p>
<ol>
<li><strong>An MCP server.</strong> The agent gets typed tools such as <code>get_quote</code> and <code>generate_video</code>.</li>
<li><strong>An agent skill / rules file.</strong> The agent calls the REST API with <code>curl</code>, following a short set of rules you write once.</li>
</ol>
<p>You can use either or both. MCP is less work. The skill gives you more control and works in any agent that can run a shell.</p>
<p>Everything below is also in a public repo, <a href="https://github.com/Aribk7/revana-skills">Aribk7/revana-skills</a>: ready-made Agent Skills for Claude Code, Codex and Cursor, runnable examples, and two n8n workflows.</p>
<h2>Option 1: the MCP server</h2>
<p>Revana runs a remote MCP server over streamable HTTP at:</p>
<pre><code>https://revana.si/api/mcp
</code></pre>
<h3>Claude Code</h3>
<pre><code class="language-bash">claude mcp add --transport http revana https://revana.si/api/mcp
</code></pre>
<p>Then type <code>/mcp</code> inside Claude Code and choose <strong>revana</strong> to sign in. The OAuth flow creates a separate API key for this connection. You can revoke it from the dashboard without touching your other keys.</p>
<h3>Claude Desktop / claude.ai</h3>
<p>Add it as a custom connector with the same URL, then click <strong>Connect</strong> and approve it on revana.si.</p>
<h3>Cursor</h3>
<p>Add the server to <code>~/.cursor/mcp.json</code> (global) or <code>.cursor/mcp.json</code> (per project). With OAuth, Cursor asks you to sign in the first time a tool is used:</p>
<pre><code class="language-json">{
  "mcpServers": {
    "revana": {
      "url": "https://revana.si/api/mcp"
    }
  }
}
</code></pre>
<h3>Any client that takes headers</h3>
<p>If your client doesn't support OAuth, pass an API key from the dashboard as a header. Keep this file out of git: the key spends real credits.</p>
<pre><code class="language-json">{
  "mcpServers": {
    "revana": {
      "type": "http",
      "url": "https://revana.si/api/mcp",
      "headers": { "Authorization": "Bearer sr_live_YOUR_KEY" }
    }
  }
}
</code></pre>
<p>VS Code and Codex CLI one-liners, for completeness:</p>
<pre><code class="language-bash">code --add-mcp '{"name":"revana","type":"http","url":"https://revana.si/api/mcp"}'
codex mcp add revana --url https://revana.si/api/mcp &amp;&amp; codex mcp login revana
</code></pre>
<h3>What the agent gets</h3>
<table>
<thead>
<tr>
<th>Tool</th>
<th>What it does</th>
<th>Costs money</th>
</tr>
</thead>
<tbody><tr>
<td><code>list_models</code></td>
<td>Every video/image model with options and price</td>
<td>no</td>
</tr>
<tr>
<td><code>get_quote</code></td>
<td>Exact price for a request, before anything runs</td>
<td>no</td>
</tr>
<tr>
<td><code>get_balance</code></td>
<td>What the credits can spend now, what's held by running jobs</td>
<td>no</td>
</tr>
<tr>
<td><code>upload_file</code></td>
<td>Turn a local image/clip/sound into an https link for references (14 days)</td>
<td>no</td>
</tr>
<tr>
<td><code>generate_video</code></td>
<td>Start the generation, capped by <code>max_cost_usd</code></td>
<td><strong>yes</strong></td>
</tr>
<tr>
<td><code>get_video</code></td>
<td>Wait for a job (long-poll up to 40 s per call)</td>
<td>no</td>
</tr>
<tr>
<td><code>get_video_link</code></td>
<td>A download link for the result (valid one hour)</td>
<td>no</td>
</tr>
</tbody></table>
<p>The design choice that matters is that <strong>quoting is a separate, free tool</strong> and the server instructions tell the model to quote first and ask before calling <code>generate_video</code>. In practice, this prompt:</p>
<blockquote>
<p>Make a 5-second 720p video of a paper boat in the rain with Seedance 2.5. Tell me the price first.</p>
</blockquote>
<p>produces a <code>get_quote</code> call, a message like "That's $1.25 (5 s x $0.25/s at 720p, price as of 2026-10-30). Go ahead?", and only after you answer yes, a <code>generate_video</code> call with <code>max_cost_usd: 1.25</code> and an idempotency key the agent makes up. If the agent retries after a network error, it reuses that key, and the server returns the same job instead of starting a second paid one.</p>
<p>The other detail is <code>get_video</code> with <code>wait_seconds</code>. Without it, agents tend to poll in a tight loop and fill their own context with status messages. A long-poll lets one tool call wait up to 40 seconds.</p>
<h2>Option 2: an agent skill that calls the REST API</h2>
<p>MCP is convenient, but sometimes you want the agent to drive the HTTP API directly: in CI, in a sandbox without MCP, or when you want the generated files written straight into the repo. In that case, give the agent a short rules file and a key in the environment.</p>
<p>The key goes in the environment, set by you, never pasted into the chat:</p>
<pre><code class="language-bash">export REVANA_API_KEY=sr_live_...   # in your shell profile or a .env the agent can't print
</code></pre>
<p>Then add a skill. For Claude Code that's <code>.claude/skills/video/SKILL.md</code>. For Cursor, put the same text in <code>.cursor/rules/video.mdc</code>.</p>
<p>If you'd rather not write it yourself, the <a href="https://github.com/Aribk7/revana-skills">revana-skills</a> repo has finished skills for video, images, UGC ads, music and picking the cheapest model. Copy a folder from its <a href="https://github.com/Aribk7/revana-skills/tree/main/skills"><code>skills/</code></a> directory into <code>~/.claude/skills/</code> (or your agent's skills directory). The minimal version looks like this:</p>
<pre><code class="language-markdown">---
name: video
description: Generate a video or image with the Revana API (Seedance 2.5, Veo 3.1, Kling, Wan, Nano Banana Pro). Use when the user asks to make, render or animate a video or image.
---

# Rules
1. The key is in $REVANA_API_KEY. Never print, echo or write it to a file.
2. Quote first from the live price list (no key needed) and wait for a "yes":
   curl -s https://api.revana.si/v1/models | jq '.data[] | select(.id=="seedance-2.5") | .price'
   Cost = price.usd[resolution] (or "&lt;res&gt;+audio" when present and audio is on) x duration.
3. Always send max_cost_usd equal to the agreed price.
4. Send an Idempotency-Key header. On any retry, send the same key and the same body.
5. Poll GET /v1/generations/{id} every 5 s until status is succeeded or failed.
6. Download with GET /v1/generations/{id}/content into ./media/, then tell the user the path and the final cost_usd.
7. If error.retryable is false, don't retry: change the request or ask the user.
</code></pre>
<p>With the skill in place, the agent's generated commands look like this:</p>
<pre><code class="language-bash"># 1. submit (202 Accepted; a replay of the same key returns 200 with the same job)
GEN=$(curl -s https://api.revana.si/v1/generations \
  -H "Authorization: Bearer $REVANA_API_KEY" \
  -H "Content-Type: application/json" \
  -H "Idempotency-Key: boat-rain-v1" \
  -d '{
    "model": "seedance-2.5",
    "input": {
      "prompt": "a paper boat drifting down a rain-soaked street, low angle, shallow depth of field",
      "resolution": "720p", "duration": 5, "aspect_ratio": "16:9", "audio": true
    },
    "max_cost_usd": 1.25
  }')
ID=$(echo "$GEN" | jq -r .id)

# 2. poll
while :; do
  G=$(curl -s https://api.revana.si/v1/generations/$ID -H "Authorization: Bearer $REVANA_API_KEY")
  S=$(echo "$G" | jq -r .status)
  [ "$S" = succeeded ] || [ "$S" = failed ] &amp;&amp; break
  sleep 5
done
echo "$G" | jq '{status, cost_usd, error}'

# 3. download (the endpoint redirects to a short-lived file URL, hence -L)
mkdir -p media
curl -sL https://api.revana.si/v1/generations/$ID/content \
  -H "Authorization: Bearer $REVANA_API_KEY" -o media/boat-rain.mp4
</code></pre>
<h3>Image-to-video from a local file</h3>
<p>Agents often generate a start frame first and then animate it. Reference media must be public <code>https://</code> URLs, so a local file goes through <code>/v1/uploads</code> first. The upload is free, and the returned link is valid for 14 days:</p>
<pre><code class="language-js">// upload.mjs: node upload.mjs frame.png  -&gt;  prints a https link to use as image_url
import { readFile } from "node:fs/promises";
import { basename } from "node:path";

const file = process.argv[2];
const bytes = await readFile(file);
const headers = { Authorization: `Bearer ${process.env.REVANA_API_KEY}`, "Content-Type": "application/json" };

const up = await fetch("https://api.revana.si/v1/uploads", {
  method: "POST",
  headers,
  body: JSON.stringify({ content_type: "image/png", bytes: bytes.length, filename: basename(file) }),
}).then((r) =&gt; r.json());

// PUT the bytes with exactly the headers the API returned (content type and length are signed)
const put = await fetch(up.upload_url, { method: up.upload_method, headers: up.upload_headers, body: bytes });
if (!put.ok) throw new Error(`upload failed: ${put.status}`);

console.log(up.url); // e.g. https://api.revana.si/v1/uploads/upl_..., good for 14 days
</code></pre>
<p>Then the generation takes it as the first frame:</p>
<pre><code class="language-json">{
  "model": "seedance-2.5",
  "input": { "prompt": "the boat starts to drift, rain intensifies", "image_url": "https://api.revana.si/v1/uploads/upl_...",
             "resolution": "720p", "duration": 5, "aspect_ratio": "16:9", "audio": true },
  "max_cost_usd": 1.25
}
</code></pre>
<h2>What actually goes wrong with agents and paid APIs</h2>
<p>After a few weeks of running this daily, these are the problems I've seen and what fixes each one:</p>
<ul>
<li><strong>Double spend on retry.</strong> The agent sees a timeout, assumes failure, and resubmits. Fix: an idempotency key per <em>intent</em>, not per HTTP call. The same key with the same body returns the original job, and the same key with a different body is a <code>409 idempotency_conflict</code>. The conflict is useful, because it means the agent tried to "retry" with a changed prompt.</li>
<li><strong>Silent upgrades.</strong> The agent decides 1080p "looks better" and triples the cost. Fix: <code>max_cost_usd</code> on every request. A route above the cap is skipped and the request fails with <code>max_cost_exceeded</code> instead of charging more.</li>
<li><strong>Polling floods.</strong> The agent polls every second and fills its context. Fix: in MCP, use the long-poll tool. With REST, put an explicit interval in the rules.</li>
<li><strong>Retrying the unretryable.</strong> A content-policy refusal won't pass on the fifth attempt. Fix: read <code>error.retryable</code>. <code>content_policy</code>, <code>invalid_input</code> and <code>max_cost_exceeded</code> are final, while <code>model_busy</code>, <code>timeout</code> and <code>all_routes_failed</code> are worth retrying later.</li>
<li><strong>Leaked keys.</strong> Agents happily <code>echo $REVANA_API_KEY</code> while debugging. Fix: say so in the rules, prefer OAuth-scoped MCP keys you can revoke individually, and keep a low balance on agent keys.</li>
</ul>
<h2>Which one to use</h2>
<ul>
<li><strong>Interactive use in Claude Desktop, claude.ai or Cursor chat:</strong> MCP. Two minutes of setup, with quoting and confirmation built in.</li>
<li><strong>Claude Code / Cursor working inside a repo,</strong> where you want files on disk and scripts you can re-run: the skill plus REST. Keep MCP connected for quotes.</li>
<li><strong>CI or batch jobs:</strong> REST only, with the same three rules: quote, cap, idempotency key.</li>
</ul>
<h2>SDKs, examples and n8n</h2>
<p>The official SDKs are open source on GitHub: <a href="https://github.com/Aribk7/revana-js">revana-js</a> (TypeScript/JavaScript) and <a href="https://github.com/Aribk7/revana-python">revana-python</a>. Both wrap the same quote, cap, idempotency key and wait flow described above. They aren't on npm or PyPI yet, so for now read the source there and use curl or the MCP server in your projects.</p>
<p>The <a href="https://github.com/Aribk7/revana-skills">revana-skills</a> repo also has <a href="https://github.com/Aribk7/revana-skills/tree/main/examples">runnable examples</a> (curl, Node, Python, Next.js route handlers, Vercel AI SDK tools, MCP configs) and <a href="https://github.com/Aribk7/revana-skills/tree/main/n8n">importable n8n workflows</a> that quote, create, wait and download.</p>
<p>The full API is described in an OpenAPI file at <a href="https://revana.si/openapi.json">https://revana.si/openapi.json</a>, and <code>GET https://api.revana.si/v1/models</code> lists every model and price without a key. Prices quoted in this post are as of 2026-10-30.</p>
]]></content:encoded></item></channel></rss>