Gen Console · MCP API

Async jobs: wait:false + check_progress

Submit a generation job, get a job_id back in milliseconds, then poll for the result. No more requests hanging for minutes.

TL;DR
1 · SubmitCall generate_image / generate_video with "wait": false → returns job_id immediately.
2 · PollCall check_progress every 5–10 s with that job_id → status + url when done.
3 · DownloadWhen status:"done", fetch the artifact URL. Job record is kept for 2 hours.

Omitting wait keeps the old blocking behaviour, so existing integrations keep working.

1. Endpoint & authentication

ItemValue
MCP endpointhttps://genmcp.azkazamdigital.com/mcp
TransportStreamable HTTP (JSON-RPC 2.0 over POST)
Auth headerAuthorization: Bearer <your API key>
Get an API keyDashboard → Profile → API key
Content-Typeapplication/json

Client config for most MCP clients (mcp-remote, Claude Desktop, Cursor):

{
  "mcpServers": {
    "gen-console": {
      "url": "https://genmcp.azkazamdigital.com/mcp",
      "headers": { "Authorization": "Bearer YOUR_API_KEY" }
    }
  }
}

2. The two-step flow

Step 1 — submit with wait: false

{
  "jsonrpc": "2.0",
  "id": 1,
  "method": "tools/call",
  "params": {
    "name": "generate_video",
    "arguments": {
      "prompt": "a cat walking on a beach at sunset",
      "duration": 10,
      "aspect_ratio": "16:9",
      "resolution": "720p",
      "wait": false
    }
  }
}

Response — immediate (≈27 ms in testing):

{
  "jsonrpc": "2.0", "id": 1,
  "result": { "content": [{
    "type": "text",
    "text": "{\"job_id\":\"4cf05701199d7b59\",\"tool\":\"generate_video\",\"status\":\"queued\",\"next\":\"Job diterima. Panggil check_progress dengan job_id ini untuk memantau status dan mendapatkan URL hasil.\"}"
  }]}
}

Step 2 — poll check_progress

{
  "jsonrpc": "2.0",
  "id": 2,
  "method": "tools/call",
  "params": { "name": "check_progress", "arguments": { "job_id": "4cf05701199d7b59" } }
}

While running:

{"job_id":"4cf05701199d7b59","tool":"generate_video","status":"running","durasi_detik":45,
 "next":"Belum selesai — panggil check_progress lagi beberapa saat."}

When finished:

{"job_id":"4cf05701199d7b59","tool":"generate_video","status":"done",
 "url":"https://genmcp.azkazamdigital.com/artifacts/9f2c...e1",
 "detail":"https://genmcp.azkazamdigital.com/artifacts/9f2c...e1 (video beach.mp4, rasio 16:9, resolusi 720p)",
 "durasi_detik":312}

On failure:

{"job_id":"4cf05701199d7b59","tool":"generate_video","status":"failed",
 "error":"Terjadi gangguan saat memproses permintaan. Coba lagi beberapa saat lagi ya.",
 "ref":"b1de706e","durasi_detik":8}

3. Tools

generate_image

ParameterTypeNotes
promptstringrequired
aspect_ratiostringe.g. 1:1, 16:9, 9:16
imagesarray (1–5)reference images in order 1→5 — the easiest way to send several references
imagestring | objectsingle reference — data URL or {mimeType, base64}
extraImagesarray (max 4)additional references → image + extraImages = max 5
countinteger 1–4several images in parallel
waitbooleanfalse → return job_id immediately. Default true.

Typical duration: 5–30 s per image. Model is fixed to the fast default (flash-image).

Up to 5 reference images — send them all in one call and the model merges them into a single new image:
{ "name": "generate_image",
  "arguments": {
    "prompt": "merge these five references into one cinematic scene, dark background",
    "images": ["data:image/png;base64,…", "data:image/png;base64,…",
               "data:image/png;base64,…", "data:image/png;base64,…",
               "data:image/png;base64,…"],
    "aspect_ratio": "1:1"
  } }

Each reference must be JPEG/PNG/WebP and at most 10 MB. URLs (http/https) and data URLs are both accepted. Sending more than 5 keeps the first five.

generate_video

ParameterTypeNotes
promptstringrequired
imagestring | objectoptional reference image → image-to-video
aspect_ratio16:9 | 9:16default 16:9
resolution720p | 1080pdefault 720p (faster)
durationinteger 1–15seconds, default 15
waitbooleanfalse → return job_id immediately. Default true.

Typical duration: 2–10 minutes. Use wait:false for video — otherwise the request stays open for the whole render and clients with a 60 s timeout will fail.

generate_video_ingredients — 1–3 reference images ("Ingredients" Frame to video)

ParameterTypeNotes
promptstringrequired — describe the single scene that combines every ingredient
imagesarray (1–3)required — reference images in order 1→3 (URL or data URL)
aspect_ratio16:9 | 9:16default 16:9
resolution720p | 1080pdefault 720p (faster)
durationinteger 1–15seconds, default 15
waitbooleanfalse → return job_id immediately. Default true.

This is a separate feature from generate_video: all 1–3 images appear together in one shot (the same behaviour as the "Ingredients Frame to video" mode in the console). It counts as one video towards the quota, no matter how many ingredients you send. Use wait:false + check_progress, exactly like generate_video.

