Back to .md Directory

envious-canvas v2 API Contract

Defines a six-tool API contract for AI-powered marketing asset creation, planning, editing, and layout rendering.

May 2, 2026
0 downloads
1 views
ai prompt openai workflow
View source

What this file does

Defines a six-tool API contract for AI-powered marketing asset creation, planning, editing, and layout rendering.

When to use it

  • Building an AI agent that generates marketing images from creative briefs
  • Designing a tool-based API for brand asset management and image editing
  • Implementing a planning step before generative image calls
  • Creating a system that renders HTML/CSS layouts to pixel images

Assumes this stack

PythonFastMCPPILPlaywright

envious-canvas v2 API Contract

North star: Every creative request starts as a Creative Brief interview, not a render call.

1. Plan Object Schema (the backbone)

The plan object is the central artifact. All generation flows through it. Prompts shape workflow, but execution gating happens server-side via approved plan validation.

class CreativePlan(BaseModel):
    """A validated, approved plan for asset creation."""

    plan_id: str                          # Unique ID (plan_<uuid>)
    status: Literal["draft", "approved", "executing", "completed", "expired", "failed"]
    created_at: str                       # ISO 8601
    expires_at: str                       # Plan expires after 1 hour

    # Intent
    normalized_intent: str                # Server-normalized version of user's goal
    subject_type: Literal["product", "logo", "person", "pet", "object", "scene"] | None
    target_channels: list[str]            # ["ig_feed_square", "ig_stories"]
    variant_count: int                    # 1-5

    # Assets & References
    product_image: str | None             # Path to reference product photo
    brand_kit_id: str | None              # Brand kit identifier
    style_image: str | None               # Style reference (or auto from brand kit)

    # Fidelity
    fidelity_mode: Literal["creative", "fidelity_assisted", "fidelity_strict", "composite_lock"]
    identity_lock: Literal["none", "medium", "hard"]
    allowed_changes: list[str]            # ["background", "lighting", "camera_angle", "styling"]
    forbidden_changes: list[str]          # ["shape", "proportions", "hardware", "colorway"]

    # Creative Direction
    prompt: str                           # The generation prompt (may be refined during intake)
    negative_prompt: str
    color_grading: str | None             # Preset name or None
    logo_overlay: bool

    # Execution Plan
    workflow: Literal["creative_generation", "conditioned_edit", "composite_lock", "html_layout"]
    provider_model: str | None            # "openai/gpt-image-1" or "replicate/flux-kontext-pro"
    tuning: dict[str, Any]                # Validated tuning params

    # Output
    output_specs: list[dict]              # [{channel, width, height, format}]
    estimated_cost: dict | None           # {per_image_usd, total_usd, note}

    # Validation
    missing_fields: list[str]             # Fields the user still needs to provide
    required_questions: list[str]         # Questions to ask the user before approval
    warnings: list[str]                   # Non-blocking concerns

    # Requirements by fidelity mode:
    # - fidelity_strict: product_image REQUIRED
    # - composite_lock: product_image REQUIRED (source for cutout)
    # - fidelity_assisted: product_image optional, warning if missing
    # - creative: no requirements

2. Tools (6)

Tool 1: create_marketing_image

Purpose: Generate marketing assets from an approved creative brief.

Use when: The user wants new marketing images — product ads, social content, campaign packs, hero banners. ONLY after preview_generation_plan has returned an approved plan.

Do not use when: The user wants to modify an existing image, manage brand assets, render an HTML layout, or explore options. Never call without an approved plan_id.

class CreateMarketingImageInput(BaseModel):
    plan_id: str                              # REQUIRED — from approved preview_generation_plan
    override_prompt: str | None = None        # Optional last-minute prompt tweak
    override_seed: int | None = None          # Override for reproducibility

Returns: Generated images + per-channel exports + metadata envelope. Includes applied_fidelity, applied_color_grading, applied_logo, provenance metadata.

Server behavior: Rejects with ToolError if plan_id is missing, expired, or not approved.


Tool 2: render_marketing_layout

Purpose: Render HTML/CSS templates or raw HTML to pixel-perfect images.

Use when: The user wants designed banners, cards, or layouts with precise typography and positioning — things AI image generation can't do well.

Do not use when: The user wants AI-generated photography or illustrations.

class RenderMarketingLayoutInput(BaseModel):
    template: str | None = None               # Named template ID (hero_banner, feature_grid, etc.)
    html: str | None = None                   # Raw HTML (if not using template)
    variables: dict | None = None             # Template variables
    brand_kit_id: str | None = None
    viewports: list[ViewportSpec] | None = None  # Responsive multi-viewport
    format: Literal["png", "jpeg", "webp"] = "png"
    background: str = "white"

