# Shorz MCP

> Control Shorz via MCP to create and edit projects, configure panels, import assets, generate videos and thumbnails, publish to YouTube or TikTok, and run headless single-file video/image edits (no project required).

- Skill: `vossy/shorz-mcp` (Agent Skill, multi-file: 35 files)
- Install (CLI): `npx skillmds@latest add vossy/shorz-mcp`
- Raw SKILL.md: https://api.skillmd.com/api/skills/vossy/shorz-mcp/raw
- Safety review: pending
- Works with: Claude Code, Claude.ai, OpenAI Codex
- Category: AI & ML
- Author: Vossy (https://skillmd.com/u/vossy)
- Updated: 2026-09-17
- Page: https://skillmd.com/skills/vossy/shorz-mcp

---


# Shorz MCP Server Agent Skill

Before any Shorz MCP tool call, confirm **Shorz is running** and the **MCP server is connected**. For panel-level or project-type-specific work, **load the matching reference file** from `references/` (paths in the tables below) before changing settings or triggering renders.

This skill is the entry point (`SKILL.md`). Detailed workflows live under `references/panel-workflows/` (cross-project panels), `references/project-workflows/` (per-`projectType` end-to-end flows), `references/headless-workflows/` (**project-independent** file operations), and `references/creative-strategy/` (viral formats, hooks, and content brainstorming). The routing tables tell you which file to open.

## Operating Model

- Treat Shorz as a stateful desktop runtime. Always read current state before writes.
- Most menu/panel option changes map to `read_project_settings` + `update_project_settings` (targeted deep-merge patches).
- Generation and long-running workflows are asynchronous. For **project video creation**, poll **`get_video_generation_status`** as the default way to know running vs done vs error. Use **`fetch_app_events`** only if the user asks for live logs/progress, or when you are **diagnosing** a failed or stuck render (see **Event stream**).
- Prefer deterministic/headless arguments (override paths) over dialog-driven behavior.
- **Live UI vs MCP write races** — when the Shorz Electron app has the target project open, certain fields (notably `UI_SETTINGS.user_input_instructions` / PromptBar) can be reactively rewritten by the renderer shortly after an MCP write, because the input box owns its own in-memory state. If a configure-only patch on PromptBar appears to "revert," it is the live UI overwriting disk. Workarounds: write again after a moment, ask the user to close the project, or accept the renderer's value as the new truth. This is not a tool failure — the `set_user_instructions` write itself succeeded.

## Workflow routing

Pick the matching workflow file before editing a project or panel. Don't improvise — the per-file skills lock in tool names, enums, persistence keys, and known clamps/rejects.

### Guided creation (wizard) — vague idea, no concrete inputs

When the user expresses **intent to make a video or asset without the concrete inputs** it needs (no file path/URL, no format, no counts, no script — e.g. "i want to clip a youtube video", "make me an avatar video", "I need a thumbnail", "an animated intro"), do **NOT** improvise questions and do **NOT** start configuring. Open **`references/guided-creation/README.md`** first (trigger rules, question protocol, the 11 design rules, summary + confirmation contract), then run the matching flow:

| Intent sounds like | Guided flow file |
|---|---|
| clip/cut a long video into shorts | `references/guided-creation/clipping.md` |
| edit my own footage/photos into one video | `references/guided-creation/auto-edit.md` |
| make a video about X / from a script / faceless | `references/guided-creation/text-to-video.md` |
| talking head / AI presenter | `references/guided-creation/avatar.md` |
| two-person dialogue / AI podcast | `references/guided-creation/podcast.md` |
| ad / promo for my product | `references/guided-creation/advertisement.md` |
| thumbnail / cover image | `references/guided-creation/thumbnail-creator.md` |
| animated intro, logo reveal, kinetic text, title card | `references/guided-creation/animation-studio.md` |

Each flow asks a short option-driven sequence (interactive question tool when available; ≤4 preset options, recommended default first, custom input always possible, never re-asking anything already given), ends with a readable summary + an explicit confirmation, and only then executes via the matching `references/project-workflows/*.md` or panel workflow. If the user already gave a complete spec, skip the wizard and confirm the gaps in one question.

### Project workflows (one per `projectType`)

| `projectType` | Workflow file | Primary MCP tools |
|---|---|---|
| `auto-edit` | `references/project-workflows/auto-edit.md` | Panel `set_*_settings` (subtitle, title, border, audio, overlay, broll, general video) + `switch_project_aspect_ratio` |
| `text-to-video` | `references/project-workflows/text-to-video.md` | `set_text_to_video_settings`, `save_text_to_video_speech_audio`, `save_text_to_video_reference_images` |
| `avatar` | `references/project-workflows/avatar.md` | `set_avatar_settings`, `select_avatar_image`, `select_avatar_angle_image`, `select_avatar_audio`, `save_avatar_image`, `save_avatar_audio` |
| `podcast` | `references/project-workflows/podcast.md` | `set_podcast_settings`, `select_podcast_avatar_image`, `list_elevenlabs_voices` |
| `advertisement` | `references/project-workflows/advertisement.md` | `set_advertisement_settings`, `select_advertisement_image`, `remove_advertisement_image` |
| `clipping` | `references/project-workflows/clipping.md` | `update_project_settings` (`CLIPING`, `ASSET_PATHS`), `download_social_video`, `switch_project_aspect_ratio` |

### Headless workflows (no project, no `projectType`)

These MCP tools work on **local files only**. They do **not** use projects, `settings.json`, PromptBar, Create Video, or any `projectType`. Do **not** call `create_project` or `read_project_settings` before using them.

| Workflow | File | Primary MCP tools |
|---|---|---|
| Single-file deterministic edits | `references/headless-workflows/single-asset-edit.md` | `trim_video`, `crop_media`, `change_video_speed`, `remove_audio`, `set_audio_volume`, `rotate_media`, `flip_media`, `reverse_video`, `loop_video`, `fade_video`, `freeze_frame`, `set_image_duration`, `audio_fade`, `remove_silence`, `edit_image_with_ai`, `get_media_info`, `resize_media`, `fit_to_aspect`, `extract_audio`, `replace_audio`, `concat_media`, `extract_video_frames` |
| Pexels stock media search | `references/headless-workflows/pexels-stock-media.md` | `pexels_search_photos`, `pexels_search_videos`, `pexels_curated_photos`, `pexels_popular_videos`, `pexels_get_photo`, `pexels_get_video` |
| X (Twitter) live search | `references/headless-workflows/x-search.md` | `x_search` (natural-language X search with citations; **spends Shorz credits**) |
| Free Nano Banana 2 images (own Google key) | `references/headless-workflows/nano-banana-free-images.md` | `generate_images_nano_banana_free` (**no Shorz credits** — runs on the user's Google AI Studio free-tier quota) |

See **`references/headless-workflows/README.md`** for when to use headless edits vs a full project workflow. To put an edited file into a project afterward, that is a **separate** import step (`import_frontend_assets`, `save_file_as`, or project-specific path patches).

### Companion skills

| Skill | Use it for |
|---|---|
| **`shorz-ui-automation`** | Driving the app through its **actual UI** with a real, visible cursor — tutorial footage, feature demos, screenshots for the website or docs, onboarding walkthroughs. It owns the `get_ui_map` → `ui_click` → `get_ui_map` loop and the six UI tools (`get_ui_map`, `scroll_ui_element_into_view`, `ui_click`, `ui_type`, `ui_press_keys`, `ui_scroll`), which are **not** covered anywhere else in this skill. Load it whenever the deliverable is a *visual of the UI*. For changing a setting or rendering **without filming it**, stay here — the tools in this skill are faster and need no UI. |
| **`shorz-motion-graphics`** | Generating a green-screen motion graphics overlay and chroma-keying it onto an existing video, any length, in `16:9` / `9:16` / `1:1`. Covers every style — explainer graphics, infographics and data callouts, kinetic typography, broadcast lower-thirds, annotation arrows, chapter cards, and high-energy short-form. Load it instead of improvising from the Animation Studio panel workflow whenever the goal is graphics **on top of existing footage** — it carries the source-analysis, safe-zone, segmentation, chroma-key and compositing rules that panel reference does not. |

### Creative and strategy workflows

| Strategy area | Workflow file | Primary purpose |
|---|---|---|
| Content brainstorming & viral playbooks | `references/creative-strategy/content-brainstorming.md` | Designing high-retention hooks, formats, and CTAs mapped to Shorz tools |

Open **`references/creative-strategy/content-brainstorming.md`** when the user asks for content ideas, viral formats, organic growth playbooks, or hook/CTA variants — then switch to the matching **`references/project-workflows/*.md`** (or headless workflow) to execute.

### Panel workflows (cross-project; usable on matching sidebar panels)

| Sidebar panel | Workflow file | Primary MCP tool |
|---|---|---|
| General Video / Auto Zoom | `references/panel-workflows/settings.md` | `set_general_video_settings` |
| B-roll (Assets / Web / GIF / AI / Emoji) | `references/panel-workflows/broll.md` | `set_broll_settings` |
| Audio (mix, dubbing, reverb, visualization) | `references/panel-workflows/audio.md` | `set_audio_settings` + `set_audio_visualization_settings` |
| Overlay | `references/panel-workflows/overlay.md` | `set_overlay_settings` + `get_overlay_effects` lifecycle |
| Border | `references/panel-workflows/border.md` | `set_border_settings` |
| Text → captions / subtitles | `references/panel-workflows/subtitle.md` | `set_subtitle_settings` |
| Text → headline / banner | `references/panel-workflows/title.md` | `set_title_settings` |
| Thumbnail Creator (modal) | `references/panel-workflows/thumbnail-creator.md` | `open_thumbnail_creator`, `set_thumbnail_creator_settings`, `thumbnail_creator_generate`, `get_thumbnail_creator_generation_status` |
| Animation Studio (modal) | `references/panel-workflows/animation-studio.md` | `animation_studio_*`, `compile_remotion_preview`, `remotion_render` |
| Your Library (VIDEO / BROLL / SOUND / MUSIC) | `references/panel-workflows/your-library-assets.md` | `update_project_settings` (`ASSET_PATHS`); `delete_asset` only when deleting files on disk |

The **Text** sidebar shows subtitles and titles together but uses **two** tools with **two** key families (`subtitle*` vs `title*`). Never send subtitle keys to `set_title_settings` or vice versa — for both kinds, call each tool once with a minimal patch.

### Main VIDEO import (“Import Main Assets”) vs project type

In the Electron app, **`import_frontend_assets`** with `assetType: "video"` mirrors the UI **Import Main Assets** lane (fills the main timeline / `assets.VIDEOS`, which persists to **`ASSET_PATHS.main_video_asset_paths`** on save alongside other library categories). The single source of truth is **`isMainAssetsLaneEnabled`** (`frontend/src/utils/mediaAssetUtils.ts`): the main VIDEOS lane is **disabled** for **`podcast`**, **`avatar`**, **`advertisement`**, and for **`text-to-video`** unless **`textToVideoSourceMedia === "imported"`**. That rule gates the UI (import button, Your Library → VIDEOS tab, timeline strip) **and is enforced main-process-side**: a headless `import_frontend_assets` call with `overridePaths` and `assetType: "video"` into a lane-disabled open project is **rejected with an error** (redirecting to `"broll"`) instead of persisting `main_video_asset_paths`. Match this in guidance: never tell users they can “add main timeline videos” in templates that hide or disable main-video import.

| `projectType` | Main VIDEO import in UI (timeline / PromptBar gateway) | Your Library shows VIDEOS tab |
|---|---|---|
| `auto-edit` | Yes | Yes |
| `clipping` | Yes (typically one long source patched to **`ASSET_PATHS.main_video_asset_paths`**) | Yes |
| `text-to-video` | **Only when** Source Media is **`imported`** | **Only then** |
| `avatar` | **No** (`canImportMainAssets` false) | **No** |
| `podcast` | **No** (`canImportMainAssets` false) | **No** |
| `advertisement` | **No** (main lane disabled) — **workflow is reference stills**, not a main VIDEO lane — use **`select_advertisement_image`** / **`import_frontend_assets`** with **`image`** | **No** (tab hidden) |

**Agents:** Use **`avatar`**, **`podcast`**, **`advertisement`**, **`text-to-video`** (unless `imported`) workflows for their **typed** inputs (still images, avatars, product/person images, script/audio, generated sources). `import_frontend_assets` with **`video`** into these projects is **rejected with an error** (headless `overridePaths` path) — import supporting footage as **`broll`**, which every project type keeps.

### One asset per file name (every library lane)

A lane (VIDEO / BROLL / SOUND / MUSIC) holds **at most one asset per file name** — compared case-insensitively, folder ignored, and **per lane** (the same file may legitimately sit in both VIDEO and BROLL) — because the backend addresses assets by basename: PromptBar `@mentions`, the Python render maps, and music/SFX name lookups. The **UI and MCP enforce the same rule**: `import_frontend_assets` **skips** a file whose name the lane already holds and lists it in **`skipped`** (reason `duplicate-name`), and an `update_project_settings` patch of a lane collapses repeated names and reports **`duplicateAssetNamesDropped`**. Nothing errors and nothing is overwritten — so **always check `skippedCount` before telling the user an import is done**, especially after a paid generation. Need both files? Rename one. Full contract and result shapes: **`references/panel-workflows/your-library-assets.md`** → *One asset per file name*.

### PromptBar semantics by project type

The PromptBar (`set_user_instructions` → `UI_SETTINGS.user_input_instructions`) does **not** mean the same thing for every project type. Source: `frontend/src/data/projects.ts` (`projectTypePromptPlaceholderMap`).

| Type | PromptBar role | Spoken/script content lives in |
|---|---|---|
| `auto-edit` | Primary creative brief (required for Create Video). Can also direct per-clip editing with no panel toggle: transitions between clips, camera motion, speed/volume/reverse/loop/flip/rotate, fill-the-frame framing, beat-synced cuts to imported music, and the imported-music bus itself (MUSIC lane holds any number of tracks, played back to back — the brief can reorder tracks, trim each one, and place/fade/mix the music) — full vocabulary in `references/project-workflows/auto-edit.md` → *What the PromptBar can direct* | n/a |
| `text-to-video` | Optional **look and mood** guidance | `textToVideoScript` (or saved speech audio in `audio` input mode) |
| `avatar` | Optional guidance for **enabled auto-edit features** (captions, B-roll, music, etc.) — **not the spoken script**. Can also arrange imported music (order, sections, placement, volume, fades, mix) | `avatarScript` (script mode) or `avatarAudioUrl` (audio mode) |
| `podcast` | Optional B-roll / music / SFX guidance — **not the dialogue**. Can also arrange imported music (order, sections, placement, volume, fades, mix) | `podcastScript` with `[Interviewer]` / `[Interviewee]` line tags |
| `advertisement` | Ad creative brief | Product/person images via `set_advertisement_settings` |
| `clipping` | **Optional** — only when steering topics, clip length, platform, or hooks | Source video at `ASSET_PATHS.main_video_asset_paths` |

**Rule:** Never overwrite PromptBar with a script for `avatar`, `podcast`, or `text-to-video`. Put the spoken text in the panel field listed above.

**Referencing a specific asset:** to point the brief at one file in the project library, write its **filename in double quotes** — `use "intro.mp4" as the opener`. That is exactly what the desktop app's `@` picker and its drag-from-timeline shortcut insert, and the app renders any such reference as a pill. Get the real filenames from `read_project_settings` (`ASSET_PATHS`) or `get_video_assets` / `get_audio_assets` first — a name that isn't in the library is just prose to the analyzers.

**Output framing (all project types):** PromptBar / `set_user_instructions` text is **never** the place for export geometry. Do **not** include aspect-ratio tokens (`9:16`, `16:9`, `1:1`), pixel dimensions (`1080×1920`, `1920×1080`, `1280×720`), `video_width` / `video_height`, or output **fps** in the creative brief — the project already has **`VIDEO_SIZE`** from the UI or **`switch_project_aspect_ratio`**. Platform or delivery names for creative intent (`TikTok`, `Reels`, `Shorts`, “vertical short”) are fine; technical sizing belongs in the aspect tool, not PromptBar. When the user asks for a format change, call **`switch_project_aspect_ratio`** and strip sizing lines from any PromptBar text you persist.

### PromptBar main AI model

The PromptBar **model dropdown** (all project types) selects which AIML chat model Create Video and backend LLM calls use. It is **not** the same as panel-specific models (`textToVideoImageModel`, thumbnail `imageGenerator`, etc.).

| MCP tool | Persistence | Allowed values |
|---|---|---|
| `set_main_ai_model` | `AI_MODEL.main_ai_model_name` | Server-driven — call **`list_main_ai_models`** for the live lineup and pass an id from its response. At time of writing: `anthropic/claude-opus-5` (Opus 5, default), `anthropic/claude-fable-5` (Fable 5), `anthropic/claude-sonnet-5` (Sonnet 5, re-enabled 2026-08-10 — 1 / 2 credits per 1k, the cheapest Claude), `openai/gpt-5-6-terra` (GPT 5.6 Terra), `openai/gpt-5-6-sol` (GPT 5.6 Sol, frontier tier), `google/gemini-3.7-flash` (Gemini 3.7 Flash, ~0.2 / 1 credits per 1k tokens — much cheaper than Opus 5; a normal paid-selectable model billed like any other, **and** the only id a free run zero-rates) — six models; treat that as a snapshot, not an allowlist. This model also drives image / video-frame asset analysis, including key-subject localization. **On a free-tier run** (`auto-edit` / `clipping`, zero-balance user) only `google/gemini-3.7-flash` is zero-rated — the in-app picker locks itself to it, but the MCP path does not, so **you** must `set_main_ai_model` (or pass `mainAiModelName`) with that id or the render bills and 402s |

- **Read current model:** `read_project_settings` → `AI_MODEL.main_ai_model_name`.
- **Before Create Video:** When the user names Opus, Fable, GPT 5.6 Terra, GPT 5.6 Sol, Gemini 3.7 Flash, or a specific model id, call **`set_main_ai_model`** before **`trigger_create_video`** / **`generate_video`**, or pass **`mainAiModelName`** on **`trigger_create_video`** for a one-shot run (persists to disk first, matching the UI dropdown at generate time).
- **Live UI:** Patches go through `update-project-settings`; the open app reloads the dropdown from disk (unlike PromptBar text, the model selector rarely races with in-memory UI state).

### Animation Studio chat model

The Animation Studio modal has its **own** model picker (labels like `claude-opus-5`). It does **not** read `AI_MODEL.main_ai_model_name`.

| MCP | Role |
|---|---|
| `animation_studio_list_models` | Supported chat **`model`** ids for this build |
| `animation_studio_send_*` optional **`model`** | Per-call override; default **`anthropic/claude-opus-5`** |

**Allowed ids:** server-driven — `animation_studio_list_models` returns the live lineup (same `main_ai` catalog category as the PromptBar picker; at time of writing six: Opus 5, Fable 5, Sonnet 5, GPT 5.6 Terra, GPT 5.6 Sol, Gemini 3.7 Flash). Call it and pass an id from the response rather than one from this page. **`anthropic/claude-opus-4-6`**, **`anthropic/claude-opus-4-7`** and **`anthropic/claude-opus-4-8`** are retired from the selector — use **`anthropic/claude-opus-5`** instead. (**`anthropic/claude-sonnet-5`** was retired by `0017` but **re-enabled by `0033`** on 2026-08-10 — it is selectable again.) (The proxy still resolves `anthropic/claude-opus-4-8` via a disabled legacy-alias catalog row so older saved projects keep working, but do not pass it for new work.) Full workflow: **`references/panel-workflows/animation-studio.md`** → *Model selection*.

### Project targeting rules (apply to every **project** workflow)

**Skip this section for headless single-asset edit tools** (`trim_video`, `crop_media`, etc.) — they only need `inputPath` and Shorz running. See **`references/headless-workflows/single-asset-edit.md`**.

1. If the user names a project, resolve it with `list_projects` and use it. If not found, follow rule 5.
2. If the user says "current / open / this project", use `get_current_open_project`.
3. If the user gives no project identity, prefer `get_current_open_project`; otherwise ask whether to use existing or create new. **Never auto-pick** when multiple candidates match.
4. If the requested workflow conflicts with the current `projectType`, **do not silently repurpose** — ask whether to continue in the current project or create/select a matching type.
5. Create a new project only when the user explicitly asks, when a named target is missing and they approve creation, or when they ask for clean separation.
6. Never run destructive project actions (`delete_project`) without explicit confirmation. Same for `delete_asset`, `delete_overlay_effect`, `remove_animation_studio_export`, `clear_animation_studio_exports`.

### Asset verification: project main media vs My Assets library

Two different things — agents mix them up. Pick the right one.

- **Project main / timeline media** (the path(s) synced from the main VIDEO lane — **only workflows that expose main VIDEO import**, see **Main VIDEO import** above — e.g. `auto-edit` montage clips, clipping’s long source, `text-to-video` with **`imported`** source media): read `read_project_settings` → `ASSET_PATHS.main_video_asset_paths`, verify with **`file_exists`**. **`avatar`** / **`podcast`** templates do **not** use this lane in the shipped UI — do not treat `main_video_asset_paths` as required for Create Video there. **`advertisement`** center on **`ADVERTISEMENT.*` image paths**, not timeline video. There is **no** MCP tool that lists "the project's VIDEOS tab"; for clipping do not verify the source via `get_video_assets`.
- **My Assets library tabs** (cross-project inventory; AI-generated outputs, imports, downloads): use the per-tab `get_*_assets` tools or filtered **`query_my_assets`** as described in `references/panel-workflows/README.md` → *Library and cross-cutting MCP tools*.

### Removing imported project assets (Your Library)

When the user asks to **remove**, **clear**, or **delete** something from the project **Your Library** lanes (**VIDEO**, **BROLL**, **SOUND**, **MUSIC**), they usually mean **drop it from the project**, not erase the file from disk. Full tab mapping and steps: **`references/panel-workflows/your-library-assets.md`**.

| UI tab | `ASSET_PATHS` field | Clear entire lane |
|---|---|---|
| VIDEO (Import Main Assets) | `main_video_asset_paths` | `update_project_settings` → `{ ASSET_PATHS: { main_video_asset_paths: "" } }` |
| BROLL | `broll_video_asset_paths` | `… { broll_video_asset_paths: "" }` |
| SOUND (sound effects) | `audio_fx_asset_paths` | `… { audio_fx_asset_paths: "" }` |
| MUSIC | `music_asset_paths` | `… { music_asset_paths: "" }` |

- **Default tool:** **`update_project_settings`** on the matching field. Paths are **comma-separated**; to remove one file, read settings, split/filter/rejoin, then patch. **Do not** use `delete_asset` unless the user explicitly wants the **file deleted from disk** (destructive; confirm first).
- **UI parity:** In-app trash on a library card only updates project paths (same as clearing via MCP); it does not unlink the source file.
- **Not the same as:** `get_*_assets` / My Assets modal inventory (use `delete_asset` there only when user wants the library file gone). Playback outputs (`last_generated_final_video_for_playback_mode`, `playback_history_video_paths`) stay until changed separately.
- **VIDEO tab availability** depends on `projectType` — see *Main VIDEO import* above before clearing main video on `avatar` / `podcast` / etc.

## Required Workflow Rules

0. **Pre-flight: the app's requirements guard does NOT protect you**
   - Since 2026-08-19 the desktop UI blocks **Create Video** and names what is missing when a
     project cannot possibly render. That check lives in the PromptBar's submit handler
     (`frontend/src/utils/generationRequirements.ts`) — **`trigger_create_video` and
     `generate_video` bypass it entirely.** Over MCP you get the old behaviour: the render starts,
     burns time, and fails (or silently returns the user's own file). Verify these yourself before
     triggering a render:

     | Project type | Must be true before you trigger |
     |---|---|
     | `auto-edit` | ≥1 file in the main VIDEOS lane **and** PromptBar instructions **> 10 chars** (below that the auto-editor is skipped and the source is re-exported untouched) |
     | `clipping` | **exactly one** video in the main lane — zero or two+ both fail |
     | `avatar` | avatar image set **and** (script **> 10 chars** **or** saved audio). Either input alone is enough; the mode toggle does not invalidate the other |
     | `podcast` | script **> 10 chars** in `[Interviewer]`/`[Interviewee]` form **and BOTH** avatar images set |
     | `text-to-video` | script **≥ 50 chars** **or** saved speech audio; plus ≥1 imported main asset when `textToVideoSourceMedia` is `imported` |
     | `advertisement` | **at least one** of product image / character image (either alone is fine) |

   - These mirror the renderer's own guards, so treat them as hard preconditions rather than
     advice. Re-read a setting after writing it when the render depends on it (`read_project_settings`).

1. **Project targeting first**
   - Prefer `get_current_open_project` first (active UI project source of truth).
   - If no active project is set, call `list_projects` and ask user to choose existing vs approve creating new.
   - Use absolute paths from `get_projects_path` and returned project metadata.

2. **Safe setting edits**
   - Call `read_project_settings` when you need the full picture or before non-trivial decisions.
   - Persist nested changes with `update_project_settings`: pass only the `updates` object for keys/sections to change (objects merge recursively; scalars and arrays replace). Optional `unsetPaths` deletes keys by dot notation. The main process reads the file, merges, validates, and writes atomically—**do not** reintroduce a manual read→edit full blob→write loop for ordinary edits.
   - For `set_*_settings` tools, MCP now performs strict validation:
     - unknown keys are rejected
     - invalid enum/color values are rejected
     - numeric values are range-validated; out-of-bounds values are **rejected or clamped per tool** (for example `set_border_settings` **clamps** `borderWidth` to 1–100 and `borderAnimationDuration` to 1–5)

3. **Asynchronous operations**
   - **Video generation (`generate_video` / Create Video)** — Poll **`get_video_generation_status`** until `lastStatus` is terminal (`completed`, `completed_no_output`, `error`, `stopped`, etc.). That is the main tool for project renders.
   - **`fetch_app_events`** — Use **sparingly**: when the user **explicitly** wants the raw event/log stream, or when **`get_video_generation_status` is unclear** (stuck, unexpected state) and you need the IPC log lines to see what the UI would have seen. Not the default loop for “wait for video.”
   - **Social video downloads (`download_social_video`)** — async by default; poll **`get_social_video_download_status`** until `lastStatus` is `completed` or `error`. YouTube extraction alone regularly takes minutes, so the blocking `awaitCompletion: true` form is only safe for short clips.
   - **Social publishing (`social_publish`)** — async by default; the tool starts the job and returns immediately. Poll **`get_social_publish_status`** until `lastStatus` is terminal (`completed` | `error`); its `result` carries the same per-platform payload as the blocking form. Media upload alone routinely runs for minutes, so pass `awaitCompletion: true` only when the MCP client's tool-call timeout exceeds ~8 minutes. A per-platform `status: "publishing"` is NOT final — PostPeer pushes to the platform asynchronously and it can still fail (e.g. X video-length limits); keep polling **`social_get_post_status`** with the returned `id` until it reports `published` (success) or `failed` (carries `lastError`, credits auto-refunded).
   - **All other long-running tools — async by default, poll `get_job_status`.** `generate_scene_image`, `generate_image_to_video`, `generate_images`, `edit_image_with_ai`, `remove_silence`, `transcribe_video_file`, `remotion_render`, `x_search`, `youtube_upload_video`, `youtube_upload_from_local_file`, `download_generated_video`, `download_generated_music`, and `animation_studio_send_message` / `_send_and_compile` / `_send_compile_export` each return `{ started, jobId }` immediately. Poll **`get_job_status { jobId }`** every ~10–20s until `lastStatus` is `completed` (the `result` field holds the tool's full payload and `outputPaths` lists the absolute output files) or `error` (real error message in `error`). Pass `awaitCompletion: true` only when your tool-call timeout comfortably exceeds the tool's documented blocking window. **If a call or poll times out, the job is still running — NEVER blindly re-call the tool; paid generation would charge credits twice.** Jobs survive client-side timeouts but not an app restart.
   - **Other long-running work** (thumbnail creator UI flow) — follow each workflow; use `fetch_app_events` when those flows rely on IPC progress **and** the user cares about step-by-step output, or when debugging problems (see **Event stream**).
   - Report final completion/error to the user; use `fetch_app_events` for detail only when requested or when troubleshooting.

4. **Destructive actions**
   - Confirm user intent before `delete_project`, `delete_asset`, `delete_overlay_effect`,
     `remove_animation_studio_export`, or `clear_animation_studio_exports`.
   - **Removing a project library asset** (VIDEO/BROLL/SOUND/MUSIC) is usually **`update_project_settings`**, not `delete_asset` — see *Removing imported project assets* above.
   - For overlays, only imported/user overlays are deletable; default bundled overlays are protected.

5. **Keys and auth dependencies**
   - If provider features fail, check `read_api_keys`, balances, and auth status tools first.
   - For social posting, ensure account auth is active before upload.
   - Paid generation runs on Shorz account credits — use `get_shorz_credits` to confirm the user is
     signed in and has balance. If not signed in, sign them in with `shorz_sign_in_send_code` then
     `shorz_sign_in_verify_code` (ask the user for the emailed code).
   - **Zero-balance exception — the free tier.** A signed-in user whose balance is effectively empty
     (under 10 credits) still gets **4 free renders per week** (resets Monday 00:00 UTC, no rollover)
     on **`auto-edit` and `clipping` only**. So a low balance is not automatically a blocker for those
     two types — but it *is* for `text-to-video`, `avatar`, `podcast` and `advertisement`, which are
     paid only. The lease is opened and settled by the Electron **main** process around every render,
     so an MCP-triggered `trigger_create_video` / `generate_video` is covered exactly like the in-app
     Create Video button. A failed, stopped, or empty render does **not** consume one of the 4.
     (Unrelated to `generate_images_nano_banana_free`, which is credit-free for *any* user via their
     own Google key. Also unrelated to a desktop build that predates the tier: there the lease simply
     never opens and every call bills as before — a 402 on a zero-balance `auto-edit` render means
     this build, or the kill switch, not a mistake on your side.)
   - **Two things the app does for its own UI but NOT for you — do them yourself before a free render:**
     1. **Pin the model.** A free run zero-rates chat on **`google/gemini-3.7-flash`** and nothing else;
        any other model bills and 402s part-way through the render. The PromptBar pins it for
        button-started renders, but nothing pins it on the MCP path — call **`set_main_ai_model`** (or
        pass `mainAiModelName` on `trigger_create_video`) with `google/gemini-3.7-flash` first.
     2. **Check the source length.** Free runs cap the source at **30 minutes**. The renderer sends the
        probed duration so the proxy can refuse before the render starts; the MCP bridge does not send
        it, so an over-long source is **not** refused up front — probe with `get_media_info` and honour
        the cap yourself. Per-run abuse ceilings still apply server-side (6h lease, 400 LLM calls, 3M
        tokens, 5400 transcription seconds); tripping one ends the zero-rating mid-render, and the
        render then bills — i.e. 402s — from that point on.
   - **What a free run covers — the allowlist IS the paid boundary** (there is no second enforcement
     path): main-AI chat on the pinned model (plus a cheap analysis-tier Gemini id that a few pipeline
     steps hardcode — nothing for you to set), ElevenLabs **transcription**, and **web-image search**
     (the WEB B-roll source). Everything else bills normally inside a free run and therefore **402s at
     zero balance**. Free in a render: the LLM edit itself, local effects (cuts, filler-word/silence
     removal, zooms, face tracking, freeze frame, video colors), subtitles, titles, borders, overlays,
     **Auto SoundFX** (it places bundled sound files, no provider call), **four of the five B-roll
     sources — Assets, WEB, GIF and EMOJI** — and saving or exporting the finished file.
     Not free (do **not** enable or queue these for a zero-balance user):
     AI B-roll (`set_broll_settings.automaticAiBroll`, image *or* video), dubbing and auto-music and
     noise removal (`set_audio_settings`), thumbnail generation (`thumbnail_creator_generate`),
     Animation Studio chat (`animation_studio_send_*` — compiling/exporting an already-built animation
     is local and stays free), every standalone generation tool (`generate_images`,
     `generate_scene_image`, `generate_image_to_video`, `edit_image_with_ai`,
     `proxy_aiml_video_generation`, `generate_tts_preview`), `x_search`, and publishing
     (`social_publish`, `youtube_upload_*` — connecting an account is not itself billed, but the
     in-app Connect buttons are gated for these users, so don't promise a publish they can't pay for).
     **No silent-degradation cases:** everything on the free list above works in full on a free run —
     the only paid B-roll source is the **AI** tab, and every other lock above fails loudly with a 402
     rather than quietly dropping content from the finished video.
   - **Out of runs or out of credits?** There is no MCP purchase tool. Tell the user to buy credits in
     the app (**Buy Credits** opens Stripe Checkout in their **default browser**; the wallet is
     credited server-side and the app polls the balance), or to wait for the Monday 00:00 UTC reset.

## Tool Families

**Media always comes back as a file path, never base64.** No Shorz tool returns a data URL or a raw
base64 blob: generated and rendered media is written to disk and the response carries the absolute
path (`savedLocalFilePaths`, `pngFilePath`, `audioFilePath`, `outputPath`, `filePath`). Read that path
to inspect the file, or pass it straight to any tool that takes a local path. If a payload ever does
arrive inline, the server writes it to `%TEMP%\shorz-mcp-media\` and substitutes the path, appending a
note that names the extracted files — so a path is always what you act on.

### App and Configuration
- `check_for_update`
- `get_resource_path`
- `open_file_directory`, `file_exists`, `get_local_file_size`
- `read_api_keys`, `validate_elevenlabs_api_key`
- `get_elevenlabs_balance` (BYO ElevenLabs key balance; AIMLAPI key/license tools retired — paid generation runs on Shorz credits via the proxy)

### Shorz Account (credits + sign-in)
- `get_shorz_credits` — Shorz credit balance + entitlement for the signed-in user. Errors if no one is
  signed in or the credit server is unreachable. Check this before running paid generation tools. A
  balance under 10 credits means the free tier may apply — see *Keys and auth dependencies* below.
  The reply still carries a legacy **`watermark_free`** field: it is a deprecated alias for
  `has_purchased` and says nothing about exports. **Shorz watermarks nothing, at any tier** — never
  report that field to the user or treat it as an entitlement.
- `get_shorz_usage_and_pricing` — current model costs + recent usage for the signed-in user (same data
  as the in-app Usage & Pricing window): balance, recent calls, and per-model credit pricing. All
  amounts are in the credits the user pays (markup already included; never exposes our cost or markup).
  Free-tier renders appear in `recent_calls` as their own rows — `operation` reads
  **"Free run · Auto edit"** / **"Free run · Clipping"**, `units` shows the token spend, and `credits`
  is **0** (the in-app Usage tab renders those rows as "Free"). They carry `type: "addition"` as a
  quirk of the shared mapping — nothing was added; report them as free runs, not as credit grants.
- `shorz_sign_in_send_code` (email) → `shorz_sign_in_verify_code` (email + code) — email-OTP sign-in,
  identical to the in-app Sign-in button. Step 1 emails a one-time code; ask the user for it, then
  pass it (the 6–10 digit, usually 8-digit, value) to step 2. The Shorz desktop app must be running.

### Projects and Settings
- `create_project`, `list_projects`, `delete_project`, `get_current_open_project`
- `read_project_settings`, `update_project_settings`
- `set_user_instructions` (PromptBar creative brief), `set_main_ai_model` (PromptBar main LLM)
- `switch_project_aspect_ratio` (directly updates `VIDEO_SIZE` width/height + optional fps)
- Direct panel settings tools:
  - `set_subtitle_settings`
  - `set_title_settings`
  - `set_border_settings`
  - `set_overlay_settings`
  - `set_audio_settings`
  - `set_audio_visualization_settings`
  - `set_broll_settings`
  - `set_text_to_video_settings`
  - `set_avatar_settings`
  - `set_podcast_settings`
  - `set_advertisement_settings`
  - `set_general_video_settings`
- Panel styling and “looks” are applied only via granular `set_*_settings` (and project settings read/update); there are no MCP preset shortcut tools.

### Generation and Rendering
- `generate_video`, `stop_video_generation`, `get_video_generation_status` (primary status for project renders), `fetch_app_events` (IPC log stream; optional `since` cursor), `render_text_preview`
- `compile_remotion_preview`, `remotion_render`
- `proxy_aiml_video_generation`
- `generate_images` (standalone AIML image generation to library paths; no Thumbnail Creator modal). Optional `referenceImages` (up to 3 face/subject photos) preserve the **same face/identity** — the headless equivalent of the Avatar Creator modal's face-reference picker; use it to generate an avatar or podcast host that must look like a specific person.
- **Standalone generation assets (no project / no Create Video):**
  - `generate_scene_image` — Python text-to-video image stack (`Nano Banana 2`, `GPT Image 2`, optional reference paths). Explicit `aspectRatio` or `width`/`height`. Does **not** read or write `SCRTIPT_TO_VIDEO`.
  - `generate_image_to_video` — Python i2v stack; requires `imagePath`, `prompt`, and explicit `videoModel` (same ids as `textToVideoVideoModel`). Optional `durationSec` (**rejected with an error if outside the per-model range** — Gemini Omni 1–10, Seedance 2.5 4–30, Seedance 2.0 family 4–15, Kling/Happy Horse 3–15; omitted → 8), `aspectRatio` (**`16:9` or `9:16` ONLY, default `9:16`** — **1:1 is retired for video generation** and is rejected, even though standalone *image* generation still accepts it) or explicit dimensions, `generateAudio`. Does **not** run a project timeline.
  - Use these to produce library assets; use `import_frontend_assets` / project workflows when assembling a f

…(truncated)
