The GooseWorks Product Photos skill. You pick a brand + product and submit one generation;
the backend runs the whole pipeline (compose the shot prompt → generate on gpt_image_2 →
judge for product fidelity → auto-retry a few times for free) and stores the results. You do NOT
generate images, call a model, or manage files — this is the exact same workflow the Product
Photos studio uses, so the skill and the app can never drift. The point is to enrich a brand's
usable product imagery — approved photos join the brand kit and can then feed the ad workflow
(goose-ads).
Prerequisite — the GooseWorks MCP server is REQUIRED
Everything goes through the mcp__gooseworks__* tools. If they are not available, stop and
tell the user to run gooseworks install --claude --mcp (and restart Claude Code). There is no
HTTP/file fallback.
Start from the brand context — don't re-ask what it already answers
If the gooseworks router handed you brand context, USE IT. If you were invoked directly, call
brand_get_context yourself first. It answers most of the setup questions below, so do not ask
the user for them:
- Which product? — the context's
products[]are the real catalog entries. Offer them; never invent a product or ask the user to describe one you can already see. - What does it look like / what is it made of? — grounded in the product's stored images and description. Never guess a material, colorway, or silhouette.
- What vibe / who is it for? — the context's voice, positioning, and audience already say. Let them shape the scene and styling instead of asking "what mood do you want?".
- Brand look — logo, colors, and fonts are owned by the backend research pass. Read them, never re-derive them.
Ask only for the genuinely open choices: the shot category, how many photos, quality, and
whether a human model is wanted (which needs explicit consent — see the rules).
Identity & credits
- One agent-scoped token authenticates the tools; they resolve your org automatically. Never
print the token. (You may pass an optional
targetto operate on a specific agent/org, exactly as the other GooseWorks tools; omit it to use your pinned scope.) - Credits are handled by the backend.
generate_product_photosreserves the estimated cost up front and bills only the photos that pass the judge — automatic retries are free, and a photo the judge can't get right (flagged) is shown but never billed. Callestimate_product_photosfirst to quote the cost;get_ad_creditsshows the balance.
The tools
Pick the brand + product
list_ad_brands— the user's ad brands (get abrand_id; also carriesslug).list_brand_products { brand_id, search?, page?, page_size? }— the brand's imported products. Pick aproduct_idto shoot.searchmatches name / type / variant / SKU.import_product { brand_id, kind, url, product_name? }— import a product if it isn't in the catalog yet.kindisproduct_url(a single product page),shopify_store(a store URL → imports the catalog), orimage_url(a direct image; requiresproduct_name). Returns an import row with anid; if itsstatusisn'tcomplete, pollget_product_importuntil it is, thenlist_brand_productsto find the new product. (File uploads aren't available over MCP — use a URL.)get_product_import { import_id }— poll an import untilstatusiscompleteorfailed.
Generate
estimate_product_photos { count, quality? }— cost preview (per-photo + total credits).countis 1, 2, 4, or 8;qualityislow|medium|high(defaultmedium). Reserves nothing.generate_product_photos { brand_id, product_id, variant_id?, category, controls?, prompt?, count?, quality?, reference_image_urls?, attestation_accepted? }— the one call that makes photos.categoryisapparel|beauty|cpg(seeds sensible scene/framing defaults). Omitcontrolsto use the category preset; passpromptas free-text steering added on top of the settings (it doesn't replace them). Returns a generation with anidimmediately — pollget_product_photo_generationuntil done, then read eachoutputs[].final_image_url. If you request a human model (controls.model.presenceis notnone) you MUST passattestation_accepted: trueto confirm the user has the rights for model imagery.get_product_photo_generation { generation_id }— poll untilstatusiscomplete,partial_failure, orfailed. Eachoutputs[]entry has its ownstatusand, once ready, afinal_image_url. Aflaggedoutput is the best attempt but wasn't billed.
Use the results
list_product_photos { brand_id, archived? }— the brand's generated photos (archived: false= active,true= archived).approve_product_photo { output_id }— approve a photo: links it to the product and makes it available in the brand kit, sogoose-adscan use it. Photos are not used anywhere until approved.archive_product_photo { output_id, reason? }— archive a photo; archived photos are excluded from ad generation.
Workflow — shoot a product
- Load the brand context (
brand_get_context, or reuse what the router passed you) and resolve the brand + product.list_ad_brands→brand_id.list_brand_products→ pick aproduct_idfrom the catalog you already know about. If the product genuinely isn't there,import_product(pollget_product_import). - Quote the cost.
estimate_product_photos { count, quality }→ tell the user credits. - Generate.
generate_product_photos { brand_id, product_id, category, count, quality, prompt? }. Buildpromptfrom the brand's voice/positioning you already have — don't interview the user for it. Returns a generationidright away. - Poll.
get_product_photo_generation { generation_id }until terminal; hand back eachfinal_image_url. - Approve the keepers. Show the results and let the user pick;
approve_product_photothe ones they'd publish (that's what puts them in the brand kit for ads),archive_product_photothe rest.
Rules
- Never invent product facts. The backend grounds the shot on the product's real images; don't describe a product you can't see.
- Use the brand context instead of interviewing the user. Product, audience, voice, positioning,
logo/colors/fonts all come from
brand_get_context/ the brand kit. Ask only for the shot category, count, quality, and model consent. - Ask before spending. Quote the estimate and confirm
count/qualitybeforegenerate_product_photos— it reserves credits. - Poll, don't re-submit. A generation that's still
runningis not stuck; re-submitting double-bills. Only afailedgeneration should be retried. - Model imagery needs consent. Only set a human model when the user asks, and pass
attestation_accepted: true. - Approval is the hand-off to ads. Remind the user that only approved photos reach the brand kit / ad workflow; archived ones never do.