{ "name": "generate_video_ingredients",
  "arguments": {
    "prompt": "red ball, blue box and green triangle appear together on a reflective floor",
    "images": ["https://…/red.png", "https://…/blue.png", "https://…/green.png"],
    "aspect_ratio": "16:9", "duration": 5, "wait": false
  } }

check_progress

ParameterTypeNotes
job_idstringrequired — from generate_image/generate_video with wait:false
StatusMeaningExtra fields
queuedaccepted, not startednext
runningin progressdurasi_detik, next
donefinishedurl, detail, durasi_detik
failedfailederror (user-safe), ref, durasi_detik

check_progress also accepts job ids from the older video flow, so it is a drop-in replacement for check_video (which remains available for compatibility).

3b. Quota — only successful jobs count

4. Polling guidance

5. Example — Node.js

const URL = 'https://genmcp.azkazamdigital.com/mcp';
const KEY = process.env.GEN_API_KEY;

let id = 0;
async function rpc(method, params) {
  const res = await fetch(URL, {
    method: 'POST',
    headers: { 'Content-Type': 'application/json', Authorization: `Bearer ${KEY}` },
    body: JSON.stringify({ jsonrpc: '2.0', id: ++id, method, params }),
  });
  const json = await res.json();
  if (json.error) throw new Error(json.error.message || 'rpc error');
  return json.result;
}

async function callTool(name, args) {
  const result = await rpc('tools/call', { name, arguments: args });
  return JSON.parse(result.content[0].text);   // every tool returns JSON text
}

const job = await callTool('generate_video', {
  prompt: 'a cat walking on a beach at sunset',
  duration: 10,
  wait: false,
});
console.log('job_id', job.job_id);

for (;;) {
  await new Promise(r => setTimeout(r, 7000));
  const st = await callTool('check_progress', { job_id: job.job_id });
  console.log(st.status, st.durasi_detik + 's');
  if (st.status === 'done')   { console.log('URL:', st.url); break; }
  if (st.status === 'failed') { throw new Error(`${st.error} (ref ${st.ref})`); }
}

6. Example — Python

import os, time, json, requests

URL = "https://genmcp.azkazamdigital.com/mcp"
HEADERS = {"Content-Type": "application/json",
           "Authorization": f"Bearer {os.environ['GEN_API_KEY']}"}

_id = 0
def call_tool(name, args):
    global _id
    _id += 1
    r = requests.post(URL, headers=HEADERS, timeout=60, json={
        "jsonrpc": "2.0", "id": _id, "method": "tools/call",
        "params": {"name": name, "arguments": args}})
    r.raise_for_status()
    payload = r.json()
    if "error" in payload:
        raise RuntimeError(payload["error"]["message"])
    return json.loads(payload["result"]["content"][0]["text"])

job = call_tool("generate_image", {"prompt": "a red apple on a white table", "wait": False})
print("job_id", job["job_id"])

while True:
    time.sleep(5)
    st = call_tool("check_progress", {"job_id": job["job_id"]})
    print(st["status"])
    if st["status"] == "done":
        print(st["url"]); break
    if st["status"] == "failed":
        raise RuntimeError(f"{st['error']} (ref {st['ref']})")

7. Errors & limits

Quota exceeded — JSON-RPC error code -32000 with current counters:

{"jsonrpc":"2.0","id":3,"error":{"code":-32000,
 "message":"Batas video harian tercapai (5/5). Naikkan batas di menu Pengguna atau coba lagi besok.",
 "videoToday":5,"videoDailyLimit":5,"videoWeek":9,"videoWeeklyLimit":20}}

Image quota is reported the same way (imageToday, imageDailyLimit).

CaseHTTPBody
Bad / missing API key401{"error":"Kunci API tidak valid atau sudah tidak aktif."}
Rate limit (RPM)429{"error":"Terlalu banyak permintaan dalam waktu singkat. Coba lagi sebentar ya.","rpmLimit":30} + retry-after
Too many parallel requests429{"error":"Maksimal N job berjalan bersamaan untuk akun ini. Tunggu salah satu selesai ya.","activeJobs":N,"workerLimit":N}
Unknown tool200JSON-RPC error code -32601
Missing argument200JSON-RPC error code -32602

Caveats

8. FAQ

Do I have to change my existing code?

No. Without wait, tools behave exactly as before (blocking, returning the URL).

Is check_video gone?

No — it still works for video jobs. New code should use check_progress, which handles both.

Can I submit several jobs at once?

Yes. Submit with wait:false in parallel, then poll each job_id. Jobs run concurrently subject to per-account concurrency limits.

Why is my image job still running?

Images typically take 5–30 s. If a job stays running beyond ~2 minutes, poll again or submit a new job.

Where do result URLs point?

https://genmcp.azkazamdigital.com/artifacts/<id> — publicly reachable, inline for video/audio, download for images. Store the URL if you need the file permanently.

How many reference images can I send?

Image: up to 5 — pass images: [...] (or image + extraImages). Video "Ingredients": up to 3 — pass images: [...] to generate_video_ingredients. Regular generate_video takes a single reference image.