Returns: Rendered image(s) with viewport metadata.


Tool 3: edit_existing_asset

Purpose: Modify, enhance, or transform an existing image.

Use when: The user has an image and wants to change it — edit content, remove background, upscale, apply grading.

Do not use when: The user wants to create something new from scratch.

class EditAction(str, Enum):
    EDIT = "edit"
    REMOVE_BACKGROUND = "remove_background"
    UPSCALE = "upscale"
    APPLY_GRADING = "apply_grading"

class EditExistingAssetInput(BaseModel):
    image: str                                # Path to source image
    action: EditAction                        # Enum, not free string
    prompt: str | None = None                 # Edit instruction (for action=edit)
    scale: int = 2                            # For upscale (2-4x)
    color_grading: ColorGradingConfig | None = None
    brand_kit_id: str | None = None
    preserve_identity: bool = False           # When True, use fidelity-strict editing

Returns: Modified image + metadata.


Tool 4: manage_asset_library

Purpose: Create, update, list, and inspect brand kits and product assets.

Use when: The user wants to set up or modify brand identity, register products, manage logos.

Do not use when: The user wants to generate or edit images.

class AssetAction(str, Enum):
    CREATE_BRAND_KIT = "create_brand_kit"
    UPDATE_BRAND_KIT = "update_brand_kit"
    LIST_BRAND_KITS = "list_brand_kits"
    REGISTER_PRODUCT = "register_product"
    LIST_PRODUCTS = "list_products"
    INSPECT = "inspect"

class SubjectType(str, Enum):
    PRODUCT = "product"
    LOGO = "logo"
    PERSON = "person"
    PET = "pet"
    OBJECT = "object"

class IdentityLock(str, Enum):
    NONE = "none"
    MEDIUM = "medium"
    HARD = "hard"

class ManageAssetLibraryInput(BaseModel):
    action: AssetAction                       # Enum, not free string
    # Brand kit fields (for create/update)
    name: str | None = None
    color_palette: list[str] | None = None
    typography: list[str] | None = None
    visual_vibe: str | None = None
    logo_path: str | None = None
    positive_prompt: str | None = None
    negative_prompt: str | None = None
    safe_use_rules: list[str] | None = None
    custom_css: dict[str, str] | None = None
    # Product asset fields (for register_product)
    product_name: str | None = None
    product_image: str | None = None
    subject_type: SubjectType | None = None
    identity_lock: IdentityLock = IdentityLock.HARD
    forbidden_changes: list[str] | None = None
    # Inspect
    asset_id: str | None = None               # For inspect action

Returns: Created/updated asset details, listing, or inspection result.


Tool 5: preview_generation_plan

Purpose: Plan and validate a creative request BEFORE execution. Returns a structured CreativePlan. Also used to approve a draft plan.

Use when: ALWAYS before calling create_marketing_image. Also useful for exploring options, estimating costs, or validating configurations.

Do not use when: Never skip this step for generation workflows.

class FidelityMode(str, Enum):
    AUTO = "auto"
    CREATIVE = "creative"
    FIDELITY_ASSISTED = "fidelity_assisted"
    FIDELITY_STRICT = "fidelity_strict"
    COMPOSITE_LOCK = "composite_lock"

class PreviewGenerationPlanInput(BaseModel):
    # --- Planning mode (create new plan) ---
    goal: str | None = None                   # "Instagram ad for this handbag"
    subject_type: SubjectType | None = None
    product_image: str | None = None
    brand_kit_id: str | None = None
    target_channels: list[str] | None = None
    fidelity_mode: FidelityMode | None = None # auto = server decides
    variant_count: int = 1
    color_grading: str | None = None          # Preset name
    prompt: str | None = None                 # Specific prompt (optional)

    # --- Approval mode (approve existing plan) ---
    plan_id: str | None = None                # If set, approve/update this plan
    approve: bool = False                     # Flip plan status to "approved"

    # --- Fast mode ---
    auto_approve: bool = False                # Approve immediately if no critical missing fields

Returns: CreativePlan object.

Two modes:

  1. Plan mode (goal is set, plan_id is None): Creates a new draft plan.
  2. Approve mode (plan_id is set, approve=True): Validates and approves an existing plan.

Tool 6: inspect_asset

Purpose: Analyze an image using AI vision — describe contents, extract structured attributes, QA check fidelity.

Use when: The user wants to understand an image, verify product fidelity, compare reference vs output, or extract attributes.

Do not use when: The user wants to generate or modify images.

class InspectAssetInput(BaseModel):
    image: str                                # Path to image
    question: str = "Describe this image in detail."
    structured: bool = False                  # When True, extract structured attributes
    compare_to: str | None = None             # Path to reference image for fidelity comparison

