MCP server

MCP tools

Every RiffAds MCP tool, its arguments and what it returns.

Read as Markdown

17 tools. A spend connection sees all 17. A read connection sees 12: the 5 spend tools are not registered. Setup: connect a client.

ToolAccessLimit per personWhat it does
riffads_pingReadnoneWorkspace, plan, whether this connection may spend
list_capabilitiesRead120/minA page of what this workspace can generate
get_capability_schemaRead120/minOne capability's JSON Schema and an example config
estimate_generationRead60/minThe number for max_credits. Reserves nothing
list_actorsRead120/minA page of actors
list_voicesRead120/minA page of voices
get_credit_balanceRead120/minAvailable credits and the agent spend ceiling in force
submit_generationSpend20/minStarts one generation
generate_talking_actorSpend20/minsubmit_generation for actor_ultra
wait_for_generationRead120/minBlocks up to 20 seconds, answers when done
get_generationRead120/minReads one generation now
get_batchRead120/minReads every variant of one submit
create_uploadSpend30/minReserves an ast_ id and a signed PUT URL
finalize_uploadSpend30/minConfirms the bytes landed, answers usable
list_templatesRead120/minRunnable workflow templates and their inputs
run_workflowSpend20/minRuns a whole workflow
get_workflow_runRead120/minReads a run node by node

Limits count per person, not per connection. Over a limit: rate_limited with retry_after_seconds.

Results and refusals

  • Success: one text block of JSON with ok: true plus the tool's fields. Files also come as resource_link blocks with signed URLs.
  • Refusal: isError set. Body: ok: false, code, message, retryable, credits_charged.
  • Schemas are strict. Unknown keys are refused. maxCredits instead of max_credits is a refusal.
  • capability_id is a free string, not an enum. New capabilities work with an old tool list.
  • Branch on code and retryable, never on message. Every code: errors.
A refusal
{
  "ok": false,
  "code": "max_credits_exceeded",
  "message": "This generation costs more than the max_credits you set (600 credits). This one needs 660 credits. Raise max_credits, or ask for something cheaper, such as a shorter or lower resolution render.",
  "retryable": false,
  "credits_charged": 0,
  "limit": { "bound_by": "max_credits", "limit_credits": 600, "required_credits": 660 }
}

limit.bound_by names the cap that refused: max_credits (yours to raise) or a workspace cap (ask an owner or admin).

Orient

riffads_ping

Read. No arguments. Spends nothing, no rate limit.

Result
{
  "ok": true,
  "user_id": "...",
  "organization_id": "org_...",
  "workspace": "Northwind Studio",
  "plan": "growth",
  "mode": "spend",
  "can_spend_credits": true,
  "server_time": "2026-09-17T10:04:11.882Z"
}

mode: "read" means no spend tools until a person changes it.

Discover

list_capabilities

Read. Paged.

ArgumentTypeRequiredNotes
limitinteger > 0noCapped server side
cursorstringnonext_cursor from the previous page

Returns capabilities[], total, next_cursor.

Result, trimmed
{
  "ok": true,
  "next_cursor": "gpt_image_2",
  "capabilities": [
    {
      "capability_id": "actor_ultra",
      "name": "Talking Actor",
      "description": "OmniHuman 1.5: an actor speaks your script.",
      "category": "avatar",
      "output_kind": "video",
      "status": "available"
    }
  ]
}
  • status: available, requires_plan, coming_soon, retired, unknown.
  • requires_plan rows carry required_plan.
  • Retired rows are left out of the list.
  • Category order: avatar, video, image, preset, tool.

Every id: capabilities.

get_capability_schema

Read.

ArgumentTypeRequired
capability_idstringyes

Returns { capability: { capability_id, name, description, category, output_kind, config_schema, example_config } }.

  • config_schema: JSON Schema for config.
  • example_config: the smallest config that validates.
  • Refusals may add capability_status and required_plan.

Some rules do not fit in JSON Schema

A schema-valid config can still be refused (some settings depend on other fields). Run estimate_generation to check a config before you submit.

Price

estimate_generation

Read. Reserves nothing, charges nothing.

ArgumentTypeRequired
capability_idstringyes
configobjectyes
Result. The numbers are arbitrary
{
  "ok": true,
  "capability_id": "veo_31",
  "credits": 400,
  "is_ceiling": false,
  "seconds": 8,
  "breakdown": [
    { "label": "Video, 1080p with audio", "unit": "second", "quantity": 8, "credits": 400 }
  ]
}
  • is_ceiling: true: an upper bound, not the exact number.
  • seconds and note show up only when they apply.
  • Numbers change. Estimate every time, do not cache.

Do not send the estimate back as max_credits

Send at least ceil(estimate * 1.1). The bare estimate is refused with max_credits_exceeded.

Library

list_actors

Read. Paged.

ArgumentTypeRequiredNotes
searchstringnoMatches names and descriptions
limitinteger > 0noCapped server side
cursorstringnoFrom the previous page

