wait:false + check_progressSubmit a generation job, get a job_id back in milliseconds, then poll for the
result. No more requests hanging for minutes.
generate_image / generate_video with
"wait": false → returns job_id immediately.check_progress every 5–10 s with that
job_id → status + url when done.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.
| Item | Value |
|---|---|
| MCP endpoint | https://genmcp.azkazamdigital.com/mcp |
| Transport | Streamable HTTP (JSON-RPC 2.0 over POST) |
| Auth header | Authorization: Bearer <your API key> |
| Get an API key | Dashboard → Profile → API key |
| Content-Type | application/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" }
}
}
}
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.\"}"
}]}
}
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}
generate_image| Parameter | Type | Notes |
|---|---|---|
prompt | string | required |
aspect_ratio | string | e.g. 1:1, 16:9, 9:16 |
images | array (1–5) | reference images in order 1→5 — the easiest way to send several references |
image | string | object | single reference — data URL or {mimeType, base64} |
extraImages | array (max 4) | additional references → image + extraImages = max 5 |
count | integer 1–4 | several images in parallel |
wait | boolean | false → return job_id immediately. Default true. |
Typical duration: 5–30 s per image. Model is fixed to the fast default (flash-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| Parameter | Type | Notes |
|---|---|---|
prompt | string | required |
image | string | object | optional reference image → image-to-video |
aspect_ratio | 16:9 | 9:16 | default 16:9 |
resolution | 720p | 1080p | default 720p (faster) |
duration | integer 1–15 | seconds, default 15 |
wait | boolean | false → 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)| Parameter | Type | Notes |
|---|---|---|
prompt | string | required — describe the single scene that combines every ingredient |
images | array (1–3) | required — reference images in order 1→3 (URL or data URL) |
aspect_ratio | 16:9 | 9:16 | default 16:9 |
resolution | 720p | 1080p | default 720p (faster) |
duration | integer 1–15 | seconds, default 15 |
wait | boolean | false → 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| Parameter | Type | Notes |
|---|---|---|
job_id | string | required — from generate_image/generate_video with wait:false |
| Status | Meaning | Extra fields |
|---|---|---|
| queued | accepted, not started | next |
| running | in progress | durasi_detik, next |
| done | finished | url, detail, durasi_detik |
| failed | failed | error (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).
wait:false, quota is recorded when the job finishes successfully, not at submit time.failed (provider hiccup, timeout, content rejection) costs nothing.failed, so most hiccups never reach your users.job_id; polling is free and does not consume quota.tools/call of generation tools counts against quota.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})`); }
}
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']})")
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).
| Case | HTTP | Body |
|---|---|---|
| Bad / missing API key | 401 | {"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 requests | 429 | {"error":"Maksimal N job berjalan bersamaan untuk akun ini. Tunggu salah satu selesai ya.","activeJobs":N,"workerLimit":N} |
| Unknown tool | 200 | JSON-RPC error code -32601 |
| Missing argument | 200 | JSON-RPC error code -32602 |
job_id can no longer be polled (finished artifacts stay downloadable).wait:false jobs are charged to quota only when they finish successfully — a failed job
costs nothing.failed messages are intentionally generic; the ref code is what support
needs to trace the real cause in server logs.wait is supported by generate_image, generate_video and
generate_video_ingredients; other tools are synchronous by nature.workerLimit jobs with wait:false at once returns 429
(activeJobs / workerLimit), even though each request returns immediately.No. Without wait, tools behave exactly as before (blocking, returning the URL).
check_video gone?No — it still works for video jobs. New code should use check_progress, which handles both.
Yes. Submit with wait:false in parallel, then poll each job_id. Jobs run
concurrently subject to per-account concurrency limits.
running?Images typically take 5–30 s. If a job stays running beyond ~2 minutes, poll again or submit a new job.
https://genmcp.azkazamdigital.com/artifacts/<id> — publicly reachable, inline for
video/audio, download for images. Store the URL if you need the file permanently.
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.