# Customuse MCP

> Generate and iterate on images, videos, 3D meshes, textures, rigs and Roblox-ready assets through the Customuse MCP server (node-editor workflows, CustoCoins credits). Use when: "generate an image/video", "make a 3D model of this", "turn this image into a mesh", "make a game asset / prop / character reference", "retexture this model", "rig this mesh", "decimate / UV-unwrap this mesh", "make a Roblox accessory or clothing item", "how many Customuse credits do I have", "open my Customuse workflow", "extract assets from this image", or the user names Customuse, customuse.com, CustoCoins, the nodes editor, or a model hosted there (Nano Banana, FLUX, GPT Image, Kling, Seedance, Tripo, Meshy, Hunyuan 3D, Hi3D). NOT for: local/offline image or mesh editing (use file tools, Blender, ffmpeg), inspecting an already-downloaded asset, non-Customuse generation APIs, or Unreal/engine-side import work — Customuse ends at the produced asset URL.

- Skill: `djstarcom/customuse-mcp` (Agent Skill, multi-file: 5 files)
- Install (CLI): `npx skillmds@latest add djstarcom/customuse-mcp`
- Raw SKILL.md: https://api.skillmd.com/api/skills/djstarcom/customuse-mcp/raw
- Safety review: pending (external: skill-scanner PASS, skillspector PASS)
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: DJStarCOM (https://skillmd.com/u/djstarcom)
- Updated: 2026-09-22
- Page: https://skillmd.com/skills/djstarcom/customuse-mcp

---


<!-- Keep this file under ~130 lines. If a section would not change what the agent does next, move it to references/. -->

# Customuse MCP

Customuse is an async, credit-based platform. Every result is a **node on a node-editor workflow** (a graph at `https://customuse.com/workflow/<id>`) the user can reopen, tweak and re-run. 16 MCP tools; the real parameter space lives in the node-type catalog, not in the tool schemas.

## Completion contract

Done means: the requested asset exists (`resultUrl` / `meshUrl`), it is handed to the user together with `canvasUrl`, the credit cost is stated, and anything the user asked to be saved locally is on disk. A `status: "started"` response, a created node, or an applied graph edit is **not** completion. Never end a turn while a node is still `generating`.

## Read what this request needs

| Need | Reference |
|---|---|
| Tool parameters, response fields, error → recovery | [tools.md](references/tools.md) |
| Which node type; exact enum, range, handle, precondition | [node-types.md](references/node-types.md) |
| Multi-node pipeline payloads, resume/repair of a broken graph | [recipes.md](references/recipes.md) |
| Writing prompts for generate vs edit vs video motion | [prompting.md](references/prompting.md) |

Read the one that applies. Recipes are alternatives to adapt to the request, not a chain to execute end to end.

## Preflight

1. Tools appear as `mcp__customuse__<tool>`. If missing, the server is not registered for this project directory — tell the user to run there: `claude mcp add --transport http customuse https://api.customuse.com/v1/mcp --header "Authorization: Bearer <cmk_live_…>"`. MCP scope is per project path; a worktree of a registered project does not inherit it.
2. **The catalog is a mapping, not the database.** `references/node-types.md` is a dated snapshot; `customuse_list_node_types` is authoritative and account-gated. On any conflict the tool wins. If the user says a model or node type exists and your memory disagrees, call the tool unfiltered before telling them it doesn't.
3. Check `customuse_get_credits_balance` before any 3D, video, cascade or autorun run.

## Money rules

- Balance is **not** a price. `customuse_get_credits_balance` returns what is left, never what a run costs.
- Measured `chargedCredits` 2026-09-20 — the server's "image 10–40 / 3D 55–150" wording is wrong. **Images:** `flux2Image` = 2 · `gptImage2` 1K, `flux2ProImage`, `fluxImg2Img`, the four presets = 4 · `gemini25FlashImage`/`Edit`, `fluxKontextPro`, `flux2ProEdit`, `imageOptimize` = 6 · `nanoBananaLiteImage`/`Edit`, `twoDTextures` = 7 · `gptImage2Edit` = 8 · `nanoBanana2Image`, `imageVariations` ×2 = 11 · `nanoBanana2Edit` = 16 (2K default) · `clarityUpscaler` ×2 = 17 · `gemini3ProImage`/`Edit` 1K = 20 · `assetExtractor` = 21 (on its child) · `imageToMultiview` = 27 · `bgRemoval` = 0. **3D:** mesh-post stages = 2 each · `autoRigging` = 33 · `textureGeneration` 1K = 40 · `hi3dV21Mesh3d` = 65 · `modelTextureGeneration` = 70 · `tripoV31Mesh3d` / `tripoP1Mesh3d` / `customuseCr1Mesh3d` / `meshyV7Mesh3d` / `hunyuanV31Mesh3d` / `roblox3d` = 100 · `tripoP2Mesh3d` = 130 · `hi3dV3Mesh3d` = 234. **Video:** `seedance20Video` 4 s/480p = 37 · `kling3Video` 3 s/pro = 44 (1920×1080) · `seedance25Video` 4 s/480p = 54 — price tracks duration × resolution, so "premium" Kling is the *cheaper* option at short lengths. Uploads and graph edits without a run are free; prices are versioned (`pricingVersion`) — quote them with the date.
- Exact cost per node = `customuse_get_workflow {includeData: true}` → `nodes[].data.chargedCredits` (`reimbursedCredits` after a failure). The field is per node type: present on `imageToMultiview`, **absent on `imageVariations` and its children** though the run was billed, and `customuse_duplicate_workflow` copies it verbatim — fall back to the balance delta there, and never sum across a workflow and its copy. Never report 0 for a node you did not check.
- Work stage by stage inside the scope the user approved. Ask again only before a **new kind** of charge: first video, first 3D, a cascade over several paid nodes, or an autorun budget.
- **`budgetCredits` on `customuse_autorun_workflow` is a soft cap, checked after a cycle.** Measured: budget 10 → `spentCredits: 44`, `status: "budget_exhausted"`, while the graph held one node worth 7 credits; the rest was the agent's own reasoning, billed on top. Assume a 4–5× overshoot on small budgets, quote that to the user before starting, and prefer building the graph yourself when the balance is tight.
- **Never resubmit a paid job.** A polling gap, a tool timeout or a lost response does not cancel or lose the remote run: re-poll `customuse_get_workflow` and match by node type + prompt. A second `customuse_generate` is a second charge.
- **Reuse before regenerate.** "Make a low-poly version of that" means find the existing node and run only the missing step. Lookup order: this conversation → `customuse_get_workflow` on the session workflow → `customuse_list_workflows` → ask, listing the candidates. Stop at the first confident match. Regenerating something that already exists is both a wrong answer and a second charge.

## Core loop

```
customuse_list_node_types                    # once per session, or when a type id is rejected
customuse_generate {nodeType, prompt, data}   # omit workflowId on the FIRST call of the session
  → {workflowId, nodeId, runId, status:"started", canvasUrl}
customuse_get_workflow {workflowId}           # poll until terminal
  → nodes[].status: idle | generating | complete | error   (in flight is `generating`, not `running`)
  → resultUrl (image/video), meshUrl (3D — `resultUrl` repeats it), previewUrl (often null), error
```

- Poll schedule: first check at ~3 s, back off ×1.5, cap at 15 s for images and 30 s for video/3D. Stop only on `complete` or `error`; anything else means still running. Measured: image ~15 s, image→mesh cascade ~2 min end to end.
- **One workflow per conversation.** Omit `workflowId` on the first generation, reuse the returned id for everything after. Pass an explicit id only to extend a workflow from `customuse_list_workflows`.
- `data` merges shallowly onto the type's `defaultData` — pass only what changes. Same for `update_nodes[].data_patch`.
- Tool results are a prose line plus a JSON blob: parse the JSON, don't read the prose. Echoed `position` is advisory — the server re-lays out nodes.
- **Check the run receipt before polling.** `customuse_run_nodes` answers `{runId, tracked_node_ids, skipped_node_ids, status}` and returns `status: "noop"` with `runId: null` — *without* `isError` — when the ids were unknown. `status: "started"` with a non-empty `tracked_node_ids` is the only green light; in `cascade` mode downstream nodes are absent from that list, so poll the graph.
- Values are validated by the provider at run time, not by the tool: an out-of-enum `data` value is accepted, stored, and comes back as a node `error` with the list of legal values (refunded). Prompts shorter than 3 characters fail the same way.
- **A node in `error` never re-runs.** `customuse_run_nodes` answers `started` but nothing executes — same `generationRunId`, same error, no charge — even after an `update_nodes` `data_patch`. Delete the node and create a corrected copy instead. Nodes that merely never ran (`idle`) do re-run normally.

## Choosing the approach

| Situation | Path | Never |
|---|---|---|
| One generation (text→image, edit, image→3D, video, Roblox asset) | `customuse_generate` | — |
| Chain of 2+ nodes | `customuse_edit_workflow_graph` + `run_node_ids` + `run_mode: "cascade"` | three separate `customuse_generate` calls |
| Mesh already exists, user wants texture/LOD/UV/rig | add the post-process node, connect `mesh-out → mesh-in`, run it | re-running image→3D |
| Some nodes failed mid-graph | `customuse_run_nodes` on the failed nodes + `cascade` | re-running the whole graph |
| Hands-off "just make me X", budget-capped | `customuse_autorun_workflow` → poll `customuse_get_autorun` | spending the whole balance as budget |
| User supplied a reference image | `customuse_upload_media`, then connect `image-out` | inventing a URL or a `mediaId` |

## Building graphs

`customuse_edit_workflow_graph` applies `create_nodes` + `connect_nodes` + `update_nodes` + `disconnect_nodes` + `delete_node_ids` in one transaction, optionally followed by a run.

- Reference new nodes by a caller-chosen `client_id`; the response maps each to its real id. `connect_nodes` and `run_node_ids` accept either form.
- Handles are exact catalog ids: `image-out → image-in`, `mesh-out → mesh-in`, `prompt-in`, `texturePrompt-in`, `start-image-in` / `end-image-in` (video), `front|left|right|back-image-in` (multiview), `target-mesh-in` / `source-mesh-in` (bake), `mesh-1-in … mesh-10-in` (UV packing).
- Fan-out nodes spawn children when the run starts, already wired parent `image-out` → child `image-in`; the parent's own `resultUrl` is `null` or just echoes the source. Child types differ per parent: `imageVariations` → `imageVariationResult` (ids in `data.outputNodeIds`), `imageToMultiview` → `multiviewResult` (identify by `data.viewLabel`), `assetExtractor` → a single `assetExtractorPreview` (id in `data.previewNodeId`). **A `complete` parent does not mean the results are ready**: `assetExtractor` completes in ~5 s with only its analysis (`detectedParts`), while the child keeps generating for another ~45 s and carries the charge. Always poll the children, via `customuse_get_workflow {workflowId, nodeIds:["<parent>"], includeData: true}`.
- Wiring a fan-out child into a downstream node is a second, free edit call.
- Unknown types, bad handles, cycles and out-of-plan types are rejected as `invalid_request` before anything is charged — fix the shape locally rather than retrying blind.

## Reference images

`customuse_upload_media` takes exactly one of `url` (public http(s)), `base64` (data URI or raw + `mimeType`), or `mediaId` from `customuse_list_my_media`. It costs nothing and returns `{nodeId, imageUrl, workflowId, canvasUrl}`; the created `imageUpload` node feeds edit/3D/video nodes from `image-out` and stays `idle` with `resultUrl: null` by design. The `url` route depends on Customuse's fetcher reaching that host — measured: `upload.wikimedia.org` returns `internal_error` twice, the same bytes as `base64` succeed, so on `internal_error` switch route instead of retrying the URL. `mediaId` covers only images the user added on customuse.com: MCP uploads do **not** appear in My Media (measured), so reuse one by connecting its existing `imageUpload` node or re-passing its `imageUrl`.

**Look at the reference before spending on it.** Download it and open it; never trust a filename, URL slug, page title or the user's description of what is in it. A file called `Toolbox.jpg` turned out to be a night shot of a house, and the variations faithfully restyled that house — 11 credits for garbage. If the image cannot be opened, say so and ask rather than wiring it into a paid node. The same applies to anything routed into a 3D node: the reference decides the result, so a wrong or cluttered reference is a wasted mesh charge.

## Picking a node type

Ordered defaults — when two could apply, the higher entry wins. Do not downgrade to a model because its parameters look simpler; verify the better one first.

1. Text → image: `gptImage2` (4 credits, cleanest subject separation and text) or `flux2ProImage` (4) for drafts and layouts; `nanoBanana2Image` (11) when photo-realism matters most; `gemini3ProImage` for maximum fidelity; `nanoBananaLiteImage` (7) only when an even cheaper draft is wanted.
2. Game-asset concept art with a free alpha background: `gameAsset` / `vehicle` / `character` / `clothingReference` (4 each). They **rewrite the prompt** into a "3D stylized game X, 3/4 front angle, transparent background" template, so camera and background wording is ignored — pick a plain model instead when those matter.
3. Edit an existing image (needs `image-in`): `nanoBanana2Edit`; `gptImage2Edit` for text/typography; `fluxKontextPro` for style transfer. Several takes of one image: `imageVariations`.
4. Prepare an image for 3D: `imageOptimize` (6; white bg, centered, A/T-pose). It is a **pass-through**: it completes with `resultUrl: null` and hands the corrected image to whatever pulls its `image-out`, so there is nothing to show the user from it. Then `bgRemoval` (free) or `clarityUpscaler` (17, 1024→2048) only if still needed. Back-side accuracy: `imageToMultiview` (27) before a multiview mesher.
5. Image → **textured** mesh in one node: `modelTextureGeneration` (Customuse CR1) — 70 credits, ~3.5 min, PBR maps in `textureMaps` (albedo/normal/orm). Cheaper and simpler than `tripoV31Mesh3d` + `textureGeneration` (100 + 40).
6. Image → geometry only (all untextured, measured at `faceLimit: 12000`): `meshyV7Mesh3d` (100) or `tripoP2Mesh3d` (130) keep stamped relief on hard-surface props · `tripoV31Mesh3d` / `tripoP1Mesh3d` / `customuseCr1Mesh3d` (100) are smoother and flatten fine detail · `hunyuanV31Mesh3d` (100) gives the cleanest surface at an exact 40 000 tris · `hi3dV21Mesh3d` (65) is cheapest but ignores the budget (676 k tris) and `hi3dV3Mesh3d` (234) is the density option. **Hi3D requires `faceLimit` ≥ 100 000**; Hi3D and Hunyuan ship POSITION only, so add `meshCleanup` (`recalculateNormals`) + `uvUnwrap` before texturing. `quad: true` never survives the GLB export.
7. Mesh post (2 credits each unless noted): `meshCleanup`, `meshDecimation` (LOD), `uvUnwrap`, `uvPacking`, `textureBaking` (`bakeChannel` is an **array**: `all|base-color|normal|roughness|specular|metalness`), `textureGeneration` (retexture, 40), `autoRigging` (33 — the provider accepts only `aquatic|avian|biped|hexapod|octopod|quadruped|serpentine`). Tileable PBR maps from a photo: `twoDTextures` (7) returns `baseColorUrl` / `normalUrl` / `roughnessUrl` / `metalnessUrl`.
8. Roblox deliverable: `roblox3d` with the matching `generationType` (100, ~5 min, comes out textured) — not a generic mesh node.
9. Video (all three verified): `seedance20Video` (4–15 s, `480p|720p|1080p|4k`) = 37 for 4 s @480p · `kling3Video` (3–15 s, `pro|4k`) = 44 for 3 s @pro and delivers full 1920×1080 · `seedance25Video` (4–30 s, `mov` option) = 54 for 4 s @480p. Match `videoInputMode` to the wiring and set `generateAudio: false` unless sound is wanted — it defaults to **true** on both Seedance nodes. Video nodes expose a poster frame in `data.mediaArtifacts.posterUrl` / `thumbnailUrl`, so a still can be shown without downloading the clip.
10. Break one object into parts: `assetExtractor` (21) — see the fan-out note below; it decomposes a single subject, it does not split a multi-object image.
11. Character/NPC model end to end: `character` → `imageOptimize` (`aPose`) → `modelTextureGeneration` → `autoRigging` `biped`. Measured 113 credits, ~6.5 min, 18 986 tris, 58 joints. The **rigged GLB loses the textures** — hand over the textured mesh and the rigged file together. See [recipes.md](references/recipes.md) recipe 12.

## Delivery

- Hand over: the asset URL, `canvasUrl`, the workflow id and node ids (they make the next request cheap), the credit cost, and one concrete limitation.
- Save locally only what the user asked for, or when they are clearly working in a repo: `./customuse/<workflow-slug>/<nodeType>-<first8-of-nodeId>.<ext>`. Never overwrite an existing file; verify the download is non-empty and of the expected type.
- Open results before describing them. Images: open `resultUrl`. 3D: the top-level `previewUrl` is usually `null`, but `customuse_get_workflow {includeData: true}` exposes `data.thumbnailUrl` — a real render (verified) — so open that; check `textureMaps` / `isPBR` for texture presence, and parse the GLB (magic `glTF`, triangle/vertex counts) only for numbers a render cannot show. A render establishes subject, composition and completeness, never topology, watertightness, UV or rig quality. Never imply you inspected something you did not open.
- Keep chat clean: polling is silent (a status change is worth a line, individual polls are not), no raw node/run ids, no tool names, no JSON dumps unless the user asks.

## Errors

`isError: true` responses carry `structuredContent.error.code`: `invalid_request` (bad node type, non-existent handle — the message lists the valid ones, graph cycle, two upload sources), `not_found` (well-formed but unknown workflow or autorun session), `not_enough_credits` (stop, tell the user to top up at customuse.com — never retry in a loop), `internal_error` (server-side; for an upload switch route, otherwise one retry then report). Nothing is charged for any of them. Malformed arguments fail earlier still, as `MCP error -32602: Input validation error` with no code. A node ending `status: "error"` is not a tool error: it is refunded automatically (`reimbursedCredits`); read `nodes[].error` — provider messages name the offending field — before retrying with different inputs. Full table: [tools.md](references/tools.md).

`customuse_delete_workflow` is irreversible (assets already in My Assets survive). Never delete a workflow the user did not name.

