Edit the CE.SDK canvas by running JavaScript against the live CreativeEngine. The result becomes the session’s current state.
⚠ BEFORE your first `edit`, read `skill://codesign/api/SKILL.md`, `skill://codesign/judge/SKILL.md` and `skill://codesign/judge/design-rules.md` in one message: the edit-code API and the rules a design is built to. Worked examples, and the lookup for a cryptic error ("Known engine defects"), are `skill://codesign/api/recipes.md`.
There is no `parent` and no revision id: `edit` always continues the design in front of you.
Every successful edit is kept — the product stores the design after each one, so there is nothing to save.
Returns: the first content part is JSON `{ names? }` — every named block in the scene, which are exactly the strings `engine.design.findByName(name)` resolves in your NEXT edit. Name what you build and re-find it instead of matching copy. When the edit changed the scene, the result also ends with a downscaled render of the first page it changed — verify your work from it instead of spending a `preview` call. `preview` remains required for full-size judging (the judge loop) and multi-page review; set `render: false` to skip the image on read-only/diagnostic edits.
`code` return contract (STRICT): return `undefined` (no message back), `{ type: "text", text }`, `{ type: "image", data: "<base64>", mimeType }`, or an array of those. Anything else (raw strings, Blobs, plain objects) is rejected.
DIALECT (enforced): edit code uses `engine.design` only — scene, pages, props, text, variables, spot colours, transitions. Code that reaches engine.block/scene/variable/editor/asset/event is rejected before execution with the equivalent calls.
`code` scope: `engine.design`, plus `await findText(copy)` / `await findAllText(copy)` (async like every `engine.design` member) for reaching a block by its text. Use them instead of comparing text strings with `===` — the copy on the page routinely carries a trailing space or an NBSP (bef
Parameterscoderequirednoterenderskills
Render blocks (almost always pages) to inline images so you can see the result.
`blockIds` is a LIST, rendered in the order you give it: `[pageId]` for one page, `[a, b]` for a subset, or OMIT it entirely to preview every page of the design in document order.
There is nothing to address: this always renders the design you are working on. Its last state is loaded before rendering, so the ids always resolve against the right scene — never against a different design another agent may have loaded on a shared server.
Preview several blocks at once when REVIEWING or capturing a finished design — a whole deck in one call, or just the pages a fix touched. While BUILDING a multi-page design, keep previewing each page right after you build it: batching there does not avoid the bugs, it only delays finding them.
Animated or video designs (any scene mode): pass `time` (seconds along the timeline of what is rendered — the page, since a block is previewed in place on it; 0 = first frame) to render any frame as a still — use `export format=mp4` for motion. `time` applies to every block in the call, and each timed preview (and every preview of a Video-mode scene) returns `{blockId, time, duration}` JSON alongside its image; `duration` is the scrubbable range.
Output is always PNG (lossless — thin lines render exactly). EACH block gets the full 0.25 megapixel budget, adjusted to its own aspect ratio — a square page renders 512×512, a 1:4 page 256×1024. There are no size or format knobs, and at most 10 blocks render per call.
To SEE SMALL TYPE as it is actually set — legal copy, a caption, a label — the simplest zoom is to pass THAT BLOCK'S id: `blockIds: [<textId>]` renders it on its page, framed, with context around it. No coordinates needed.
For a tighter or arbitrary crop, pass `region`: a rectangle `{x, y, width, height}` with ONE id in `blockIds`. It is measured from the top-left corner of that block's PAGE, in the design's units (the page's own width and height bound it
ParametersuritimeregionblockIds
Import a design FILE as a NEW design — a CE.SDK `.imgly` design file; a Photoshop (`.psd`/`.psb`), InDesign (`.idml`), PowerPoint (`.pptx`) or PDF file; a Lottie animation (`.json`/`.lottie`), which becomes a one-page video design with editable, keyframed layers; or a plain image or SVG, which becomes a one-page design sized to the image. To make a media file (image/font/video/audio) available for use INSIDE a design instead of becoming one: Use `asset_add` to bring it into the workspace, then apply it inside `edit`.
Source: `{ url }` (http/https), `{ uri }` (a saved design — you get a copy of it — or a file you were given, or a template `asset_search` lists from an installed pack: the new design starts as that template), `{ demo }` (a design bundled with this server — ids: `"cybernews"`) or `{ path }` (absolute local file path).
Every import starts a NEW design, and you work on it from then on: the design you were on is left as it is. To keep working on a saved design itself rather than a copy, use `open` with its `uri`. With only a `title` and no `source` (`import({ title: 'Poster' })`), it starts an empty design: do that for every new design, before you name it or build it.
Returns JSON `{ uri, title, format }` — the new design, saved after every change; to continue it later, `open` this `uri`. Foreign-format imports may add `warnings` (what did not survive — substituted fonts, unsupported features): treat them as your touch-up list — `preview` the pages, then repair with `edit`.
Parameterstitlesource
Work on a saved design itself: every later edit is saved into it. Pass the `uri` an `import` or `designs` gave you, verbatim. To start from a copy instead, `import` its `uri`. Returns `{ uri, title }`.
Parametersurirequired
Export the design you are working on — `format` picks the deliverable: `pdf` (print PDF), `png`/`jpeg`/`webp` (image), `svg` (vector SVG), `pdfx` (print-ready PDF/X), `html` (responsive HTML5), `idml` (Adobe InDesign), `psd` (layered PSD), `mp4` (video), `wav`/`m4a` (audio), `imgly` (editable design file). Renders `blockId` — or the whole document when `blockId` is omitted.
Your edits are saved to the design as you make them (the viewer shows it), so there is nothing to save: every format is a copy for the person — a pdf to print, a png to post, `imgly` the editable design file with its images.
Returns JSON `{ uri, httpUrl, bytes, format }` (plus `path` when you pass `outPath`). `uri` is the durable handle — treat it as opaque and pass it back verbatim where a URI is wanted. `httpUrl` is a fetch link tied to this process — fetch it to download the bytes to disk, or hand it to the user.
`outPath` (optional, absolute path including the filename): ALSO write the artifact to that local path — the workspace copy is still written, and the response then carries `path`. Use it when the file is wanted on disk, and always when your environment cannot fetch localhost URLs (sandboxed harnesses, e.g. Claude Cowork — target your session's outputs folder there).
`format=svg` is the engine's vector export: text is outlined into paths (renders identically everywhere, but is not editable text in the SVG), and raster images are embedded in the file at source resolution — an SVG of a photo-heavy design can be much larger than its source images.
`format=imgly` writes a portable, self-contained `.imgly` design file (scene + all asset bytes bundled) — the inverse of `import`, and the only format that stays editable: a copy to move, back up or send. Whole-design only: `blockId` is not accepted. Because the archive inlines every referenced asset it is the heaviest call this server makes, and it REFUSES rather than ship a design whose fonts or images could not be loaded — retry it, do n
ParametersfpshtmlpagepdfxbleedwidthformatrequiredheightblockIdoutPath
The saved designs you can `open` or `import` a copy of, newest first. The one you are working on is marked `current`. A row marked `unsaved` is work an earlier session left before it was saved: `open` it to continue.
Takes no parameters.
Record the verdict of a judge loop you have COMPLETED for the design you are working on — the final step of the `judge` skill. Recording is a claim about work already done: never call this without having scored the axes against actual `preview` renders of the design in its current state.
`verdict: 'pass'` — every applicable axis scored ≥ 8; the design may be delivered. `verdict: 'fail'` — you are stopping with an axis below 8 (e.g. the user cut remediation short); the failure stays on record.
There is no `revision`: the verdict covers the session as it stands when you record it, and ANY later `edit` or `import` clears it — so judge last, and re-judge after every further change. `export` checks it: delivering or saving a design the latest verdict does not cover adds a reminder to its result.
Parametersverdictrequired
Give a design its title — the name `designs` lists it by.
Without `uri`, renames the design you are working on: if it is saved already, it is renamed now, and otherwise its first change saves it under the title. With `uri` (from `designs` or `import`), renames that saved design.
Returns JSON `{ uri, title, previousTitle }`, or `{ title }` while the design has no change saved yet. Nothing in the design changes.
Parametersurititlerequired
Register the brief's ACCEPTANCE CHECKLIST — the machine-checkable claims that must hold before the work counts as done. Do this right after intake, before the first edit: extract from the brief every required output file, every string of approved copy that must land verbatim, and note the constraints only you can check. Skip it only when the brief fixes no outputs or copy.
Item kinds:
- { kind: 'output', path } — a file that must exist when the work is delivered (absolute path, e.g. every variant the brief orders exported with `outPath`).
- { kind: 'copy', text } — approved copy that must appear VERBATIM in the design's text (whitespace variants are folded; wording, case and punctuation are exact).
- { kind: 'note', text } — a self-check the server cannot verify. Kept on record; never machine-checked.
Every `export` checks the list against the design and the disk and names the unmet items — treat one exactly like a failing test. Calling `checklist` again REPLACES the whole list; it lasts for this conversation.
Returns `{ itemCount }`, or `{ items }` (the current list) when called without `items`.
Parametersitems
Get a URL the human can open to view and edit the design you are working on in CE.SDK. Returns the design's stable URL — it follows every change, so print it ONCE and don't re-emit a fresh URL every turn. The page is a full editor; when the human clicks 'Save', their edits land in the same design file. A new design is viewable after its first change. The editor opens in the mode the human last chose for this design, else 'motion' (timeline) when the design has animation, video, audio or timed blocks and 'design' otherwise; pass `mode` to force one. The human can change the design there and save it: your next call says so, and you `preview` their version before you continue.
Parametersmode
Search every asset source of this server's engine, live; earlier sources rank first. Read-only — never creates a revision.
Returns JSON `{ sources: [{ source, description, total, nextPage, failed, assets }] }`; each asset has `id`, `label`, `mimeType`, `uri` (a style: none) and `groups`, an image its `width` and `height` in pixels, and a typeface asset (e.g. from the `ly.img.gfonts` Google Fonts source) a compact `typeface: { name, weights, styles }`. A source's `description` says what it holds. Typefaces: `mimeType: ['application/ubq-typeface']`.
The `ly.img.workspace.styles` source is the workspace's saved styles (brand kits), a pack's among them: when the user names a style or brand ("in our Acme style"), find it here by name or id and apply it with the `brand` skill.
The `ly.img.workspace.images` source is this workspace's own store: every image the agent generated or imported AND every image the human uploaded in the browser editor, newest first. Uploads carry their original filename as `label` (the human will refer to it — "use logo-final.png" — so search that name) and `groups: ["uploads"]`. "The image the user just uploaded" = the first asset in the `uploads` group.
⚠ Asset sources are deployment configuration — where their content lives (IMG.LY CDN, a customer host) is the server's business. NEVER fetch asset manifests (`content.json`) or hardcode `cdn.img.ly` URLs yourself; this tool and the uris it returns are the only correct path. A source this tool does not name is not available on this server — say so instead of working around it.
To USE a result, apply it inside `edit` code — either:
(A) new block: `await engine.design.create({ type: 'graphic', props: { shape: { type: 'rect' }, fill: { type: 'image', uri }, width, height, contentFill: { mode: 'Cover' } } }, { parent: page })` with the asset's `uri` — keep `width`:`height` at the asset's aspect, or let `Cover` crop a photo; a logo or other mark takes `{ mode: 'Contain' }`; audio: `{ type: 'audio'
ParameterspagequerygroupssourceperPagemimeType
Add a local media file (image, font, video, or audio) to the workspace so it can be used in a design. Copies the file bytes into the content-addressed asset store at `assets/<sha>.<ext>`. The bring-your-own arm of the asset family: `asset_search` finds an existing asset, `asset_generate` makes one, `asset_add` takes one off your disk — all three return a `workspace://` uri to embed in `edit`. (To turn a design FILE into a new design, use `import`.)
Returns JSON: `{ uri, httpUrl, mimeType, kind, width?, height?, bytes, note? }`; an image has `width` and `height` in pixels — frame it at that aspect unless you mean to crop it (a logo never). `uri` (a `workspace://` URI) is the durable handle — embed it in a subsequent `edit` (image/video fill or audio `fileURI`; for a font, as both `text.font: { typeface, uri }`'s uri and `typeface.fonts[].uri`), NEVER `httpUrl` and NEVER the original `file://` path, so the design stays loadable after a restart or rewind. `httpUrl` is a fetch link for opening the asset now.
Files are classified by content (magic bytes) first, extension second: a misnamed file is stored under its actual type, and `note` explains any correction, rescue, or unverifiable content.
Make-available only: it never edits a scene and never creates a revision — do the placement in `edit`.
Source: `{ path }` — an absolute path to a file the server can read.
Parameterssourcerequired
Unavailable until you sign in with `login` (free IMG.LY account); once signed in, it works.
Generate images, video, speech, music and sound effects — and transcribe speech — via the AI gateway.
Call with NO arguments first: returns the curated model catalog, every entry tagged with its `capability` (text2image, image2image, text2video, image2video, text2speech, speech2text, …). Filter with `capability`; `source: "all"` (together with a `capability`) adds the provider models — e.g. music and sound effects under `text2audio`.
Then call with `model` + `prompt` (+ optional `format` aspect like "16:9"). Model-specific inputs (duration, voice, lyrics, an input file…) go in `params`, sent to the model verbatim under the model's own field names — `model` + `schema: true` returns the fields a model takes. A `uri` inside `params` that an earlier tool result gave you is uploaded for the model first (e.g. `{ audio_url: '<id>' }` to transcribe).
For image-to-image, pass `image_uris` with uris an earlier tool result gave you. Never assemble one by hand.
Returns ONE JSON part: the asset, in the engine's own asset shape — `{ id, meta: { uri, mimeType, kind, duration?, width?, height?, thumbUri? } }`, the same objects `asset_search` returns, plus `cost` (credits) when the gateway reports it. `id` is the durable handle: embed it in `edit` code (image or video fill, audio block) and pass it back here as an `image_uris` entry. It equals `meta.uri`. `meta.kind` is `image`, `video`, `audio` or `file`; `meta.duration` (seconds, as a string) is present for audio/video when the file header states it. Any other result that is data rather than a file comes back as `{ output }`, verbatim.
Transcription (speech2text, e.g. `elevenlabs/scribe-v2`): put the recording's uri in the model's audio field (`audio_url`) — audio OR video of any length; the audio is extracted and long recordings are split for you. The result is `{ transcriptUri, duration, language?, wordCount, lines }`: `lines` holds
Parametersmodelformatparamspromptschemasourcecapabilityimage_uris
Inspect this codesign MCP server: `status` (default) reports what is running (build id, CE.SDK engine version, non-secret config); `summary` returns a bounded digest of recent activity (counts, recent errors, slowest calls); `open` returns a clickable local viewer URL with a Download-ZIP button to send diagnostics to IMG.LY.
Parametersaction
Sign in with your IMG.LY account (opens the system browser). The session saves automatically once the user finishes in the browser; call login again anytime to confirm — safe to repeat, it does not restart the flow or invalidate the open link. Pass switch_account=true to sign in as a different user.
Parametersswitch_account
Sign out of your IMG.LY account (revokes the session and clears local credentials).
Takes no parameters.
Read a CoDesign skill file by its `skill://codesign/<name>/<path>` URI; returns the same text as the MCP resource. Use it only when your host can't read MCP resources — otherwise read the resource directly.
Parametersurirequired