Row: { actor_id, name, description, gender, age_band, tags[], default_voice_id, default_voice_name, is_platform }, plus next_cursor. is_platform is true for RiffAds actors, false for your workspace's own. No preview images.

list_voices

Read. Paged.

ArgumentTypeRequiredNotes
searchstringnoMatches voice names
languagestringnoExact language tag, for example en or es
limitinteger > 0noCapped server side
cursorstringnoFrom the previous page

Row: { voice_id, name, language, gender, tags[], is_cloned, is_premium, is_platform }, plus total and next_cursor. No preview audio.

get_credit_balance

Read. No arguments.

Result. The numbers are arbitrary
{
  "ok": true,
  "credits_available": 4820,
  "credits_on_hold": 660,
  "agent_limits": {
    "max_credits_per_generation": 600,
    "daily_credits_limit": 2000,
    "spent_last_24_hours": 740,
    "daily_credits_remaining": 1260,
    "most_one_generation_may_cost": 600,
    "bound_by": "org_per_generation"
  }
}
  • most_one_generation_may_cost: the highest max_credits a submit can pass right now.
  • bound_by: max_credits, org_per_generation, api_key_budget or org_daily. API key fields never appear on MCP.

Spend

Spend tools are marked destructiveHint: true, so some clients ask before calling them.

submit_generation

Spend. Answers when the job is accepted, not when it is ready.

Prop

Type

Arguments
{
  "capability_id": "veo_31",
  "config": { "prompt": "...", "duration": 8, "resolution": "1080p" },
  "max_credits": 440
}
Result, trimmed
{
  "ok": true,
  "generation_id": "gen_0193c8f0a1b24e7f9d3c5a6b7e8f0033",
  "generation_ids": ["gen_0193c8f0a1b24e7f9d3c5a6b7e8f0033"],
  "run_id": "run_9f2c1b",
  "replay": false,
  "capability_id": "veo_31",
  "capability_name": "Veo 3.1",
  "output_kind": "video",
  "outputs_expected": 1,
  "credits": { "credits_held": 440, "credits_charged": null, "settlement": "open", "terminal": false },
  "charge_summary": "440 credits are on hold. Nothing has been charged so far.",
  "next_action": "Call wait_for_generation with this generation_id..."
}
  • generation_ids is always set. batch_group_id (bg_) appears only when variants is above 1.
  • run_id: null is not a failure. Follow generation_id.
  • replay: true: the same request already ran. You get that generation back, no second charge.
  • variants above the limit is refused, never clamped.
  • voice_id (argument) is not voiceId (inside config).
  • No output link on submit.

One agent submission at a time

A second submit while one is running gets submission_in_flight (retryable). Wait for the first, then send.

generate_talking_actor

Spend. Same as submit_generation with capability_id: "actor_ultra".

Prop

Type

Two generations, in this order

  1. submit_generation with capability_id: "tts". Wait until it completes.
  2. generate_talking_actor with that generation_id as approved_voice_generation_id.

Audio made for a different actor, voice or script is refused.

Step 1: the voice (submit_generation)
{
  "capability_id": "tts",
  "config": { "script": "Three weeks in and I am not going back." },
  "actor_id": "act_4f1e...",
  "voice_id": "voc_9a20...",
  "max_credits": 40
}
Step 2: the video (generate_talking_actor)
{
  "script": "Three weeks in and I am not going back.",
  "actor_id": "act_4f1e...",
  "voice_id": "voc_9a20...",
  "approved_voice_generation_id": "gen_1f88...",
  "max_credits": 700
}
  • The max_credits values are placeholders. Estimate each step.
  • script wins over a script inside settings.
  • actor_ultra has no aspect_ratio. Sending one is refused as an unknown key. Output matches the start frame's shape.
  • Result shape matches submit_generation.

Full walkthrough: talking actor.

Follow

wait_for_generation

Read. The right way to follow a job.

ArgumentTypeRequired
generation_idstringyes

Blocks up to 20 seconds and answers the moment the job ends. still_running: true: call it again right away.

Result, trimmed
{
  "ok": true,
  "still_running": true,
  "waited_seconds": 20,
  "age_seconds": 74,
  "retry_after_seconds": 0,
  "generation": {
    "generation_id": "gen_0193c8...",
    "status": "rendering",
    "outputs": []
  },
  "next_action": "Still working..."
}
  • Branch on still_running, not status.
  • age_seconds is the job's total age.
  • Pass a progressToken to get notifications/progress over an event stream.

get_generation

Read. One check, now. Do not loop it: use wait_for_generation.

