asset_generate_illustration
Generate brand-locked illustrations from a textual brief. Choose external prompt or API mode to apply brand colors and style references. Output multiple images in various aspect ratios.
Instructions
Generate one or more brand-locked illustrations. Two modes (external_prompt_only / api); inline_svg is not supported — path budget too small for a composed scene. Injects brand bundle (palette, style_refs, LoRA, style_id) where supported.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| brief | Yes | ||
| mode | No | ||
| brand_bundle | No | ||
| count | No | ||
| aspect_ratio | No | 4:3 | |
| output_dir | No |
Implementation Reference
- Main handler function for asset_generate_illustration. Processes the input, resolves mode (external_prompt_only or api), generates illustrations via AI providers (Flux/Ideogram/MJ), writes PNG files, runs validation, and returns results.
export async function generateIllustration( input: GenerateIllustrationInputT ): Promise<AssetGenerationResult> { const { width, height } = aspectToPixels(input.aspect_ratio); const spec = await enhancePrompt({ brief: input.brief, asset_type: "illustration", ...(input.brand_bundle && { brand_bundle: input.brand_bundle }) }); const { mode } = resolveMode(input.mode, "illustration", spec.target_model, spec.fallback_models); if (mode === "inline_svg") { // Modes.ts doesn't list inline_svg for illustration, so resolveMode // will have already thrown if the caller asked for it. Belt-and-braces. throw new Error( "mode=inline_svg is not supported for asset_generate_illustration — path budget insufficient for a composed scene. Use external_prompt_only or api." ); } if (mode === "external_prompt_only") { return buildExternalPromptPlan("illustration", input.brief, spec); } // api mode const chosen = chooseApiTargetOrFallback("illustration", input.brief, spec, { images: input.count }); if (chosen.kind === "external") return chosen.plan; const apiModel = chosen.model; const outDir = input.output_dir ?? resolve(CONFIG.outputDir, `illustration-${Date.now()}`); mkdirSync(outDir, { recursive: true }); const variants: Array<{ path: string; format: string; width?: number; height?: number; bytes?: number; }> = []; const warnings: string[] = [...spec.warnings, ...chosen.warnings]; let modelUsed = apiModel; let firstSeed = 0; let prompt_hash = ""; let params_hash = ""; for (let i = 0; i < input.count; i++) { const seed = (typeof spec.params["seed"] === "number" ? spec.params["seed"] : 0) + i * 1000003; const ck = computeCacheKey({ model: apiModel, seed, prompt: spec.rewritten_prompt, params: spec.params }); if (i === 0) { firstSeed = seed; prompt_hash = ck.prompt_hash; params_hash = ck.params_hash; } const gen = await generate(apiModel, { prompt: spec.rewritten_prompt, width, height, seed, ...(input.brand_bundle?.style_refs && { reference_images: input.brand_bundle.style_refs }), ...(input.brand_bundle?.style_id && { style_id: input.brand_bundle.style_id }), ...(input.brand_bundle?.palette && { palette: input.brand_bundle.palette }), ...(input.brand_bundle?.lora && { lora: input.brand_bundle.lora }), output_format: "png" }); modelUsed = gen.model; const p = resolve(outDir, `illustration-${String(i + 1).padStart(2, "0")}.png`); writeFileSync(p, gen.image); variants.push({ path: p, format: "png", width, height, bytes: gen.image.length }); } const validation = await tier0({ image: Buffer.alloc(1), asset_type: "illustration", expected_width: width, expected_height: height, transparency_required: false, ...(input.brand_bundle && { brand_bundle: input.brand_bundle }) }); return { mode: "api", asset_type: "illustration", brief: input.brief, brand_bundle_hash: hashBundle(input.brand_bundle ?? null), variants, provenance: { model: modelUsed, seed: firstSeed, prompt_hash, params_hash }, validations: validation, warnings }; } - Helper function that converts aspect ratio strings (e.g. '4:3', '16:9') to pixel dimensions for image generation.
function aspectToPixels(ar: string): { width: number; height: number } { switch (ar) { case "1:1": return { width: 1024, height: 1024 }; case "4:3": return { width: 2048, height: 1536 }; case "16:9": return { width: 1920, height: 1080 }; case "2:1": return { width: 1600, height: 800 }; case "3:2": return { width: 1500, height: 1000 }; default: return { width: 2048, height: 1536 }; } } - Zod input schema for GenerateIllustrationInput. Validates brief (min 3 chars), mode, brand_bundle, count (1-20), aspect_ratio, and output_dir.
export const GenerateIllustrationInput = z.object({ brief: z.string().min(3), mode: ModeSchema.optional(), brand_bundle: BrandBundleSchema.optional(), count: z.number().int().min(1).max(20).default(1), aspect_ratio: z.enum(["1:1", "4:3", "16:9", "2:1", "3:2"]).default("4:3"), output_dir: z.string().optional() }); - packages/mcp-server/src/server.ts:228-252 (registration)Tool registration entry: defines name, description, and inputSchema for asset_generate_illustration in the MCP server tool list.
{ name: "asset_generate_illustration", description: "Generate one or more brand-locked illustrations. Two modes (external_prompt_only / api); inline_svg is not supported — path budget too small for a composed scene. Injects brand bundle (palette, style_refs, LoRA, style_id) where supported.", inputSchema: { type: "object", properties: { brief: { type: "string" }, mode: { type: "string", enum: ["external_prompt_only", "api"] }, brand_bundle: { type: "object" }, count: { type: "integer", minimum: 1, maximum: 20, default: 1 }, aspect_ratio: { type: "string", enum: ["1:1", "4:3", "16:9", "2:1", "3:2"], default: "4:3" }, output_dir: { type: "string" } }, required: ["brief"] }, annotations: { openWorldHint: true } }, - packages/mcp-server/src/server.ts:801-803 (registration)Case statement in the tool dispatch switch that routes 'asset_generate_illustration' to call generateIllustration() with parsed input.
case "asset_generate_illustration": result = await generateIllustration(GenerateIllustrationInput.parse(args ?? {})); break;