{
 "service": "VidMee \u2014 the Visual Agent Runtime",
 "mcp_endpoint": "https://mcp.vidmee.ai/mcp",
 "rest_base": "https://api.vidmee.ai",
 "auth": "Authorization: Bearer <vm_live_... API key> \u2014 free key at https://dashboard.vidmee.ai",
 "docs": "https://vidmee.ai/docs/",
 "llms_txt": "https://api.vidmee.ai/llms.txt",
 "tool_count": 49,
 "tools": [
  {
   "name": "analyze_document",
   "description": "QA a PDF (\u00a711.4): render each page to an image and analyze layout/legibility/content. Accepts a public PDF URL or base64. Cost: 5 analysis credit(s) (~$0.05).",
   "params": [
    "source",
    "pdf_base64",
    "max_pages",
    "questions",
    "client_ref"
   ],
   "required": []
  },
  {
   "name": "analyze_screenshot",
   "description": "Analyze a screenshot the agent already has. Pass EITHER `image_base64` (bytes of an image you have LOCALLY \u2014 a screenshot, a composited/edited image, a logged-in page) OR `image_url` (a PUBLIC http(s) image URL \u2014 fetched SSRF-guarded). Local files (`file://` or a filesystem path) CANNOT be used as `image_url`: the hosted service can't reach your disk \u2014 read the file's bytes and pass them as `image_base64`. Returns a structured UI/UX + accessibility critique and an asset_id you can pass to compare_screenshots. Tag it to find it via search_visual_memory. Cost: free (utility/read).",
   "params": [
    "image_base64",
    "image_url",
    "questions",
    "tags",
    "client_ref"
   ],
   "required": []
  },
  {
   "name": "analyze_video",
   "description": "Judge a user-journey recording (\u00a711.3): sample frames from a video URL or stored asset and analyze the sequence, catching mid-flow issues (stuck spinner, layout break after a click). Cost: 10 analysis credit(s) (~$0.10).",
   "params": [
    "source",
    "asset_id",
    "frames",
    "questions",
    "client_ref"
   ],
   "required": []
  },
  {
   "name": "analyze_visual",
   "description": "Analyze a stored visual asset and return a structured critique. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "asset_id",
    "questions",
    "comparison_asset_id",
    "client_ref"
   ],
   "required": [
    "asset_id"
   ]
  },
  {
   "name": "browser_act",
   "description": "Act on the page and get the resulting observation. `action` \u2208 click|fill|hover|select|press| scroll|wait. Target by `ref` (from an observation, preferred) or a CSS `selector`. `text` = the value to fill/select, the key to press, or scroll pixels. Cost: free (utility/read).",
   "params": [
    "session_id",
    "action",
    "ref",
    "selector",
    "text"
   ],
   "required": [
    "session_id",
    "action"
   ]
  },
  {
   "name": "browser_close",
   "description": "Close a browser session and free its Chromium context. Always call when finished. Cost: free (utility/read).",
   "params": [
    "session_id"
   ],
   "required": [
    "session_id"
   ]
  },
  {
   "name": "browser_navigate",
   "description": "Navigate a session to a URL. Returns a compact observation: page title + interactive elements (role/label/state/bbox) each with a stable `ref` you pass to browser_act. Cost: free (utility/read).",
   "params": [
    "session_id",
    "url"
   ],
   "required": [
    "session_id",
    "url"
   ]
  },
  {
   "name": "browser_observe",
   "description": "Re-observe the session's current page (after an action or a client-side update): title + interactive elements with refs. Cost: free (utility/read).",
   "params": [
    "session_id"
   ],
   "required": [
    "session_id"
   ]
  },
  {
   "name": "browser_open",
   "description": "Open a persistent browser session you can drive turn-by-turn, then close. Returns a `session_id`. Pass `start_url` to land ON a page immediately (else it starts on about:blank and browser_observe returns nothing until you browser_navigate). Flow: browser_open \u2192 browser_observe \u2192 browser_act (click/fill by the `ref`s in the observation) \u2192 browser_screenshot \u2192 browser_close. LOGGED-IN: pass `login=<name>` to resume a VAULTED authenticated session (see save_login / list_logins) \u2014 log in once, reuse by name forever. Or pass raw `storage_state` from a prior browser_save_session. ALWAYS browser_close when done (sessions are capped + auto-reaped). Cost: free (utility/read).",
   "params": [
    "viewport",
    "start_url",
    "storage_state",
    "login"
   ],
   "required": []
  },
  {
   "name": "browser_save_session",
   "description": "Export the session's logged-in state (cookies + localStorage) so you can resume it later via browser_open(storage_state=...). Useful for authenticated flows \u2014 log in once, reuse. You hold the state; VidMee does not store credentials server-side. Cost: free (utility/read).",
   "params": [
    "session_id"
   ],
   "required": [
    "session_id"
   ]
  },
  {
   "name": "browser_screenshot",
   "description": "Screenshot the session's current page. Returns {screenshot_b64} (PNG). Cost: free (utility/read).",
   "params": [
    "session_id"
   ],
   "required": [
    "session_id"
   ]
  },
  {
   "name": "capture_element",
   "description": "Screenshot ONE element on a page at full resolution and store it as an asset \u2014 the fix for \"the logo is inline SVG/CSS with no <img src> and the favicon is too small to composite.\" Give a CSS `selector` for the element (first match, e.g. 'header a[aria-label*=logo]', 'svg.logo', '.brand'); returns an asset_id + signed URL you can feed straight into composite_image, plus a short vision `analysis` of the element (colors/text) \u2014 capture also runs one describe pass. Errors clearly if the selector matches nothing, so you refine it instead of compositing the wrong thing. Cost: free (utility/read).",
   "params": [
    "url",
    "selector",
    "viewport",
    "client_ref"
   ],
   "required": [
    "url",
    "selector"
   ]
  },
  {
   "name": "check_crawlability",
   "description": "AEO agent-readability check (Sympha \u00a76.5): compares the raw no-JS HTML an AI crawler ingests against VidMee's JS-rendered view, and quantifies the content INVISIBLE to non-JS crawlers / LLM answer-engines (the \"client-side JS hides your content from AI discovery\" failure mode). Returns {verdict: crawler_blind|partial_gap|mostly_visible, invisible_ratio, raw/rendered word counts, invisible_word_sample, recommendation}. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "source",
    "client_ref"
   ],
   "required": [
    "source"
   ]
  },
  {
   "name": "compare_screenshots",
   "description": "Compare two screenshots (pixel + semantic) and return regression deltas. Cost: 2 analysis credit(s) (~$0.02).",
   "params": [
    "before_asset_id",
    "after_asset_id",
    "criteria",
    "client_ref"
   ],
   "required": [
    "before_asset_id",
    "after_asset_id"
   ]
  },
  {
   "name": "compose_video",
   "description": "Assemble generated frames/clips into ONE finished video, server-side (\u00a711) \u2014 no ffmpeg on your end. Each clip in `clips` = {asset_id|asset_uri|url, duration_seconds, kind?: image|video, motion?: zoom_in|zoom_out|none}. `default_motion` applies Ken Burns to every clip at once (turns a slideshow into real motion video). Add voiceover/music via audio_url or audio_asset_id (muxed without truncation). transition = crossfade|fade|none; fit = cover (crop) | contain (letterbox). analyze=true adds a built-in frame-level QA. Returns the stored video asset + signed URL + duration. Tip: image models garble text \u2014 composite crisp text into your frames first. Cost: generation \u2014 provider cost + margin (variable); call `estimate` for a quote.",
   "params": [
    "clips",
    "audio_url",
    "audio_asset_id",
    "transition",
    "transition_duration",
    "default_motion",
    "captions",
    "resolution",
    "fps",
    "fit",
    "analyze",
    "client_ref"
   ],
   "required": [
    "clips"
   ]
  },
  {
   "name": "composite_image",
   "description": "Edit a REAL photo deterministically: REMOVE existing branding and ADD new branding, keeping the product pixel-identical (what diffusion/generate_until_pass CANNOT do \u2014 it mangles labels and reshapes the product). `base` = {asset_id|asset_uri|url} (or a bare url/asset_id string). `elements` is an ordered list; each is ONE of: \u2022 {type:\"patch\", x, y, width, height, anchor?, sample_dx?, sample_dy?, feather?, brightness_match?} \u2014 COVER/REMOVE a region (e.g. the old partner logo/wordmark) by cloning a clean part of the SAME photo over it (no white box, no diffusion). Defaults clone the strip just below the rect; sample_dx/sample_dy pick a different clean source; feather softens the seam; brightness_match (default true) matches a gradient surface. \u2022 {type:\"text\", text, x, y, font_size, color, weight?, letter_spacing?, anchor?, background?} \u2014 draw crisp brand text (use letter_spacing for a wordmark look). \u2022 {type:\"image\", source:{asset_id|url} (or asset_id/url inline), x, y, width?, height?, opacity?, anchor?} \u2014 overlay a logo image. x/y/width/height accept pixels or \"NN%\" of the base; anchor \u2208 top-left|center|top-right| bottom-left|... To SWAP a co-brand for another (the canonical job): first a `patch` over the old mark, THEN a `text`/`image` with the new mark at the same spot, e.g. elements=[{type:\"patch\", x:645, y:335, width:264, height:43}, {type:\"text\", text:\"TASHI\", x:\"66%\", y:\"39%\", anchor:\"center\", font_size:31, color:\"#d29a6c\", letter_spacing:7}] Tip: get the pixel box of the mark to remove from render_and_analyze/analyze findings (which carry bounding boxes) rather than guessing. Returns the stored image asset + signed URL. Cost: generation \u2014 provider cost + margin (variable); call `estimate` for a quote.",
   "params": [
    "base",
    "elements",
    "description",
    "client_ref"
   ],
   "required": [
    "base",
    "elements"
   ]
  },
  {
   "name": "create_baseline",
   "description": "Save a known-good screenshot `asset_id` as a named baseline. Later inspect(compare_to=\"<name>\") diffs a fresh render against it \u2192 a regression verdict. (Shortcut: inspect(save_baseline=\"<name>\") captures the current render as the baseline in one step.) Cost: free (utility/read).",
   "params": [
    "name",
    "asset_id",
    "viewport",
    "description"
   ],
   "required": [
    "name",
    "asset_id"
   ]
  },
  {
   "name": "create_ticket",
   "description": "File a finding as a ticket. `target`='github' (needs `repo`=\"owner/repo\" + `token` PAT) or 'slack' (needs `webhook_url`). Body is built from an inspect/test_flow `result`, or pass `body` markdown directly. Creds are used in-request and never stored. Returns {filed, issue_url}. Cost: free (utility/read).",
   "params": [
    "target",
    "title",
    "result",
    "body",
    "repo",
    "token",
    "webhook_url",
    "labels"
   ],
   "required": [
    "target"
   ]
  },
  {
   "name": "creative_self_qa",
   "description": "One-call creative gate (Sympha \u00a76.7): runs the self-QA a generated asset must pass before the approval queue \u2014 text renders (not garbled), brand/logo faithful (vs `reference` brand-kit), `expected_aspect` correct (1:1|4:5|16:9|9:16|...), no blocking visual defects. candidate and reference are {asset_id|asset_uri|url} or bare strings. Returns {passed, checks[], blocking}. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "candidate",
    "reference",
    "expected_aspect",
    "criteria",
    "client_ref"
   ],
   "required": [
    "candidate"
   ]
  },
  {
   "name": "delete_login",
   "description": "Delete a vaulted login by name. Cost: free (utility/read).",
   "params": [
    "name"
   ],
   "required": [
    "name"
   ]
  },
  {
   "name": "edit_image",
   "description": "Edit a REAL photo from a plain-language instruction, PRESERVING what you name and designing the rest \u2014 the grounded middle ground between composite_image (pixel-exact, no design) and generate_visual (text->image, invents the product). Hands-off co-branding: keep the real product + its branding, add/restyle a partner brand, and it SELF-CORRECTS until it passes a fidelity gate. `source` = the photo (asset_id or url). `instruction` = what to change, in English. `keep` = things that must NOT change (e.g. [\"the Mama Tan's jar, its label and all its text\", \"the bottle shape\", \"the wax seals\"]); optional \u2014 omit it and the product + its branding are preserved by default. `references` = optional image asset_ids/url to guide the design (e.g. the real partner logo). `provider`: \"gemini\" (Nano Banana, default, best preservation) or \"kontext\". A built-in gate compares the result to the original (are the KEEP items unchanged? was the edit clean, no gibberish/leftover branding?) and RE-EDITS with feedback up to `max_attempts` times. Returns the edited asset + signed URL + `fidelity`{passed, attempts, issues} + safe_to_ship. Just describe what to keep and what to change \u2014 the tool handles the rest. For PIXEL-EXACT preservation of a region (e.g. lock the real product byte-identical), pass `lock_regions` = [{x, y, width, height}] (px or \"NN%\" of the source); those pixels are restored from the original after the edit. Cost: generation \u2014 provider cost + margin (variable); call `estimate` for a quote.",
   "params": [
    "source",
    "instruction",
    "keep",
    "references",
    "provider",
    "max_attempts",
    "lock_regions",
    "client_ref"
   ],
   "required": [
    "source",
    "instruction"
   ]
  },
  {
   "name": "estimate",
   "description": "Price a job BEFORE running it \u2014 no job created, no charge (#4). Generation is pass-through provider cost + margin (an estimate that settles on the real cost); analysis is a flat credit price. Pass the same `operation` + `input` you'd send to that tool. Budget a batch with this. Cost: free (utility/read).",
   "params": [
    "operation",
    "input"
   ],
   "required": [
    "operation"
   ]
  },
  {
   "name": "extract",
   "description": "Extract structured data from any web page. Give `fields` ([\"price\",\"headline\"] or {\"price\":\"the displayed price\"}) or a `schema`; VidMee renders the page and returns JSON matching that shape, grounded on the screenshot + page text (null for anything absent \u2014 never invented). Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "source",
    "fields",
    "schema",
    "instructions",
    "viewport",
    "client_ref"
   ],
   "required": [
    "source"
   ]
  },
  {
   "name": "find_similar_visuals",
   "description": "Find screenshots that LOOK like this one (\u00a78.1) \u2014 true image\u2192image similarity (DINOv2), for regression baselines / near-duplicate detection / visual grouping. Query by a stored asset_id or an uploaded image. Returns assets ranked by visual similarity (0-1). Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "asset_id",
    "image_base64",
    "namespace",
    "limit"
   ],
   "required": []
  },
  {
   "name": "generate_until_pass",
   "description": "Closed loop (\u00a711.1): generate an image, judge it against `criteria`, and regenerate until it passes or max_attempts. Returns the passing asset + full attempt history. Pass `reference_asset_id` to judge FIDELITY to a real reference image (truth to source) instead of aesthetics \u2014 the loop then rejects convincing fakes. Note: for \"preserve this exact product unchanged\", no model can pass \u2014 use composite_image instead of regenerating. Cost: generation \u2014 provider cost + margin (variable); call `estimate` for a quote.",
   "params": [
    "prompt",
    "criteria",
    "max_attempts",
    "media_type",
    "budget_hint",
    "reference_asset_id",
    "client_ref"
   ],
   "required": [
    "prompt"
   ]
  },
  {
   "name": "generate_visual",
   "description": "Generate an image or video; VidMee routes to the best provider. Returns URL + description + which provider served it and the available alternatives. PROVIDER CHOICE: pass `provider` = 'zark' | 'fal' | 'replicate' to force one (respected exactly, no failover), or leave 'auto'. Guidance: **cinematic/action VIDEO \u2192 zark**; simple/cheap \u2192 fal; premium one-off image \u2192 replicate. Or set `quality_hint='cinematic'` and auto-routing sends video to Zark. Call `list_providers` to see all options. EMBEDDING: set `public=true` to get a permanent, CDN-cached `public_url` (safe for blog <img> / og:image); otherwise the `url` is a 24h signed link. `size` picks the shape: a preset (`og`, `square`, `x`, `story`, `banner`), pixels (`1200x630`), or a ratio (`16:9`). HOUSE STYLE: pass `brand_kit` (a stored brand slug) + `template` (`blog-hero`, `og`, `square-post`, `x-card`) + `title` instead of a full prompt to generate in a client's brand style automatically. Notes: (1) diffusion renders precise TEXT unreliably \u2014 for exact wording overlay it yourself. (2) VIDEO is one short clip per call. `namespace` files the output into a memory bucket. Cost: generation \u2014 provider cost + margin (variable); call `estimate` for a quote.",
   "params": [
    "prompt",
    "media_type",
    "style_reference_asset_id",
    "provider",
    "quality_hint",
    "budget_hint",
    "namespace",
    "public",
    "size",
    "brand_kit",
    "template",
    "title",
    "client_ref"
   ],
   "required": []
  },
  {
   "name": "get_brand_kit",
   "description": "Read one stored brand kit (palette, wordmark, style, logo asset) by slug. Cost: free (utility/read).",
   "params": [
    "slug"
   ],
   "required": [
    "slug"
   ]
  },
  {
   "name": "get_result",
   "description": "Fetch the result of a previously-started job (blocks internally up to ~60s). Cost: free (utility/read).",
   "params": [
    "job_id"
   ],
   "required": [
    "job_id"
   ]
  },
  {
   "name": "get_wallet",
   "description": "Check this project's VidMee balance before spending: returns the analysis-credit balance (discountable, for QA/analysis ops), the generation cash balance (never discounted, +margin), any funds currently held for in-flight generations, and the active wallet_mode. When wallet_mode is 'off' the wallet is inert and every balance reads zero \u2014 nothing is billed. Cost: free (utility/read).",
   "params": [],
   "required": []
  },
  {
   "name": "ingest_visual",
   "description": "Ingest an external image/video URL into visual memory (SSRF-guarded). Optionally scope it to a `namespace` and set a `ttl_seconds` after which it's auto-purged (\u00a78.2). Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "source_url",
    "context",
    "tags",
    "namespace",
    "ttl_seconds",
    "client_ref"
   ],
   "required": [
    "source_url"
   ]
  },
  {
   "name": "inspect",
   "description": "THE hero tool \u2014 inspect a page and get one actionable verdict. Fuses render + UX critique + real axe-core accessibility + deterministic runtime findings + visual-memory matches (has this screen/bug been seen before?) into: {verdict, safe_to_ship, scores, summary, findings, accessibility, memory_matches, semantic_diff, next_actions}. `goal` steers the critique; `compare_to` = a baseline NAME or a prior screenshot asset_id to diff against (\u2192 regression verdict); `save_baseline` = a name to store THIS render as the known-good baseline. Use this first for \"is this page good, did it regress, what should I do?\". Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "source",
    "goal",
    "viewport",
    "compare_to",
    "save_baseline",
    "client_ref"
   ],
   "required": [
    "source"
   ]
  },
  {
   "name": "link_asset_to_issue",
   "description": "Stamp a tracker issue ref onto the asset (reverse of the asset_uri you put in the issue), so outcomes can flow home. Idempotent. Cost: free (utility/read).",
   "params": [
    "asset_id",
    "external_system",
    "external_ref"
   ],
   "required": [
    "asset_id",
    "external_system",
    "external_ref"
   ]
  },
  {
   "name": "list_baselines",
   "description": "List this project's named visual baselines (name \u2192 asset + status). Cost: free (utility/read).",
   "params": [],
   "required": []
  },
  {
   "name": "list_brand_kits",
   "description": "List this project's stored brand kits (slug + name + palette + style). Pass a slug as `brand_kit` to generate_visual (with a `template` + `title`) to produce in that house style \u2014 no hand-carried brand prompt. Create/extract kits via the REST /v1/brands API or profile_brand. Cost: free (utility/read).",
   "params": [],
   "required": []
  },
  {
   "name": "list_logins",
   "description": "List your vaulted logged-in sessions (names + domains + expiry \u2014 never the stored credentials). Pass a name as login=\"<name>\" to browser_open / test_flow / inspect. Cost: free (utility/read).",
   "params": [],
   "required": []
  },
  {
   "name": "list_models",
   "description": "The generation model catalog: per media (video/image), per task (cinematic, product, portrait, text, ...), which model each provider uses and the tuned params. Pass `task_class` or `quality_hint` to generate_visual to target a task. Cost: free (utility/read).",
   "params": [],
   "required": []
  },
  {
   "name": "list_providers",
   "description": "List the generation providers, what each is best at, which are available now, and how to force one (feedback 2026-07-10). Use this before generate_visual to pick a provider for cinematic video, cheap images, etc. Cinematic/action video \u2192 'zark'. Cost: free (utility/read).",
   "params": [],
   "required": []
  },
  {
   "name": "og_check",
   "description": "Social share-card probe (Sympha \u00a76.3): extract a page's Open Graph / Twitter meta, fetch the og:image, and confirm it loads with correct dimensions (~1.91:1, ideal 1200x630). Returns {verdict, meta{title,description,image,twitter_card}, image_loads, image_dimensions, issues}. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "source",
    "client_ref"
   ],
   "required": [
    "source"
   ]
  },
  {
   "name": "record_outcome",
   "description": "Record downstream truth about a prior judgment (the label that makes VidMee provable): outcome \u2208 confirmed_real|fixed|reopened|false_positive|dismissed|wont_fix. Append-only + idempotent; multiple outcomes on one asset form a timeline. Pass external_system/external_ref (e.g. 'paperclip'/'BUY-59847') to tie it to the tracker issue. Cost: free (utility/read).",
   "params": [
    "asset_id",
    "outcome",
    "analysis_id",
    "source",
    "external_system",
    "external_ref",
    "detail",
    "occurred_at",
    "idempotency_key"
   ],
   "required": [
    "asset_id",
    "outcome"
   ]
  },
  {
   "name": "render_and_analyze",
   "description": "Render a URL/HTML in a sandboxed browser, screenshot it, and return a structured UI/UX + accessibility critique with concrete fixes. Returns text + a signed URL. Pass `namespace` (\u00a78.2) to file the screenshot into a memory bucket (e.g. a competitor set). Cost: 2 analysis credit(s) (~$0.02).",
   "params": [
    "source",
    "source_type",
    "viewport",
    "questions",
    "wait_for",
    "namespace",
    "client_ref"
   ],
   "required": [
    "source"
   ]
  },
  {
   "name": "render_template",
   "description": "Deterministic on-brand static from design tokens + a template (Sympha \u00a76.7 Tier-1) \u2014 no AI, so it's on-brand by construction and text is always crisp. `template` \u2208 quote_card|og_card|thumbnail|announcement|story. `tokens` = {bg, accent, text_color, subtitle_color} (hex). `content` = {title, subtitle, badge, logo:{source:{asset_id|url}}}. Returns the stored image asset + signed URL. Cost: generation \u2014 provider cost + margin (variable); call `estimate` for a quote.",
   "params": [
    "template",
    "tokens",
    "content",
    "description",
    "namespace",
    "client_ref"
   ],
   "required": [
    "template"
   ]
  },
  {
   "name": "report",
   "description": "Build a structured markdown QA report (verdict, findings table, accessibility, regression, evidence, next actions) from an inspect/test_flow `result`, or from a `source` URL (runs inspect first). Returns {report_markdown, verdict, safe_to_ship}. Feed the markdown to create_ticket. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "result",
    "source",
    "title",
    "client_ref"
   ],
   "required": []
  },
  {
   "name": "save_login",
   "description": "Vault a LOGGED-IN browser session under `name` so you can resume authenticated QA/browsing by name later \u2014 log in ONCE, reuse forever (no re-holding cookies). Three ways to provide it: `from_session_id` (capture a live browser_open session you just logged into \u2014 the usual path), a raw `cookies` list, or a full `storage_state`. Stored ENCRYPTED, scoped to your project, expires in `expires_days`. Then pass login=\"<name>\" to browser_open / test_flow / inspect. Cost: free (utility/read).",
   "params": [
    "name",
    "from_session_id",
    "cookies",
    "storage_state",
    "expires_days",
    "label"
   ],
   "required": [
    "name"
   ]
  },
  {
   "name": "search_visual_memory",
   "description": "Search the persistent visual asset library by semantic query or tags. Pass `namespace` to scope the search to one memory bucket (e.g. a specific app or baseline set) \u2014 \u00a78.2. Cost: free.",
   "params": [
    "query",
    "tags",
    "asset_type",
    "namespace",
    "limit"
   ],
   "required": [
    "query"
   ]
  },
  {
   "name": "storyboard_video",
   "description": "Turn a shot list into ONE finished multi-shot video in a single call \u2014 VidMee generates every shot through the gen-quality path (best model + tuned params + failover), then stitches them with transitions and optional audio. No hand-assembly. Each item in `shots` = {prompt, duration?: seconds, camera?: move, quality_hint?, provider?}. Camera moves (static, dolly_in, dolly_out, pan_left, pan_right, tilt_up, tilt_down, zoom_in, zoom_out, orbit, tracking, handheld, crane_up, aerial) are expressed by augmenting the prompt with cinematographer language, because our video providers do NOT accept structured camera params \u2014 the response reports this per shot in `shots[].note`. Audio: audio_url/audio_asset_id overlays SUPPLIED audio; VidMee does not synthesize speech/music (a `narration` field is rejected with a note, never silently dropped). Returns the finished video asset + signed URL + per-shot breakdown + any capability_notes. Cost: generation \u2014 provider cost + margin (variable); call `estimate` for a quote.",
   "params": [
    "shots",
    "provider",
    "quality_hint",
    "aspect_ratio",
    "audio_url",
    "audio_asset_id",
    "captions",
    "transition",
    "resolution",
    "fps",
    "analyze",
    "client_ref"
   ],
   "required": [
    "shots"
   ]
  },
  {
   "name": "sweep_site",
   "description": "Crawl a site (sitemap or same-origin BFS) and QA every page (\u00a711.2): per-page issues + axe a11y + a severity rollup + a downloadable JSON report. The nightly-QA unit / demo. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "source",
    "max_pages",
    "viewport",
    "client_ref"
   ],
   "required": [
    "source"
   ]
  },
  {
   "name": "test_flow",
   "description": "Drive a multi-step browser flow and get a pass/fail verdict with an evidence screenshot. `steps` is a list of {action, ...}: navigate {url}; click/hover {ref|selector}; fill/select {ref|selector, text}; press {text: key}; scroll {text: pixels}; wait {text: ms}; observe; expect_text {text}; expect_no_errors. Refs come from the observation each step returns. Returns {passed, verdict, steps[], failed_steps[], telemetry, evidence_asset_id, screenshot_url}. Use this to validate signup/login/checkout/onboarding flows. AUTHENTICATED QA: pass login=\"<name>\" (from save_login / list_logins) to run the flow ALREADY logged in \u2014 test the part of the app behind auth without scripting the login every time. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "start_url",
    "steps",
    "viewport",
    "login",
    "client_ref"
   ],
   "required": [
    "start_url"
   ]
  },
  {
   "name": "validate_responsive",
   "description": "Render at multiple viewports and flag layout breaks / overflow / hidden CTAs. Cost: 5 analysis credit(s) (~$0.05).",
   "params": [
    "source",
    "source_type",
    "viewports",
    "client_ref"
   ],
   "required": [
    "source"
   ]
  },
  {
   "name": "verify_against_reference",
   "description": "Verify a CANDIDATE image is FAITHFUL to a REFERENCE image (the real product / ground truth) \u2014 scores TRUTH TO SOURCE, not aesthetics. Catches AI-generated fakes that look great but show the wrong product/brand/label (analyze_screenshot can't \u2014 it scores beauty). `candidate` and `reference` are each {asset_id|asset_uri|url} (or a bare asset_id/url string). `criteria` names the protected elements (\"bottle shape, label wording, logo, brand colors\"). Returns {faithful, fidelity_score 0-1, verdict, discrepancies[], preserved[]} \u2014 faithful=false if any critical/high discrepancy (wrong product, altered label, substituted logo, wrong colors). This is the fidelity PRIMITIVE: creative_self_qa calls it as one check in a batteries-included gate, and generate_until_pass(reference_asset_id=...) calls it as the loop's pass/fail judge. Cost: 1 analysis credit(s) (~$0.01).",
   "params": [
    "candidate",
    "reference",
    "criteria",
    "client_ref"
   ],
   "required": [
    "candidate",
    "reference"
   ]
  }
 ]
}
