---
name: dgen-visual-studio
description: Make images and video with dGEN Visual Studio over MCP. Covers which model to pick, how to prompt it, working from reference images, multi-shot sequences, what things cost, and what this platform cannot do. Use whenever someone asks for a picture, a cover image, a product shot, a clip, a storyboard, or an edit of an image they already have.
---

# dGEN Visual Studio

An image and video generator you drive over MCP. One balance of credits covers every model in
it: Nano Banana, Seedream, GPT Image, FLUX, Grok Imagine, Veo, Kling, Seedance, MiniMax, Wan
and more.

Everything you do here spends the user's money. The two rules at the bottom are not optional.

## The one thing to understand first

**A model is a model, not an endpoint.** Providers publish the same model at several
addresses: one that takes only a prompt, one that also reads reference images, one that
starts from a first frame.

You do not have to know which is which. Send the prompt **and** the images in a single call.
The studio routes to the address that accepts both and reports it in `switched_ingress`.
Prompt and references are one input, never two steps.

## The shortest useful path

```text
list_models({ kind: "image" })          -> pick an id, read its promptGuidance
generate_image({ prompt, model })       -> { execution_id, credits_charged, status: "running" }
get_execution_status({ execution_id })  -> poll until status "completed"
                                        -> { assets: [{ id, url }] }
```

Fetch that `url` and you have the file. An image takes seconds, a video minutes.

## The tools

Read-only, free, call them as much as you like:

| Tool | Takes | Gives you |
| --- | --- | --- |
| `list_models` | `{ kind?: "image" \| "video" \| "edit" }` | id, label, `inputs`, `accepts`, `credits`, `strongAt`, `weakAt`, `promptStyle`, `promptGuidance`, `promptExample`, `supportsLora` |
| `list_presets` | `{ soort?: "image" \| "video" }` | preset ids with the exact phrasing each one adds |
| `list_assets` | `{ limit?, type? }` | the user's files: `{ id, type, created_at }` |
| `get_asset` | `{ asset_id }` | one file with a fetchable `url` |
| `list_loras` | `{}` | the user's own trained styles, with `id` and `base_model` |
| `list_templates` | `{}` | ready-made canvas setups |
| `list_flows` / `get_flow` | `{}` / `{ flow_id }` | saved pipelines and their contents |
| `get_credits_balance` | `{}` | `{ credits }` |
| `get_execution_status` | `{ execution_id }` | `{ status, creditsUsed, assets, error }` |

These spend credits:

| Tool | Takes | Notes |
| --- | --- | --- |
| `generate_image` | `{ prompt, model?, reference_asset_ids?, presets?, settings?, lora_id?, lora_strength? }` | returns immediately, poll for the result |
| `generate_video` | `{ prompt, model?, start_asset_id?, end_asset_id?, presets?, settings? }` | most video models need a start frame; text-to-video ones do not. A few morph between two frames and want both |
| `enhance_prompt` | `{ prompt, model? }` | rewrites a short idea into a fuller prompt |
| `build_script` | `{ title, style, kind, shots[] }` | builds a flow, does **not** run it |
| `create_flow_from_template` | `{ template_id }` | opens one of the ready-made setups from `list_templates` as a new flow, does **not** run it. Nothing is charged until you `run_flow` it |
| `run_flow` | `{ flow_id }` | runs a saved flow end to end |

`status` from `get_execution_status` is one of `queued`, `running`, `completed`, `error`.

## Picking a model

**If the user named a model, use that model.** Not the one you would have picked, not a
cheaper one, not the one that scores better on the job. They can see every model in the studio
and choosing between them is the reason they are here. Pass the id in `model`.

If it cannot do what they asked, say so and say why, and let them decide. Never swap it
quietly: they will believe the result came from the model they named.

**If they did not name one, you pick**, and `model` may simply be left out: the studio then
takes the best available one for that kind. Say which one you used and why in one line, so the
next request can be "same but with X".

Call `list_models` and read two fields.

**`inputs`** is the input class, and it decides what you may send:

| `inputs` | Means |
| --- | --- |
| `prompt` | Text only. It reads no files at all |
| `image+prompt` | Text plus one reference image |
| `multi-image+prompt` | Text plus several reference images |
| `start-end-frames` | Text plus a first frame, optionally a last one |
| `multi-type` | Images, video, audio and text, your choice |
| `video+image` | A video plus an image plus text |
| `File in, no prompt` | A tool such as an upscaler: a file goes in, no prompt |

**`strongAt`** is researched per model with a published source. Empty means nobody checked,
so do not claim anything about it.

Send references to a model whose `inputs` is `prompt` and the call is refused, so nothing is
spent, but you have burned a turn. Check first.

## Writing the prompt

These models were trained on how photographers and cinematographers describe pictures. Three
or four sentences, in this order, because the beginning weighs heaviest:

