Check Seedance 2.0 task status
seedance_check_taskQuery the status of a Seedance 2.0 video generation task by task ID. Returns running, succeeded, or failed, with download URLs on success.
Instructions
Query the status of a Seedance 2.0 task by task_id. Returns running / succeeded / failed / other. On success returns video_url (and last_frame_url if return_last_frame was true). Generated URLs expire within ~24h - download promptly.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| task_id | Yes | Seedance task id returned by seedance_create_task (e.g. cgt-...). |
Implementation Reference
- src/server.ts:92-174 (registration)Registration of the seedance_check_task tool with the MCP server, binding the tool name to its schema and handler
server.registerTool( "seedance_check_task", { title: "Check Seedance 2.0 task status", description: "Query the status of a Seedance 2.0 task by task_id. Returns running / succeeded / failed / other. On success returns video_url (and last_frame_url if return_last_frame was true). Generated URLs expire within ~24h - download promptly.", inputSchema: checkTaskInputShape, annotations: { readOnlyHint: true, idempotentHint: true, openWorldHint: true, }, }, async (input: CheckTaskInput): Promise<CallToolResult> => { try { const result = await checkSeedanceTask(input.task_id); const status = result.status.toLowerCase(); let summary: string; switch (status) { case "succeeded": { const lines = [ `Task ${result.id} succeeded.`, result.videoUrl ? `video_url: ${result.videoUrl}` : "WARNING: succeeded but no video_url was returned by ARK.", ]; if (result.lastFrameUrl) { lines.push(`last_frame_url: ${result.lastFrameUrl}`); } lines.push(""); lines.push( "Reminder: ARK signed URLs typically expire within 24 hours. Download the video promptly.", ); summary = lines.join("\n"); break; } case "failed": { summary = `Task ${result.id} failed.${ result.failReason ? ` Reason: ${result.failReason}` : "" }`; break; } case "running": case "queued": case "in_queue": case "pending": { summary = [ `Task ${result.id} is still ${status}.`, "Wait another 30-90 seconds, then call seedance_check_task again.", ].join("\n"); break; } case "cancelled": case "canceled": { summary = `Task ${result.id} was cancelled.`; break; } case "expired": { summary = `Task ${result.id} has expired. Submit a new task with seedance_create_task.`; break; } default: { summary = `Task ${result.id} is in status "${result.status}". This is not one of the standard states (running/succeeded/failed/cancelled/expired); see the structured response for details.`; } } return { content: [{ type: "text", text: summary }], structuredContent: { task_id: result.id, status: result.status, video_url: result.videoUrl, last_frame_url: result.lastFrameUrl, fail_reason: result.failReason, raw: result.raw, }, }; } catch (err) { return errorResult(formatApiError(err)); } }, ); - src/server.ts:105-173 (handler)Handler function for seedance_check_task — receives CheckTaskInput, calls checkSeedanceTask, and formats the response based on status (succeeded/failed/running/etc.)
async (input: CheckTaskInput): Promise<CallToolResult> => { try { const result = await checkSeedanceTask(input.task_id); const status = result.status.toLowerCase(); let summary: string; switch (status) { case "succeeded": { const lines = [ `Task ${result.id} succeeded.`, result.videoUrl ? `video_url: ${result.videoUrl}` : "WARNING: succeeded but no video_url was returned by ARK.", ]; if (result.lastFrameUrl) { lines.push(`last_frame_url: ${result.lastFrameUrl}`); } lines.push(""); lines.push( "Reminder: ARK signed URLs typically expire within 24 hours. Download the video promptly.", ); summary = lines.join("\n"); break; } case "failed": { summary = `Task ${result.id} failed.${ result.failReason ? ` Reason: ${result.failReason}` : "" }`; break; } case "running": case "queued": case "in_queue": case "pending": { summary = [ `Task ${result.id} is still ${status}.`, "Wait another 30-90 seconds, then call seedance_check_task again.", ].join("\n"); break; } case "cancelled": case "canceled": { summary = `Task ${result.id} was cancelled.`; break; } case "expired": { summary = `Task ${result.id} has expired. Submit a new task with seedance_create_task.`; break; } default: { summary = `Task ${result.id} is in status "${result.status}". This is not one of the standard states (running/succeeded/failed/cancelled/expired); see the structured response for details.`; } } return { content: [{ type: "text", text: summary }], structuredContent: { task_id: result.id, status: result.status, video_url: result.videoUrl, last_frame_url: result.lastFrameUrl, fail_reason: result.failReason, raw: result.raw, }, }; } catch (err) { return errorResult(formatApiError(err)); } }, - src/seedance.ts:201-278 (helper)Core API helper that performs a GET request to the ARK API to check the status of a video generation task by task_id
export async function checkSeedanceTask( taskId: string, ): Promise<CheckTaskResponse> { if (!taskId || taskId.trim().length === 0) { throw new Error("task_id is required"); } const apiKey = getApiKey(); const baseUrl = getBaseUrl(); const res = await fetch( `${baseUrl}/contents/generations/tasks/${encodeURIComponent(taskId.trim())}`, { method: "GET", headers: { Authorization: `Bearer ${apiKey}`, }, }, ); const parsed = await parseJsonSafe(res); if (!res.ok) { throw new SeedanceApiError( extractApiErrorMessage(parsed, res.status), res.status, parsed, ); } if (!parsed || typeof parsed !== "object") { throw new SeedanceApiError( "ARK API returned an unexpected non-JSON response", res.status, parsed, ); } const obj = parsed as Record<string, unknown>; const id = typeof obj.id === "string" ? obj.id : taskId; const status = typeof obj.status === "string" ? obj.status : "unknown"; // Different SDK versions surface results in slightly different places; be // lenient and probe the most common shapes. const content = (obj.content as Record<string, unknown> | undefined) ?? (obj.result as Record<string, unknown> | undefined) ?? {}; const videoUrl = pickString( content.video_url, content.videoUrl, obj.video_url, obj.videoUrl, ); const lastFrameUrl = pickString( content.last_frame_url, content.lastFrameUrl, obj.last_frame_url, obj.lastFrameUrl, ); const failReason = pickString( obj.fail_reason, obj.failReason, obj.error_message, (obj.error as Record<string, unknown> | undefined)?.message, ); return { id, status, videoUrl, lastFrameUrl, failReason, raw: obj, }; } - src/schema.ts:132-140 (schema)Schema definition for the check_task input — requires a non-empty task_id string
export const checkTaskInputShape = { task_id: z .string() .min(1, "task_id cannot be empty") .describe("Seedance task id returned by seedance_create_task (e.g. cgt-...)."), }; export const checkTaskInputSchema = z.object(checkTaskInputShape); export type CheckTaskInput = z.infer<typeof checkTaskInputSchema>;