Tool reference · 3D Models
Animate a 3D model
Add one animation clip, generated from a description, to a rigged humanoid model, with motion checks and a preview.
- MCP tool
- animate_3d_model
- MCP endpoint
- /mcp/3d
- REST
- POST /v1/tools/animate_3d_model/jobs
- Price
- 20 credits per job
Overview
Send a rigged humanoid GLB (one skeleton with two legs, two arms and a head) and a description of a motion. The motion is generated for a standard human body and transferred onto your skeleton: bone roles are found from the skeleton's shape, the character's own facing and proportions are kept, and feet that touch the ground are kept planted and on the floor. The file comes back unchanged except for one added animation, 1 to 10 seconds at 30 frames per second; earlier animations are replaced. motion.json lists the bone roles, facing, travel and foot contacts; motion_report.json gives the status (passed, needs_review or failed_checks) and each issue with its frames; motion_preview.png shows six moments from the front and the side. A succeeded job means the motion was written, not that it looks right. Start the job, keep its id and poll: it takes a few minutes.
- Formats
- .glb with one skeleton: two legs, two arms and a head (a rig from Rig a 3D model, or from any other tool)
- Outputs
- model.glb (the same model with one animation), motion.json, motion_report.json, motion_preview.png and generation.json
- Motion
- 1 to 10 seconds at 30 frames per second, travelling or in place
- Checks
- Planted feet sliding or floating, the model going through the floor, sudden bone turns
- Duration
- Asynchronous; one-hour deadline including queueing and execution
Quickstart
Send the file as the request body with the options in the query string, or refer to it in a JSON body (upload_id or url) with an options object. The response is the new job; poll it until its status is final, then download the files in result.files.
# idempotency_key is required: repeating it returns the same job
curl https://api.mcpbytes.com/v1/tools/animate_3d_model/jobs \
-H "Authorization: Bearer $MCPBYTES_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"job_file": {"job_id": "j_...", "name": "model.glb"},
"options": {
"description": "A person walks forward confidently.",
"duration": 5,
"seed": 42
},
"idempotency_key": "animate-example-001"
}'{
"tool": "animate_3d_model",
"status": "queued",
"input_kind": "file",
"workflow": {
"stage": "Animating",
"deadline": "2026-09-27T23:21:27.662Z",
"cancel_pending": false,
"refund_pending": false
},
"capabilities": {
"can_cancel": true
},
"options": {
"description": "A person walks forward confidently.",
"duration": 5,
"in_place": false,
"seed": 42
},
"result": null
}# JOB_ID is the id from the create response; poll until status is final
curl "https://api.mcpbytes.com/v1/jobs/$JOB_ID" \
-H "Authorization: Bearer $MCPBYTES_API_KEY"
# Once status is succeeded, download each file from result.files[].url
curl -fsSLo model.glb "$MODEL_URL"{
"tool": "animate_3d_model",
"status": "succeeded",
"result": {
"notes": [],
"duration": 5,
"frames": 150,
"status": "passed",
"issues": [],
"files": [
{
"name": "model.glb",
"bytes": 15060048,
"content_type": "model/gltf-binary",
"url": "https://api.mcpbytes.com/blob/dl/…"
},
{
"name": "motion.json",
"bytes": 3168,
"content_type": "application/json",
"url": "https://api.mcpbytes.com/blob/dl/…"
},
{
"name": "motion_report.json",
"bytes": 580,
"content_type": "application/json",
"url": "https://api.mcpbytes.com/blob/dl/…"
},
{
"name": "motion_preview.png",
"bytes": 252926,
"content_type": "image/png",
"url": "https://api.mcpbytes.com/blob/dl/…"
},
{
"name": "generation.json",
"bytes": 2323,
"content_type": "application/json",
"url": "https://api.mcpbytes.com/blob/dl/…"
}
]
},
"compute_ms": 27551,
"credits_charged": 20
}Jobs of this tool take minutes. While one runs, workflow.stage says what is happening and workflow.deadline when it must finish. Cancel it with POST /v1/jobs/{id}/cancel while capabilities.can_cancel is true.
More on statuses, downloads and retention in Jobs & files.
Input
One file per job, in one of these ways. The formats it accepts are listed in the overview.
- File body
- REST only: the file itself as the request body, with ?filename= and the options in the query string.
- upload_id
- Create an upload URL (POST /v1/uploads, or create_upload over MCP), PUT the file to it, then pass the upload_id. Upload URLs expire after an hour.
- url, filename
- A public https URL. Add filename when the URL does not end in the file's extension.
- job_file
- {job_id, name}: an output of one of your own completed jobs that has not expired. It is copied into the new job, with no download in between.
# 1. Create an upload URL (valid for one hour)
curl https://api.mcpbytes.com/v1/uploads \
-H "Authorization: Bearer $MCPBYTES_API_KEY" \
-H "Content-Type: application/json" \
-d '{"filename": "model.glb"}'
# 2. PUT the file to the "url" from the response
curl -X PUT --data-binary @model.glb "$UPLOAD_URL"
# 3. Start the job with the "upload_id"
curl https://api.mcpbytes.com/v1/tools/animate_3d_model/jobs \
-H "Authorization: Bearer $MCPBYTES_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"upload_id\": \"$UPLOAD_ID\",
\"options\": {
\"description\": \"A person walks forward confidently.\",
\"duration\": 5
},
\"idempotency_key\": \"animate_3d_model-upload-001\"
}"idempotency_key is required. Repeating a request with the same key returns the original job instead of starting and charging a new one; reusing a key for a different request returns 409 idempotency_conflict. With a file body, send the key as an Idempotency-Key header.Options
Put options in the query string when the file is the request body, in the options object of a JSON body, or pass them as arguments of the MCP tool. Leave an option out to use its default.
- description
text, 3 to 500 charactersThe motion, best as "A person…" with one or two actions. Required; kept out of generation.json.- duration
1 – 10 secondsLength of the clip. Default 4.- in_place
true · falseKeep the character on the spot instead of travelling. Default false.- seed
integer 0 – 4294967295Reproducible within the same tested runtime; another seed gives another take. Random when omitted.
Outputs
A succeeded job lists its files in result.files, each with a download url.
- model.glb
- The input model with one added animation named "animation": keys at 30 frames per second on the skeleton's rotations and the hips' position. Mesh, textures and skeleton are unchanged; earlier animations are replaced.
- motion.json
- What was made and how it maps onto the rig: frames, fps, duration, in_place, the model's facing, the hips' travel, each role's bones, and each foot's contacts (heel or toes, frames).
- motion_report.json
- status, the thresholds used, the measurements (per foot: contacts, largest slide and float; floor depth; largest bone turn per frame and its change) and the issues, each with frames, value, threshold and severity.
- motion_preview.png
- 1536 × 512 image: six moments of the animated model from its own front and its own side, with the skeleton drawn over it.
- generation.json
- Provenance: your options except the description, and the animation step's version, summary and timings.
Download links last up to 24 hours, capped by output retention; read the job again for fresh links. A succeeded job means the motion was written; whether it looks right is in motion_report.json and motion_preview.png.
Result summary
Next to files, the job's result has a summary you can check without downloading anything:
- duration, frames
- Length of the clip in seconds, and its frames at 30 per second.
- status
- The report's verdict: passed, needs_review or failed_checks.
- issues
- The report's first five issues: type, side or bone, frames, value, threshold, severity.
Tips
- Describe the motion as "A person…" with one or two actions: "A person walks forward confidently", "A person waves with their right hand". Everyday movement, gestures, dancing and fighting work best.
- Send the description in the JSON body or as MCP arguments, never in a URL.
- Pass model.glb of a rig_3d_model job as job_file. A model rigged elsewhere works too, as long as its skeleton has two legs, two arms and a head.
- Read motion_report.json and look at motion_preview.png before using the clip. If it fails its checks, try another seed or wording; each job is charged.
- in_place keeps the character on the spot, for a clip your engine moves itself; otherwise it travels as it walks or runs.
- Motions on the ground (sitting, kneeling) can push a tail or long clothes through the floor; the report says so.
MCP
Your agent calls animate_3d_model. Connect it to https://api.mcpbytes.com/mcp/3d for this tool and the rest of its family (Create a 3D model, Create a 3D model from an image, Optimize a 3D model, Simplify a 3D model, Rig a 3D model, Split a 3D model into parts, Inspect a 3D model), or to https://api.mcpbytes.com/mcp for every tool.
Claude CodeOAuth or API key
The first form signs in through the browser when you run /mcp. In .mcp.json (project root), ${MCPBYTES_API_KEY} is read from the environment when Claude Code connects. Claude Code docs
claude mcp add --transport http mcpbytes \
https://api.mcpbytes.com/mcp/3dCodex (CLI, IDE extension, ChatGPT desktop)OAuth or API key
Codex reads the key from the environment variable each time it connects. The same table can go in ~/.codex/config.toml by hand; codex mcp add has no --header flag. Codex docs
codex mcp add mcpbytes --url https://api.mcpbytes.com/mcp/3d \
--bearer-token-env-var MCPBYTES_API_KEYCursorOAuth or API key
~/.cursor/mcp.json (all projects) or .cursor/mcp.json (one project). Remote servers take no "type". Without the header, the client signs you in with GitHub or an email link (OAuth). Cursor docs
{
"mcpServers": {
"mcpbytes": {
"url": "https://api.mcpbytes.com/mcp/3d",
"headers": {
"Authorization": "Bearer ${env:MCPBYTES_API_KEY}"
}
}
}
}Arguments
The tool's options are flat arguments. Besides them:
- upload_id
- From create_upload, after the agent has PUT the file.
- url, filename
- A public https URL, and the file name when the URL does not end in the extension.
- job_file
- {job_id, name}: an owned output of an earlier job.
- wait_seconds
0–45, default 0How long the call waits for the result. If the job is still running, the agent calls get_job.- idempotency_key
RequiredReuse it with the same arguments when retrying after a network error.
What the agent sees
- animate_3d_model
- Animates a rigged humanoid GLB (two legs, two arms and a head, e.g. model.glb of a rig_3d_model job) from a description of a motion such as "A person walks forward confidently". Adds one animation clip, up to 10 seconds at 30 frames per second, to the model's own file: its mesh, textures and skeleton are unchanged. Planted feet stay where they land and on the floor. Accepts one self-contained, uncompressed GLB with one skeleton, up to 1,000,000 triangles. Returns model.glb (animated), motion.json (bone roles, facing, travel, foot contacts), motion_report.json (foot sliding, floating, floor penetration and rotation pops; status passed, needs_review or failed_checks) and motion_preview.png. Works best on everyday movement, gestures, dancing and combat, described as "A person…". A succeeded job means the motion was written, not that it looks right: read motion_report.json and look at motion_preview.png. This is an asynchronous job; use get_job to follow progress. A failed job is refunded. Costs 20 credits per job. Requires idempotency_key; retain it when retrying the same request.
- create_upload
- Returns a one-hour URL to PUT a local file to; then call the tool with the upload_id.
- get_job
- Status and results of a job; waits up to wait_seconds for it to finish.
- cancel_job
- Cancel a queued job or a workflow whose can_cancel is true; repeated calls never delete results.
- list_jobs
- Your most recent jobs, newest first.
- read_job_file
- Reads a text output (.txt .json .md .csv) of a succeeded job in chunks.
Setup for every client, and how agents upload files and read results: Connect over MCP.
Agent skill
The MCPBytes skill teaches agents this tool, in references/animate_3d_model.md. Agents without MCP run it through the REST API with the skill's script, which reads your key from MCPBYTES_API_KEY:
printf '{"description": "A person waves with their right hand.",
"duration": 4}' > options.json
python scripts/mcpbytes.py run animate_3d_model \
--job-file j_... model.glb \
--options-json options.json --out animated/Pricing
20 credits per job. The price is charged before paid processing starts, and a canceled job is refunded. A job that fails costs nothing: its charge is refunded.
- Per job
- 20 credits
One credit balance pays for every tool, and a new account starts with free credits. See Credits & prices or buy credits.
Limits
- Jobs
- 20 per rolling 24 hours; 3 until your first credit purchase
- Model
- Up to 50 MB and 1,000,000 triangles
- At a time
- 1 job
- Uploads
- Up to 50 MB
- Results
- 24 hours by default
GET /v1/me returns your limits and current usage; Limits & errors explains what happens when one is reached.
Errors
A failed job comes back with error.code, error.message and often error.hint. Codes are stable. Inputs that fail with a validation error will fail again unchanged.
- invalid_mesh
- Not a readable, self-contained GLB.
- limit_exceeded
- More than 1,000,000 triangles.
- too_large
- HTTP 413 at creation: the file is larger than your plan's upload size.
- unsupported_rig
- No skeleton, several skeletons, over 256 joints, or no humanoid found (two legs, two arms and a head). Refused before any charge.
- inference_failed
- The motion could not be made. Try again with another seed or wording. Any charge is refunded.
- timeout
- The job did not finish within its deadline. Any charge is refunded.
- capacity_exhausted
- Budget exhausted: HTTP 503 at creation, or a failed job if the budget ran out before charging. Try again later.
- unsupported_format
- The tool does not take this file type.
- insufficient_credits
- The job costs more than the balance, which is known once the input has been measured. Nothing was charged.
- out_of_memory
- The file needs more memory than a job has.
Requests can also fail before a job starts, with an HTTP status: see HTTP errors.