Returns: Text description, structured attribute dict, or fidelity comparison report.


3. Resources

All URIs use stable IDs, not display names. Display names live inside the resource payload.

URIDescriptionReplaces
canvas://brand-kitsList all brand kitsbrand://kits
canvas://brand-kits/{kit_id}Brand kit detailsbrand://kits/{name}
canvas://assetsList registered product/logo assetsNEW
canvas://assets/{asset_id}Asset details + fidelity rulesNEW
canvas://templatesList HTML templates + schemaslist_templates tool
canvas://templates/{template_id}Template detail + example varsNEW
canvas://modelsProvider/model catalog with tuning paramsmodels://catalog
canvas://presetsChannel export presets (69 channels)platform://specs
canvas://presets/{preset_id}Preset detailplatform://specs/{name}
canvas://policies/fidelityDefault fidelity policies by subject typeNEW

4. Prompts

Prompt 1: creative_brief (the default intake)

Purpose: The mandatory front door for all creative work. Runs the Creative Brief interview.

You are a creative director at a marketing agency.

A client has a creative request. Before producing anything, run this intake:

1. UNDERSTAND the goal:
   - What asset(s) do they need?
   - What channels/platforms?
   - What's the product/subject?

2. GATHER references:
   - Do they have a product photo? (critical for fidelity)
   - Do they have a brand kit set up?
   - Any style references or mood boards?
   - Any specific constraints? (no people, specific colors, etc.)

3. DETERMINE fidelity needs:
   - Is there a specific real-world product that must look exact? → fidelity_strict
   - Is there a logo that must be pixel-perfect? → composite_lock
   - Is this purely creative/conceptual? → creative

4. BUILD the creative direction:
   - Mood/vibe
   - Color temperature
   - Setting/scene
   - Camera angle and framing
   - Text overlay space needed?

5. CALL preview_generation_plan with everything gathered.

6. PRESENT the plan to the client. Show: workflow, outputs, cost, warnings, questions.

7. WAIT for approval. Do not generate without it.

8. CALL create_marketing_image with the approved plan_id.

Never skip the planning step. Even when the user provides a product photo, still
validate assumptions about channels, mood, fidelity, and creative direction.

Prompt 2: product_hero_campaign

Quick-start for product photography campaigns. Pre-fills subject_type=product, fidelity_mode=fidelity_strict, asks for product photo upfront.

Prompt 3: social_ad_variants

Quick-start for social media A/B testing. Pre-fills variant_count=3, asks about target channels + audience.

Prompt 4: html_creative

Quick-start for designed layouts (banners, cards). Routes to render_marketing_layout instead of create_marketing_image.

5. Compatibility Policy

Client TypePromptsResourcesNotes
Full MCP (Claude Code)Native promptsNative resourcesBest experience
Tool-only clientsprompt__creative_brief etc.resource__brand_kits_list etc.Prefixed fallbacks

FastMCP supports PromptManager and ResourceManager transforms. Enable both. Fallback tool names are prefixed with prompt__ and resource__ to avoid muddying the main tool list.

6. Internal Modules (NOT exposed as tools)

These remain as private functions called by the orchestrator:

  • _resolve_style_image() — style image resolution
  • _apply_color_grading() — 7 presets + channel math
  • _apply_logo_overlay() — PIL compositing
  • _export_to_spec() — smart upscale + crop
  • _embed_png_metadata() — provenance embedding
  • _resolve_color_grading() — vibe auto-derive
  • validate_tuning() — ParamSpec validation
  • _build_brand_css() — CSS variable injection
  • _build_logo_html() — base64 logo for HTML
  • render_html_to_image() — Playwright rendering
  • _resolve_fidelity_workflow() — fidelity mode selection
  • _composite_product_into_scene() — cutout + scene + blend
  • Route selection (provider routing)

What's inside

One plan schema, six tool definitions, eight resource URIs, four prompts, a compatibility policy, and 13 internal modules.

Change this for your project

  • Replace saurabhav88/envious-canvas with your own repository name
  • Replace canvas:// resource URIs with your own resource namespace
  • Replace openai/gpt-image-1 and replicate/flux-kontext-pro with your model IDs
  • Replace 69 channels in presets with your own channel count and specs

Where it goes

Keep it in your repository where the agent or team that needs it will read it.

Worth borrowing

  • Requiring a planning step (preview_generation_plan) before any generation call enforces user validation and cost estimation
  • Separating HTML layout rendering from AI image generation avoids forcing AI to do precise typography
  • Using a CreativePlan object as the central artifact makes the entire workflow auditable and resumable

Related Documents