Loading...
Loading...
Loading...
> North star: Every creative request starts as a Creative Brief interview, not a render call.
# 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.
```python
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.
```python
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.
```python
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.
```python
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.
```python
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.
```python
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.
```python
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.
| URI | Description | Replaces |
|-----|-------------|----------|
| `canvas://brand-kits` | List all brand kits | brand://kits |
| `canvas://brand-kits/{kit_id}` | Brand kit details | brand://kits/{name} |
| `canvas://assets` | List registered product/logo assets | NEW |
| `canvas://assets/{asset_id}` | Asset details + fidelity rules | NEW |
| `canvas://templates` | List HTML templates + schemas | list_templates tool |
| `canvas://templates/{template_id}` | Template detail + example vars | NEW |
| `canvas://models` | Provider/model catalog with tuning params | models://catalog |
| `canvas://presets` | Channel export presets (69 channels) | platform://specs |
| `canvas://presets/{preset_id}` | Preset detail | platform://specs/{name} |
| `canvas://policies/fidelity` | Default fidelity policies by subject type | NEW |
## 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 Type | Prompts | Resources | Notes |
|-------------|---------|-----------|-------|
| Full MCP (Claude Code) | Native prompts | Native resources | Best experience |
| Tool-only clients | `prompt__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)
_Status: Work in progress_
1. [Overview](#overview)
You will need to decide where your entity should be located and how it will be structured. This is largely driven by tax considerations, but may also be driven by governance preferences.
This document aims to help you get started with profiling test suites and answers the following questions: which profiles to run first? How do we interpret the results to choose the next steps? Etc.