
Lamina — On-brand product photos, video & try-onsVerifiedFeatured
@Lamina
About Lamina — On-brand product photos, video & try-ons
Generate on brand product videos from Claude Code (or any assistant) in one line. Lamina turns a brief + your brand kit into finished product photos, video reels, and campaign banners - no prompt engineering; brand guardrails (colors, fonts, tone, style) enforced automatically. For developers: 14+ tools - lamina_brand (pull brand guidelines), lamina_run (product_shoot, vertical_reel, ad_variant, virtual_try_on, campaign_banner, brand_film), lamina_status (poll jobs). Bearer-token auth, ~12s/asset, 35+ variants per product, multi-channel auto-formatting. For marketers in Claude Code: describe the shot, get campaign-ready, on-brand creative back — no design tool, no photoshoot. Install: npx add-mcp https://app.uselamina.ai/mcp/agent Works with Claude, Claude Code, Cursor, Windsurf, VS Code, Zed, Raycast, n8n, Zapier. Free to start · Docs: docs.uselamina.ai
Connection details
https://app.uselamina.ai/mcp/agentSetup
claude mcp add lamina --transport http https://app.uselamina.ai/mcp/agentTools
31Discover Lamina apps (creative workflows) that can produce what the user wants. ALWAYS start here when the user asks to create new content — discover the right app first, then describe it, then run it. How to use it: 1. Provide an array of `keywords` describing the desired creative outcome. Be concrete: combine medium (image / video / audio), form (reel, banner, hero shot, headshot, voiceover), and context (ecommerce, social, lifestyle). Examples: ["product video", "reel", "9:16"], ["hero banner", "lifestyle photo"], ["voiceover", "narration"]. 2. Read the returned apps. Each entry shows the app's name, description, outputs, and estimated credit cost when known. Choose by PURPOSE match — does the app's actual delivery match the user's intent? Keyword overlap alone is not enough. 3. If no listed app fits, call this tool again with different / broader / more specific keywords. The catalog is large; rephrasing often surfaces matches you missed. 4. If after a few sensible searches you still cannot find a suitable app, tell the user honestly that the catalog has no app for this exact need — suggest the closest related app or propose they refine their goal. 5. Once a candidate looks right, call `lamina_describe` with its `appId` for the full parameter contract. The result is ordered by relevance. Apps may be owned by your workspace or shared publicly. `estimatedCredits` is omitted when it cannot be computed quickly — treat absence as "unknown", not zero.
Get the full input/output specification for a single Lamina app. Use it in the MANUAL flow (after `lamina_discover` and before `lamina_run`) when the calling LLM wants to inspect or verify an app's schema directly. The AGENTIC flow via `lamina_create` does discover + describe internally — no need to call describe before create. Returns: - `parameters[]` — every input the app accepts. Each entry has a stable `key` (used in `lamina_run.inputs`), a `type` (`text` / `options` / `url`), an optional `default`, and `accept`/`options`/`multiple` where relevant. - `outputs[]` — what the app produces. Each entry is `{ index, label, type: 'image'|'video'|'audio'|'text' }`. Labels are canonical identifiers — pass them verbatim to `lamina_run.outputs` when subsetting. - `estimatedCredits` — typical cost of one run (omitted when unknown). The user has NO form UI — they only see what you tell them in chat. Every parameter in `parameters[]` is something the workflow author wanted set; defaults shown are the AUTHOR's choices, not the user's authorization. Treat each parameter accordingly: - For USER-OWNED assets (their product, headshot, logo, brand asset, garment): never use the default. ASK the human in chat first — defaults here are demo placeholders that misrepresent the user's subject. - For PRESET / STYLE / REFERENCE groups (mood-board refs, style refs, palette refs, etc.) — often appearing as a numbered series of similar slots: ASK the human in chat ONCE per group — 'use the app's curated defaults or supply your own?' Don't silently default; the user can't see these slots exist. - For text/copy you can confidently draft from the brief (headlines, descriptions): draft. Otherwise ASK. - For dialog-derived settings (aspect ratio, variant count, output format): use the user's explicit input or hint; if absent, ASK. When in doubt about ANY parameter, ASK the human in chat before calling `lamina_run`. One extra question is cheaper than a wrong-output run. Constructing `inputs` for `lamina_run`: - Use the parameter `key` as the input key. Example: `inputs: { "your_photo_image_url": "https://..." }`. - For `type: "options"`, send the option label as a string (case-insensitive match). - For `type: "url"` with `multiple: true`, send an array of URLs. Otherwise send a single URL string. Never invent URLs. - Omit a parameter from `inputs` only when the user has explicitly authorized falling back to the workflow default.
Dispatch a run for an app the user has authorized. Pure trigger — no LLM, no brief analysis, no app picking. Takes an `appId` you already know (from `lamina_create.selectedApp.appId` after the agentic flow, or from `lamina_describe` in the manual flow) plus `inputs` keyed by parameter `key`, optionally `outputs` (label subset). How to use it: - Pass `appId` and `inputs`. `inputs` is a map keyed by parameter `key` from describe (option label for `options`, URL string for `url`, array of URLs for `url + multiple`). - Only include parameters the user has explicitly authorized — by giving the value in the brief, answering an ask in chat, or saying 'use defaults' on a surfaced preset group. Silent defaults on user-owned slots produce wrong-subject output. - `outputs` (optional): when the user asked for only a subset of the app's outputs (e.g. 'just the front view and the lifestyle shot'), pass `outputs: ['Front View', 'Lifestyle View']` with labels verbatim from `lamina_describe.outputs[].label` (case-insensitive). Omit or pass an empty array to produce every output the app declares (default). - `applyBrand` (optional): set true to make the output on-brand — Lamina folds the workspace's brand negatives + visual style onto the app's visual nodes before running. Your `inputs` still win over brand. Use it instead of hand-writing brand into every input. - Returns immediately with `{runId, workflowId, status}`. Most apps take 1–5 minutes (median ~2 min, p90 ~5 min). Poll with `lamina_status`. - On `Invalid inputs` error, the response includes `details: [{param, code, message}]`. Common causes: option label not in declared list, malformed URL, wrong type. On `outputs_resolution_failed`, the label you passed doesn't match any output on the app — re-check `lamina_describe.outputs[].label`. - For variants/series (e.g. "5 selfies with different celebrities"), call this tool once per variant in parallel. Each call returns its own `runId`.
Check the status of any Lamina run through one normalized lifecycle — pipeline (`lamina_execute`), atomic image/video generation, app workflows, and narrated-video composition. - Default: returns the current normalized snapshot (`queued` / `running` / `completed` / `failed` / `cancel_requested` / `cancelled`). - With `wait: true`: waits for a terminal state for at most 25 seconds. A non-terminal timeout returns the latest snapshot with `timedOut: true`; call again to continue waiting. Every run kind returns `{ runId, runKind, status, progress, outputs, error, credits, startedAt, completedAt }`. Completed assets are always in `outputs[]`; images are also rendered as MCP image blocks when supported by the client. - While the run is `running`, surface progress to the user instead of making them wait silently.
List Lamina's curated atomic generation models. Each entry has `id`, `displayName`, `modality`, and — to help you pick deliberately rather than guess — `bestFor` (what the model is good at / when to reach for it), `categories` (capability tags like `talking-avatar`, `lip-sync`, `text-to-video` — match these to the task; e.g. lip-syncing a presenter to a PROVIDED audio track → a `talking-avatar` model such as `omnihuman-v15`), `cost` (`low`|`medium`|`high` relative credit tier), `speed` (`fast`|`standard`|`slow`), and `provider` (the infra that serves it: `fal`|`wavespeed`|`vertex`). Pass `modality: 'image'` (default), `'video'`, or `'audio'` to scope the response. Audio covers voiceover/TTS and music beds. RESPONSE: `{ models: [...] }`. Read model entries from the `models` array. Use `bestFor` to match the task, and `cost`/`speed` to break ties — prefer a `low`/`fast` model unless the task needs the quality of a `high` one. After picking an `id`, call `lamina_models_describe({modelId})` to get the input contract before calling `lamina_generate_image` / `lamina_generate_video` / `lamina_generate_audio`.
Get the input contract for one atomic-generate model (image, video OR audio). Returns `id`, `displayName`, `modality`, and a flat `paramSchema` (every accepted field, including `prompt`, with `type`, allowed `values`, `default`, `min`/`max`/`step`, and short field-level `description` text). Hybrid models present a single merged schema where mode-specific fields are marked optional; the description on each field tells you when it applies. Models that don't accept a prompt (e.g. `bria-bg-remove`) simply omit the `prompt` field from `paramSchema`. For audio TTS models `prompt` is the words to speak; for the music model it describes the music. Polymorphic — tries image, then video, then audio registry, so any id from `lamina_models_list({modality:'image'|'video'|'audio'})` is accepted. Call this BEFORE `lamina_generate_image` / `lamina_generate_video` / `lamina_generate_audio` for a model you haven't used. The response is the contract — what you pass in `params` must match these field rules, and the cross-field constraints noted in each field's `description` (e.g. gpt-image-2's custom dimensions are capped at 8.3M pixels and 3:1 aspect).
Dispatch a single atomic image generation. Sibling of `lamina_create` (the agentic router) — use this when you already know which model fits, or when no app fits the brief. WORKFLOW: (1) `lamina_models_list({ modality: "image" })` → pick a model. (2) `lamina_models_describe({ modelId })` → read its flat `paramSchema`. (3) `lamina_generate_image({ model, prompt, params })` → dispatch, get runId. (4) `lamina_status({ runId, wait: true })` → poll until completed; the image URL is in `outputs[0].url`. ONE TOOL, BOTH OPERATIONS: • Text-to-image — call with just `prompt` (and any text-mode params). The model id you picked is the only thing that selects the operation. • Image-to-image (edit / remix / background-swap / etc.) — call the same tool, but include a source image in `params`. Hybrid models (nano-banana-pro, gpt-image-2, gemini-2.5-flash-image, seedream-4.5, flux-2-flex, nano-banana-2, gpt-image-1, gpt-image-1.5) flip to image-to-image automatically when `params.imageUrls` is a non-empty array (or `params.imageUrl` is set for single-source models like flux-pro-kontext). Edit-only models (bria-bg-remove, ideogram-character, ideogram-v3-remix/reframe/replace-background, flux-pro-kontext, ideogram-character-remix) only have image-to-image — `params.imageUrls`/`imageUrl` is required. INPUTS: • `model` (required): a model id from `lamina_models_list`. Don't invent it. • `prompt` (required for most models; check `paramSchema.prompt.required` from `lamina_models_describe`; absent from `paramSchema` for prompt-less models like `bria-bg-remove` and `ideogram-v3-reframe`): natural-language brief; ≤2000 chars. • `params` (model-specific): every key MUST be declared in the chosen model's `paramSchema` (call `lamina_models_describe` first). Unknown keys are rejected with a structured `invalid_params` error; each error has `field` + `allowed`/`range`/`got` so you can correct on retry. Omitted optional keys fall back to schema defaults. • `webhookUrl` (optional): HTTPS URL. On terminal status Lamina POSTs `{runId, status, model, prompt, resolvedParams, output, errorMessage, completedAt}` HMAC-signed. RESPONSE: `{runId, status: "queued", model, mode, prompt, resolvedParams}`. `mode` is the resolved value ("text-to-image" | "image-to-image"). The `runId` is the fal_request_id — pass it to `lamina_status`. SYNC vs ASYNC: dispatch always returns `status: "queued"` because completion is read through `lamina_status`. Vertex-backed models (`imagen-4.0-*`, `gemini-2.5-flash-image`) often return `status: "completed"` on the first poll; fal-backed models usually take 5–60s. ERROR HANDLING: validation failures return `code` + `details.errors[]` with `field` + `error` + `allowed`/`range`/`got`. Common codes: `model_not_supported`, `mode_not_supported`, `invalid_params`, `dispatch_failed`.
Dispatch a single atomic video generation. Sibling of `lamina_generate_image` — same shape, but outputs a video URL. WORKFLOW: (1) `lamina_models_list({ modality: "video" })` → pick a model. (2) `lamina_models_describe({ modelId })` → read its flat `paramSchema` to learn required inputs. (3) `lamina_generate_video({ model, prompt, params })` → dispatch, get runId. (4) `lamina_status({ runId, wait: true })` → poll until completed; the video URL is in `outputs[0].url`. ONE TOOL, EVERY VIDEO OPERATION. Model id selects the operation; `params` matches the model's paramSchema: • Text-to-video — Kling t2v variants, Wan-2.6-text-to-video, Wan-turbo, Minimax, Seedance t2v, Veo3-text-to-video. Just `prompt` (+ duration / aspectRatio / etc. per the model). • Image-to-video — Kling i2v variants, Wan-2.6/turbo image-to-video, Minimax i2v, Seedance i2v (uses `startImageUrl`), `happy-horse-image-to-video`, `veo3-image-to-video` (sync). `params` includes `imageUrl` (or `startImageUrl` for Seedance, `firstFrameUrl` for Veo3 first-frame). • Keyframe — `veo3-keyframe-to-video` (sync). `params` requires `firstFrameUrl` AND `lastFrameUrl` — model interpolates between them. • Motion-control — `kling-v26-motion-control`, `kling-v26-motion-control-pro`. `params` requires `imageUrl` (character) AND `videoUrl` (motion reference). • Video-to-video (edit) — `wan-video-to-video`, Kling v2v variants. `params` requires `videoUrl` (source). • Reference-to-video — Kling/Seedance/Wan reference variants. `params` uses `referenceImageUrls`. INPUTS: • `model` (required): a model id from `lamina_models_list({modality:"video"})`. • `prompt`: most models require it; some (motion-control variants) are optional. Check `paramSchema.prompt.required` from `lamina_models_describe`. Some models omit `prompt` from `paramSchema` entirely (prompt-less). • `params`: model-specific fields per the model's `paramSchema`. Required URL fields vary by operation — see list above. • `webhookUrl` (optional): HTTPS URL for HMAC-signed completion POST. • `includeCitationKit` (optional, PREVIEW): set `true` to request a citation-ready kit alongside the video — cleaned transcript, question-formatted chapters, a 30–60 word summary, and drop-in `VideoObject` JSON-LD — so wherever you host the video it is answer-engine-citable. Preview signal only: it records interest and returns a `coming_soon` note; the kit is not yet produced. RESPONSE: `{runId, status: "queued", model, mode, prompt, resolvedParams}`. `mode` is the resolved operation. Pass `runId` to `lamina_status`. If `includeCitationKit` was set, the response also carries `citationKit: {status: "coming_soon"}`. SYNC vs ASYNC: dispatch always returns `status: "queued"` because completion is read through `lamina_status`. Veo3 variants are Vertex-backed and often complete by the first poll; fal-backed models usually take 30s–5min. ERROR HANDLING: same shape as `lamina_generate_image` — `code` + `details.errors[]` with `field` + `allowed`/`range`/`got`.
Dispatch a single atomic audio generation — a voiceover (TTS) or a music bed. Sibling of `lamina_generate_image` / `lamina_generate_video`; outputs an audio URL. WORKFLOW: (1) `lamina_models_list({ modality: "audio" })` → pick a model. (2) `lamina_models_describe({ modelId })` → read its flat `paramSchema`. (3) `lamina_generate_audio({ model, prompt, params })` → dispatch, get runId. (4) `lamina_status({ runId, wait: true })` → poll until completed; the audio URL is in `outputs[0].url`. ONE TOOL, TWO ASSET TYPES. Model id selects the operation; `params` matches the model's paramSchema: • Voiceover / TTS — `eleven_multilingual_v2` (default), `eleven_turbo_v2` (fast/cheap), `eleven_v3` (most expressive). `prompt` is the words to speak; `params.voiceId` selects the voice (pass a brand-kit or cloned voice id to speak in a saved brand voice); `params.stability` / `params.similarityBoost` tune delivery. • Music — `eleven_music`. `prompt` describes the music; `params.duration` is the length in seconds (10–300). INPUTS: • `model` (required): a model id from `lamina_models_list({modality:"audio"})`. • `prompt` (required): TTS → the narration text; music → a description of the desired music. • `params`: model-specific fields per the model's `paramSchema` (voiceId/stability/similarityBoost for TTS, duration for music). RESPONSE: `{runId, status: "queued", model, mode, prompt, resolvedParams}`. `mode` is `text-to-speech` or `text-to-music`. Pass `runId` to `lamina_status`. ERROR HANDLING: same shape as `lamina_generate_image` — `code` + `details.errors[]` with `field` + `allowed`/`range`.
Generate a category-matched background plate for a Meta PLV (Product-Led-Video) ad. WHAT IT DOES: product attributes → LLM background brief (category-level, not SKU) → background-only image dispatch. The plate deliberately contains NO product, NO people and NO text — it leaves clean negative space so Meta can composite the product cutout on top. Colours are chosen to harmonise with the product colour (pink apparel → soft pink set; summer dress → sunlit coastal scene). WORKFLOW: (1) `lamina_plv_background({ product, aspectRatio })` → dispatch, get runId. (2) `lamina_status({ runId, wait: true })` → poll; `outputs[0].url` is the plate. INPUTS: • `product` (required): `{ category (required), color?, season?, occasion?, description? }`. `category` drives the scene; `color` drives the palette harmony; `season`/`occasion` steer the setting. • `theme` (optional): `{ label?, cue? }` — the ~monthly thematic event this creative runs in (e.g. label "Diwali", cue "festive gifting"), folded into the brief. • `aspectRatio` (optional, default `9:16`): PLV plate ratio. `4:5` maps to the nearest supported portrait preset. • `model` (optional): image model id; defaults to a photoreal 4K backdrop model. • `webhookUrl` (optional): HTTPS URL POSTed on terminal status. RESPONSE: `{ runId, status, model, aspectRatio, brief, prompt, negativePrompt }`. `brief` is the structured art-direction the plate was generated from; `prompt` is the exact background-only prompt dispatched.
Generate a short (~4s) HOOK opener clip that plays BEFORE the product appears in a Meta ad. WHAT IT DOES: product category (+ optional vertical/theme) → LLM hook brief → text-to-video dispatch of a category-relevant human action (person knocking, kicking a football, applying makeup). The clip deliberately shows NO product, logo, price or text — it exists to stop the scroll; the product reveal comes after. WORKFLOW: (1) `lamina_generate_hook({ product, aspectRatio })` → dispatch, get runId. (2) `lamina_status({ runId, wait: true })` → poll; `outputs[0].url` is the clip. INPUTS: • `product` (required): `{ category (required), vertical? }`. `category` drives the action; `vertical` (e.g. "Forward", "Lux", "M Now") tunes the subject + tone. • `theme` (optional): `{ label?, cue? }` — thematic event to reflect. • `aspectRatio` (optional, default `9:16`). • `durationSeconds` (optional, default `4`; clamped to 4–12, the model floor/ceiling). • `model` (optional): video model id override. • `webhookUrl` (optional): HTTPS URL POSTed on terminal status. RESPONSE: `{ runId, status, model, aspectRatio, durationSeconds, brief, prompt, negativePrompt }`.
Compose a NARRATED, MULTI-SHOT video from a script — NOT a single ~5s clip. Use this when you need a coherent minute-ish demo / explainer with voiceover: it synthesizes the narration, storyboards it into beats, generates a continuity-chained shot per beat, and renders with transitions + Ken-Burns + captions via Remotion. (For a single short clip, use `lamina_generate_video`.) WORKFLOW: (1) `lamina_compose_video({ script })` → dispatch, get `runId`. (2) Prefer `lamina_status({ runId, wait: true })` for the normalized lifecycle. The legacy `lamina_compose_video({ runId, wait? })` polling overload remains supported. INPUTS: • `script` (required to start): the narration to speak (a hook + one insight + a CTA; ≤~90s). The transcript/chapters derive from this, so the video stays coherent with its text. • `title` / `generationBrief` (optional): title + a one-paragraph visual brief for the shots. • `direction` (optional): free-text creative direction ("open on the product, fast cuts, end on the logo") — steer the render WITHOUT naming models. • `sections` (optional): structured per-span hints `[{ text?, hint, mustShow? }]` for a prescriptive storyboard. • `format` / `inputs` (optional): pick a format (else auto-routed); `inputs` supplies a format's required assets (e.g. `{ presenterImage }`). • `aspectRatio` (optional): `16:9` | `9:16` | `1:1` | `4:5` | `4:3` (default 16:9). • `voiceId` (optional): ElevenLabs voice id. Omit and the narration director picks a voice whose tone fits the script. • `expressive` (optional): true → adds v3 audio tags for stronger, more human inflection. (Prosody/pacing is always applied.) • `brandProfileId` (optional): steer the render on-brand — the brand DNA conditions the style bible, the narration voice (Brand Kit), and the caption color/font. Omit for a generic look. Fail-soft: a brand miss never blocks the render. • `idempotencyKey` (optional): dedupe retries — the same key returns the same run (no double charge). • `runId` (+ optional `wait`): pass a prior runId to poll instead of starting a new run. TIP: call `lamina_compose_plan({ script })` first to preview the format + cost + storyboard for free before committing. RESPONSE: dispatch → `{ runId, status: "running" }`. Poll → `{ status, progress: { stage, current?, total? } }`. Terminal → `{ status: "completed", result: { videoUrl, durationSeconds, shotCount } }` or `{ status: "failed", error }`. Invalid input → an error with a machine-readable `reason` (e.g. `MISSING_REQUIRED_INPUT`, `SCRIPT_TOO_LONG`).
List the video FORMATS `lamina_compose_video` can render — e.g. animated slideshow, cinematic b-roll, motion-graphics explainer, talking head, picture-in-picture. Each renders differently and internally uses different models; the user picks a FORMAT (never a model) and sees its cost. RESPONSE: `{ formats: [{ id, label, description, available, costTier, estimatedCredits, trending, tags, inputs, previewUrl, thumbnailUrl }], capabilities: { maxScriptDurationSec, aspectRatios, clipSeconds, … } }`. Pass a chosen `id` as `format` to `lamina_compose_video`; supply a format's `inputs` when it lists required ones. `available: false` entries are on the roadmap and cannot be dispatched yet. Optional `durationSeconds` scales the cost estimate.
PREVIEW what `lamina_compose_video` will do for a script — the format it routes to, the estimated cost, and the per-beat storyboard — WITHOUT generating anything or spending a credit. Fast (one LLM call). Call this first to show the user the plan + cost and confirm before committing. RESPONSE: `{ ready, format: { id, label, reason }, cost: { tier, estimatedCredits }, estimatedDurationSec, beatCount, beats: [{ treatment, effect, transition, imagePrompt }], missingInputs }`. `ready: false` + `missingInputs`/`blockers` tell you exactly what to supply before dispatching. Accepts the same steering inputs as `lamina_compose_video` (`script`, `format`, `direction`, `sections`, `inputs`, `aspectRatio`).
Load the brand context the user has configured for this workspace — voice, visual identity, guardrails, prompt directives, performance patterns. Call this when the user mentions branding, voice, or guardrails, or when you want to weave brand-aware language into `inputs` for `lamina_run`. Returns three independent sections (each can be `null` or empty): - `brandDna` — voice, visual identity, content pillars, audience signals, guardrails. Populated when the workspace has both a brand profile filled in AND brand-DNA synthesis has run. - `guidance` — workflow-scoped prompt directives, negative prompts, recommended moves. Populated by content scoring / recommendation jobs. - `topPatterns` — top-performing patterns aggregated from this workspace's run history. Populated by content scoring runs (look at `topPatterns.itemCount` — `0` means no scored content yet). If a section is `null` (or `topPatterns.itemCount === 0`), the user hasn't configured that piece yet. **Do not fabricate brand attributes.** Tell the user honestly: "I don't see a brand profile / content history configured for your workspace — you can add one in your Lamina workspace settings to get brand-aware suggestions. For now I'll proceed with what you've supplied directly." Then continue with the task using only the user's stated inputs. When sections are populated, fold the relevant strings into the `inputs` you build for `lamina_run` — voice cues into text fields, guardrails into negative prompts, winning patterns as suggestions you can offer the user.
Load the workspace's reusable BRAND KIT for a brand — the identity assets that get reused across reels: voices, avatars, characters, and motion graphics. Call this alongside `lamina_brand` when the user wants a specific brand voice/character/avatar applied, or to see what reusable assets exist. Returns `{ resolved, elements }`: - `resolved` — hydrated + ready to use: `voices` (with ElevenLabs ids), `avatars` (presenter images), `characters` (reference-image URLs), `motionGraphics`. Fold these into `lamina_run` / `lamina_compose_video` inputs. - `elements` — the raw registered rows (id, type, name, version). Empty sections mean the user hasn't registered that kind of asset yet — do not fabricate one.
Register (or supersede, by name) one reusable brand asset in the brand kit — a voice, avatar, character, or motion graphic that points at an existing primitive. - `elementType` (required): `voice` | `avatar` | `character` | `motion_graphic`. - `name` (required): a stable handle, unique per brand + type (e.g. "spokesperson"). Registering the same name again supersedes it (version bump) — downstream reels keep resolving the handle. - `refKind` / `refId` (optional): the primitive it points at — `cloned_voice` (a cloned_voices id), `persona` (an influencer_personas id), or `asset`. - `spec` (optional): type-specific detail (voice tone/pace, avatar presenter_image_url, character canonical_reference_urls[], motion_graphic template). - Requires an owner/admin identity. To save a GENERATED output instead, use `lamina_save_to_brand_kit`.
Save a GENERATED output into the brand kit as a reusable asset — the "I like this, keep it" loop. `runId` lookup currently supports completed atomic generation runs. For workflow, pipeline, or compose output, pass its completed `outputUrl` and `mediaType` explicitly. The output is persisted to a durable CDN URL first, then registered. - `elementType` (required): `voice` | `avatar` | `character` | `motion_graphic`. (Images → avatar/character, video → motion_graphic, audio → voice.) - For `voice`, the audio clip is cloned into ElevenLabs to become a reusable voice — pass an `outputUrl` (+ `mediaType: audio`). - `name` (required): stable handle for the new element. - `runId` OR `outputUrl` (one required). With `outputUrl`, also pass `mediaType` (`image`|`video`|`audio`). - Requires an owner/admin identity.
Get a pre-signed upload URL for one user-supplied asset (image, video, or audio). Use this when the user provides a local file you need to feed into `lamina_run` (or `lamina_create.inputs`) as a URL parameter (e.g. `your_photo_image_url`, `product_image_url`, `brand_logo_url`). How to use: - Pass `filename` (the name to register, e.g. "selfie.jpg") and `mediaType` ("image" | "video" | "audio"). - This tool returns `{ uploadUrl, assetUrl, contentTypeHint }`. The `uploadUrl` is a pre-signed PUT URL; the `assetUrl` is what you eventually pass to `lamina_run` (or pre-supply in `lamina_create.inputs`). - THE BYTES DO NOT GO THROUGH THIS TOOL CALL. After this returns, perform an HTTP PUT of the file bytes directly to `uploadUrl` with the `Content-Type` header set to `contentTypeHint` (or override). When the PUT completes (200), the asset is live at `assetUrl`. If your host has shell access (Claude Code, Cursor): execute `curl -X PUT --data-binary @<local-path> -H 'Content-Type: <contentTypeHint>' '<uploadUrl>'` to upload, then call `lamina_run` with `assetUrl` in the relevant input (or pre-supply it to `lamina_create.inputs` so the router doesn't re-ask). If your host doesn't have shell access (Claude Desktop, Claude.ai, ChatGPT): tell the user honestly that you cannot move the bytes from your context to Lamina's storage, and ask them to provide a publicly-reachable URL of the asset (or upload via the Lamina CLI / SDK from a shell), then continue with that URL. Asset URLs returned here are stable and can be reused across multiple runs. There is no need to re-upload between dispatches of related runs.
Request cancellation for any Lamina run by `runId`. Works across pipeline, workflow, and cancellable atomic runs. Compose or provider operations that cannot be interrupted return `not_cancellable` instead of falsely claiming success. Terminal runs return `terminal` idempotently. Use this when the user changes their mind mid-flight (wrong inputs after dispatch, oversized variant set, abandoned long-running job). Stopping `lamina_status` polls does NOT cancel a run — the orchestrator keeps spending credits until the workflow terminates on its own.
Plan a creative run from a free-text brief. The router agent searches the catalog, picks the best-fit app by CAPABILITY ENVELOPE (an app whose `outputs[]` can deliver the brief, even when broader than asked — narrow via output selection), drafts inputs from the brief, and surfaces every choice the human still needs to make. NEVER dispatches a run — that's `lamina_run`'s job. The MCP result is the bare response object. Branch on its top-level `status` field: • `"plan"` (mode `"app"`): the router committed to an app. Response includes `selectedApp.appId`, `draftedInputs`, optional `selectedOutputs` (output-label subset), and `askUser[]` (per-parameter questions the human must answer). The `askUser` list is the WORKHORSE — it covers everything the human needs to decide on the chosen app: USER-OWNED slots (product photo, brand logo, headshot), PRESET option picks (background style, aesthetic, mood-board references — curated option labels are listed INLINE in the question text), output subsetting (when the entry is `{ name: "__outputs", question }`, the answer is a comma-separated list of output labels), and any KNOB the user needs to choose. To dispatch: ask the human each `askUser[i].question` in chat, collect answers, then call `lamina_run` with `appId: selectedApp.appId`, `inputs: {…draftedInputs, …<answers keyed by askUser[i].name>}`, and `outputs: <selectedOutputs OR the labels parsed from the __outputs answer>`. ONE dispatch round. • `"needs_clarification"`: the router could not commit to an app because the brief is genuinely ambiguous between two or more apps (e.g. banner vs. reel vs. video, single hero vs. multi-language adapts). This is PRE-COMMIT ROUTING ambiguity only. Response includes `clarifications: [{ question }]`. Ask the human each clarification, fold the answers into a refined brief, then call `lamina_create` again. This is the ONLY status where re-calling `lamina_create` with a refined brief is correct. • `"unmatched"`: no pre-built app fits. Response includes `reason`, and — when the brief is a buildable creative request (no catalog app matched) — a `suggestion` object `{ tool: "lamina_generate_workflow", instruction }`. If `suggestion` is present, offer to BUILD a bespoke app: call `lamina_generate_workflow` with the given `instruction`, then `lamina_run` the returned `appId`. If there is no `suggestion`, the brief is outside Lamina's creative-media surface — tell the human and do not retry. ANTI-DRIFT: After `lamina_create` returns `"plan"`, NEVER re-call `lamina_create` to resolve `askUser` items — that re-rolls the LLM and may pick a different app or drop the partial plan. Resolve asks via `lamina_run` with the answers folded into `inputs`/`outputs`. Re-calling `lamina_create` is ONLY correct when the prior response was `"needs_clarification"`, in which case fold the human's clarification answers into a REFINED BRIEF and call `lamina_create` again. The user has NO form UI — they only see what you (the calling LLM) tell them in chat. Treat every `askUser` entry as a question to surface to the human verbatim, including the inline option lists for PRESET choices (so the human knows what they can pick) and the output labels for `__outputs` asks (so they know which deliverables are available). The router never silently uses demo defaults on user-owned slots — that's why those slots appear in `askUser`. Always collect answers first, then dispatch via `lamina_run`.
Generate a BRAND-NEW Lamina app from a plain-language instruction, OR EDIT an existing generated app in place. Use this when NO existing app fits the goal (check `lamina_discover` first) and the user wants a bespoke workflow — or when a follow-up needs the app itself changed (add a step, new output, swap a model), not just a different run. A headless Claude planner assembles a validated node graph from the Lamina node catalog and auto-publishes it as a PRIVATE, immediately-runnable app. - `instruction`: describe what the app should create (or, in edit mode, the change to make), including the inputs the user will provide and the desired output(s). - `baseAppId` (optional): EDIT MODE. The appId of a generated app to modify. The planner starts from its current graph and applies `instruction` as a targeted edit — keeping everything it does not touch — then updates that SAME app in place (same appId, run history preserved). Requires you to be the app creator or a workspace owner/admin. Omit to build a new app. - `ops` (optional, EDIT MODE — the CHEAP path): when you already know the exact change and can express it as a small op list, pass `ops` instead of `instruction` — the server applies them deterministically with NO planner LLM call and no credit charge. Each op is one of: `{op:"setNodeData",nodeId,data}`, `{op:"addNode",node:{id,type,data?}}`, `{op:"removeNode",nodeId}`, `{op:"addEdge",edge:{source,target,sourceHandle?,targetHandle?}}`, `{op:"removeEdge",source,target}`, `{op:"addParameter",parameter:{key,name,type,targetNodeId,targetField,...}}`, `{op:"removeParameter",key}`, `{op:"setParameter",key,patch}`. Requires `baseAppId`. Read the app first (`lamina_describe`/`GET /v1/apps/{id}`) to get node ids. Use `instruction` (planner) for anything you cannot express as ops. - `name` (optional): preferred app name. - `visibility` (optional): `private` (default), `shared` (whole workspace), or `public`. Ignored in edit mode. - `brandProfileId` (optional): bake a brand's voice/visual/guardrails into the app so it's on-brand by default. Omit to use the workspace's active brand. - Returns `{ appId, name, parameters[], outputs[], runUrl }`. In edit mode `appId` is unchanged. Feed `appId` + the returned parameter `key`s straight into `lamina_run` — the parameter set may have changed, so re-read `parameters[]`. - May include `editability` `{ score (0–1), subscores, notes[] }` — a deterministic measure of how tweakable the generated app is (param clarity, grouping, node granularity, output-steerability). A low score + its `notes` tell you what to refine; offer the user an edit (via `ops`/`instruction`) when it is low. - On failure returns `{ error, details[] }` where details are the validation problems the planner could not resolve.
List a generated app's saved graph versions, or restore it to one. Every `lamina_generate_workflow` edit snapshots the app's prior graph first, so a bad edit is always recoverable. - `appId` (required): the generated app. - Omit `restore` to LIST versions — returns `{ versions: [{ version, createdAt, createdBy, changeSummary }] }`, newest first. - Pass `restore: <version>` to RESTORE that version — swaps its graph + parameters back in (the current graph is snapshotted first, so restore is itself undoable). Returns `{ appId, restoredFrom, newVersion }`. Requires you to be the app creator or a workspace owner/admin.
Change an app's reach: `private` (creator/workspace-owner only), `shared` (any workspace member — discoverable via `lamina_discover`), or `public`. Only the app creator or a workspace owner may change visibility. Useful right after `lamina_generate_workflow` to share a freshly generated app.
Create or update this workspace's brand and ground it with a starter Brand DNA, so generated apps and `applyBrand` runs produce on-brand output. Use it to make a fresh workspace on-brand before generating. - `brandName` (required): the brand to create, or the display name when updating. - `brandProfileId` (optional): update an existing profile instead of creating one. Omit to create. - `guardrails` (optional): things to avoid — these become the brand's negatives on visual generation. - `brandVoice` / `visualStyle` / `targetAudience` / `description` / `industry` (optional): shape the brand. - Returns `{ brandProfileId, reused }`. Pass that `brandProfileId` to `lamina_generate_workflow` or `lamina_run` (with `applyBrand`). - Requires an owner/admin identity. Read the current brand with `lamina_brand`.
Give an on/off-brand verdict on a completed WORKFLOW run, closing the loop so the next generation respects it. Atomic, pipeline, and compose IDs are not accepted because feedback is attached to workflow provenance. A `reject` with a `note` turns the reason into a brand guardrail. - `runId` (required): a run you started (from `lamina_run` / `lamina_status`). - `verdict` (required): `approve` or `reject`. - `note` (optional but recommended on reject): why it is off-brand — this becomes the guardrail. - `brandProfileId` (optional): target brand. Defaults to the run's app brand. - Returns `{ brandProfileId, verdict, applied, guardrails }`. Requires an owner/admin identity.
Read Brand Guard data already stored on a WORKFLOW execution — the per-node on/off-brand status and score against the workspace brand. This is not an on-demand scorer and does not accept atomic, pipeline, or compose IDs. Use `lamina_brand_score` for a completed supported image output. - `runId` (required): a run you started (from `lamina_run` / `lamina_status`). - Returns `{ enabled, brandProfileId, nodes[] }`. Each node has `{ nodeId, status, overallScore, brandDeviations, checkedAt }`. `enabled: false` means Brand Guard was off for that app — use `lamina_brand_score` to score it on demand instead.
Score how on-brand a completed run's image output is, and get back the specific deviations. Supports workflow, atomic, and pipeline runs that contain a completed image. Video-only and compose results return a typed unsupported-media error before scoring. - `runId` (required): a completed run from `lamina_run`, atomic image generation, or `lamina_execute`. - `brandProfileId` (optional for workflow runs): brand to score against. Workflow runs default to the app's brand; atomic and pipeline results must pass it explicitly. - Returns `{ brandFit, dimensions, deviations, suggestions }`. `brandFit` is 0–100; `deviations` lists the specific guideline breaks; `suggestions` is how to fix them. - Use it as the check in a generate → score → refine loop: if `brandFit` is below your bar, feed `deviations`/`suggestions` back into the next generation.
Automatically improve a completed WORKFLOW image run until it's on-brand — without letting it run away on cost. Atomic, pipeline, and compose runs are rejected with `unsupported_run_kind` before spend because the current refinement service requires workflow-node provenance. - `runId` (required): a completed run to improve. - `minScore` (optional, default 80): stop once `brandFit` reaches this (0–100). - `maxIterations` (optional, default 3): most refine passes to try (1–10). - `maxCredits` (optional, default 200): hard credit ceiling — never exceeded. - `brandProfileId` (optional): brand to score against. Defaults to the run's app brand. - Returns `{ best, iterations, creditsSpent, stoppedBecause, attempts }`. `best.imageUrl` is the highest-scoring result; `stoppedBecause` is `met_score` | `max_iterations` | `budget` | `no_output`.
Check the workspace's credit balance and the credit packages available to buy — so you can tell the user what they have and what a top-up would cost before running anything expensive. - Returns `{ balance, packages[] }`. Each package has `{ packageId, name, credits, priceCents, currency }`. - To buy one, hand the `packageId` to `lamina_topup`.
Start a credit top-up and get back a Stripe-hosted checkout link to give the user. Use this when the balance is too low to run what they asked for — no need to send them to a dashboard. - `packageId` (required): from `lamina_credits`. - Returns `{ checkoutUrl, credits, amountCents, currency }`. Give the user the `checkoutUrl` to pay; once they do, the credits land on the workspace and you can retry the run. - Never ask the user for card details — they pay on the Stripe page the link opens.
Overview
What is Lamina?
Lamina is a remote MCP server that gives your agent on-brand product video & image generation. Point it at a brief and a brand kit and it returns finished product photos, video reels, and campaign ready banners -> brand guardrails (colors, fonts, tone, style) enforced automatically, no prompt engineering.
Who it is for
- Developers wiring generative media into an app or agent — one endpoint, 14 typed tools, Bearer-token auth.
- Marketers in Claude Code — describe the shot in plain language and get campaign-ready, on-brand creative back. No design tool, no photoshoot.
Tools
lamina_brand— pull the workspace brand guidelineslamina_run— generate: product_shoot, vertical_reel, ad_variant, virtual_try_on, campaign_banner, brand_filmlamina_status— poll a job to completion
~12s per asset · 35+ variants per product · multi-channel auto-formatting.
Quickstart
npx add-mcp https://app.uselamina.ai/mcp/agent
Get an API key at https://app.uselamina.ai/developers. Works with Claude, Claude Code, Cursor, Windsurf, VS Code, Zed, Raycast, n8n, and Zapier. Free to start.
Frequently asked questions
What is the Lamina — On-brand product photos, video & try-ons remote MCP server?
The Lamina — On-brand product photos, video & try-ons remote MCP server is a hosted Model Context Protocol endpoint at https://app.uselamina.ai/mcp/agent, so AI assistants can connect to it without installing or running anything locally.
How do I connect to the Lamina — On-brand product photos, video & try-ons MCP server?
Add the endpoint https://app.uselamina.ai/mcp/agent to any MCP-compatible client such as Claude Code, Cursor, or VS Code. The setup snippets on this page configure each client in one step.
Does the Lamina — On-brand product photos, video & try-ons MCP server require authentication?
Yes. Lamina — On-brand product photos, video & try-ons requires an API key — check the official documentation for how to obtain one, then include it in your client's configuration.
Which transport does the Lamina — On-brand product photos, video & try-ons MCP server use?
Lamina — On-brand product photos, video & try-ons exposes a Streamable HTTP endpoint, the transport used by remote MCP servers and supported by all major MCP clients.
Basic information
Transport
Streamable HTTP
Authentication
API Key
Category
Media & Design
Author
Lamina
Submitted by
Barada Sahu
More remote MCP servers
Aiera
Earnings events, transcripts, filings
Agent Utility MCP
Production remote MCP server with exactly 100 bounded tools: 85 paid tools and 15 free tools for text, JSON, encoding, identifiers, numbers, dates, URLs, validation, and secure web intelligence. Paid calls are charged on
AllTrails
Find your next hike
Air Pipe
Build, validate, deploy — HTTP APIs, cron jobs, webhooks and MCP tools — from your AI client.
Amplitude
Give your teams powerful behavioral insights
Comments