[
  {
    "name": "add_captions",
    "description": "Auto-transcribe a video's audio (Whisper → Deepgram → Gemini waterfall, word-level timings) OR burn caller-provided captions in one pass. Manual mode accepts either plain `subtitle_text` or timestamped `subtitles[]` and skips transcription. Four styles: `tiktok` (word-by-word purple highlight, Bebas Neue, all caps), `hormozi` (bold centered 1-3 words, yellow highlight), `classic` (bottom subtitle bar, semi-transparent), `karaoke` (word-by-word progressive color fill). CJK languages (zh/ja/ko) auto-detected and rendered with Noto CJK fonts bundled in the worker image. Return `transcript` is the provider transcript in auto mode; in manual mode it is normalized visible text with control characters stripped, whitespace collapsed, and segment texts joined by spaces.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https:\\/\\/.*",
          "description": "Source video URL (must be https://). Audio extracted server-side in auto mode."
        },
        "style": {
          "default": "classic",
          "description": "Caption preset name. Built-ins: tiktok, hormozi, classic, karaoke (+ neon-pop demonstrator). Each declares default position, font, colors, animation. Designer-extendable via YAMLs in skills-worker/src/pika_mcp_skills/add_captions/presets/. Default: classic.",
          "type": "string"
        },
        "position": {
          "description": "Caption vertical position; overrides preset default.",
          "type": "string",
          "enum": [
            "top",
            "middle",
            "bottom"
          ]
        },
        "font": {
          "description": "Bundled font. CJK content always uses noto-cjk regardless.",
          "type": "string",
          "enum": [
            "bebas-neue",
            "inter",
            "noto-cjk"
          ]
        },
        "font_color": {
          "description": "Main text color. Hex (#RRGGBB) or named: white|black|gray|red|orange|yellow|green|cyan|blue|purple|pink|magenta.",
          "type": "string"
        },
        "highlight_color": {
          "description": "Word-highlight color (word-swap / karaoke styles only). Hex or named.",
          "type": "string"
        },
        "outline_color": {
          "description": "Text outline color. Hex or named.",
          "type": "string"
        },
        "font_size": {
          "description": "Explicit pixel font size. Mutually exclusive with font_scale.",
          "type": "integer",
          "minimum": 12,
          "maximum": 200
        },
        "font_scale": {
          "description": "Font size multiplier of preset auto-base. S=0.75x, M=1.0x, L=1.25x, XL=1.5x. Mutually exclusive with font_size.",
          "type": "string",
          "enum": [
            "S",
            "M",
            "L",
            "XL"
          ]
        },
        "language": {
          "description": "Optional BCP-47 language hint (e.g. 'en', 'zh'). Auto-detect if omitted.",
          "type": "string"
        },
        "caption_mode": {
          "description": "Caption source. Omit for automatic behavior: auto when no manual input is provided, manual when subtitle_text or subtitles is provided. auto transcribes audio; manual skips transcription and burns caller-provided text.",
          "type": "string",
          "enum": [
            "auto",
            "manual"
          ]
        },
        "subtitle_text": {
          "description": "Plain script text for manual mode. The worker splits it into caption groups and spreads timing across the detected video duration; very dense text may be rejected after media probing.",
          "type": "string",
          "minLength": 1,
          "maxLength": 20000
        },
        "subtitles": {
          "description": "Timestamped manual subtitles. Segments must be sorted, non-overlapping, and worker-validated against the video duration plus 0.25s tolerance after media probing.",
          "minItems": 1,
          "maxItems": 500,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "start_s": {
                "description": "Subtitle start time in seconds.",
                "type": "number",
                "minimum": 0
              },
              "end_s": {
                "description": "Subtitle end time in seconds; must be greater than start_s.",
                "type": "number",
                "minimum": 0
              },
              "text": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500,
                "description": "Subtitle text for this time range."
              }
            },
            "required": [
              "start_s",
              "end_s",
              "text"
            ]
          }
        }
      },
      "required": [
        "video_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "allOf": [
        {
          "not": {
            "allOf": [
              {
                "required": [
                  "subtitle_text"
                ]
              },
              {
                "required": [
                  "subtitles"
                ]
              }
            ]
          }
        },
        {
          "not": {
            "allOf": [
              {
                "required": [
                  "font_size"
                ]
              },
              {
                "required": [
                  "font_scale"
                ]
              }
            ]
          }
        },
        {
          "if": {
            "properties": {
              "caption_mode": {
                "const": "auto"
              }
            },
            "required": [
              "caption_mode"
            ]
          },
          "then": {
            "not": {
              "anyOf": [
                {
                  "required": [
                    "subtitle_text"
                  ]
                },
                {
                  "required": [
                    "subtitles"
                  ]
                }
              ]
            }
          }
        },
        {
          "if": {
            "properties": {
              "caption_mode": {
                "const": "manual"
              }
            },
            "required": [
              "caption_mode"
            ]
          },
          "then": {
            "anyOf": [
              {
                "required": [
                  "subtitle_text"
                ]
              },
              {
                "required": [
                  "subtitles"
                ]
              }
            ]
          }
        }
      ]
    }
  },
  {
    "name": "analyze_brief",
    "description": "Extract a structured product brief (default) OR a free-form prose summary from up to 5 sources: web pages, GitHub repos, PDFs, or raw text. The format field switches output shape:\n- `brief` (default): Gemini returns a normalised JSON brief —   product name, tagline, features, pain points, tone, etc. Ready   for video-script generation.\n- `summary` (AGNT-68 #21): Gemini returns 2-5 paragraphs of prose   capturing each source's gist + cross-source notable points. Use   for non-product content (research, news, docs) where the rigid   brief shape doesn't fit.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "sources": {
          "minItems": 1,
          "maxItems": 5,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "url",
                  "github",
                  "text"
                ],
                "description": "\"url\" for any web page or PDF, \"github\" for a public repo (README extracted), \"text\" for raw text or chat history."
              },
              "url": {
                "description": "Required when type is url or github.",
                "type": "string",
                "format": "uri"
              },
              "content": {
                "description": "Required when type is text.",
                "type": "string",
                "maxLength": 50000
              }
            },
            "required": [
              "type"
            ]
          },
          "description": "Input sources to extract product information from."
        },
        "context": {
          "description": "Optional extra context: target audience, video purpose, what to focus on.",
          "type": "string",
          "maxLength": 2000
        },
        "format": {
          "default": "brief",
          "description": "Output shape. `brief` (default) returns structured JSON product brief; `summary` returns free-form prose (2-5 paragraphs). Both share the same source-fetching pipeline (AGNT-68 #21).",
          "type": "string",
          "enum": [
            "brief",
            "summary"
          ]
        }
      },
      "required": [
        "sources"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "analyze_clip_highlights",
    "description": "Find the most viral-worthy clip ranges in a video transcript. Returns scored start/end timestamps + hook + reason for each clip. Pure atomic primitive — single LLM call, no media output. Caller chains: transcribe_audio → analyze_clip_highlights → edit_trim (and optionally add_captions). Pass EITHER `transcript_words` (word-level timestamps from transcribe_audio) OR `transcript_text` (pre-formatted with `[XmYs]` timestamps), not both. Optional face-detection metadata (`first_face_time_s`, `no_face_intervals`) constrains clips so they don't start during slides/title cards.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "transcript_words": {
          "description": "Word-level transcript with timestamps in seconds. Output of `transcribe_audio`. Mutually exclusive with `transcript_text`.",
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "text": {
                "type": "string"
              },
              "start_s": {
                "type": "number",
                "minimum": 0
              },
              "end_s": {
                "type": "number",
                "minimum": 0
              }
            },
            "required": [
              "text",
              "start_s",
              "end_s"
            ]
          }
        },
        "transcript_text": {
          "description": "Pre-formatted transcript with `[XmYs]` timestamps (e.g. `[1m23s] hello world`). Mutually exclusive with `transcript_words`.",
          "type": "string",
          "minLength": 1,
          "maxLength": 50000
        },
        "source_duration_s": {
          "type": "number",
          "exclusiveMinimum": 0,
          "maximum": 7200,
          "description": "Source video duration in seconds (max 2h)."
        },
        "num_clips": {
          "default": 3,
          "description": "How many clips to return (1-20, default 3).",
          "type": "integer",
          "minimum": 1,
          "maximum": 20
        },
        "clip_duration_s": {
          "default": 30,
          "description": "Target clip duration in seconds (10-120, default 30). Min/max are clamped server-side to ±15 of this target, bounded by [15, 90].",
          "type": "integer",
          "minimum": 10,
          "maximum": 120
        },
        "first_face_time_s": {
          "description": "Optional: time at which the speaker first appears on screen. Clips that would start before this are dropped (intro title cards).",
          "type": "number",
          "minimum": 0
        },
        "no_face_intervals": {
          "description": "Optional: list of `[start_s, end_s]` intervals where the speaker is NOT visible (slides, title cards, b-roll). Clips starting inside these intervals are dropped.",
          "maxItems": 100,
          "type": "array",
          "items": {
            "type": "array",
            "items": [
              {
                "type": "number"
              },
              {
                "type": "number"
              }
            ]
          }
        }
      },
      "required": [
        "source_duration_s"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "clips": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "rank": {
                "type": "integer",
                "exclusiveMinimum": 0,
                "maximum": 9007199254740991
              },
              "virality_score": {
                "type": "number",
                "minimum": 0,
                "maximum": 10
              },
              "hook": {
                "type": "string"
              },
              "reason": {
                "type": "string"
              },
              "start_s": {
                "type": "number",
                "minimum": 0
              },
              "end_s": {
                "type": "number",
                "minimum": 0
              },
              "duration_s": {
                "type": "number",
                "exclusiveMinimum": 0
              }
            },
            "required": [
              "rank",
              "virality_score",
              "hook",
              "reason",
              "start_s",
              "end_s",
              "duration_s"
            ],
            "additionalProperties": {}
          }
        },
        "source_duration_s": {
          "type": "number",
          "exclusiveMinimum": 0
        },
        "num_clips_requested": {
          "type": "integer",
          "exclusiveMinimum": 0,
          "maximum": 9007199254740991
        },
        "llm_model": {
          "type": "string"
        }
      },
      "required": [
        "clips",
        "source_duration_s",
        "num_clips_requested",
        "llm_model"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "analyze_media",
    "description": "Analyze an image, short video, OR audio clip with Gemini's multimodal model. The worker auto-routes based on the Content-Type the `media` URL responds with: image/*, video/*, audio/* (mp3/wav/m4a/aac/flac/ogg/opus/webm) all flow through the same inlineData → :generateContent path. Use for transcribing intent, mood/tone analysis, audio quality scoring, content moderation, etc. (AGNT-68 #20).",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "default": "gemini",
          "description": "Multimodal analysis provider. Default: `gemini` (gemini-3-flash). No alternative providers wired today.",
          "type": "string",
          "enum": [
            "gemini"
          ]
        },
        "media": {
          "type": "string",
          "format": "uri",
          "description": "Image, video, OR audio URL to analyze. Content-Type drives routing — audio/* MIME types are now accepted in addition to image/* and video/*."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Natural-language question or directive about the media."
        }
      },
      "required": [
        "media",
        "query"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "capture_website",
    "description": "Capture a public website via server-side Playwright. mode=video (default): navigates to the URL and records for duration_s seconds; the recorder ping-pong-scrolls the page (top → bottom → top) so a 53s recording covers a typical README in motion. Returns video_url (MP4). mode=screenshot: executes actions then captures a single PNG; faster and more reliable for single-frame use cases. Returns image_url. Use timed_actions for frame-accurate scroll/click/zoom steps synchronized to a TTS audio track; use actions for natural-language steps translated by Claude. Set mobile=true for 390×844 iPhone viewport (portrait output). Only public https:// URLs are supported.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https:\\/\\/.*",
          "description": "The public URL to record or screenshot (must be https://)."
        },
        "mode": {
          "default": "video",
          "description": "'video' (default) — record duration_s seconds, return video_url. 'screenshot' — execute actions then capture a PNG, return image_url.",
          "type": "string",
          "enum": [
            "video",
            "screenshot"
          ]
        },
        "actions": {
          "description": "Natural language description of actions to perform live during recording (e.g. 'scroll down slowly, click the sign-up button'). Translated to structured steps by Claude and executed by Playwright. Mutually exclusive with timed_actions.",
          "type": "string"
        },
        "timed_actions": {
          "description": "Structured actions with explicit timestamps (seconds from page-ready). Use for frame-accurate sync with a TTS audio track. Mutually exclusive with actions.",
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "type": {
                "type": "string",
                "enum": [
                  "scroll",
                  "click",
                  "type",
                  "navigate",
                  "wait",
                  "zoom"
                ]
              },
              "at_s": {
                "type": "number",
                "minimum": 0,
                "description": "Seconds from page-ready to fire this action."
              },
              "selector": {
                "description": "CSS selector. Scroll: element to scroll to. Click: element to click.",
                "type": "string"
              },
              "text_content": {
                "description": "Visible heading text to scroll to — resolved at runtime via 7-pass DOM search.",
                "type": "string"
              },
              "bbox_selector": {
                "description": "CSS selector to capture the post-action bbox of, separate from the action's own `selector`. Use when the action drives page state (e.g. `navigate`, px-based `scroll`) but the bbox you want measured is a different element on the resulting page — the worker scrolls `bbox_selector` into view (best-effort) before measuring. Timing replicates the legacy dual-emission follow-up: 600 ms post-action settle → scroll-into-view → 1300 ms post-scroll (1200 ms scroll-anim default + 100 ms) → measure. If both `selector` and `bbox_selector` are set, `bbox_selector` wins for the `action_bboxes` entry; `selector` still drives the action. Eliminates the dual-emission workaround where callers had to schedule a follow-up `scroll` step at `at_s + 0.6 s` purely to get a bbox.",
                "type": "string"
              },
              "px": {
                "description": "Pixels to scroll (relative). For scroll actions without selector/text_content.",
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "direction": {
                "description": "Scroll direction for px-based scroll.",
                "type": "string",
                "enum": [
                  "up",
                  "down"
                ]
              },
              "duration_ms": {
                "description": "Animation duration ms. Scroll default 1200, zoom default 800.",
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "text": {
                "description": "Text to type. For type actions.",
                "type": "string"
              },
              "url": {
                "description": "https:// URL to navigate to. For navigate actions.",
                "type": "string"
              },
              "ms": {
                "description": "Milliseconds to wait. For wait actions.",
                "type": "integer",
                "minimum": -9007199254740991,
                "maximum": 9007199254740991
              },
              "value": {
                "description": "Zoom percent (e.g. 150). For zoom actions.",
                "type": "integer",
                "minimum": 25,
                "maximum": 300
              }
            },
            "required": [
              "type",
              "at_s"
            ]
          }
        },
        "mobile": {
          "default": false,
          "description": "When true, renders at 390×844 with iPhone UA — triggers mobile layouts and produces portrait output. Default false = 1280×720 desktop.",
          "type": "boolean"
        },
        "extra_css": {
          "description": "CSS injected after page load. Use for loading web fonts (e.g. CJK fonts via @import). A 2-second settle is applied automatically.",
          "type": "string"
        },
        "duration_s": {
          "default": 30,
          "description": "Recording duration in seconds (default 30, max 300). Video mode only.",
          "type": "integer",
          "minimum": 10,
          "maximum": 300
        }
      },
      "required": [
        "url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "clone_voice",
    "description": "Dispatch on `action` to manage cloned voices:\n- `clone` (default): clone a custom voice from an audio/video file   or an existing Kling video. Returns a voice_id you can bind to a   Kling element via create_kling_element (element_voice_id). Audio   must be 5-30s, single clean voice, no background noise.\n- `design` (AGNT-44 #B1): generate a brand-new voice from a text   description via ElevenLabs `/v1/text-to-voice/create-previews`.   No audio sample needed — just a prose description (e.g. 'warm   female narrator with British accent'). Returns the generated   voice_id + an optional preview sample URL.\n- `list` (AGNT-66 #14) + `provider`: list user's custom voices   (provider=kling supported in v1). Returns `{voices[], count}`.\n- `get` (AGNT-66 #15) + provider + voice_id: fetch one voice's   metadata.\n- `delete` (AGNT-66 #16) + provider + voice_id: delete a custom voice.\n\nMinimax voice CRUD is filed as a follow-up ticket; v1 ships kling for clone/list/get/delete and elevenlabs for design.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "action": {
          "default": "clone",
          "description": "Which voice operation to perform. Default `clone` preserves pre-Wave-1 behavior. `design` was added in AGNT-44 Tier-C #B1.",
          "type": "string",
          "enum": [
            "clone",
            "design",
            "list",
            "get",
            "delete"
          ]
        },
        "provider": {
          "description": "Required for action=list/get/delete (use `kling`). For action=design, callers may explicitly pass `elevenlabs` to make the routing explicit — design always routes there. action=clone always routes to kling regardless of this field. PR #206 minor: accepting `elevenlabs` so explicit design-route calls don't surface a confusing 'invalid enum value' error.",
          "type": "string",
          "enum": [
            "kling",
            "elevenlabs"
          ]
        },
        "voice_name": {
          "description": "Voice name (max 20 chars). Required when action=clone.",
          "type": "string",
          "maxLength": 20
        },
        "voice_url": {
          "description": "Audio/video source URL (mp3/wav/mp4/mov). 5-30s, single clean voice. Mutually exclusive with video_id. Used by action=clone.",
          "type": "string",
          "format": "uri"
        },
        "video_id": {
          "description": "Existing Kling video ID to extract voice from. Must be a v2.6 sound=on video, avatar, or lipsync output. Mutually exclusive with voice_url. Used by action=clone.",
          "type": "string"
        },
        "voice_description": {
          "description": "Free-text description of the voice. Required when action=design. Example: 'warm female narrator with British accent, slow pacing'.",
          "type": "string",
          "minLength": 1
        },
        "preview_text": {
          "description": "Optional 100-1000-char preview text for action=design. When omitted, ElevenLabs auto-generates a sample.",
          "type": "string"
        },
        "seed": {
          "description": "Optional seed for reproducibility on action=design.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "voice_id": {
          "description": "Required when action=get or action=delete. The voice id (returned by action=clone, action=design, or action=list).",
          "type": "string"
        },
        "page_num": {
          "description": "Optional pagination on action=list (default 1).",
          "type": "integer",
          "minimum": 1,
          "maximum": 9007199254740991
        },
        "page_size": {
          "description": "Optional page size on action=list (default 30; cap 500).",
          "type": "integer",
          "minimum": 1,
          "maximum": 500
        }
      },
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "connect_auth",
    "description": "Auth operations for Composio third-party tools. Use op=list_accounts to see which apps the user has authorized and which are allowlisted; use op=request_link to obtain an OAuth URL for an unauthorized toolkit. To execute a tool, call connect_discover first (to get the slug + schema), then connect_call.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "op": {
          "type": "string",
          "enum": [
            "list_accounts",
            "request_link"
          ],
          "description": "Operation discriminator. `list_accounts` returns the caller's connected accounts (across all toolkits, or filtered by `toolkit`); `request_link` mints a fresh OAuth connect URL for the specified `toolkit` (which becomes required)."
        },
        "toolkit": {
          "description": "Composio toolkit slug (kebab-case, e.g. `gmail`, `slack`, `github`, `notion`). REQUIRED when `op=request_link`; for `op=list_accounts` it is optional — pass to filter results to a single toolkit, omit to list across all toolkits.",
          "type": "string",
          "pattern": "^[a-z0-9-]{1,64}$"
        }
      },
      "required": [
        "op"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "op": {
          "type": "string",
          "enum": [
            "list_accounts",
            "request_link"
          ]
        },
        "accounts": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "toolkit_slug": {
                "type": "string"
              },
              "status": {
                "type": "string",
                "enum": [
                  "ACTIVE",
                  "INITIATED",
                  "EXPIRED",
                  "INACTIVE"
                ]
              },
              "connected_at": {
                "type": "string"
              }
            },
            "required": [
              "id",
              "toolkit_slug",
              "status"
            ],
            "additionalProperties": false
          }
        },
        "available_toolkits": {
          "type": "array",
          "items": {
            "type": "string"
          }
        },
        "connect_url": {
          "type": "string",
          "format": "uri"
        },
        "expires_at": {
          "type": "string"
        },
        "account_id": {
          "type": "string"
        }
      },
      "required": [
        "op"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": false
    }
  },
  {
    "name": "connect_call",
    "description": "Execute one Composio action. You MUST have called connect_discover first to obtain the exact action slug and the JSON Schema for args — do NOT guess. For write actions (slug containing SEND/CREATE/DELETE/POST/UPDATE/REMOVE/WRITE/INSERT/ADD/PATCH/REPLY/FORWARD/MERGE/ARCHIVE/SCHEDULE/ASSIGN/REVOKE/MOVE/DRAFT/CLEAR/RESET), the user must confirm; pass confirm: true after confirmation. On needs_oauth, call connect_auth({op:'request_link', toolkit}).",
    "inputSchema": {
      "type": "object",
      "properties": {
        "toolkit": {
          "type": "string",
          "pattern": "^[a-z0-9-]{1,64}$",
          "description": "Composio toolkit slug (kebab-case, e.g. `gmail`, `slack`). Must be a toolkit the caller has connected — if not, the response is `{ok:false, error:{code:'needs_oauth', …}}` and the caller should invoke `connect_auth({op:'request_link', toolkit})` to mint a connect URL."
        },
        "action": {
          "type": "string",
          "pattern": "^[A-Z0-9_]{1,128}$",
          "description": "Composio action slug (SCREAMING_SNAKE_CASE, e.g. `GMAIL_SEND_EMAIL`, `SLACK_POST_MESSAGE`). Discover via `connect_discover({query})` — the returned `slug` field is exactly this value."
        },
        "args": {
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {},
          "description": "Action-specific arguments matching the `arguments_schema` returned by `connect_discover`. The schema is action-specific and validated server-side by Composio."
        },
        "confirm": {
          "default": false,
          "description": "Destructive-action gate. For action slugs matching the destructive verb list (SEND / CREATE / DELETE / POST / UPDATE / REMOVE / WRITE / INSERT / ADD / PATCH / REPLY / FORWARD / MERGE / ARCHIVE / SCHEDULE / ASSIGN / REVOKE / MOVE / DRAFT / CLEAR / RESET), callers MUST pass `confirm: true` (after explicit user confirmation) or the call short-circuits with `{ok: false, error: {code: 'confirmation_required', ...}}` before any upstream call is made. No-op for read-only actions. The destructive-slug list is matched on the `action` slug verbatim — see `isDestructiveSlug` in `lib/composio-shim.ts` for the canonical regex.",
          "type": "boolean"
        }
      },
      "required": [
        "toolkit",
        "action",
        "args"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "ok": {
          "type": "boolean"
        },
        "data": {},
        "error": {
          "type": "object",
          "properties": {
            "code": {
              "type": "string",
              "enum": [
                "feature_disabled",
                "toolkit_not_allowed",
                "unknown_tool",
                "needs_oauth",
                "confirmation_required",
                "invalid_arguments",
                "proxy_failure",
                "upstream_failure",
                "rate_limited",
                "timeout",
                "downstream_auth_failed",
                "internal"
              ]
            },
            "message": {
              "type": "string",
              "maxLength": 500
            },
            "correlation_id": {
              "type": "string"
            },
            "toolkit_slug": {
              "type": "string"
            }
          },
          "required": [
            "code",
            "message"
          ],
          "additionalProperties": false
        }
      },
      "required": [
        "ok"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": false
    }
  },
  {
    "name": "connect_discover",
    "description": "Search Composio's third-party tool catalog by use case ('send an email', 'create a Linear issue', 'schedule a meeting'). Returns up to 25 matching tools with their full JSON Schema inline — you can construct a connect_call invocation directly from this output without a separate inspect call. Use connect_discover BEFORE connect_call; do NOT guess slug names or argument shapes.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "query": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Free-text search across the Composio action catalog. Matches tool slugs, descriptions, and parameter names. Examples: `send email`, `create issue`, `search drive`. Result objects carry their full `arguments_schema` inline so a follow-up `inspect_tool` call is not needed."
        },
        "limit": {
          "default": 10,
          "description": "Max number of matching actions to return (1-25). Default 10. Each result carries the full JSON Schema for its arguments and output, so a wider limit increases response payload size non-trivially — pick the smallest value that gives enough candidates for the caller to pick from.",
          "type": "integer",
          "minimum": 1,
          "maximum": 25
        },
        "toolkit": {
          "description": "Optional toolkit slug filter (kebab-case, e.g. `gmail`). When set, only actions from that toolkit match. Omit to search the entire Composio catalog (discovery is connection- independent — connection is only required at `connect_call` time, not for discovery).",
          "type": "string",
          "pattern": "^[a-z0-9-]{1,64}$"
        }
      },
      "required": [
        "query"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "tools": {
          "maxItems": 25,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "slug": {
                "type": "string"
              },
              "toolkit_slug": {
                "type": "string"
              },
              "description": {
                "type": "string",
                "maxLength": 500
              },
              "write_class": {
                "type": "boolean"
              },
              "arguments_schema": {},
              "output_schema": {},
              "score": {
                "type": "number",
                "minimum": 0,
                "maximum": 1
              }
            },
            "required": [
              "slug",
              "toolkit_slug",
              "description",
              "write_class",
              "arguments_schema",
              "score"
            ],
            "additionalProperties": false
          }
        },
        "next_step_hint": {
          "type": "string"
        }
      },
      "required": [
        "tools",
        "next_step_hint"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": false
    }
  },
  {
    "name": "create_kling_element",
    "description": "Dispatch on `action` to manage Kling subjects (elements):\n- `create` (default): create an element from image/video   references. Returns `{element_id, element_name, task_id}`.   Use the element_id with generate_reference_video as   element_ids[] for character/style consistency in kling omni r2v.\n- `list` (AGNT-65 #5): list user's Kling elements. Returns   `{elements[], count}`. Supports `page_num`/`page_size`.\n- `get` (AGNT-65 #6) + element_id: fetch one element's metadata.\n- `delete` (AGNT-65 #7) + element_id: delete an element.\n- `presets` (AGNT-65 #8): list available tag preset IDs   (o_101..o_108) with human-readable labels. Free, no network call.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "action": {
          "default": "create",
          "description": "Which element operation to perform. Default `create` preserves pre-Wave-1 behavior.",
          "type": "string",
          "enum": [
            "create",
            "list",
            "get",
            "delete",
            "presets"
          ]
        },
        "element_name": {
          "description": "Subject name (max 20 chars). Required when action=create.",
          "type": "string",
          "maxLength": 20
        },
        "element_description": {
          "description": "Subject description (max 100 chars). Required when action=create.",
          "type": "string",
          "maxLength": 100
        },
        "reference_type": {
          "description": "Required when action=create. \"image_refer\": define from multi-angle photos (all styles). \"video_refer\": define from a short video clip (realistic human only, kling-v3+ models).",
          "type": "string",
          "enum": [
            "image_refer",
            "video_refer"
          ]
        },
        "frontal_image": {
          "description": "Front-facing reference image URL. Required when reference_type=image_refer.",
          "type": "string",
          "format": "uri"
        },
        "refer_images": {
          "description": "1-3 additional angle/detail reference images. Required when reference_type=image_refer.",
          "minItems": 1,
          "maxItems": 3,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          }
        },
        "refer_video": {
          "description": "Reference video URL (MP4/MOV, 3-8s, 1080p, 16:9 or 9:16). Required when reference_type=video_refer.",
          "type": "string",
          "format": "uri"
        },
        "element_voice_id": {
          "description": "Bind an existing voice ID to this element. Get one from clone_voice.",
          "type": "string"
        },
        "tag_ids": {
          "description": "Category tags: o_101=趣梗 o_102=人物 o_103=动物 o_104=道具 o_105=服饰 o_106=场景 o_107=特效 o_108=其他. Call action=presets to fetch the list at runtime.",
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "o_101",
              "o_102",
              "o_103",
              "o_104",
              "o_105",
              "o_106",
              "o_107",
              "o_108"
            ]
          }
        },
        "element_id": {
          "description": "Required when action=get or action=delete. The Kling element id (returned by action=create or action=list).",
          "type": "string"
        },
        "page_num": {
          "description": "Optional pagination on action=list (Kling default 1).",
          "type": "integer",
          "minimum": 1,
          "maximum": 9007199254740991
        },
        "page_size": {
          "description": "Optional page size on action=list (Kling default 30; cap 500).",
          "type": "integer",
          "minimum": 1,
          "maximum": 500
        }
      },
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "create_sora_character",
    "description": "Dispatch on `action` to manage sora persistent assets:\n- `create_character` (default): create a reusable character from a   short reference video. Requires `name` + `video`. Returns   `{character_id, name}`.\n- `get_character` (AGNT-67): fetch character metadata by   `character_id`. Returns `{character_id, name, metadata}`.\n- `list_videos` (AGNT-67 #17): list the user's previously-generated   sora videos. Returns `{videos[], count}`.\n- `download_video` (AGNT-67 #18): fetch a sora video by   `video_id` and upload to the CDN. Returns `{url, video_id, bytes}`.\n- `delete_video` (AGNT-67 #19): delete a sora video by `video_id`.   Returns `{video_id, deleted}`.\n\nCharacter-creation reference video must be 2-4 seconds long, 16:9 or 9:16 aspect ratio — sora upstream rejects off-aspect clips with a 400 carrying the rule verbatim.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "action": {
          "default": "create_character",
          "description": "Which sora asset operation to perform. Default `create_character` preserves pre-Wave-1 behavior.",
          "type": "string",
          "enum": [
            "create_character",
            "get_character",
            "list_videos",
            "download_video",
            "delete_video"
          ]
        },
        "name": {
          "description": "Display name for the character. Required when action=create_character.",
          "type": "string",
          "minLength": 1,
          "maxLength": 80
        },
        "video": {
          "description": "Reference video URL (mp4 / mov). 2-4 seconds, 16:9 or 9:16 aspect ratio. Required when action=create_character. The worker forwards this to sora's /v1/videos/characters endpoint as a multipart `video` upload.",
          "type": "string",
          "format": "uri"
        },
        "character_id": {
          "description": "Required when action=get_character.",
          "type": "string"
        },
        "video_id": {
          "description": "Required when action=download_video or action=delete_video. The sora video id (returned by generate_video provider=sora).",
          "type": "string"
        }
      },
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_animate_zoom",
    "description": "Apply animated zoom-in/hold/zoom-out segments to a video, each centered on pixel coordinates with an ease-out ramp. Mirrors the original github_explainer zoom step packaged as an atomic primitive. Use to highlight specific UI elements during an explainer recording — feed (cx, cy) from `capture_website` element bounding boxes.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https:\\/\\/.*",
          "description": "Source video URL (must be https://). Audio preserved if present."
        },
        "zoom_keyframes": {
          "minItems": 1,
          "maxItems": 32,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "t_start": {
                "type": "number",
                "minimum": 0,
                "description": "Segment start time (seconds)."
              },
              "t_end": {
                "type": "number",
                "exclusiveMinimum": 0,
                "description": "Segment end time (seconds). Must be > t_start."
              },
              "cx": {
                "type": "number",
                "minimum": 0,
                "description": "Zoom-target X in source frame pixels."
              },
              "cy": {
                "type": "number",
                "minimum": 0,
                "description": "Zoom-target Y in source frame pixels."
              },
              "scale": {
                "type": "number",
                "exclusiveMinimum": 1,
                "maximum": 4,
                "description": "Target zoom factor. 1.35 matches the original recipe; max 4.0."
              },
              "ramp_s": {
                "default": 0.5,
                "description": "Ease-in/out ramp duration. Held flat at target between.",
                "type": "number",
                "exclusiveMinimum": 0,
                "maximum": 5
              }
            },
            "required": [
              "t_start",
              "t_end",
              "cx",
              "cy",
              "scale"
            ]
          },
          "description": "Non-overlapping zoom segments, sorted by t_start. Up to 32 segments."
        }
      },
      "required": [
        "video_url",
        "zoom_keyframes"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_audio_denoise",
    "description": "Remove background noise / music / ambient sound from an audio recording, isolating the vocals or speech. Powered by ElevenLabs `/v1/audio-isolation`. Atomic primitive: single ~5-30s multipart POST, no poll. Returns the cleaned audio URL on the CDN. Pair with `transcribe_audio` for cleaner ASR on noisy field recordings, or with `generate_lipsync` to get a clean voice track before driving a face.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "audio_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the audio file to clean (mp3/wav/m4a/etc.). The file may contain background music, noise, or non-speech audio; the denoise model removes it and returns just the vocals."
        }
      },
      "required": [
        "audio_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_audio_isolate",
    "description": "⚠ DEPRECATED — Renamed to `edit_audio_denoise`. Same parameters, same behavior. See `edit_audio_denoise` for full documentation. This alias will be removed in a future release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "audio_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the audio file to clean (mp3/wav/m4a/etc.). The file may contain background music, noise, or non-speech audio; the denoise model removes it and returns just the vocals."
        }
      },
      "required": [
        "audio_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_audio_mix",
    "description": "Overlay an audio track (music, voiceover) onto a video. Original video audio is preserved; the new track is mixed at the specified volume.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the video"
        },
        "audio_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the audio file to overlay"
        },
        "audio_volume": {
          "default": 0.25,
          "description": "Volume of overlaid audio (0–1). Default 0.25.",
          "type": "number",
          "minimum": 0,
          "maximum": 1
        }
      },
      "required": [
        "video_url",
        "audio_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_audio_trim",
    "description": "Trim an audio file to a specified time range. Sample-accurate — re-encodes by default so output boundaries land exactly on the requested seconds (lossy codecs like mp3/aac/opus can't be byte-copied at arbitrary offsets without snapping to the previous codec frame). Supports mp3, wav, m4a, aac, flac, ogg, opus. Use to split a longer narration into segments for per-chunk lipsync, extract a clip, or convert format.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "audio_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the audio file to trim (mp3, wav, m4a, aac, flac, ogg, opus)"
        },
        "start_s": {
          "description": "Start time in seconds (inclusive). Default 0 (start of file).",
          "type": "number",
          "minimum": 0
        },
        "end_s": {
          "type": "number",
          "exclusiveMinimum": 0,
          "description": "End time in seconds (exclusive). Must be greater than start_s."
        },
        "output_format": {
          "description": "Output container format. Defaults to the input file's format (preserves codec for fastest path).",
          "type": "string",
          "enum": [
            "mp3",
            "wav",
            "m4a",
            "aac",
            "flac",
            "ogg",
            "opus"
          ]
        }
      },
      "required": [
        "audio_url",
        "end_s"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_beat_sync",
    "description": "Align video cuts or zoom pulses to musical beats. Caller supplies the music track via music_url (chain generate_music first if needed) and the BPM. Pure ffmpeg + ffprobe — no provider calls. Typical runtime 30-90s for h264 input; HEVC/4K + mode=both can take 90-150s. Returns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "description": "Source video URL."
        },
        "music_url": {
          "type": "string",
          "format": "uri",
          "description": "Music track URL. Chain generate_music first if you don't have one. Audio formats accepted by ffmpeg work (mp3, m4a, wav, etc.)."
        },
        "bpm": {
          "type": "integer",
          "minimum": 30,
          "maximum": 300,
          "description": "Beats per minute of the music track. Required (intentionally — the hub-skill's silent-disable footgun when bpm is unknown is structurally impossible here). Range is wider than internal defaults (65-130 in plugin's vibe-to-BPM table) by design."
        },
        "mode": {
          "default": "auto",
          "description": "`sync` re-times scene cuts to beats; `zoom` adds beat-aligned zoom pulses; `both` runs sync then zoom; `auto` picks based on scene-cut count + audio_mode. `none` is intentionally not exposed — caller uses edit_audio_mix for that case.",
          "type": "string",
          "enum": [
            "sync",
            "zoom",
            "both",
            "auto"
          ]
        },
        "audio_mode": {
          "default": "auto",
          "description": "`replace` = music only; `mix` = duck music under original audio via sidechain compressor; `auto` = mix if input has audio, else replace.",
          "type": "string",
          "enum": [
            "replace",
            "mix",
            "auto"
          ]
        },
        "beat_intensity": {
          "default": "medium",
          "description": "Zoom-pulse density. low = every 4th beat, medium = every 2nd, high = every beat. No effect on `sync` mode.",
          "type": "string",
          "enum": [
            "low",
            "medium",
            "high"
          ]
        },
        "orig_video_url": {
          "description": "Optional separate audio source for `mix` mode when video_url's audio was stripped by upstream beat effects. Falls back to video_url's audio track if omitted.",
          "type": "string",
          "format": "uri"
        },
        "speed_clamp_min": {
          "default": 0.8,
          "description": "`sync` mode: minimum playback speed.",
          "type": "number",
          "minimum": 0.5,
          "maximum": 1
        },
        "speed_clamp_max": {
          "default": 1.25,
          "description": "`sync` mode: maximum playback speed.",
          "type": "number",
          "minimum": 1,
          "maximum": 2
        },
        "zoom_scale": {
          "default": 1.04,
          "description": "`zoom` mode: scale factor at beat peaks.",
          "type": "number",
          "minimum": 1.01,
          "maximum": 1.2
        },
        "zoom_window_s": {
          "default": 0.12,
          "description": "`zoom` mode: half-width of each zoom pulse window.",
          "type": "number",
          "minimum": 0.05,
          "maximum": 0.5
        }
      },
      "required": [
        "video_url",
        "music_url",
        "bpm"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_browser_frame",
    "description": "Wrap a video inside a 1280×800 macOS Sonoma desktop + browser-window chrome (traffic lights, tab title, URL address bar, drop shadow). Use to give explainer recordings a polished 'watching a browser' look. Output is always 1280×800 regardless of input dimensions; the input is letterboxed/pillarboxed to fit the 1168×637 content area.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "pattern": "^https:\\/\\/.*",
          "description": "Source video URL (must be https://). Audio preserved if present."
        },
        "style": {
          "default": "mac",
          "description": "Output style. Currently only 'mac' (Sonoma desktop).",
          "type": "string",
          "enum": [
            "mac"
          ]
        },
        "url": {
          "default": "",
          "description": "URL string shown in the address bar. Plain text — not validated as a real URL. Truncated at 65 chars in the display.",
          "type": "string",
          "maxLength": 80
        },
        "tab_title": {
          "default": "",
          "description": "Tab title shown in the browser tab. Truncated at 30 chars in the display.",
          "type": "string",
          "maxLength": 40
        }
      },
      "required": [
        "video_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_concat",
    "description": "Join multiple video clips into a single video, played in sequence. Useful for multi-shot workflows where each clip was generated separately.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_urls": {
          "minItems": 2,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          },
          "description": "List of video URLs to concatenate in order"
        }
      },
      "required": [
        "video_urls"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_pip",
    "description": "Overlay one video on top of another as a picture-in-picture window. Supports rectangular or circular overlay shape. Use for tutorials, reaction videos, or avatar overlays.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "main_video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the background video"
        },
        "overlay_video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the overlay video"
        },
        "position": {
          "default": "bottom_right",
          "description": "Corner to place the overlay (ignored if `position_px` is supplied)",
          "type": "string",
          "enum": [
            "top_left",
            "top_right",
            "bottom_left",
            "bottom_right"
          ]
        },
        "position_px": {
          "description": "Pixel-precise placement on the main video. Overrides `position`. Use to escape the corner-margin model — e.g. `{x: 20, y: H-h-78}` to leave clearance for a Mac-dock area below the overlay.",
          "type": "object",
          "properties": {
            "x": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            "y": {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            }
          },
          "required": [
            "x",
            "y"
          ]
        },
        "size": {
          "description": "Size of overlay relative to main video (0.05–0.5). Defaults to 0.25 inside the worker when omitted. Mutually exclusive with `size_px` — leaving this off lets callers use `size_px` for absolute pixel sizing.",
          "type": "number",
          "minimum": 0.05,
          "maximum": 0.5
        },
        "size_px": {
          "description": "Absolute pixel diameter for the scaled overlay (square). Mutually exclusive with `size`. Use when the caller needs the overlay to land at a specific pixel size on the main video regardless of the overlay's intrinsic dimensions. Upper bound is also capped at min(main_w, main_h) at runtime once the main video is probed.",
          "type": "integer",
          "minimum": 32,
          "maximum": 2160
        },
        "shape": {
          "default": "rectangle",
          "description": "Shape of the overlay window",
          "type": "string",
          "enum": [
            "rectangle",
            "circle"
          ]
        },
        "stroke_width_px": {
          "default": 0,
          "description": "Outline thickness around a `circle` overlay, in pixels (0 = no stroke). Requires `shape: 'circle'`. Max 20.",
          "type": "integer",
          "minimum": 0,
          "maximum": 20
        },
        "stroke_color": {
          "default": "white",
          "description": "Outline color when `stroke_width_px > 0`. Restricted palette to keep the YUV color math simple — file a follow-up if you need hex/RGB.",
          "type": "string",
          "enum": [
            "white",
            "black",
            "red",
            "green",
            "blue"
          ]
        }
      },
      "required": [
        "main_video_url",
        "overlay_video_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_text_overlay",
    "description": "Burn text onto a video at a specified position and optional time range. Use for branding, CTAs, watermarks, or titles.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the video"
        },
        "text": {
          "type": "string",
          "description": "Text to display on the video"
        },
        "position": {
          "default": "bottom_center",
          "description": "Where to place the text",
          "type": "string",
          "enum": [
            "top_left",
            "top_center",
            "top_right",
            "center",
            "bottom_left",
            "bottom_center",
            "bottom_right"
          ]
        },
        "font_size": {
          "default": 48,
          "description": "Font size in pixels",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "font_color": {
          "default": "white",
          "description": "Font color (ffmpeg color name or hex)",
          "type": "string"
        },
        "start_s": {
          "description": "Show text starting at this second",
          "type": "number"
        },
        "end_s": {
          "description": "Hide text after this second",
          "type": "number"
        }
      },
      "required": [
        "video_url",
        "text"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "edit_trim",
    "description": "Trim a video to a specified time range and/or convert format. Use to extract a segment from a longer video.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the video to trim"
        },
        "start_s": {
          "description": "Start time in seconds",
          "type": "number"
        },
        "end_s": {
          "description": "End time in seconds",
          "type": "number"
        },
        "output_format": {
          "default": "mp4",
          "description": "Output container format",
          "type": "string",
          "enum": [
            "mp4",
            "webm",
            "mov"
          ]
        }
      },
      "required": [
        "video_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "extract_audio",
    "description": "⚠ DEPRECATED — Renamed to `extract_audio_from_video`. Same parameters, same behavior. See `extract_audio_from_video` for full documentation. This alias will be removed in a future release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the source video"
        },
        "format": {
          "default": "mp3",
          "description": "Output audio format. `mp3` (default) is compact and universally compatible; `wav` is sample-accurate PCM (larger file size) and what most downstream audio-analysis tools prefer.",
          "type": "string",
          "enum": [
            "mp3",
            "wav"
          ]
        }
      },
      "required": [
        "video_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "extract_audio_from_video",
    "description": "Pull the audio track out of a video file as an mp3 (default, 192k libmp3lame) or wav (PCM s16le) file. Input video is unchanged. Returns the audio URL on the CDN. Composes with `edit_audio_trim` if you need to slice a sub-range out of the extracted audio.\n\nCommon upstream sources: video clips you want to feed into `transcribe_audio` directly (note: transcribe_audio accepts video URLs too and extracts internally — use extract_audio_from_video when you want the intermediate audio URL exposed for later steps).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the source video"
        },
        "format": {
          "default": "mp3",
          "description": "Output audio format. `mp3` (default) is compact and universally compatible; `wav` is sample-accurate PCM (larger file size) and what most downstream audio-analysis tools prefer.",
          "type": "string",
          "enum": [
            "mp3",
            "wav"
          ]
        }
      },
      "required": [
        "video_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "extract_frame",
    "description": "Extract one or more frames from a video as PNG image(s). Single-frame mode (default): pass `time_s` (or omit; defaults to 0) — returns `{url}`. Batch mode (AGNT-69 #22): pass exactly one of `count`, `interval_s`, or `at_times[]` (mutually exclusive with `time_s`) — returns `{urls: [...], url: urls[0]}` (the legacy `url` field is kept set to the first frame for back-compat with single-frame callers that handle both shapes).\n\nUses fast input-side seek so extraction is near-instant even for long videos.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video_url": {
          "type": "string",
          "format": "uri",
          "description": "URL of the video to extract a frame from"
        },
        "time_s": {
          "description": "Timestamp in seconds to extract (default 0 = first frame). Single-frame mode only — mutually exclusive with batch knobs.",
          "type": "number",
          "minimum": 0
        },
        "count": {
          "description": "Batch: extract N frames evenly spaced across the video (skipping the first/last 5% so title/outro frames don't dominate). Requires the video duration to be probeable via ffprobe. 1-32. Mutually exclusive with time_s/interval_s/at_times.",
          "type": "integer",
          "minimum": 1,
          "maximum": 32
        },
        "interval_s": {
          "description": "Batch: extract a frame every N seconds starting at t=0 until either the video ends or the 32-frame cap is reached. Requires the video duration to be probeable via ffprobe. Mutually exclusive with time_s/count/at_times.",
          "type": "number",
          "exclusiveMinimum": 0
        },
        "at_times": {
          "description": "Batch: explicit list of timestamps in seconds. 1-32 entries. Sorted + de-duplicated server-side. Mutually exclusive with time_s/count/interval_s.",
          "minItems": 1,
          "maxItems": 32,
          "type": "array",
          "items": {
            "type": "number",
            "minimum": 0
          }
        }
      },
      "required": [
        "video_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_effect_video",
    "description": "⚠ DEPRECATED — Renamed to `pika_effect`. Same parameters, same behavior. See `pika_effect` for full documentation. This alias will be removed in a future release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "image": {
          "type": "string",
          "format": "uri",
          "description": "Source image URL the effect is applied to."
        },
        "effect": {
          "type": "string",
          "enum": [
            "Cake-ify",
            "Crumble",
            "Crush",
            "Decapitate",
            "Deflate",
            "Dissolve",
            "Explode",
            "Eye-pop",
            "Inflate",
            "Levitate",
            "Melt",
            "Peel",
            "Poke",
            "Squish",
            "Ta-da",
            "Tear"
          ],
          "description": "One of 16 named viral Pikaffects."
        },
        "prompt": {
          "description": "Optional prompt to guide the effect.",
          "type": "string",
          "maxLength": 500
        },
        "negative_prompt": {
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "image",
        "effect"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_image",
    "description": "Generate an image. One tool, multiple providers via the `provider` enum. **Default: nano-banana-pro** (Gemini 3 Pro — best balance of quality + character/style consistency). Alternatives: gemini-flash-image (fastest, lower fidelity); seedream (cinematic + 2K/4K); gpt-image-2 (best for editing an existing image, supports `quality` + `output_format`). When the user expresses preference for speed, cost, image editing, or 4K resolution, surface the provider choice and let them override — otherwise stick with the default.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "description": "Which image model. Default nano-banana-pro. gpt-image-2: best for editing an existing image (supports `quality` + `output_format`). seedream: cinematic + 2K/4K. gemini-flash-image: fastest, lower fidelity. Surface this choice when the user shows a cost/speed/quality preference.",
          "type": "string",
          "enum": [
            "nano-banana-pro",
            "gemini-flash-image",
            "seedream",
            "gpt-image-2"
          ]
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "description": "Description of the image. Be specific about subject, style, lighting."
        },
        "reference_image": {
          "description": "DEPRECATED — use `reference_images`. Single reference image URL for style / character consistency. When both are present, this is prepended to the head of `reference_images`.",
          "type": "string",
          "format": "uri"
        },
        "reference_images": {
          "description": "Reference image URLs for style / character consistency (multi-ref). Per-provider caps: gemini up to 14, seedream up to 14, gpt-image-2 up to 16. Order matters — refs are sent in array order. On gpt-image-2 this routes through /v1/images/edits with one `image[]` multipart entry per ref.",
          "maxItems": 16,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          }
        },
        "aspect_ratio": {
          "description": "Output aspect ratio (default 1:1).",
          "type": "string",
          "enum": [
            "1:1",
            "16:9",
            "9:16",
            "3:4",
            "4:3"
          ]
        },
        "resolution": {
          "description": "Output resolution (default 1K). 4K supported on seedream and gemini (post-BACK-339 imageSize fix). gpt-image-2 native sizes are 1024x1024 / 1536x1024 / 1024x1536 / 1792x1024 / 1024x1792 only (all 1K-class) — passing resolution=2K or 4K on gpt-image-2 is rejected with a pointer to seedream (BACK-339 round-1+2 fixup; AGNT-149 widened the 16:9/9:16 native sizes).",
          "type": "string",
          "enum": [
            "1K",
            "2K",
            "4K"
          ]
        },
        "quality": {
          "description": "Image quality, gpt-image-2 only. `low` is ~10–20s; `medium`/`auto` are slower. `high` is the slowest tier — typically around two minutes. Rejected on other providers.",
          "type": "string",
          "enum": [
            "auto",
            "low",
            "medium",
            "high"
          ]
        },
        "output_format": {
          "description": "Output image format, gpt-image-2 only. Defaults to png upstream. Rejected on other providers.",
          "type": "string",
          "enum": [
            "png",
            "jpeg",
            "webp"
          ]
        },
        "mask": {
          "description": "Alpha PNG URL — mask for inpainting. gpt-image-2 only (routes to /v1/images/edits). Transparent regions are repainted, opaque regions are kept. Mask dimensions must match the reference image; mismatches return a clean upstream 400. Rejected on other providers.",
          "type": "string",
          "format": "uri"
        },
        "watermark": {
          "description": "Toggle the seedream watermark. seedream only — defaults to false (no watermark). Mirrors the kling watermark precedent (BACK-278). Rejected on other providers.",
          "type": "boolean"
        },
        "n": {
          "description": "Number of images to generate (default 1, max 10). Output schema gains `urls[]` regardless; `url` retained as `urls[0]` for back-compat. gpt-image-2 native `n`; seedream uses `sequential_image_generation`; gemini does NOT support n>1 for image gen and returns a clean InvalidInput.",
          "type": "integer",
          "minimum": 1,
          "maximum": 10
        }
      },
      "required": [
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_keyframes_video",
    "description": "Generate a video transitioning between two keyframe images. Uses pika-api/generate/2.2/pikaframes (model pika-2.2-pikaframes).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "first_frame": {
          "type": "string",
          "format": "uri",
          "description": "First keyframe image URL."
        },
        "last_frame": {
          "type": "string",
          "format": "uri",
          "description": "Last keyframe image URL."
        },
        "prompt": {
          "description": "Transition prompt — motion between frames.",
          "type": "string"
        },
        "negative_prompt": {
          "description": "Negative prompt — what to avoid.",
          "type": "string"
        },
        "duration": {
          "description": "Output duration in seconds (5 or 10; default 5).",
          "anyOf": [
            {
              "type": "number",
              "const": 5
            },
            {
              "type": "number",
              "const": 10
            }
          ]
        },
        "resolution": {
          "description": "Output resolution (default 720p).",
          "type": "string",
          "enum": [
            "720p",
            "1080p"
          ]
        },
        "seed": {
          "description": "Integer seed for reproducibility.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "first_frame",
        "last_frame"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_lipsync",
    "description": "Sync a face (portrait image) to an audio track — talking or singing head video. Providers: kling (/v1/videos/avatar/image2video, default) or pika (parrot audio-to-video).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "description": "kling (default) or pika",
          "type": "string",
          "enum": [
            "kling",
            "pika"
          ]
        },
        "image": {
          "type": "string",
          "format": "uri",
          "description": "Source portrait image URL."
        },
        "audio": {
          "type": "string",
          "format": "uri",
          "description": "Source audio URL to sync to."
        },
        "prompt": {
          "description": "Optional style/motion hint. Parrot uses promptText; kling wires it through to the avatar payload for emotion / camera guidance (BACK-339 — was previously dropped).",
          "type": "string"
        },
        "mode": {
          "description": "Kling avatar quality tier (std default upstream, pro higher fidelity). Kling only.",
          "type": "string",
          "enum": [
            "std",
            "pro"
          ]
        },
        "watermark": {
          "description": "Burn kling's watermark into the output. Default `false` — clean output by default. Pass `true` to opt back into kling's account-tier-dependent watermark. Kling only.",
          "type": "boolean"
        }
      },
      "required": [
        "image",
        "audio"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_motion_control_video",
    "description": "Transfer motion from a reference video onto a character image. Providers: pika (parrot-staging/animate, default) supports promptText + resolution. kling (/v1/videos/motion-control) uses the std/pro mode knob.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "description": "pika (default) or kling",
          "type": "string",
          "enum": [
            "pika",
            "kling"
          ]
        },
        "image": {
          "type": "string",
          "format": "uri",
          "description": "Character image URL."
        },
        "video": {
          "type": "string",
          "format": "uri",
          "description": "Motion reference video URL."
        },
        "prompt": {
          "description": "Optional motion / style hint.",
          "type": "string"
        },
        "resolution": {
          "description": "Output resolution (pika only; default 720p).",
          "type": "string",
          "enum": [
            "480p",
            "720p",
            "1080p"
          ]
        },
        "model": {
          "description": "Kling model id (kling only; default kling-v3). kling-v2-6 is intentionally kept on motion-control (worker enforces the same two-value allow-list); the v2-6 deferral on `generate_video.kling_model` is t2v/i2v-specific.",
          "type": "string",
          "enum": [
            "kling-v3",
            "kling-v2-6"
          ]
        },
        "mode": {
          "description": "Kling generation mode (kling only; default std).",
          "type": "string",
          "enum": [
            "std",
            "pro"
          ]
        },
        "character_orientation": {
          "description": "Kling motion-control character orientation source. `video` (default) uses the reference video's orientation; `image` uses the source image. Kling only. (BACK-339.)",
          "type": "string",
          "enum": [
            "video",
            "image"
          ]
        },
        "keep_original_sound": {
          "description": "Preserve the reference video's original audio in the output. Default `false` (BACK-339; was previously silently `yes`). Kling only.",
          "type": "boolean"
        },
        "seed": {
          "description": "Reproducibility seed for pika animate. Pika only — kling motion-control rejects this. (BACK-339.)",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "negative_prompt": {
          "description": "What pika animate should AVOID generating. Pika only — kling motion-control rejects this. (BACK-339.)",
          "type": "string",
          "maxLength": 2500
        },
        "watermark": {
          "description": "Burn kling's watermark into the output. Default `false` — clean output by default. Pass `true` to opt back into kling's account-tier-dependent watermark. Kling only.",
          "type": "boolean"
        }
      },
      "required": [
        "image",
        "video"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_music",
    "description": "Generate music or sound effects. Default kind=`music` + provider=`minimax-music` calls MiniMax music-2.5 (full song with vocals; duration determined by lyric length, 20-60s typical). Set `kind: sfx` (AGNT-44 #B2) + `provider: elevenlabs` to generate a short sound effect via ElevenLabs `/v1/sound-generation` (e.g. 'thunder', 'glass breaking', 0.5-30s, no lyrics). Set `provider: kling-audio` (AGNT-44 #A2/A3) to generate music/SFX/BGM via Kling — `mode: text_to_audio` from a prompt, or `mode: video_to_audio` cued to an existing video (provide `source_video_url` or `source_video_id`).",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "default": "minimax-music",
          "description": "Music/SFX provider. `minimax-music` (default) for full songs with vocals + lyrics. `elevenlabs` for sound effects (requires kind=sfx). `kling-audio` (AGNT-44 #A2/A3) for music/SFX/BGM from a prompt (mode=text_to_audio) or cued to a video (mode=video_to_audio).",
          "type": "string",
          "enum": [
            "minimax-music",
            "elevenlabs",
            "kling-audio"
          ]
        },
        "kind": {
          "default": "music",
          "description": "What to generate. `music` (default) — full song with vocals/instruments via MiniMax. `sfx` (AGNT-44 #B2) — short sound effect via ElevenLabs (no lyrics; provider must be elevenlabs). Ignored for kling-audio (Kling handles music/SFX/BGM via mode + per-mode prompt fields).",
          "type": "string",
          "enum": [
            "music",
            "sfx"
          ]
        },
        "mode": {
          "description": "AGNT-44 #A2/A3 — kling-audio only. `text_to_audio` (default): generate audio from a prompt. `video_to_audio`: generate audio cued to a source video (provide source_video_url or source_video_id).",
          "type": "string",
          "enum": [
            "text_to_audio",
            "video_to_audio"
          ]
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "description": "Style/genre/mood description (kind=music) OR the sound effect to generate (kind=sfx; e.g. 'thunder cracking with rain', 'glass shattering on tile') OR the audio prompt for kling-audio text_to_audio."
        },
        "lyrics": {
          "description": "Song lyrics with optional [verse]/[chorus] section tags. Output length scales with lyric length; omit for a ~33s instrumental. Ignored when kind=sfx or provider=kling-audio.",
          "type": "string"
        },
        "duration_seconds": {
          "description": "kind=sfx only: target duration in seconds (0.5-30). When omitted, ElevenLabs auto-picks.",
          "type": "number",
          "minimum": 0.5,
          "maximum": 30
        },
        "prompt_influence": {
          "description": "kind=sfx only: how strictly to follow the prompt (0-1, default 0.3). Higher = more literal interpretation.",
          "type": "number",
          "minimum": 0,
          "maximum": 1
        },
        "loop": {
          "description": "kind=sfx only: when true, generate seamless looping audio.",
          "type": "boolean"
        },
        "source_video_url": {
          "description": "kling-audio mode=video_to_audio: HTTPS URL of the source video (3-20s, mp4/mov). Mutually exclusive with source_video_id.",
          "type": "string",
          "format": "uri"
        },
        "source_video_id": {
          "description": "kling-audio mode=video_to_audio: Kling video id (last 30 days). Mutually exclusive with source_video_url.",
          "type": "string",
          "minLength": 1
        },
        "sound_effect_prompt": {
          "description": "kling-audio mode=video_to_audio: sound-effect prompt cued to the video (max 200 chars).",
          "type": "string",
          "maxLength": 200
        },
        "bgm_prompt": {
          "description": "kling-audio mode=video_to_audio: background-music prompt cued to the video (max 200 chars).",
          "type": "string",
          "maxLength": 200
        },
        "asmr_mode": {
          "description": "kling-audio mode=video_to_audio: enable ASMR generation mode.",
          "type": "boolean"
        }
      },
      "required": [
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_reference_video",
    "description": "Generate a video from multi-modal reference inputs (images, videos, audio) used as character / style / motion anchors — NOT as start frames. **Default: kling** (omni-video, sound-capable, up to 7 image / 3 video / 8 audio refs). Specialist: seedance — cinematic look, supports `fast` / `seed` / `resolution` / `auto_duration`, accepts up to 9 image / 3 video / 3 audio refs (combined ≤ 12). Specialist: minimax — S2V-01 single-subject consistent-character mode (exactly 1 reference_image, no videos or audio). Subject must be a HUMAN face (animals/objects fail subject extraction).\n\nReference assets in prompt via <<<image_1>>>/<<<video_1>>> (kling) or @Image1/@Video1/@Audio1 (seedance).\n\nSound defaults to on; pass sound=false to suppress. For text- or single-image-driven video use `generate_video` instead.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "description": "Which video model. **Default: kling** — fastest + cheapest, omni-video supports sound + up to 7/3/8 refs. seedance for cinematic look, fast tier, seed control, auto duration. minimax for S2V-01 single-subject (exactly 1 reference_image). Subject must be a HUMAN face (animals/objects fail subject extraction).",
          "type": "string",
          "enum": [
            "kling",
            "seedance",
            "minimax"
          ]
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "description": "Description or motion direction. Use @Image1 / @Video1 / @Audio1 tokens (seedance) or <<<image_1>>> / <<<video_1>>> tokens (kling) to anchor specific references."
        },
        "reference_images": {
          "description": "Reference image URLs. **Multi-ref is supported — pass an array of character / style anchors (NOT start frames).** Per-provider caps: seedance up to 9 (jpeg/png/webp, ≤30MB each); kling up to 7. Requires at least one entry here or in reference_videos.",
          "maxItems": 9,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          }
        },
        "reference_videos": {
          "description": "Reference video URLs (motion / style). Up to 3 videos. Requires at least one entry here or in reference_images.",
          "maxItems": 3,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          }
        },
        "reference_audio": {
          "description": "Reference audio URLs. **Multi-ref is supported on both providers — pass an array.** Per-provider caps: seedance accepts up to 3 (combined ≤ 15s, mp3 / wav only); kling accepts up to 8 (mp3 / wav / m4a). Reference via @Audio1/@Audio2/@Audio3 tokens (seedance) or paired with the prompt (kling).",
          "maxItems": 8,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          }
        },
        "duration": {
          "description": "Length in seconds (default 5). Per-provider valid values: kling 3-15 · seedance 4-15. For seedance auto-length, pass `auto_duration: true` instead — `duration` is then ignored.",
          "type": "integer",
          "minimum": 3,
          "maximum": 20
        },
        "auto_duration": {
          "description": "Let fal pick the best clip length automatically. seedance only — rejected on kling. When true, the `duration` field is ignored. Default false.",
          "type": "boolean"
        },
        "aspect_ratio": {
          "description": "Output aspect ratio (default 16:9). `21:9`, `4:3`, `3:4`, `auto` are seedance-only — passing them on kling is rejected.",
          "type": "string",
          "enum": [
            "16:9",
            "9:16",
            "1:1",
            "21:9",
            "4:3",
            "3:4",
            "auto"
          ]
        },
        "sound": {
          "description": "Enable ambient audio. If omitted, defaults to true on both providers. Pass sound=false explicitly to suppress audio.",
          "type": "boolean"
        },
        "seed": {
          "description": "Reproducibility seed. seedance only (echoed in result). Rejected on kling.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "fast": {
          "description": "Use the fast (cheaper, ~20% off) seedance tier. seedance only. Caps resolution at 720p — combining `fast=true` with `resolution=1080p` is rejected.",
          "type": "boolean"
        },
        "resolution": {
          "description": "Output resolution. seedance only. Default 720p. 1080p requires `fast=false`.",
          "type": "string",
          "enum": [
            "480p",
            "720p",
            "1080p"
          ]
        },
        "shots": {
          "description": "Multi-shot narrative segments. kling only. 1-6 shots, each with its own prompt + duration. Sum of `shot.duration` MUST equal the top-level `duration`.",
          "minItems": 1,
          "maxItems": 6,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "prompt": {
                "type": "string",
                "minLength": 1
              },
              "duration": {
                "type": "integer",
                "minimum": 1,
                "maximum": 15
              }
            },
            "required": [
              "prompt",
              "duration"
            ]
          }
        },
        "prompt_adherence": {
          "description": "How strictly to follow the prompt. loose ≈ 0.3 (more creative interpretation), balanced ≈ 0.5 (default), strict ≈ 0.85 (literal). Kling only.",
          "type": "string",
          "enum": [
            "loose",
            "balanced",
            "strict"
          ]
        },
        "negative_prompt": {
          "description": "What the model should AVOID generating. Kling only. Common patterns: 'blur, distortion, low quality'; 'watermarks, text overlay, logos'; 'deformed anatomy, extra fingers'.",
          "type": "string",
          "maxLength": 2500
        },
        "watermark": {
          "description": "Burn kling's watermark into the output. Default `false` — clean output by default. Pass `true` to opt back into kling's account-tier-dependent watermark. Kling only.",
          "type": "boolean"
        },
        "image_types": {
          "description": "Per-image role for reference_images (parallel array, same length). \"first_frame\" → starting frame; \"end_frame\" → ending frame; \"reference\" or null → plain style/character reference. Kling only. kling-video-o1 limits first/end frame usage when >2 images total.",
          "type": "array",
          "items": {
            "anyOf": [
              {
                "type": "string",
                "enum": [
                  "first_frame",
                  "end_frame",
                  "reference"
                ]
              },
              {
                "type": "null"
              }
            ]
          }
        },
        "video_refer_types": {
          "description": "Per-video role for reference_videos (parallel array, same length). \"feature\" = character/style reference; \"base\" = video to restyle/edit. Default: all feature. Kling only.",
          "type": "array",
          "items": {
            "type": "string",
            "enum": [
              "feature",
              "base"
            ]
          }
        },
        "video_keep_sounds": {
          "description": "Per-video flag: preserve original audio track (parallel array to reference_videos). Default: false for all. Kling only.",
          "type": "array",
          "items": {
            "type": "boolean"
          }
        },
        "element_ids": {
          "description": "Kling subject library element IDs to bind (up to 3). Create elements first with create_kling_element. Kling only.",
          "minItems": 1,
          "maxItems": 3,
          "type": "array",
          "items": {
            "type": "integer",
            "minimum": -9007199254740991,
            "maximum": 9007199254740991
          }
        },
        "kling_model": {
          "description": "Omni model variant (default \"kling-v3-omni\"). Kling only.",
          "type": "string",
          "enum": [
            "kling-v3-omni",
            "kling-video-o1"
          ]
        },
        "quality_mode": {
          "description": "Output quality tier. \"std\" = 720p; \"pro\" = 1080p (default); \"4k\" = 4K. Kling only.",
          "type": "string",
          "enum": [
            "std",
            "pro",
            "4k"
          ]
        },
        "intelligence_shot": {
          "description": "Let Kling auto-determine shot breakdown from the prompt (shot_type=intelligence). Mutually exclusive with shots[]. Kling only.",
          "type": "boolean"
        },
        "voice_ids": {
          "description": "Cloned voice IDs from `clone_voice`. Pass voice_ids for previously-cloned voices (small payload — Kling stores the binary server-side); pass `reference_audio` for ad-hoc inline audio (forces base64 binary in the request body — large). **Use voice_ids whenever possible** to avoid request-size limits. Combined `len(voice_ids) + len(reference_audio)` is capped at 8 (Kling upstream cap). Kling only — rejected on seedance.",
          "minItems": 1,
          "maxItems": 8,
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "callback_url": {
          "description": "Webhook URL Kling will POST to when generation completes. Useful for fire-and-forget patterns where the caller doesn't want to poll. Must be https://. Kling only.",
          "type": "string",
          "format": "uri",
          "pattern": "^https:\\/\\/.*"
        },
        "external_task_id": {
          "description": "Caller-supplied tracing identifier passed through to Kling (echoed back in upstream task records). Useful for correlating Pika job IDs with downstream systems. ≤64 chars. Kling only.",
          "type": "string",
          "minLength": 1,
          "maxLength": 64
        }
      },
      "required": [
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_scene_video",
    "description": "⚠ DEPRECATED — Renamed to `pika_scene`. Same parameters, same behavior. See `pika_scene` for full documentation. This alias will be removed in a future release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "ingredients": {
          "minItems": 2,
          "maxItems": 6,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          },
          "description": "2-6 ingredient image URLs. The model composites the objects/people from these images into a coherent scene driven by `prompt`."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50000,
          "description": "Scene description — what happens with the ingredients (camera, action, mood, environment)."
        },
        "ingredients_mode": {
          "description": "How faithful to the ingredients. `precise` (default) keeps each ingredient recognizable. `creative` lets the model interpret them loosely.",
          "type": "string",
          "enum": [
            "precise",
            "creative"
          ]
        },
        "model": {
          "description": "Pika model. 2.2 (default, best quality, supports duration / resolution); 2.1 (older); turbo (fastest, cheapest).",
          "type": "string",
          "enum": [
            "2.2",
            "2.1",
            "turbo"
          ]
        },
        "duration": {
          "description": "Duration in seconds, model 2.2 only — 5 or 10.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "resolution": {
          "description": "Output resolution, model 2.2 only. Default 720p.",
          "type": "string",
          "enum": [
            "720p",
            "1080p"
          ]
        },
        "aspect_ratio": {
          "description": "Output aspect ratio. Forwarded to pika-api as `aspectRatio`.",
          "type": "string",
          "enum": [
            "16:9",
            "9:16",
            "1:1"
          ]
        },
        "negative_prompt": {
          "description": "Optional — describe what to avoid.",
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "description": "Optional seed for reproducibility.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "ingredients",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_slide_animation",
    "description": "Generate an animated motion-graphics video from a text prompt. Claude writes the HTML composition (scene structure, typography, GSAP animations, shader transitions); the HyperFrames engine renders it deterministically to MP4. Use for: title cards, product launch teasers, animated logo reveals, explainer slides, text-on-screen intros, designed motion graphics, and any 'slide animation' / 'animated text' / 'animated slides' request. NOT for real-footage video (use `generate_video`), still-image-to-video transitions (use `generate_keyframes_video`), or assembling existing clips (use `edit_concat`). Default returns the HTML CDN URL only (~30s); set `record_video=true` to also render an MP4 via deterministic per-frame seek (~30–90s typical, longer for dense shader work).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "prompt": {
          "type": "string",
          "maxLength": 50000,
          "description": "Describe the slide deck — style, content, mood, audience. Example: \"A dark energetic product launch for a fitness app. Bold typography, neon accents.\""
        },
        "brief": {
          "description": "Optional structured product context. Merged with prompt.",
          "type": "object",
          "properties": {
            "product_name": {
              "type": "string"
            },
            "tagline": {
              "type": "string"
            },
            "key_features": {
              "type": "array",
              "items": {
                "type": "string"
              }
            },
            "tone": {
              "type": "string",
              "enum": [
                "professional",
                "technical",
                "casual",
                "playful"
              ]
            },
            "call_to_action": {
              "type": "string"
            }
          }
        },
        "aspect_ratio": {
          "description": "Canvas aspect ratio (default \"9:16\").",
          "type": "string",
          "enum": [
            "9:16",
            "16:9",
            "1:1"
          ]
        },
        "record_video": {
          "description": "When true, renders the animation to MP4 via the HyperFrames engine (deterministic per-frame seek, not real-time recording). Returns the video/mp4 URL as the primary result (html_url also included in structured output). Adds ~30–90s to total run time depending on animation length.",
          "type": "boolean"
        },
        "fps": {
          "description": "Render frame rate (24, 30, or 60). Default 30. Only applies when record_video=true.",
          "anyOf": [
            {
              "type": "number",
              "const": 24
            },
            {
              "type": "number",
              "const": 30
            },
            {
              "type": "number",
              "const": 60
            }
          ]
        },
        "quality": {
          "description": "HyperFrames render quality preset. `draft` = fastest (lower bitrate), `standard` = default, `high` = max quality (slower). Only applies when record_video=true.",
          "type": "string",
          "enum": [
            "draft",
            "standard",
            "high"
          ]
        }
      },
      "required": [
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_speech",
    "description": "Generate speech from text (default mode=text_to_speech), transform audio to a different voice (mode=speech_to_speech, AGNT-44 #B3), or dub a video into another language (mode=dub, AGNT-44 #B4). Default provider: minimax-tts (fast). elevenlabs: premium, more voice options + the only provider for speech_to_speech / dub. kling-tts (AGNT-44 #A4): Kling voice catalogue; runs in the worker envelope.\n\n**Typed result (sync paths only):** `text_to_speech` and `speech_to_speech` modes return `structuredContent` with measured `duration_seconds`, `word_count`, `char_count`, `pace_wps`, `pace_cps`. Pass `expected_duration_s` to also receive `drift_seconds` + `drift_pct`. Worker-backed paths (`mode=dub`, `provider=kling-tts`) return via `task_status` with the original opaque envelope; v2 typed coverage tracked.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "default": "minimax-tts",
          "description": "TTS provider. Default minimax-tts; elevenlabs / kling-tts alternatives. speech_to_speech + dub modes require provider=elevenlabs.",
          "type": "string",
          "enum": [
            "minimax-tts",
            "elevenlabs",
            "kling-tts"
          ]
        },
        "mode": {
          "description": "What to generate. `text_to_speech` (default) — synthesize speech from text. `speech_to_speech` (AGNT-44 #B3) — transform a source audio recording to a target voice (elevenlabs only; requires voice_id + source_audio_url). `dub` (AGNT-44 #B4) — translate and dub a video into another language (elevenlabs only; requires source_video_url + target_language).",
          "type": "string",
          "enum": [
            "text_to_speech",
            "speech_to_speech",
            "dub"
          ]
        },
        "text": {
          "description": "Text to speak. Required for mode=text_to_speech (default); ignored for speech_to_speech / dub. Max 10000 chars.",
          "type": "string",
          "maxLength": 10000
        },
        "voice_id": {
          "description": "Provider-specific voice ID. For mode=speech_to_speech the target voice (required).",
          "type": "string"
        },
        "language": {
          "default": "en",
          "description": "BCP-47 language tag (e.g. 'en', 'zh', 'es').",
          "type": "string"
        },
        "voice_language": {
          "description": "kling-tts only: kling-specific voice language code (overrides voice's default language; see kling docs).",
          "type": "string"
        },
        "speed": {
          "description": "kling-tts only: playback speed multiplier (kling defaults).",
          "type": "number"
        },
        "source_audio_url": {
          "description": "mode=speech_to_speech only: HTTPS URL of the source audio (mp3/wav/m4a, ≤50 MB). Worker fetches and uploads as multipart to ElevenLabs STS.",
          "type": "string",
          "format": "uri"
        },
        "remove_background_noise": {
          "description": "mode=speech_to_speech only: when true, strip background noise from the input before voice conversion.",
          "type": "boolean"
        },
        "source_video_url": {
          "description": "mode=dub only: HTTPS URL of the source video (mp4/mov). Passed to ElevenLabs as source_url (upstream pulls it).",
          "type": "string",
          "format": "uri"
        },
        "target_language": {
          "description": "mode=dub only: ISO-639 target language code (e.g. 'es', 'fr', 'ja', 'de'). Required for dub.",
          "type": "string"
        },
        "source_language": {
          "description": "mode=dub only: ISO-639 source language code, or 'auto' (default) to let ElevenLabs detect.",
          "type": "string"
        },
        "num_speakers": {
          "description": "mode=dub only: number of speakers in the source (0 = auto-detect, default).",
          "type": "integer",
          "minimum": 0,
          "maximum": 20
        },
        "expected_duration_s": {
          "description": "Optional — caller's predicted audio duration in seconds. When provided, the response also includes `drift_seconds` (signed actual - expected) and `drift_pct` (fraction). Skills with no schedule can omit. AGNT-175.",
          "type": "number",
          "minimum": 0
        }
      },
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "audio_url": {
          "type": "string"
        },
        "duration_seconds": {
          "anyOf": [
            {
              "type": "number"
            },
            {
              "type": "null"
            }
          ]
        },
        "model_used": {
          "type": "string"
        },
        "voice_id": {
          "type": "string"
        },
        "provider": {
          "type": "string"
        },
        "mode": {
          "type": "string",
          "enum": [
            "text_to_speech",
            "speech_to_speech",
            "dub"
          ]
        },
        "word_count": {
          "anyOf": [
            {
              "type": "integer",
              "minimum": 0,
              "maximum": 9007199254740991
            },
            {
              "type": "null"
            }
          ]
        },
        "char_count": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        },
        "pace_wps": {
          "anyOf": [
            {
              "type": "number",
              "minimum": 0
            },
            {
              "type": "null"
            }
          ]
        },
        "pace_cps": {
          "anyOf": [
            {
              "type": "number",
              "minimum": 0
            },
            {
              "type": "null"
            }
          ]
        },
        "drift_seconds": {
          "type": "number"
        },
        "drift_pct": {
          "type": "number"
        }
      },
      "required": [
        "audio_url",
        "duration_seconds",
        "model_used",
        "voice_id",
        "provider"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "generate_video",
    "description": "Generate a video from a text prompt or a single image. One tool, multiple providers via the `provider` enum. **Default: kling** — fastest + cheapest, supports ambient sound/music. Prefer kling unless the caller's request has a concrete reason that maps to a specialist.\n\nFor reference-based generation (multi-ref images / videos / audio as character or style anchors) use `generate_reference_video` instead.\n\nSpecialist alternatives: veo3 — use when the clip needs synthesized natural-language dialogue or voice-over (higher cost). sora — up to 20s, premium quality. seedance — cinematic look, supports `end_image` / `fast` / `seed` / `resolution`. minimax — Hailuo, image_to_video only. pika — fast t2v + i2v on 2.2 / 2.1 / turbo models, no sound.\n\nSound defaults to on for sound-capable providers (kling / veo3 / sora / seedance); pass sound=false to suppress. Passing sound=true on pika or minimax is rejected.\n\nThree modes: text_to_video (default), image_to_video (requires `image`), video_extend (AGNT-44 #A1 / BACK-397 — extend an existing Kling video by 4-5s; requires `source_video_id`, kling only).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "description": "Which video model. **Default: kling** — always prefer kling unless the caller's request has a concrete reason that maps to a specialist (veo3 for natural-language dialogue / voice-over; sora for 15–20s premium clips; seedance for cinematic look, supports `end_image` / `fast` / `seed` / `resolution`). A generic request for any video with sound/music/ambient audio should use kling — kling supports sound natively.",
          "type": "string",
          "enum": [
            "kling",
            "pika",
            "veo3",
            "sora",
            "seedance",
            "minimax"
          ]
        },
        "mode": {
          "description": "text_to_video (default): from prompt only. image_to_video: animate a source image as the first frame (requires `image`). minimax requires image_to_video. video_extend (AGNT-44 #A1 / BACK-397): extend an existing Kling video by 4-5 seconds (requires `source_video_id`; `prompt` optional; kling only). For reference-based generation (multi-ref images / videos / audio) use the sibling tool `generate_reference_video`.",
          "type": "string",
          "enum": [
            "text_to_video",
            "image_to_video",
            "video_extend"
          ]
        },
        "prompt": {
          "description": "Description or motion direction for the video. Required for text_to_video / image_to_video; optional for video_extend (when omitted, kling continues the source video's existing motion).",
          "type": "string",
          "minLength": 1
        },
        "source_video_id": {
          "description": "mode=video_extend only: Kling video id of the source clip to extend (must be ≤ 30 days old). Kling only — passing this on other providers is rejected.",
          "type": "string",
          "minLength": 1
        },
        "image": {
          "description": "Source image URL. Required when mode=image_to_video.",
          "type": "string",
          "format": "uri"
        },
        "duration": {
          "description": "Length in seconds (default 5; **sora defaults to 4** since 5 isn't in its accepted set; **veo3 defaults to 4** since 5 isn't in its accepted set). Per-provider valid values: kling 5 or 10 · pika 5 or 10 · veo3 4, 6, or 8 · seedance 4–15 · minimax 6 or 10 · **sora 4, 8, or 12 (no other values accepted)**.",
          "type": "integer",
          "minimum": 3,
          "maximum": 20
        },
        "aspect_ratio": {
          "description": "Output aspect ratio (default 16:9). `21:9`, `4:3`, `3:4`, `auto` are seedance-only — passing them on other providers is rejected.",
          "type": "string",
          "enum": [
            "16:9",
            "9:16",
            "1:1",
            "21:9",
            "4:3",
            "3:4",
            "auto"
          ]
        },
        "sound": {
          "description": "Enable ambient audio. If omitted, defaults to true on providers that support sound (kling, veo3, sora, seedance) and to false on providers that don't (pika, minimax). Pass sound=false explicitly to suppress audio on a sound-capable provider; passing sound=true on pika or minimax is rejected.",
          "type": "boolean"
        },
        "end_image": {
          "description": "Optional end-frame image URL for image_to_video transitions (start→end morph). seedance only. Rejected on other providers.",
          "type": "string",
          "format": "uri"
        },
        "seed": {
          "description": "Reproducibility seed. Accepted on seedance / pika / veo3. Rejected on kling / sora / minimax with a clear error.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "fast": {
          "description": "Use the fast (cheaper, ~20% off) seedance tier. seedance only. Caps resolution at 720p — combining `fast=true` with `resolution=1080p` is rejected.",
          "type": "boolean"
        },
        "resolution": {
          "description": "Output resolution. Accepted on seedance (480p/720p/1080p), pika (720p/1080p), veo3 (720p/1080p), and minimax (512p/720p/768p/1080p — sent upstream uppercased). Other providers (kling, sora) reject this field with a clear error. seedance fast tier additionally rejects 1080p.",
          "type": "string",
          "enum": [
            "480p",
            "512p",
            "720p",
            "768p",
            "1080p"
          ]
        },
        "shots": {
          "description": "Multi-shot narrative segments (kling t2v only; rejected on kling i2v). 1-6 shots, each with its own prompt + duration. Sum of `shot.duration` MUST equal the top-level `duration`.",
          "minItems": 1,
          "maxItems": 6,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "prompt": {
                "type": "string",
                "minLength": 1
              },
              "duration": {
                "type": "integer",
                "minimum": 1,
                "maximum": 15
              }
            },
            "required": [
              "prompt",
              "duration"
            ]
          }
        },
        "prompt_adherence": {
          "description": "How strictly to follow the prompt. loose ≈ 0.3 (more creative interpretation), balanced ≈ 0.5 (default, kling's natural feel), strict ≈ 0.85 (literal, every detail honored). Kling only.",
          "type": "string",
          "enum": [
            "loose",
            "balanced",
            "strict"
          ]
        },
        "negative_prompt": {
          "description": "What the model should AVOID generating. Accepted on kling / pika / veo3; rejected on sora / seedance / minimax with a clear error. Common patterns: general quality → 'blur, distortion, low quality'; clean look → 'watermarks, text overlay, logos'; character work → 'deformed anatomy, extra fingers'; single-shot → 'cuts, scene transitions'.",
          "type": "string",
          "maxLength": 2500
        },
        "watermark": {
          "description": "Burn kling's watermark into the output. Default `false` — clean output by default. Pass `true` to opt back into kling's account-tier-dependent watermark. Kling only.",
          "type": "boolean"
        },
        "quality_mode": {
          "description": "Kling quality tier (std = 720p; pro = 1080p; 4k). Kling only.",
          "type": "string",
          "enum": [
            "std",
            "pro",
            "4k"
          ]
        },
        "image_tail": {
          "description": "Kling i2v end-frame URL — drives a start→end morph. Requires mode=image_to_video. Kling only.",
          "type": "string",
          "format": "uri"
        },
        "voice_ids": {
          "description": "Cloned voice IDs from `clone_voice` for sound generation. Up to 8 (kling upstream cap). Kling only.",
          "minItems": 1,
          "maxItems": 8,
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "kling_model": {
          "description": "Kling t2v/i2v model variant. Currently kling-v3 only; kling-v2-6 deferred per plan D6. Kling only.",
          "type": "string",
          "enum": [
            "kling-v3"
          ]
        },
        "pika_model": {
          "description": "Pika model version. 2.2 = best quality (default); 2.1 / turbo are legacy and ignore duration / resolution. Pika only.",
          "type": "string",
          "enum": [
            "2.2",
            "2.1",
            "turbo"
          ]
        },
        "veo3_model": {
          "description": "Veo3 model variant. Default veo-3.0. `-fast` cuts latency + cost; 3.1 is the newer model. veo3 only.",
          "type": "string",
          "enum": [
            "veo-3.0",
            "veo-3.0-fast",
            "veo-3.1",
            "veo-3.1-fast"
          ]
        },
        "sora_model": {
          "description": "Sora model variant. Default sora-2; sora-2-pro is the higher-fidelity tier (~$1/clip). sora only.",
          "type": "string",
          "enum": [
            "sora-2",
            "sora-2-pro"
          ]
        },
        "size": {
          "description": "Sora output resolution grid. When omitted, falls back to the aspect_ratio-derived selection. sora only.",
          "type": "string",
          "enum": [
            "720x1280",
            "1280x720",
            "1024x1792",
            "1792x1024"
          ]
        },
        "character_id": {
          "description": "Sora character ID (from `create_sora_character` — separate atomic tool, future work). sora only.",
          "type": "string",
          "minLength": 1
        },
        "minimax_model": {
          "description": "Minimax video model variant. Default MiniMax-Hailuo-2.3-Fast. I2V-01-Director adds camera-movement tokens. minimax only.",
          "type": "string",
          "enum": [
            "MiniMax-Hailuo-2.3",
            "MiniMax-Hailuo-2.3-Fast",
            "MiniMax-Hailuo-02",
            "I2V-01-Director",
            "I2V-01-live",
            "I2V-01"
          ]
        },
        "last_frame_image": {
          "description": "Minimax end-frame image URL (start→end morph). minimax only.",
          "type": "string",
          "format": "uri"
        },
        "prompt_optimizer": {
          "description": "Toggle minimax's server-side prompt rewrite. Default true. minimax only.",
          "type": "boolean"
        }
      },
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_video_addition",
    "description": "⚠ DEPRECATED — Renamed to `pika_addition`. Same parameters, same behavior. See `pika_addition` for full documentation. This alias will be removed in a future release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video": {
          "type": "string",
          "format": "uri",
          "description": "Source video URL (mp4 / mov / webm) to add an element into."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50000,
          "description": "What to add, e.g. 'a small dog walking across the foreground'."
        },
        "object_image": {
          "type": "string",
          "format": "uri",
          "description": "Required reference image of the object to composite in. Despite the canonical pika-video CLI documenting this as optional, pika-api/generate/pikadditions returns 422 'Unprocessable entity' without it (verified 2026-05-10, BACK-345). For prompt-only object generation use `pika_swap` with `region_text` instead."
        },
        "negative_prompt": {
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "video",
        "prompt",
        "object_image"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_video_sora_edit",
    "description": "⚠ DEPRECATED — Use `sora_edit({mode: \"edit\"})` instead. Same parameters, same behavior. This alias is kept for one release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "source_video_id": {
          "type": "string",
          "minLength": 1,
          "description": "Sora upstream task id (e.g. `video_abc...`) to re-prompt."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1500,
          "description": "New prompt describing the edits."
        },
        "seconds": {
          "description": "Optional output duration. Sora only accepts 4 / 8 / 12. Defaults to whatever the upstream's billing model picks.",
          "type": "string",
          "enum": [
            "4",
            "8",
            "12"
          ]
        }
      },
      "required": [
        "source_video_id",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_video_sora_extension",
    "description": "⚠ DEPRECATED — Use `sora_edit({mode: \"extension\"})` instead. Same parameters, same behavior. This alias is kept for one release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "source_video_id": {
          "type": "string",
          "minLength": 1,
          "description": "Sora upstream task id of the video to extend."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1500,
          "description": "Prompt for the extension segment."
        },
        "seconds": {
          "description": "Extension duration (default `4`). Sora only accepts 4 / 8 / 12.",
          "type": "string",
          "enum": [
            "4",
            "8",
            "12"
          ]
        }
      },
      "required": [
        "source_video_id",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_video_sora_remix",
    "description": "⚠ DEPRECATED — Use `sora_edit({mode: \"remix\"})` instead. Same parameters, same behavior. This alias is kept for one release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "source_video_id": {
          "type": "string",
          "minLength": 1,
          "description": "Sora upstream task id of the video to remix."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1500,
          "description": "New prompt driving the remix."
        }
      },
      "required": [
        "source_video_id",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "generate_video_swap",
    "description": "⚠ DEPRECATED — Renamed to `pika_swap`. Same parameters, same behavior. See `pika_swap` for full documentation. This alias will be removed in a future release.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video": {
          "type": "string",
          "format": "uri",
          "description": "Source video URL."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50000,
          "description": "What to replace the region with."
        },
        "mask": {
          "description": "Mask image URL (white = replace, black = keep). Mutually exclusive with `region_text`.",
          "type": "string",
          "format": "uri"
        },
        "region_text": {
          "description": "Free-text description of the region to replace, e.g. 'the person wearing red'. 1-500 chars after trim (whitespace-only rejected). Mutually exclusive with `mask`. At least one of `mask` or `region_text` is required.",
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "replacement_image": {
          "description": "Optional reference image of the replacement subject.",
          "type": "string",
          "format": "uri"
        },
        "negative_prompt": {
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "video",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "oneOf": [
        {
          "required": [
            "mask"
          ]
        },
        {
          "required": [
            "region_text"
          ]
        }
      ]
    }
  },
  {
    "name": "html_to_pdf",
    "description": "Render an HTML document (or array of body fragments) to PDF, PNG, or JPG via a persistent server-side Chromium subprocess. Single-html mode is preferred (native @page support). body_pages mode renders pages in parallel and merges via PyPDF — no cross-page CSS counters / scripts / shared state. Assets are server-fetched and served to Chromium via route interception (deny-by-default). Async by default; sync mode requires preflight (html ≤100KB, single page, ≤10 assets, no external @import).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "html": {
          "description": "Complete HTML document as a string. Mutually exclusive with body_pages. All external assets (fonts, images, stylesheets) referenced via https:// are pre-fetched and inlined before Chromium renders — no live network access occurs during the render. Data URIs are served as-is. JavaScript is disabled in the render context. Max 10 MB.",
          "type": "string",
          "minLength": 1,
          "maxLength": 10485760
        },
        "body_pages": {
          "description": "Array of per-page HTML body fragments (1–50). Each fragment is wrapped in a full HTML document using shared_head, rendered by its own Chromium subprocess, and merged into a single PDF via PyPDF. Pages are rendered in parallel. No cross-page CSS counters, scripts, or shared DOM state. Requires shared_head. Mutually exclusive with html.",
          "minItems": 1,
          "maxItems": 50,
          "type": "array",
          "items": {
            "type": "string",
            "minLength": 1
          }
        },
        "shared_head": {
          "description": "HTML <head> content injected into every body_pages page (e.g. <style> blocks, <link rel='stylesheet'>, <meta> tags). Required when body_pages is set. Ignored when html is set (single-html mode carries its own <head>). Max 512 KB.",
          "type": "string",
          "maxLength": 524288
        },
        "format": {
          "default": "pdf",
          "description": "Output format. 'pdf' (default) — vector PDF via Chromium's print pipeline. 'png' — raster PNG at viewport resolution. 'jpg' — raster JPEG at viewport resolution.",
          "type": "string",
          "enum": [
            "pdf",
            "png",
            "jpg"
          ]
        },
        "wait_for": {
          "default": "fonts_and_images",
          "description": "Readiness mode before capture. 'fonts_and_images' (default) awaits document.fonts.ready + all <img> elements — safest for pixel-perfect output. 'fonts' awaits fonts only. 'domcontentloaded' / 'none' — capture immediately (fastest, may miss late-loading assets).",
          "type": "string",
          "enum": [
            "none",
            "domcontentloaded",
            "fonts",
            "fonts_and_images"
          ]
        },
        "mode": {
          "default": "async",
          "description": "Execution mode. 'async' (default) — submit to worker queue, poll task_status for result. 'sync' — render inline within the MCP request (≤100 KB html, single page, ≤10 assets, no external @import). Use 'sync' only for small documents where latency matters more than throughput. Async is recommended for production use.",
          "type": "string",
          "enum": [
            "async",
            "sync"
          ]
        },
        "pdf_options": {
          "description": "PDF layout options. Ignored when format != 'pdf'.",
          "type": "object",
          "properties": {
            "paper_size": {
              "description": "Paper size. Named: 'letter' (default), 'a4', 'a3', 'legal', 'tabloid'. Or custom object {width, height, unit}.",
              "anyOf": [
                {
                  "type": "string",
                  "enum": [
                    "letter",
                    "a4",
                    "a3",
                    "legal",
                    "tabloid"
                  ]
                },
                {
                  "type": "object",
                  "properties": {
                    "width": {
                      "type": "number",
                      "exclusiveMinimum": 0,
                      "description": "Width in the unit specified by 'unit'."
                    },
                    "height": {
                      "type": "number",
                      "exclusiveMinimum": 0,
                      "description": "Height in the unit specified by 'unit'."
                    },
                    "unit": {
                      "default": "px",
                      "description": "Unit for width/height. 'px' converted via 96 dpi.",
                      "type": "string",
                      "enum": [
                        "px",
                        "in",
                        "mm"
                      ]
                    }
                  },
                  "required": [
                    "width",
                    "height"
                  ]
                }
              ]
            },
            "margins": {
              "description": "Page margins. All sides default to 0.",
              "type": "object",
              "properties": {
                "top": {
                  "default": 0,
                  "type": "number",
                  "minimum": 0
                },
                "right": {
                  "default": 0,
                  "type": "number",
                  "minimum": 0
                },
                "bottom": {
                  "default": 0,
                  "type": "number",
                  "minimum": 0
                },
                "left": {
                  "default": 0,
                  "type": "number",
                  "minimum": 0
                },
                "unit": {
                  "default": "px",
                  "description": "Unit for all four margin values.",
                  "type": "string",
                  "enum": [
                    "px",
                    "in",
                    "mm"
                  ]
                }
              }
            },
            "landscape": {
              "default": false,
              "description": "Rotate paper 90 degrees. Ignored when paper_size is a custom width/height object (swap width/height manually for custom sizes).",
              "type": "boolean"
            }
          }
        },
        "raster_options": {
          "description": "Raster capture options. Ignored when format == 'pdf'.",
          "type": "object",
          "properties": {
            "viewport_px": {
              "description": "Viewport dimensions for raster capture (PNG/JPG).",
              "type": "object",
              "properties": {
                "width": {
                  "default": 1200,
                  "description": "Viewport width in CSS pixels (1–4096). Default 1200.",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 4096
                },
                "height": {
                  "default": 850,
                  "description": "Viewport height in CSS pixels (1–16384). Default 850.",
                  "type": "integer",
                  "minimum": 1,
                  "maximum": 16384
                }
              }
            },
            "quality": {
              "default": 90,
              "description": "JPEG quality (1–100). Ignored for PNG (lossless). Default 90.",
              "type": "integer",
              "minimum": 1,
              "maximum": 100
            }
          }
        }
      },
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_avatar_url",
    "description": "Return the user's onboarding-generated avatar image URL. Use as the `image` param for generate_lipsync or as `reference_image` for generate_image. Returns null when the user has no avatar on file — call `identity_set_avatar` to create one.",
    "inputSchema": {
      "type": "object",
      "properties": {},
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_memory_append",
    "description": "Write a new entry to the agent's daily memory file in memory/YYYY-MM-DD.md. Use this to record important facts, user preferences, or session learnings.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "text": {
          "type": "string",
          "minLength": 1,
          "maxLength": 8000,
          "description": "The memory content to store (1-8000 chars after trim)"
        },
        "source": {
          "default": "agent",
          "description": "Source of the memory (e.g. 'agent', 'user', 'skill'); 1-64 chars",
          "type": "string",
          "minLength": 1,
          "maxLength": 64
        }
      },
      "required": [
        "text"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_memory_search",
    "description": "Search long-term memory (MEMORY.md and memory/*.md) by keyword. Returns matching lines up to top_k results.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "query": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Keyword or phrase to search for in memory (1-500 chars after trim)"
        },
        "top_k": {
          "default": 5,
          "description": "Maximum number of matching lines to return (default 5, max 100)",
          "type": "integer",
          "minimum": 1,
          "maximum": 100
        }
      },
      "required": [
        "query"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_persona_read",
    "description": "Read a persona file from the agent workspace (IDENTITY.md, SOUL.md, or STYLE.md). Triggers: pika / pika bot / pikabot / pika.me / 'who am I' / 'who are you'.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "file": {
          "default": "identity",
          "description": "Which persona file to read: \"identity\", \"soul\", or \"style\"",
          "type": "string",
          "enum": [
            "identity",
            "soul",
            "style"
          ]
        }
      },
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "file": {
          "type": "string",
          "enum": [
            "identity",
            "soul",
            "style"
          ]
        },
        "content": {
          "type": "string"
        }
      },
      "required": [
        "file",
        "content"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "identity_set_avatar",
    "description": "Generate the user's 3D avatar from a reference photo and persist it. Use when `identity_avatar_url` returned null. `image_url` MUST be a Pika-CDN URL — call `upload_asset` first if you have raw bytes. Returns the final avatar URL on success. Errors: 409 if avatar is already set, 422 if the image was rejected (no face / NSFW / decode), 423 if a generation is already in progress, 502 on transient infra issues.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "image_url": {
          "type": "string",
          "format": "uri",
          "description": "Pika-CDN URL of the user's reference photo (from upload_asset)."
        }
      },
      "required": [
        "image_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_set_voice",
    "description": "Clone the user's voice from an audio sample and persist it. Use when `identity_voice_id` returned null. `audio_url` MUST be a Pika-CDN URL — call `upload_asset` first if needed. Provide a 10-30s clean voice recording. ElevenLabs is the default; choose `minimax` if ElevenLabs is unavailable. Returns JSON `{voice_id, platform}` on success. Errors: 409 if a voice is already set — by default we don't replace; first call `identity_voice_id` to surface the existing voice to the user, then re-invoke with `replace: true` if they explicitly want to swap (this overrides the 409 and replaces the existing voice clone). Other errors: 422 if the audio was rejected (low quality / wrong format), 423 if a clone is already in progress, 502 on transient infra issues.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "audio_url": {
          "type": "string",
          "format": "uri",
          "description": "Pika-CDN URL of a 10-30s clean voice sample (from upload_asset)."
        },
        "platform": {
          "description": "Voice platform. Default: elevenlabs (better quality on short samples).",
          "default": "elevenlabs",
          "type": "string",
          "enum": [
            "elevenlabs",
            "minimax"
          ]
        },
        "replace": {
          "description": "AGNT-41: when `true`, override the 409 returned when a voice already exists and replace it with the new clone. Default `false` — first call `identity_voice_id` to show the user the existing voice, then re-invoke with this flag only after explicit confirmation. The replacement happens server-side in a single write; the old voice ID is discarded.",
          "default": false,
          "type": "boolean"
        }
      },
      "required": [
        "audio_url"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_voice_id",
    "description": "Return the user's registered voice ID (raw string). Pass the result as `voice_id` to generate_speech. Returns null when the user has no routable voice on file — call `identity_voice_info` if you also need the platform, or `identity_set_voice` to create one.",
    "inputSchema": {
      "type": "object",
      "properties": {},
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_voice_info",
    "description": "Return JSON `{voice_id, platform, sample_url}` for the user's registered voice. Use when you need to dispatch generate_speech against the correct provider (generate_speech defaults to minimax-tts, but ElevenLabs voice IDs must route to the elevenlabs adapter). `sample_url` is a short-lived download URL when the current voice is backed by a workspace fallback sample. Returns null fields when the user has no routable voice/sample on file.",
    "inputSchema": {
      "type": "object",
      "properties": {},
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_voice_sample_url",
    "description": "Return a short-lived download URL (raw string) for the workspace voice sample backing the user's current routable voice. Use as the `audio` param for transcription / preview flows. Returns \"null\" when no workspace fallback sample is on file (e.g. the voice was cloned via `identity_set_voice` from a Pika-CDN upload — that recording isn't stored in the workspace). Pair with `identity_voice_info` when you also need `{voice_id, platform}`.",
    "inputSchema": {
      "type": "object",
      "properties": {},
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "identity_whoami",
    "description": "Show the currently authenticated user. Triggers: pika / pika bot / pikabot / pika.me / 'who am I' / 'who are you'.",
    "inputSchema": {
      "type": "object",
      "properties": {},
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "display": {
          "type": "string"
        },
        "name": {
          "type": "string"
        },
        "email": {
          "type": "string"
        },
        "phone": {
          "type": "string"
        },
        "agent_id": {
          "type": "string"
        }
      },
      "required": [
        "display"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "pika_addition",
    "description": "Generative video inpainting — composite an object/element into an EXISTING video. Powered by pika-api's `pikadditions`. Atomic primitive: single multipart submit + poll. The original video frames are preserved; the new subject is layered into the scene. For replacing a region instead of adding to it, use `pika_swap`.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video": {
          "type": "string",
          "format": "uri",
          "description": "Source video URL (mp4 / mov / webm) to add an element into."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50000,
          "description": "What to add, e.g. 'a small dog walking across the foreground'."
        },
        "object_image": {
          "type": "string",
          "format": "uri",
          "description": "Required reference image of the object to composite in. Despite the canonical pika-video CLI documenting this as optional, pika-api/generate/pikadditions returns 422 'Unprocessable entity' without it (verified 2026-05-10, BACK-345). For prompt-only object generation use `pika_swap` with `region_text` instead."
        },
        "negative_prompt": {
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "video",
        "prompt",
        "object_image"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "pika_effect",
    "description": "Apply one of 16 named viral Pikaffects (Cake-ify, Crumble, Crush, Decapitate, Deflate, Dissolve, Explode, Eye-pop, Inflate, Levitate, Melt, Peel, Poke, Squish, Ta-da, Tear) to a single still image, producing a short video. Atomic primitive: single multipart submit + poll.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "image": {
          "type": "string",
          "format": "uri",
          "description": "Source image URL the effect is applied to."
        },
        "effect": {
          "type": "string",
          "enum": [
            "Cake-ify",
            "Crumble",
            "Crush",
            "Decapitate",
            "Deflate",
            "Dissolve",
            "Explode",
            "Eye-pop",
            "Inflate",
            "Levitate",
            "Melt",
            "Peel",
            "Poke",
            "Squish",
            "Ta-da",
            "Tear"
          ],
          "description": "One of 16 named viral Pikaffects."
        },
        "prompt": {
          "description": "Optional prompt to guide the effect.",
          "type": "string",
          "maxLength": 500
        },
        "negative_prompt": {
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "image",
        "effect"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "pika_scene",
    "description": "Combine 2-6 ingredient images into a coherent multi-character / multi-object scene driven by `prompt`. Powered by pika-api's `pikascenes` endpoint. Atomic primitive: single multipart submit + poll. Use when you have separate character / object photos and want them composited into one scene (NOT a transition between two frames — for that use `generate_keyframes_video`).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "ingredients": {
          "minItems": 2,
          "maxItems": 6,
          "type": "array",
          "items": {
            "type": "string",
            "format": "uri"
          },
          "description": "2-6 ingredient image URLs. The model composites the objects/people from these images into a coherent scene driven by `prompt`."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50000,
          "description": "Scene description — what happens with the ingredients (camera, action, mood, environment)."
        },
        "ingredients_mode": {
          "description": "How faithful to the ingredients. `precise` (default) keeps each ingredient recognizable. `creative` lets the model interpret them loosely.",
          "type": "string",
          "enum": [
            "precise",
            "creative"
          ]
        },
        "model": {
          "description": "Pika model. 2.2 (default, best quality, supports duration / resolution); 2.1 (older); turbo (fastest, cheapest).",
          "type": "string",
          "enum": [
            "2.2",
            "2.1",
            "turbo"
          ]
        },
        "duration": {
          "description": "Duration in seconds, model 2.2 only — 5 or 10.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        },
        "resolution": {
          "description": "Output resolution, model 2.2 only. Default 720p.",
          "type": "string",
          "enum": [
            "720p",
            "1080p"
          ]
        },
        "aspect_ratio": {
          "description": "Output aspect ratio. Forwarded to pika-api as `aspectRatio`.",
          "type": "string",
          "enum": [
            "16:9",
            "9:16",
            "1:1"
          ]
        },
        "negative_prompt": {
          "description": "Optional — describe what to avoid.",
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "description": "Optional seed for reproducibility.",
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "ingredients",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "pika_swap",
    "description": "Replace a region of an existing video with a new subject. Region is defined by either `mask` (image, white=replace / black=keep) or `region_text` (free-text description). Optionally provide a `replacement_image` to anchor the new subject. Atomic primitive: single multipart submit + poll. For ADDING a subject without replacing existing content, use `pika_addition`.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "video": {
          "type": "string",
          "format": "uri",
          "description": "Source video URL."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 50000,
          "description": "What to replace the region with."
        },
        "mask": {
          "description": "Mask image URL (white = replace, black = keep). Mutually exclusive with `region_text`.",
          "type": "string",
          "format": "uri"
        },
        "region_text": {
          "description": "Free-text description of the region to replace, e.g. 'the person wearing red'. 1-500 chars after trim (whitespace-only rejected). Mutually exclusive with `mask`. At least one of `mask` or `region_text` is required.",
          "type": "string",
          "minLength": 1,
          "maxLength": 500
        },
        "replacement_image": {
          "description": "Optional reference image of the replacement subject.",
          "type": "string",
          "format": "uri"
        },
        "negative_prompt": {
          "type": "string",
          "maxLength": 500
        },
        "seed": {
          "type": "integer",
          "minimum": -9007199254740991,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "video",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "oneOf": [
        {
          "required": [
            "mask"
          ]
        },
        {
          "required": [
            "region_text"
          ]
        }
      ]
    }
  },
  {
    "name": "render_html_animation",
    "description": "Render a HyperFrames-format HTML composition to MP4/WebM/MOV via the HyperFrames engine (deterministic per-frame Chrome BeginFrame seek + ffmpeg). Pure rendering primitive — no LLM step. Caller supplies the composition HTML (must include `<div id=\"stage\" data-width=\"W\" data-height=\"H\">` and clip children with `data-start`/`data-duration`/`data-track-index`); worker scaffolds a temp project and runs the CLI. Output dimensions come from the HTML's `data-width`/`data-height`; duration is derived from track timing — neither is a tool parameter.\n\nFor Claude-driven slide-animation generation, use `generate_slide_animation` instead. This tool is for callers that have already authored the composition (hand-written, ported from Remotion, or generated by a plugin-layer skill).\n\n`format=mov` and `format=webm` produce transparent overlays for compositing into other workflows.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "html": {
          "type": "string",
          "minLength": 1,
          "maxLength": 5242880,
          "description": "HyperFrames composition HTML. Must include `<div id=\"stage\" data-width=\"W\" data-height=\"H\">` with timed clip children. Asset references must be absolute URLs (no relative paths). Max 5 MB."
        },
        "fps": {
          "description": "Render frame rate (24, 30, or 60). Default 30.",
          "anyOf": [
            {
              "type": "number",
              "const": 24
            },
            {
              "type": "number",
              "const": 30
            },
            {
              "type": "number",
              "const": 60
            }
          ]
        },
        "quality": {
          "description": "Render quality preset. `draft` = fastest (lower bitrate), `standard` = default, `high` = max quality (slower). Default standard.",
          "type": "string",
          "enum": [
            "draft",
            "standard",
            "high"
          ]
        },
        "format": {
          "description": "Output container. `mov` and `webm` produce transparent overlays for compositing into other workflows. Default mp4.",
          "type": "string",
          "enum": [
            "mp4",
            "webm",
            "mov"
          ]
        }
      },
      "required": [
        "html"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "scrape_ads",
    "description": "Scrape public ad-library data from Meta (Facebook), Google, or LinkedIn in a single call. Per-platform actions:\n- meta: ad-search | page-ads | ad-detail | advertiser-search\n- google: advertiser-search | ad-detail | company-ads\n- linkedin: ad-search | ad-detail\n\nReturns `{platform, action, count, items, meta}`. `meta.backend` is always `\"scrapecreators\"` and `meta.fallback_from` is always `null` (single-backend, no fallback chain). Backed by scrapecreators.com through Pika Proxy.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "platform": {
          "type": "string",
          "enum": [
            "meta",
            "google",
            "linkedin"
          ],
          "description": "Which ad library to scrape."
        },
        "action": {
          "type": "string",
          "minLength": 1,
          "maxLength": 40,
          "description": "Per-platform action. meta: 'ad-search' | 'page-ads' | 'ad-detail' | 'advertiser-search'. google: 'advertiser-search' | 'ad-detail' | 'company-ads'. linkedin: 'ad-search' | 'ad-detail'."
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Per-action input. For *-search actions: a free-text search term (company/brand name, advertiser name). For *-detail actions: a full upstream URL (e.g. https://www.facebook.com/ads/library/?id=123 for meta.ad-detail) — bare IDs are NOT accepted by the upstream and will 4xx. For meta.page-ads: a numeric pageId. For google.company-ads: the advertiser_id. The upstream endpoint also accepts a `domain` querystring — pass it via `params={\"domain\": \"...\"}` since `query` is fixed to `advertiser_id` for this action."
        },
        "limit": {
          "default": 20,
          "description": "Max items to return (1-200, default 20).",
          "type": "integer",
          "minimum": 1,
          "maximum": 200
        },
        "params": {
          "description": "Optional per-action knobs (e.g. country/region filters where the upstream supports them). Most actions ignore this.",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        }
      },
      "required": [
        "platform",
        "action",
        "query"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "platform": {
          "type": "string",
          "enum": [
            "meta",
            "google",
            "linkedin"
          ]
        },
        "action": {
          "type": "string"
        },
        "count": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        },
        "items": {
          "type": "array",
          "items": {}
        },
        "meta": {
          "type": "object",
          "properties": {
            "backend": {
              "type": "string",
              "const": "scrapecreators"
            },
            "fallback_from": {
              "type": "null",
              "const": null
            }
          },
          "required": [
            "backend",
            "fallback_from"
          ],
          "additionalProperties": {}
        }
      },
      "required": [
        "platform",
        "action",
        "count",
        "items"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "scrape_social",
    "description": "Scrape public data from 13 social platforms in a single call. NOT for authenticated user actions (post/reply/DM) — those go through the connect_* Composio tools. Per-platform actions (71 pairs total):\n- instagram: hashtag | profile | post | user-reels | user-posts | reels-search | post-comments | media-transcript | user-highlights\n- twitter: search | profile | tweet | user-tweets | tweet-transcript\n- linkedin: profile | post | company | job | post-detail | company-posts\n- tiktok: hashtag | keyword | profile | video | search-users | profile-videos | video-comments | video-transcript | user-followers | user-following | user-audience | trending-feed | popular-hashtags\n- youtube: search | channel | video | search-hashtag | channel-videos | channel-shorts | playlist | video-comments | video-transcript\n- facebook: profile | post | post-comments | media-transcript | user-posts | user-reels | user-photos | group-posts\n- threads: profile | post | search | search-users | user-posts\n- pinterest: profile | post | pin | search\n- twitch: profile | post | user-posts | user-schedule\n- truthsocial: profile | post | user-posts\n- bluesky: profile | post | user-posts\n- google: search\n- snapchat: profile\n\nThree tiktok actions are query-less and take their input via `params` instead: tiktok.user-audience (`params.handle` required), tiktok.trending-feed (`params.region` required), tiktok.popular-hashtags (`params.countryCode` optional). All other actions take their primary input via `query`.\n\nNote: facebook profile-shaped actions (profile, user-posts, user-reels, user-photos) accept EITHER a bare handle OR a full https://www.facebook.com/<handle>/ URL — the worker auto-expands handles, mirroring LinkedIn behavior. Facebook post/post-comments/media-transcript/group-posts require the full URL.\n\nNote: truthsocial only supports prominent public accounts; non-prominent handles raise InvalidInput.\n\nReturns `{platform, action, count, items, meta}` with per-action raw items lifted to common fields (id, author, text, url, likes, comments). `meta.backend` reports which provider actually served the response (`scrapecreators` | `apify` | `rapidapi`); `meta.fallback_from` is `null` when the primary served, otherwise a list of failed-hop records. Backed by scrapecreators.com (primary for most), Apify actors (instagram fallback), and RapidAPI twitter241 (twitter fallback), all through Pika Proxy.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "platform": {
          "type": "string",
          "enum": [
            "instagram",
            "twitter",
            "linkedin",
            "tiktok",
            "youtube",
            "facebook",
            "threads",
            "pinterest",
            "twitch",
            "truthsocial",
            "bluesky",
            "google",
            "snapchat"
          ],
          "description": "Which platform to scrape."
        },
        "action": {
          "type": "string",
          "minLength": 1,
          "maxLength": 40,
          "description": "Per-platform action (71 pairs total). instagram: 'hashtag' | 'profile' | 'post' | 'user-reels' | 'user-posts' | 'reels-search' | 'post-comments' | 'media-transcript' | 'user-highlights'. twitter: 'search' | 'profile' | 'tweet' | 'user-tweets' | 'tweet-transcript'. linkedin: 'profile' | 'post' | 'company' | 'job' | 'post-detail' | 'company-posts'. tiktok: 'hashtag' | 'keyword' | 'profile' | 'video' | 'search-users' | 'profile-videos' | 'video-comments' | 'video-transcript' | 'user-followers' | 'user-following' | 'user-audience' | 'trending-feed' | 'popular-hashtags'. youtube: 'search' | 'channel' | 'video' | 'search-hashtag' | 'channel-videos' | 'channel-shorts' | 'playlist' | 'video-comments' | 'video-transcript'. facebook: 'profile' | 'post' | 'post-comments' | 'media-transcript' | 'user-posts' | 'user-reels' | 'user-photos' | 'group-posts'. threads: 'profile' | 'post' | 'search' | 'search-users' | 'user-posts'. pinterest: 'profile' | 'post' | 'pin' | 'search'. twitch: 'profile' | 'post' | 'user-posts' | 'user-schedule'. truthsocial: 'profile' | 'post' | 'user-posts'. bluesky: 'profile' | 'post' | 'user-posts'. google: 'search'. snapchat: 'profile'."
        },
        "query": {
          "description": "Search query, hashtag, profile URL/username, or company slug — depending on action. For instagram.hashtag pass the hashtag (without '#'); for *.profile pass the URL or vanity slug. OPTIONAL for the 3 query-less tiktok actions (user-audience / trending-feed / popular-hashtags) which take their input via `params` — worker raises InvalidInput with a query-less-alternatives hint when other actions get an empty query.",
          "type": "string",
          "maxLength": 500
        },
        "limit": {
          "default": 20,
          "description": "Max items to return (1-200, default 20).",
          "type": "integer",
          "minimum": 1,
          "maximum": 200
        },
        "params": {
          "description": "Optional per-action knobs AND required input for query-less actions. Query-less actions (tiktok.user-audience requires `{handle}`; tiktok.trending-feed requires `{region}`; tiktok.popular-hashtags accepts optional `{countryCode}`) supply their primary input here. Standard actions use this for optional knobs (e.g. linkedin.job `{location?}`).",
          "type": "object",
          "propertyNames": {
            "type": "string"
          },
          "additionalProperties": {}
        }
      },
      "required": [
        "platform",
        "action"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "platform": {
          "type": "string",
          "enum": [
            "instagram",
            "twitter",
            "linkedin",
            "tiktok",
            "youtube",
            "facebook",
            "threads",
            "pinterest",
            "twitch",
            "truthsocial",
            "bluesky",
            "google",
            "snapchat"
          ]
        },
        "action": {
          "type": "string"
        },
        "count": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        },
        "items": {
          "type": "array",
          "items": {}
        },
        "meta": {
          "type": "object",
          "properties": {
            "backend": {
              "type": "string",
              "enum": [
                "scrapecreators",
                "apify",
                "rapidapi"
              ]
            },
            "fallback_from": {
              "anyOf": [
                {
                  "type": "null"
                },
                {
                  "type": "array",
                  "items": {
                    "type": "object",
                    "properties": {
                      "backend": {
                        "type": "string"
                      },
                      "reason": {
                        "type": "string",
                        "enum": [
                          "5xx",
                          "4xx_auth",
                          "4xx_quota",
                          "rate_limited",
                          "timeout",
                          "network",
                          "provider_unavailable",
                          "normalize_error",
                          "non_2xx_4xx",
                          "unknown"
                        ]
                      },
                      "status_code": {
                        "anyOf": [
                          {
                            "type": "number"
                          },
                          {
                            "type": "null"
                          }
                        ]
                      }
                    },
                    "required": [
                      "backend",
                      "reason"
                    ],
                    "additionalProperties": false
                  }
                }
              ]
            }
          },
          "additionalProperties": {}
        }
      },
      "required": [
        "platform",
        "action",
        "count",
        "items"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "search_music",
    "description": "Search a public music catalogue. Default: apple (Apple Music). Returns song metadata; pass `include_preview_url: true` to also receive each track's 30-second M4A preview URL (AGNT-70 — useful for ducking sample previews into edits, etc.).",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "default": "apple",
          "description": "Music catalogue provider. Default: `apple` (Apple Music).",
          "type": "string",
          "enum": [
            "apple"
          ]
        },
        "query": {
          "type": "string",
          "minLength": 1,
          "description": "Search query (e.g. 'radiohead creep' or 'lofi hip hop')."
        },
        "limit": {
          "default": 5,
          "description": "Max number of results to return.",
          "type": "integer",
          "minimum": 1,
          "maximum": 25
        },
        "include_preview_url": {
          "default": false,
          "description": "When true, each track in the response includes a `preview_m4a_url` field — Apple Music's official 30-second preview clip (M4A). Default false to keep the response compact for callers that only need metadata.",
          "type": "boolean"
        }
      },
      "required": [
        "query"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "tracks": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "artist": {
                "type": "string"
              },
              "album": {
                "type": "string"
              },
              "preview_url": {
                "type": "string"
              },
              "album_art_url": {
                "type": "string"
              },
              "duration_ms": {
                "type": "number"
              }
            },
            "required": [
              "id",
              "name",
              "artist",
              "preview_url"
            ],
            "additionalProperties": {}
          }
        },
        "count": {
          "type": "number"
        },
        "provider": {
          "type": "string"
        }
      },
      "required": [
        "tracks",
        "count",
        "provider"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "search_skill",
    "description": "Search for Pika skills by natural language query. Skills are reusable methodologies (e.g. 'make a singing video', 'create product showcase') that guide the agent through multi-step workflows.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "query": {
          "type": "string",
          "minLength": 1,
          "maxLength": 500,
          "description": "Natural language query to search for skills (1-500 chars after trim)"
        },
        "top_k": {
          "default": 5,
          "description": "Maximum number of results to return (default 5, max 25)",
          "type": "integer",
          "minimum": 1,
          "maximum": 25
        }
      },
      "required": [
        "query"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "results": {
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "id": {
                "type": "string"
              },
              "name": {
                "type": "string"
              },
              "description": {
                "type": "string"
              },
              "mode": {
                "type": "string"
              },
              "tags": {
                "type": "array",
                "items": {
                  "type": "string"
                }
              }
            },
            "required": [
              "id",
              "name",
              "description"
            ],
            "additionalProperties": {}
          }
        },
        "count": {
          "type": "number"
        }
      },
      "required": [
        "results",
        "count"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "sora_edit",
    "description": "Edit, extend, or remix an existing sora-generated video by upstream task id. Returns a NEW video id + CDN URL (the source is unchanged).\n\nModes:\n- `edit` (default): re-prompt — POST `/v1/videos/edits` with new prompt.\n- `extension`: append a continuation segment — POST `/v1/videos/extensions`.\n- `remix`: generative variant — POST `/v1/videos/{source_video_id}/remix`.\n\n`source_video_id` is the upstream sora task id (NOT a CDN URL) returned by a prior `generate_video` provider=sora call.\n\n⚠ Sora upstream is expected to be discontinued; the surface is intentionally minimal.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "mode": {
          "default": "edit",
          "description": "Operation type. `edit` re-prompts an existing video; `extension` appends a continuation segment; `remix` generates a creative variant. Default `edit`.",
          "type": "string",
          "enum": [
            "edit",
            "extension",
            "remix"
          ]
        },
        "source_video_id": {
          "type": "string",
          "minLength": 1,
          "description": "Sora upstream task id (e.g. `video_abc...`) of the video to edit / extend / remix. NOT a CDN URL."
        },
        "prompt": {
          "type": "string",
          "minLength": 1,
          "maxLength": 1500,
          "description": "Prompt driving the new video. For `edit` describes the changes; for `extension` describes the continuation; for `remix` describes the variant."
        },
        "seconds": {
          "description": "Optional output duration (sora only accepts 4 / 8 / 12). On `mode=edit` the upstream picks if omitted; on `mode=extension` the worker defaults to `4` when omitted; on `mode=remix` the field is silently ignored — sora's remix endpoint doesn't accept it.",
          "type": "string",
          "enum": [
            "4",
            "8",
            "12"
          ]
        }
      },
      "required": [
        "source_video_id",
        "prompt"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "task_cancel",
    "description": "Cancel a running Pika task. Returns the terminal state; rejects if the task has already completed.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "task_id": {
          "type": "string"
        }
      },
      "required": [
        "task_id"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "taskId": {
          "type": "string"
        },
        "status": {
          "type": "string",
          "const": "cancelled"
        },
        "createdAt": {
          "type": "string"
        },
        "lastUpdatedAt": {
          "type": "string"
        },
        "ttl": {
          "type": "number"
        }
      },
      "required": [
        "taskId",
        "status",
        "createdAt",
        "lastUpdatedAt",
        "ttl"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "task_status",
    "description": "Poll the status of a long-running Pika task. Call this tool in a TIGHT LOOP (no Bash, no sleep) until status is `done`, `failed`, or `cancelled`. Each call blocks server-side up to ~20s waiting for a state change, so client-side sleeping is redundant and breaks the collapsed-tool-call view in the UI. When `done`, the `result` field holds the skill output (e.g. video URL).",
    "inputSchema": {
      "type": "object",
      "properties": {
        "task_id": {
          "type": "string",
          "description": "task_id returned from any previous Pika tool invocation."
        }
      },
      "required": [
        "task_id"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "taskId": {
          "type": "string"
        },
        "status": {
          "type": "string"
        },
        "createdAt": {
          "type": "string"
        },
        "lastUpdatedAt": {
          "type": "string"
        },
        "ttl": {
          "type": "number"
        },
        "pollInterval": {
          "type": "number"
        },
        "statusMessage": {
          "type": "string"
        },
        "result": {}
      },
      "required": [
        "taskId",
        "status",
        "createdAt",
        "lastUpdatedAt",
        "ttl",
        "pollInterval"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  },
  {
    "name": "transcribe_audio",
    "description": "Transcribe audio to text. Accepts audio URLs directly OR video URLs (audio track is extracted server-side via ffmpeg). Returns text + optional auto-detected language. Three providers: `whisper` (default, supports timestamps), `deepgram` (nova-2, faster, text-only), `gemini` (2.5-flash, multilingual, text-only).\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "provider": {
          "description": "Transcription provider. `whisper` (default) supports timestamps. `deepgram` (nova-2) is faster, text-only. `gemini` (2.5-flash) is multilingual, text-only. Note: `timestamps=true` requires `whisper`.",
          "type": "string",
          "enum": [
            "whisper",
            "deepgram",
            "gemini"
          ]
        },
        "audio": {
          "type": "string",
          "format": "uri",
          "description": "Audio or video URL (MP3, WAV, M4A, MP4, WebM, etc.)."
        },
        "language": {
          "description": "Optional BCP-47 language hint. Auto-detects if omitted.",
          "type": "string"
        },
        "timestamps": {
          "description": "If true, return per-segment timestamps (start/end seconds + text) alongside the transcript. Raises an error if Whisper returns no segments. Useful for locating sentence boundaries within a longer audio file.",
          "type": "boolean"
        }
      },
      "required": [
        "audio"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "upload_asset",
    "description": "Get a presigned URL for uploading a file (image, audio, video). Returns { presigned_url, public_url, expires_at }.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "filename": {
          "type": "string",
          "minLength": 1,
          "description": "Original filename."
        },
        "mime_type": {
          "type": "string",
          "minLength": 1,
          "description": "MIME type (e.g. 'image/png', 'audio/mpeg')."
        },
        "size_bytes": {
          "type": "integer",
          "minimum": 1,
          "maximum": 524288000,
          "description": "File size in bytes. Max 500 MB."
        }
      },
      "required": [
        "filename",
        "mime_type",
        "size_bytes"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    }
  },
  {
    "name": "web_publish",
    "description": "Publish (create or update) a static site at a pika.me-hosted URL. Pass file paths + their `source_url` (the file's CDN URL — typically from a previous `upload_asset` call). Server fetches each source_url, uploads to backing storage, and finalizes the site in one atomic operation. List/delete/password operations are NOT exposed via MCP — use the pika.me dashboard for those.\n\nReturns the result inline when complete (typically 30s–5min). Progress updates are sent as notifications during execution. If the server budget expires before completion, falls back to `{ task_id, status }` — then call `task_status(task_id)` in a tight loop (no Bash, no sleep) until done.",
    "inputSchema": {
      "type": "object",
      "properties": {
        "slug": {
          "type": "string",
          "minLength": 3,
          "maxLength": 63,
          "pattern": "^[a-z0-9-]+$",
          "description": "Subdomain slug (a-z0-9-, 3-63 chars). Becomes the URL: https://{slug}.pika.me"
        },
        "files": {
          "minItems": 1,
          "maxItems": 200,
          "type": "array",
          "items": {
            "type": "object",
            "properties": {
              "path": {
                "type": "string",
                "minLength": 1,
                "maxLength": 500,
                "description": "Relative path within the site (e.g. 'index.html')."
              },
              "source_url": {
                "type": "string",
                "format": "uri",
                "description": "https URL the worker fetches and uploads to backing storage. Typically a CDN URL from upload_asset."
              }
            },
            "required": [
              "path",
              "source_url"
            ]
          },
          "description": "Files to publish (1-200)."
        },
        "title": {
          "description": "Optional site title.",
          "type": "string",
          "maxLength": 200
        },
        "description": {
          "description": "Optional site description (used in OG tags).",
          "type": "string",
          "maxLength": 1000
        },
        "password": {
          "description": "Optional shared password for site access. Omit for a public site.",
          "type": "string",
          "minLength": 4,
          "maxLength": 200
        }
      },
      "required": [
        "slug",
        "files"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#"
    },
    "outputSchema": {
      "type": "object",
      "properties": {
        "slug": {
          "type": "string"
        },
        "url": {
          "type": "string"
        },
        "file_count": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        },
        "version_id": {
          "type": "string"
        },
        "uploaded": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        },
        "skipped": {
          "type": "integer",
          "minimum": 0,
          "maximum": 9007199254740991
        }
      },
      "required": [
        "slug",
        "url",
        "file_count",
        "version_id",
        "uploaded",
        "skipped"
      ],
      "$schema": "http://json-schema.org/draft-07/schema#",
      "additionalProperties": {}
    }
  }
]