ArgumentTypeRequired
generation_idstringyes
Result, trimmed
{
  "ok": true,
  "output_urls_expire_in_seconds": 600,
  "generation": {
    "generation_id": "gen_0193c8...",
    "capability_id": "veo_31",
    "status": "completed",
    "batch_group_id": null,
    "batch_index": null,
    "outputs": [
      {
        "index": 0,
        "kind": "video",
        "url": "https://...signed...",
        "file_name": "veo-31-0.mp4",
        "width": 1080,
        "height": 1920
      }
    ],
    "outputs_expected": 1,
    "outputs_delivered": 1,
    "credits": { "credits_held": 440, "credits_charged": 400, "settlement": "captured", "terminal": true },
    "charge_summary": "400 credits were charged.",
    "error": null,
    "created_at": "2026-09-17T10:04:11.882Z",
    "completed_at": "2026-09-17T10:09:52.104Z"
  },
  "next_action": "Done. The output links are short lived..."
}
  • Statuses: queued, rendering, post_processing, completed, failed. A canceled job reads failed.
  • credits.credits_charged is null until credits.terminal is true. Null is unknown, not zero.
  • Each file also comes as a resource_link with mimeType and a title like video 1 of 1.

Links die after 600 seconds

Download the file or hand the link on right away. Expired? Call get_generation again for a fresh link. See results.

get_batch

Read. Every variant of one submit.

ArgumentTypeRequired
batch_group_idstringyes

Returns status (running, completed, failed, partial), variants, variants_finished, outputs_delivered, one credits total, charge_summary, generations[], and a resource_link per delivered variant.

get_batch does not wait. Call wait_for_generation on a running id, then get_batch again. Batch credits_charged stays null until every variant ends.

Upload a file

Three steps. The middle one is your own HTTP call, not a tool.

create_upload

Spend. Reserves a slot. No bytes, no credits.

Prop

Type

KindTypesMax
Imageimage/jpeg, image/png, image/webp, image/gif20 MB
Videovideo/mp4, video/webm, video/quicktime100 MB
Audioaudio/mpeg, audio/mp3, audio/wav, audio/x-wav, audio/webm, audio/ogg25 MB

Returns asset_id, upload_url, upload_expires_at, upload_expires_in_seconds, kind, content_type, size_bytes, max_bytes_for_kind, content_checked, next_action.

PUT the bytes

One HTTP PUT to upload_url. Same content type, exact byte count, within 5 minutes.

finalize_upload

Spend. Call after the PUT returns.

ArgumentTypeRequired
asset_idstringyes

Returns { asset_id, scan_status, usable, kind, duration_ms, next_action }.

  • scan_status: pending, clean, flagged.
  • Use the ast_ id in a config only after usable: true.
  • usable: false with flagged is final. Use another file.
  • Calling twice returns the same answer.
  • duration_ms is measured server side. Null for images.

Only images are content checked. content_checked tells you. See content policy.

Workflows

Run a workflow built in the app, or a template. Agents cannot build or edit workflows. See workflows.

list_templates

Read. No arguments.

Returns templates[]. Each: { template_key, name, description, category, step_count, inputs[] }.

One input
{
  "node": "f_brand",
  "label": "Brand context",
  "field": "text",
  "field_label": "value",
  "kind": "text",
  "max_length": 8000,
  "filled": true
}
  • kind: text, setting, actor, voice.
  • setting inputs carry accepts. text inputs carry max_length.
  • filled: true: the node already has a value.
  • Node ids cannot be guessed. Read them here.

run_workflow

Spend. Every step is charged.

Prop

Type

  • Exactly one of template_key or workflow_id. Both or neither: invalid_config.
  • value is a string, number or boolean. Field actor takes an act_ id, field voice takes a voc_ id.
  • All or nothing: one bad input refuses the whole run. The message names the node and field.
  • count cannot be set. Asset inputs cannot be filled.
Arguments
{
  "template_key": "ugc_face_cam",
  "inputs": [
    { "node": "f_brand", "field": "text", "value": "A refillable cleaning spray sold direct." }
  ],
  "max_credits": 1200
}
Result, trimmed
{
  "ok": true,
  "workflow_run_id": "wfr_7c1e...",
  "workflow_id": "wf_2ab9...",
  "template_key": "ugc_face_cam",
  "billable_node_count": 6,
  "applied_inputs": [{ "node": "f_brand", "field": "text" }],
  "replay": false,
  "next_action": "This run has 6 step(s) to work through..."
}

billable_node_count counts the steps that will run and charge. No output link on run_workflow.

get_workflow_run

Read. The only way to follow a run. There is no wait tool for runs.

ArgumentTypeRequired
workflow_run_idstringyes

Answers now. Call about every 30 seconds until still_running is false.

Returns workflow_run_id, workflow_id, status, still_running, nodes[], credits, started_at, finished_at, next_action, and output_urls_expire_in_seconds once a file is ready.

  • Run status: queued, running, completed, partial, failed, canceled.
  • Node: { node_id, label, status, credits, generations[], text, error }. Writer nodes return text, no file.
  • Every file of every node comes as a resource_link.
  • credits.credits_charged is null until the run ends. credits_charged_so_far grows while it runs.
  • partial: some steps finished, failed steps charged nothing.

Not on MCP

  • No cancel tool. A job canceled elsewhere reads failed.
  • No outbound webhooks.
  • No list of past generations. Keep your ids.
  • No MCP resources or prompts.
  • No workflow authoring.
  • Nothing posts to Meta, TikTok, YouTube or X.

Id prefixes: statuses and ids.

On this page