Tool reference · 3D Models
Optimize a 3D model
Rebuild a textured model at the triangle count you choose, with new textures that keep its look.
- MCP tool
- optimize_3d_model
- MCP endpoint
- /mcp/3d
- REST
- POST /v1/tools/optimize_3d_model/jobs
- Price
- 60 credits per job
Overview
Send a textured GLB and a triangle count between 1,000 and 200,000. The model is rebuilt at that count and gets new base-colour, metal/roughness and normal maps, tuned until it renders like the original from every side. Where simplification has to keep the original textures and stops at a quarter of the triangles or so, this goes all the way down. The result keeps the input's coordinates and scale. Start the job, keep its id and poll: it takes a few minutes.
- Formats
- .glb (self-contained, uncompressed, one material)
- Outputs
- model.glb (three 2048² or 1024² textures) and generation.json
- Targets
- 1,000 to 200,000 triangles; inputs up to 2,000,000
- 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/optimize_3d_model/jobs?filename=model.glb&target_faces=15000&seed=42" \
-H "Authorization: Bearer $MCPBYTES_API_KEY" \
-H "Content-Type: application/octet-stream" \
-H "Idempotency-Key: optimize-example-001" \
--data-binary @model.glb{
"tool": "optimize_3d_model",
"status": "queued",
"input_kind": "file",
"workflow": {
"stage": "Optimizing",
"deadline": "2026-09-22T22:23:09.206Z",
"cancel_pending": false,
"refund_pending": false
},
"capabilities": {
"can_cancel": true
},
"options": {
"target_faces": 15000,
"texture_resolution": 2048,
"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 the model from result.files[].url
curl -fsSLo model.glb "$MODEL_URL"{
"tool": "optimize_3d_model",
"status": "succeeded",
"result": {
"files": [
{
"name": "model.glb",
"bytes": 4143860,
"content_type": "model/gltf-binary",
"url": "https://api.mcpbytes.com/blob/dl/…"
},
{
"name": "generation.json",
"bytes": 2218,
"content_type": "application/json",
"url": "https://api.mcpbytes.com/blob/dl/…"
}
],
"notes": []
},
"compute_ms": 278600,
"credits_charged": 60
}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/optimize_3d_model/jobs \
-H "Authorization: Bearer $MCPBYTES_API_KEY" \
-H "Content-Type: application/json" \
-d "{
\"upload_id\": \"$UPLOAD_ID\",
\"options\": {
\"target_faces\": 15000
},
\"idempotency_key\": \"optimize_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.
- target_faces
1,000 … 200,000Target triangles after reduction. Inputs already at or below the target keep their geometry. Default 50,000.- texture_resolution
1024 · 2048Width and height of the new textures in pixels. Default 2048.- seed
integer 0 – 4294967295Reproducible optimisation seed. Random when omitted.
Outputs
A succeeded job lists its files in result.files, each with a download url.
- model.glb
- Self-contained glTF binary at the requested triangle count: one mesh with normals and tangents, base colour, metal/roughness and normal textures.
- generation.json
- Provenance: your options, the input and output triangle counts, the texture layout and timings.
Download links last up to 24 hours, capped by output retention; read the job again for fresh links. Fine print and details thinner than a texel can soften in the new textures.
Tips
- target_faces is a reduction request: an input already at or below it keeps its geometry and gets new textures. Read the achieved count in generation.json.
- Pass model.glb of a create_3d_model or create_3d_model_from_image job as job_file, with no download in between.
- Fine print and details thinner than a texel can soften in the new textures.
- Export one static, textured mesh. Skinned, animated or morph-target models, several materials and vertex colours without a texture are refused.
- A job takes a few minutes: keep its id and poll. Repeating the request with the same idempotency key returns the same job instead of a new charge.
MCP
Your agent calls optimize_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, Simplify 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
- optimize_3d_model
- Rebuilds a textured GLB at any triangle count from 1,000 to 200,000, for phones, AR, the web and games, with new base-colour, metallic-roughness and normal maps so it still looks like the original from every side. Keeps the input's coordinates and scale. Accepts one self-contained, uncompressed GLB with a single material, up to 2,000,000 triangles. Returns model.glb (textures embedded) and generation.json. This is an asynchronous job of a few minutes; use get_job to follow progress. A failed job is refunded. Costs 60 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/optimize_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:
python scripts/mcpbytes.py run optimize_3d_model model.glb \
-o target_faces=15000 --out optimized/
# Or an earlier job's output, without downloading it
python scripts/mcpbytes.py run optimize_3d_model \
--job-file j_... model.glb -o target_faces=15000 --no-wait
python scripts/mcpbytes.py job j_...Pricing
60 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
- 60 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 2,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, or no triangles.
- limit_exceeded
- More than 2,000,000 triangles.
- too_large
- HTTP 413 at creation: the file is larger than your plan's upload size.
- unsupported_feature
- Skinned, animated or morph-target models, several materials, or vertex colours without a texture; export one static textured mesh.
- timeout
- The rebuild 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.