1. The subject and what it is doing.
2. The framing and the lens.
3. The light and where it comes from.
4. The colour treatment and the material detail.

Not a row of comma-separated keywords, and do not stack adjectives: several model makers
state that six style words fight each other and most get ignored.

Every model has its own `promptGuidance` and `promptExample` in `list_models`. They really
do differ. Read them for the model you picked rather than reusing one house style.

**Presets do the trade vocabulary for you.** `list_presets` returns ids with their exact
phrasing: `f18-shallow` becomes "shot at f/1.8, shallow depth of field, the background
falling well out of focus behind the subject". Pass ids in `presets` and that wording is
added to your prompt.

Format presets (`widescreen-16-9`, `vertical-9-16`, `square-1-1`) set the aspect ratio on the
model. If the model cannot do that shape the preset is **refused**, not rounded, and it comes
back in `refused_presets`. Read that field: a refused 9:16 means you got a landscape clip.

## Working from an image the user already has

```text
list_assets({ type: "image", limit: 20 })
generate_image({
  prompt: "The mug from the reference photo, now on a windowsill in the rain, seen through wet glass. Same glaze, same brown rim.",
  model: "nano-banana-2",
  reference_asset_ids: ["dc1b0fb7-..."],
  settings: { resolution: "1K", output_format: "jpeg" }
})
```

That id is the text ingress; the studio switches to the one that reads images and says so in
`switched_ingress`. This is how you keep a person, a product or a location the same across
pictures.

One reference per thing that must stay the same beats a pile of near-duplicates: the model
averages what it is given. And do not describe the person again in the prompt, that is what
produces a lookalike instead of the same person.

`settings` keys come from that model's entry in `list_models`. Anything it does not know is
dropped, so check rather than guess.

## More than one shot

These models make clips of a few seconds, so thirty seconds is six shots of five.

```text
build_script({
  title: "Ceramic mug launch",
  style: "a matte ceramic mug on weathered oak, overcast window light from the left, 50mm, muted warm palette",
  kind: "storyboard",
  shots: [{ seconds: 5, action: "the mug sits still, steam rising" }, ...]
})
-> { flow_id, shots, estimated_credits }
run_flow({ flow_id })
```

The `style` block is repeated in every shot prompt. Without it the shots will not match,
because the model remembers nothing between two calls.

`kind: "storyboard"` gives one still per shot at roughly a tenth of the price, so the user
can approve the sequence before paying for video. `build_script` never runs anything; that is
`run_flow`, a separate call with its own permission.

## What it costs

`get_credits_balance` before a large job. Every generation reports `credits_charged`, and the
amount moves with resolution, length, number of images and audio.

Never quote a price you did not get from a tool.

If a run fails on the studio's side, everything comes back. If the model refuses after
starting, the user pays only what the attempt cost. Content filters do refuse things: that
arrives as an `error` on `get_execution_status` with the credits returned. Reword and try
once; do not hammer it.

## Errors you will actually meet

| What you get | What it means |
| --- | --- |
| `... does not read reference images` | The whole family has no ingress for them. Pick another model from `list_models` |
| `refused_presets` in the reply | That preset does not exist on this model. The format may not be what you asked for |
| `switched_ingress` in the reply | Normal. Your prompt plus images went to the address that accepts both |
| `403` | The user did not grant that permission. Ask them, do not work around it |
| `error` with "content" in it | A content filter refused it. Credits returned |
| `Not enough credits` | Stop and tell the user. Do not retry |

## Where the user sees it

Everything you make lands in their library at `/library`, with the prompt and settings
attached. Flows you build appear at `/flows`, and they can open one on the canvas and carry
on by hand. Point them there rather than pasting a URL that expires.

## What this platform does not do

- **It does not schedule or publish.** No social channels, no CMS, no calendar. That belongs
  to whatever automation the user already runs.
- **It does not edit after the fact.** There is no undo on a generation: change the prompt
  and run again.
- **It cannot exceed a model's limits.** Clip length, resolution and aspect ratio come from
  the model, and asking for more is refused rather than approximated.
- **It does not judge.** It will happily make forty covers that do not suit the brand.

## Three rules

**Never spend without a reason the user would recognise.** One generation on request is fine.
Four variations because you were unsure is their decision, not yours. Say what you are about
to make and what it costs when the job is large.

**Never invent a number.** Not a price, not a model's capabilities. Every fact you need is in
`list_models`, `list_presets` or `get_credits_balance`, and what you cannot get from a tool
does not go in your answer.

**Never override a model the user chose.** Choosing is optional for them, not for you: they
may leave it to you, and the moment they name one it is settled. If you think another model
fits better, say that in a sentence and let them answer. A studio that quietly knows better is
just an automat with more models in it.
