> ## Documentation Index
> Fetch the complete documentation index at: https://www.tella.com/docs/llms.txt
> Use this file to discover all available pages before exploring further.

# Model Context Protocol (MCP)

> Connect AI assistants to your Tella workspace

Tella's MCP server provides a standardized interface that allows any compatible AI assistant to access your Tella workspace. List videos, manage playlists, edit clips, upload new clips and B-roll videos, apply layouts, and more — all through natural language.

## Endpoint

```
https://api.tella.com/mcp
```

## Setup

<Tabs>
  <Tab title="ChatGPT">
    Add Tella as a custom connector:

    1. Open **Settings → Apps & Connectors**, then enable **Developer mode** under Advanced settings.
    2. Create a connector named **Tella** and paste the MCP server URL:

    ```
    https://api.tella.com/mcp
    ```

    3. Connect it and finish signing in to Tella. ChatGPT uses your existing Tella workspace permissions.
  </Tab>

  <Tab title="Claude Code">
    Run this command in your terminal:

    ```bash theme={null}
    claude mcp add --transport http --scope user tella https://api.tella.com/mcp
    ```

    Or for one-click install: [Add Tella MCP Server](claude://mcp/add?transport=http\&name=tella\&url=https://api.tella.com/mcp)

    You'll be prompted to authenticate with your Tella account.
  </Tab>

  <Tab title="Claude Desktop">
    Add to your `claude_desktop_config.json`:

    ```json theme={null}
    {
      "mcpServers": {
        "tella": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "https://api.tella.com/mcp"
          ]
        }
      }
    }
    ```
  </Tab>

  <Tab title="Cursor">
    Add to your MCP settings:

    ```json theme={null}
    {
      "mcpServers": {
        "tella": {
          "command": "npx",
          "args": [
            "mcp-remote",
            "https://api.tella.com/mcp"
          ]
        }
      }
    }
    ```
  </Tab>

  <Tab title="Codex">
    Run this command in your terminal:

    ```bash theme={null}
    codex mcp add tella -- npx mcp-remote https://api.tella.com/mcp
    ```

    Or add it to your `~/.codex/config.toml` manually:

    ```toml theme={null}
    [mcp_servers.tella]
    command = "npx"
    args = ["mcp-remote", "https://api.tella.com/mcp"]
    ```

    You'll be prompted to authenticate with your Tella account.
  </Tab>
</Tabs>

## Authentication

The MCP server uses OAuth 2.1 for authentication. When you first connect, you'll be redirected to Tella to authorize access. The connection uses your Tella account permissions — you can only access videos and playlists in workspaces you belong to.

## Get started with Skills

Alongside the MCP, the Tella Skills directory offers open-source, ready-made workflows built and tested for Claude Code (some also run on other agents). The MCP connects your agent to your Tella library; the skills teach it video editing on local files — cutting videos, adding zooms, dropping in B-roll, and publishing your latest Tella recording to YouTube.

<Card title="Browse the Skills directory" icon="wand-magic-sparkles" href="https://www.tella.com/skills" horizontal>
  Install a skill with one command and start editing videos with your AI agent in minutes.
</Card>

## Managing videos

Organize and share your workspace: list and update videos, manage playlists, tags, and webhook subscriptions, control access, and export finished videos.

### Videos

<AccordionGroup>
  <Accordion title="list_videos">
    List all videos in your workspace with pagination support.

    **Parameters:**

    * `cursor` (optional): Pagination cursor from previous response
    * `limit` (optional): Items per page (1-100, default: 20)
    * `playlistId` (optional): Filter videos by playlist
    * `tagIds` (optional): Filter videos by tag IDs, comma-separated. Multiple tags are combined with AND. Ignored when `playlistId` is set
  </Accordion>

  <Accordion title="get_video">
    Get video information. Returns summary fields by default. Use include flags for additional data.

    **Parameters:**

    * `id` (required): Video ID
    * `includeTranscript` (optional): Include transcript text
    * `includeChapters` (optional): Include chapter markers
    * `includeThumbnails` (optional): Include thumbnail URLs
    * `includeExports` (optional): Include export status
  </Accordion>

  <Accordion title="create_video">
    Create a new video from an uploaded source. Call `create_source` first (`kind: "video"`), `PUT` the bytes to the returned `uploadUrl`, then pass the `sourceId` here — it becomes the new video's first clip.

    **Parameters:**

    * `sourceId` (required): Source ID from `create_source` (`kind: "video"`)
    * All `update_video` settings except `id` — `name`, `description`, `playbackRate`, `captionsEnabled`, `transcriptEnabled`, `commentsEnabled`, `downloadsEnabled`, `linkScope`, `password`, `searchEngineIndexingEnabled`, `studioSound`, and `dimensions` — to configure the new video in the same call. When `dimensions` is omitted, the canvas is sized from the uploaded video.
  </Accordion>

  <Accordion title="update_video">
    Update video metadata and settings.

    **Parameters:**

    * `id` (required): Video ID
    * `name` (optional): Video title
    * `description` (optional): Video description
    * `playbackRate` (optional): Default playback speed (0.5-2.0)
    * `captionsEnabled` (optional): Show captions by default
    * `transcriptEnabled` (optional): Show transcript tab
    * `commentsEnabled` (optional): Allow comments
    * `downloadsEnabled` (optional): Allow downloads
    * `linkScope` (optional): Access level — `public`, `private`, `password`, or `embedonly`
    * `password` (optional): Password for protected videos
    * `searchEngineIndexingEnabled` (optional): Allow search engine indexing
    * `studioSound` (optional): Studio Sound (AI audio enhancement) master switch. Enabling it also starts generating the enhanced audio tracks in the background; playback and exports fall back to raw audio until they are ready. Individual clips can opt out via `update_clip`'s `studioSound`.
    * `dimensions` (optional): Canvas size in pixels as `{ width, height }` (each 16–4096). Changing it remaps every clip and section layout to a ratio-appropriate equivalent — the same transform as switching size in the editor's Setup → Size. Editor presets: `1920x1080` (16:9), `1920x1200` (16:10), `1440x1080` (4:3), `1080x1080` (1:1), `1080x1350` (4:5), `1080x1920` (9:16). No-op when the video already has the requested size.
  </Accordion>

  <Accordion title="delete_video">
    Delete a video (moves to trash).

    **Parameters:**

    * `id` (required): Video ID
  </Accordion>

  <Accordion title="duplicate_video">
    Create a copy of a video. Supports trimming to extract a time range or specific chapter.

    **Parameters:**

    * `id` (required): Video ID to duplicate
    * `name` (optional): Name for the duplicate
    * `startTime` (optional): Trim start time in seconds (use with endTime)
    * `endTime` (optional): Trim end time in seconds (use with startTime)
    * `chapterIndex` (optional): Extract a specific chapter by 0-based index (cannot be combined with startTime/endTime)
  </Accordion>

  <Accordion title="export_video">
    Start a video export. Use `get_export_status` to poll until the export completes or fails.

    Returns an `export` object. Pass its `exportId` value to `get_export_status`.

    **Parameters:**

    * `id` (required): Video ID
    * `granularity` (optional): `video` (full video, default), `clips` (one file per clip), or `raw` (raw recordings)
    * `resolution` (optional): `4k` for 4K; omit for default
    * `fps` (optional): Frames per second — `30` or `60`
    * `subtitles` (optional): Burn in subtitles
    * `speed` (optional): Playback speed — `0.5`, `0.75`, `1`, `1.25`, `1.5`, `1.75`, or `2`
  </Accordion>

  <Accordion title="get_export_status">
    Get an export's current status and progress. Poll this after `export_video` until `export.status` is `completed` or `failed`; `export.downloadUrl` appears when the export is ready.

    **Parameters:**

    * `id` (required): Video ID
    * `exportId` (required): The `export.exportId` value returned by `export_video`
  </Accordion>

  <Accordion title="add_collaborator_to_video">
    Add a collaborator to a video. The user must be a member of your workspace.

    **Parameters:**

    * `id` (required): Video ID
    * `email` (required): Email address of the user to add
    * `role` (required): Role to grant — `editor` or `viewer`
  </Accordion>

  <Accordion title="update_collaborator_on_video">
    Change a collaborator's role on a video.

    **Parameters:**

    * `id` (required): Video ID
    * `userId` (required): User ID of the collaborator
    * `role` (required): New role — `editor` or `viewer`
  </Accordion>

  <Accordion title="remove_collaborator_from_video">
    Remove a collaborator from a video.

    **Parameters:**

    * `id` (required): Video ID
    * `userId` (required): User ID of the collaborator to remove
  </Accordion>
</AccordionGroup>

<Note>
  You can't change the role of, or remove, the video's **Creator** — `update_collaborator_on_video` and `remove_collaborator_from_video` reject those calls with a `403`. To hand off ownership, use the [transfer ownership](/docs/help/teams/how-can-i-add-a-team-member-as-an-editor-of-your-video#transfer-ownership-of-a-video) flow in the app.
</Note>

### Playlists

<AccordionGroup>
  <Accordion title="list_playlists">
    List all playlists in your workspace.

    **Parameters:**

    * `visibility` (optional): Filter by `personal` or `org`
    * `cursor` (optional): Pagination cursor
    * `limit` (optional): Items per page (1-100, default: 20)
  </Accordion>

  <Accordion title="create_playlist">
    Create a new playlist.

    **Parameters:**

    * `name` (required): Playlist name
    * `description` (optional): Playlist description
    * `emoji` (optional): Emoji icon
    * `visibility` (optional): `personal` or `org`
    * `linkScope` (optional): Access level — `public`, `private`, or `password`
    * `password` (optional): Password for protected playlists
  </Accordion>

  <Accordion title="get_playlist">
    Get detailed information about a playlist.

    **Parameters:**

    * `id` (required): Playlist ID
  </Accordion>

  <Accordion title="update_playlist">
    Update playlist metadata and settings.

    **Parameters:**

    * `id` (required): Playlist ID
    * `name` (optional): Playlist name
    * `description` (optional): Playlist description
    * `emoji` (optional): Emoji icon
    * `linkScope` (optional): Access level
    * `password` (optional): Password for protected playlists
    * `searchEngineIndexingEnabled` (optional): Allow search engine indexing
  </Accordion>

  <Accordion title="delete_playlist">
    Delete a playlist.

    **Parameters:**

    * `id` (required): Playlist ID
  </Accordion>

  <Accordion title="add_video_to_playlist">
    Add a video to a playlist.

    **Parameters:**

    * `playlistId` (required): Playlist ID
    * `videoId` (required): Video ID to add
  </Accordion>

  <Accordion title="remove_video_from_playlist">
    Remove a video from a playlist.

    **Parameters:**

    * `playlistId` (required): Playlist ID
    * `videoId` (required): Video ID to remove
  </Accordion>

  <Accordion title="list_playlist_groups">
    List sidebar playlist groups.

    Personal groups are only visible to the authenticated user. Org groups are shared with the whole workspace.

    **Parameters:**

    * `visibility` (optional): Filter by `personal` or `org`
  </Accordion>

  <Accordion title="create_playlist_group">
    Create a sidebar group to organize playlists into.

    **Parameters:**

    * `name` (required): Group name
    * `emoji` (optional): Emoji icon
    * `visibility` (optional): `personal` or `org`. Defaults to `personal`
  </Accordion>

  <Accordion title="update_playlist_group">
    Rename a playlist group, change its icon, or move it within its sidebar section.

    **Parameters:**

    * `id` (required): Playlist group ID
    * `name` (optional): New group name
    * `emoji` (optional): Emoji icon
    * `position` (optional): 0-based position in the sidebar section
  </Accordion>

  <Accordion title="delete_playlist_group">
    Delete a playlist group.

    Playlists inside the group are not deleted. They become ungrouped.

    **Parameters:**

    * `id` (required): Playlist group ID
  </Accordion>

  <Accordion title="move_playlist_to_group">
    Move a playlist into a group, or remove it from its group.

    The group's visibility must match the playlist's visibility. Org playlists can only move into org groups, and personal playlists can only move into personal groups.

    **Parameters:**

    * `playlistId` (required): Playlist ID
    * `groupId` (optional): Target playlist group ID. Omit it to remove the playlist from its group
    * `position` (optional): 0-based position in the target group, or in the ungrouped sidebar section when `groupId` is omitted
  </Accordion>
</AccordionGroup>

### Tags

Tags categorize videos across your library. Workspace tags are shared with everyone in the workspace; private tags are only visible to you. Filter your library by tag with `list_videos`' `tagIds` parameter.

<AccordionGroup>
  <Accordion title="list_tags">
    List all tags visible to you: the workspace's shared tags plus your private tags.

    No parameters.
  </Accordion>

  <Accordion title="create_tag">
    Create a tag for categorizing videos. When no color is given, one is derived from the name.

    **Parameters:**

    * `name` (required): Tag name
    * `scope` (optional): Tag visibility — `workspace` (default) or `private`
    * `color` (optional): Tag color as a hex value, e.g. `#6366f1`
    * `description` (optional): Tag description
  </Accordion>

  <Accordion title="update_tag">
    Update a tag's name, color, and/or description. Private tags can only be updated by their creator.

    **Parameters:**

    * `id` (required): Tag ID
    * `name` (optional): New tag name
    * `color` (optional): New tag color as a hex value, e.g. `#6366f1`
    * `description` (optional): New tag description
  </Accordion>

  <Accordion title="delete_tag">
    Permanently delete a tag and remove it from all videos.

    **Parameters:**

    * `id` (required): Tag ID
  </Accordion>

  <Accordion title="list_video_tags">
    List the tags on a video that are visible to you.

    **Parameters:**

    * `videoId` (required): Video ID
  </Accordion>

  <Accordion title="add_video_tag">
    Tag a video. Requires edit access to the video. Adding a private tag only affects your own library.

    **Parameters:**

    * `videoId` (required): Video ID
    * `tagId` (required): Tag ID
  </Accordion>

  <Accordion title="remove_video_tag">
    Untag a video. Requires edit access to the video.

    **Parameters:**

    * `videoId` (required): Video ID
    * `tagId` (required): Tag ID
  </Accordion>
</AccordionGroup>

### Webhooks

Manage webhook subscriptions, which are scoped to your user rather than shared with your whole workspace. Your endpoint receives `video.created` events for videos you create through MCP, plus activity on videos you own, like views and milestones, even when someone else (a teammate or an anonymous viewer) triggers them. Playlist events are currently emitted only for actions performed in the Tella web app. See the [Webhooks guide](/docs/webhooks) to create an endpoint, retrieve its signing secret, or delete it.

<AccordionGroup>
  <Accordion title="list_webhooks">
    List your webhook subscriptions, with cursor pagination.

    **Parameters:**

    * `cursor` (optional): Pagination cursor from previous response
    * `limit` (optional): Items per page (1-100, default: 20)
  </Accordion>

  <Accordion title="update_webhook">
    Update a webhook subscription's URL, event filters, or delivery status. Provide at least one of `url`, `filterTypes`, or `disabled`.

    **Parameters:**

    * `id` (required): Webhook endpoint ID
    * `url` (optional): New delivery URL
    * `filterTypes` (optional): New event types to deliver, as a non-empty array (it cannot be `[]`). Pass `null` to clear the filter and deliver every event type. To stop delivery entirely, use `disabled: true` instead
    * `disabled` (optional): Pause (`true`) or resume (`false`) webhook delivery
  </Accordion>
</AccordionGroup>

## Editing videos

Build and refine the video itself: upload sources, add and cut clips, apply layouts, and add zooms, blurs, highlights, overlays, and sound effects.

### Sources

A source is an uploaded file (video, audio, or image) that can be turned into a new video or clip, or referenced as B-roll media, an overlay, a clip background, background music, or a sound effect. Create a source, upload the bytes to the returned pre-signed URL, then reference the `sourceId` from `create_video`, `upload_clip`, `add_layout` / `update_layout`, `add_overlay`, `update_clip`, `set_background_music`, or `add_sound_effect`.

<AccordionGroup>
  <Accordion title="create_source">
    Create a new source upload. Returns a `sourceId` and a pre-signed `uploadUrl` — `PUT` the file bytes to `uploadUrl` (the URL expires after 1 hour). Once uploaded, the `sourceId` can be used by `create_video` (video only, to create a new video), by `upload_clip` (video only, to add a new clip), by `add_layout` / `update_layout` (as B-roll media), by `add_overlay`, by `update_clip` (as an image/video background), by `set_background_music` (audio only), or by `add_sound_effect` (audio only).

    Upload the file as-is: any common container is accepted (MP4, MOV, WebM, MP3, WAV, M4A, …). The `.mp4` in the upload URL is just the storage key, not a container requirement — there's no need to remux or re-encode before uploading.

    For audio files, set `kind: "audio"` with the audio's duration and omit `width` / `height`.

    For images, set `kind: "image"` and omit `duration`. Tella hosts the uploaded image for rendering — you never deal with raw image URLs.

    **Parameters:**

    * `kind` (optional): Source kind — `video` (default), `audio`, or `image`
    * `width` (video/image): Width in pixels — required for `video` and `image`, omit for `audio`
    * `height` (video/image): Height in pixels — required for `video` and `image`, omit for `audio`
    * `duration` (video/audio): Duration in seconds — required for `video` and `audio`, ignored for `image`
  </Accordion>
</AccordionGroup>

### Chapters

Chapter timestamps use seconds on the final video timeline. Chapter IDs are not stable, so chapter updates always replace the complete list rather than targeting individual chapters.

<AccordionGroup>
  <Accordion title="get_chapters">
    Get every chapter on a video, ordered by timestamp.

    **Parameters:**

    * `videoId` (required): Video ID
  </Accordion>

  <Accordion title="set_chapters">
    Replace every chapter on a video. Timestamps must be non-negative, ascending, and within the video duration. Pass an empty `chapters` array to clear all chapters.

    **Parameters:**

    * `videoId` (required): Video ID
    * `chapters` (required): Complete replacement array of `{timestampSeconds, title, description}` objects
  </Accordion>
</AccordionGroup>

### Clips

A clip is a section of a video — Tella videos are made up of one or more clips. Each clip has its own cuts, layouts, transcript, and editing tools.

<Note>
  **Every timestamp is on the clip's playback timeline** — the video as watched, cuts applied, the same timeline as `get_transcript` and thumbnails. No conversions needed. The one exception is stored cut *definitions* (`get_clip`'s `cuts` / `update_clip`'s `cuts`), which describe removed ranges of the raw recording; cuts are always undoable by replacing them with `[]`.
</Note>

<AccordionGroup>
  <Accordion title="upload_clip">
    Add a new clip to a video from an uploaded source. Call `create_source` first, `PUT` the bytes to the returned `uploadUrl`, then pass the `sourceId` here.

    **Parameters:**

    * `videoId` (required): Video ID
    * `sourceId` (required): Source ID returned by `create_source`
    * `name` (optional): Clip name. Defaults to the next `Clip N`.
  </Accordion>

  <Accordion title="list_clips">
    List clips in a video, ordered by their position.

    **Parameters:**

    * `videoId` (required): Video ID
  </Accordion>

  <Accordion title="get_clip">
    Get a single clip's details.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="update_clip">
    Update a clip's name, ordering, cuts, background, or Studio Sound opt-out.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `name` (optional): New clip name
    * `order` (optional): New position within the video
    * `cuts` (optional): Replaces the clip's full cut set. Each cut is `{startTimeMs, durationMs}` in ms of the raw recording (the playback timeline you'd get after clearing all cuts). Pass `[]` to clear all cuts. To cut what you currently see, use `cut_clip` instead.
    * `background` (optional): Background `{type, color?, sourceId?, gradientColor1?, gradientColor2?, gradientAngle?}`. `type` is one of `solid`, `image`, `video`, `gradient`. For `image` and `video`, pass a `sourceId` from `create_source` (`kind: "image"` or `kind: "video"`).
    * `studioSound` (optional): Per-clip Studio Sound opt-out. `false` disables enhanced audio for this clip while the video-level switch (`update_video`'s `studioSound`) stays on; `true` re-enables it.
  </Accordion>

  <Accordion title="delete_clip">
    Remove a clip from its video.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="duplicate_clip">
    Duplicate a clip. The copy is inserted right after the original by default.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID to duplicate
    * `name` (optional): Name for the new clip
    * `order` (optional): Position for the new clip
  </Accordion>

  <Accordion title="reorder_clip">
    Move a clip to a new position; other clips shift to stay contiguous.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `order` (required): New 0-based position
  </Accordion>

  <Accordion title="cut_clip">
    Cut one or more time ranges from a clip in a single call. Overlapping or adjacent ranges are merged into the clip's existing cuts. To clear all cuts, call `update_clip` with `cuts: []`.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `cuts` (required): Array of `{fromMs, toMs}` ranges to cut, in ms on the clip's playback timeline (what you currently see and hear — the same timeline as `get_transcript` and `get_silences`). All ranges are resolved against the playback timeline as it is when the call starts, so send every range in one call instead of issuing many `cut_clip` calls.
  </Accordion>

  <Accordion title="cut_clip_by_transcript">
    Cut one or more ranges from a clip by referencing word indices in the transcript. The server resolves each word's exact start/end ms — no padding is applied. Use `get_transcript` to look up word indices.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `wordRanges` (required): Array of `{fromWordIndex, toWordIndex}` ranges. Both indices are inclusive and come from the clip's transcript (indices are stable — already-cut words are simply absent).
  </Accordion>

  <Accordion title="get_silences">
    Detect silent ranges in the clip's audio, in ms on the clip's playback timeline (cuts applied) — pass them directly to `cut_clip`. Silences already removed by cuts are not reported.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `minDurationMs` (optional): Minimum silence length to report, in ms. Defaults to 200.
  </Accordion>

  <Accordion title="remove_fillers">
    Auto-detect and cut filler words ("um", "uh", etc.) from the clip's transcript.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="remove_silences">
    Auto-detect and cut silent pauses from the clip's audio, like the editor's Remove silences tool.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `mode` (optional): How aggressively to remove silences — `natural` cuts pauses longer than 800ms, `fast` longer than 500ms, `faster` longer than 300ms. Defaults to `natural`.
  </Accordion>

  <Accordion title="list_sources">
    List the underlying recordings (camera, screen, mic) the clip was cut from.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="get_clip_thumbnail">
    Get a thumbnail of the clip — the rendered composite at the given time on the clip's playback timeline (cuts applied), including the clip's layouts, b-roll media, zooms, and masks. Static formats (jpg/png/webp/gif) return inline image content the model can see, plus a signed URL. `mp4` returns a URL only.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `format` (optional): `jpg` (default), `png`, `webp`, `gif`, or `mp4`
    * `inpointMs` (optional): Frame offset in ms on the clip's playback timeline, cuts applied (default 0)
    * `durationMs` (optional): Duration of the animated preview. Only valid when `format` is `gif` or `mp4`.
    * `width` (optional): Output width in pixels (default 320). The thumbnail keeps the video's aspect ratio.
    * `height` (optional): Output height in pixels. Derived from the video's aspect ratio when omitted.
    * `download` (optional): Suggest a download disposition on the signed URL

    While the video is still uploading or converting, the tool returns a result with `code: "thumbnail_not_ready"` and `status: "processing"` instead of an image — retry once the video finishes processing. Other upstream failures return `code: "thumbnail_fetch_failed"` with a signed URL to fetch manually.
  </Accordion>

  <Accordion title="get_video_thumbnail">
    Get a thumbnail of an entire video. Same format/sizing options as `get_clip_thumbnail`. It also returns the same `thumbnail_not_ready` processing result while the video is still being processed.

    **Parameters:**

    * `id` (required): Video ID
    * `format` (optional): `jpg`, `png`, `webp`, `gif`, or `mp4`
    * `inpointMs` (optional): Frame offset in ms from the video's playback start (cumulative across clips, cuts applied)
    * `durationMs` (optional): Duration of the animated preview (gif/mp4 only)
    * `width` / `height` (optional): Output dimensions
  </Accordion>

  <Accordion title="set_video_thumbnail">
    Set a video's thumbnail. Two modes, mutually exclusive:

    * **Uploaded image**: call `create_source` with `kind: "image"`, HTTP PUT the image bytes to the returned `uploadUrl`, then pass the `sourceId` here.
    * **Frame from the video**: pass `inpointMs`, a time on the video's playback timeline (cuts applied, cumulative across clips). Preview candidate frames first with `get_video_thumbnail` using the same `inpointMs`.

    Setting one mode clears the other.

    **Parameters:**

    * `id` (required): Video ID
    * `sourceId` (optional): Uploaded image source ID
    * `inpointMs` (optional): Thumbnail frame time in ms
  </Accordion>

  <Accordion title="remove_video_thumbnail">
    Remove a video's custom thumbnail (uploaded image or picked frame) and revert to the default auto-generated thumbnail.

    **Parameters:**

    * `id` (required): Video ID
  </Accordion>

  <Accordion title="get_source_thumbnail">
    Get a thumbnail of a specific source recording.

    **Parameters:**

    * `videoId` (required): Video ID
    * `clipId` (required): Clip ID
    * `sourceId` (required): Source recording ID
    * `format` (optional): `jpg`, `png`, `webp`, `gif`, or `mp4`
    * `inpointMs` (optional): Frame offset from the source's start
    * `durationMs` (optional): Duration of the animated preview (gif/mp4 only)
    * `width` / `height` (optional): Output dimensions
  </Accordion>

  <Accordion title="get_source_waveform">
    Get the waveform data for a source recording's audio track.

    **Parameters:**

    * `videoId` (required): Video ID
    * `clipId` (required): Clip ID
    * `sourceId` (required): Source recording ID
  </Accordion>

  <Accordion title="get_transcript">
    Transcript for the clip (cuts applied — what the viewer hears). Word indices are stable identifiers for `cut_clip_by_transcript`; they don't shift when cuts change. To see words that were cut out, clear the cuts first (`update_clip` with `cuts: []`) — cuts are always undoable.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>
</AccordionGroup>

### Layouts

A layout decides how a clip's camera and screen layers are composed at a given time. Apply a layout to the whole clip (no `startTimeMs`/`durationMs`) or to a specific time range. Times are in ms on the clip's playback timeline (cuts applied). Read the clip first to see its `layoutSceneType` — that determines which `kind` values are valid.

The `layout` field is a structured object discriminated by `kind`:

| `kind`          | Valid scene types               | Variant fields                                                                                                                                                           |
| --------------- | ------------------------------- | ------------------------------------------------------------------------------------------------------------------------------------------------------------------------ |
| `fullscreen`    | `cameraSubject`, `basicSubject` | `style: regular \| stretch` (camera-subject) or `screenFit: cover \| letterbox` (basic-subject)                                                                          |
| `middle`        | `cameraSubject`, `basicSubject` | `shape` (camera-subject)                                                                                                                                                 |
| `side-by-side`  | `combi` (landscape)             | `position: left \| right`, `style: regular \| even \| overlap`, `size` (when `style=overlap`)                                                                            |
| `tv-presenter`  | `combi` (landscape)             | `position: left \| right`, `style: regular \| full \| overlap`                                                                                                           |
| `camera-bubble` | `combi` (all ratios)            | `position` (9-way), `shape: circle \| square \| landscape \| portrait`, `size: S \| M \| L`, optional `style: regular \| full`, optional `screenFit` (when `style=full`) |
| `camera-only`   | `combi` (all ratios)            | `style: fullscreen \| middle`, `punchIn` (when `style=fullscreen`), `shape` (when `style=middle`)                                                                        |
| `screen-only`   | `combi` (all ratios)            | `style: fullscreen \| middle`, `screenFit` (when `style=fullscreen`)                                                                                                     |

<AccordionGroup>
  <Accordion title="list_layouts">
    List the layouts on a clip, including the synthetic base layout (the layout that spans the whole clip).

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="add_layout">
    Add a layout to a clip. Pass `startTimeMs` + `durationMs` for a time range, or omit both for a clip-spanning layout. To add b-roll, give a time range and set `media` — you can omit `layout` and the server applies a full-frame b-roll layout suited to the clip automatically (screen-only for combi clips, fullscreen for basic/camera-subject clips). To use a specific layout instead, pass `layout` with a `kind` valid for the clip's scene type (see the table above).

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `layout` (optional): Structured layout object (see table above). Omit it to apply the default full-frame b-roll layout for the clip's scene type.
    * `startTimeMs` (optional): Start time in ms on the clip's playback timeline (cuts applied)
    * `durationMs` (optional): Duration in ms. Minimum 200ms.
    * `transitionStyle` (optional): Transition into the layout — `spring` or `hardCut`. Only valid on time-ranged layouts. When omitted, the user's saved layout intro transition is used; the saved outro transition is applied when the following layout begins or resumes.
    * `media` (optional): B-roll content displayed during the layout. Only valid on time-ranged layouts. Either:

      * `{type: "image", sourceId}` for an image, or
      * `{type: "video", sourceId}` for a video.

      In both cases `sourceId` comes from `create_source` (`kind: "image"` or `kind: "video"`) after you upload the bytes.

      Add an optional `slot` to choose which slot the media fills: `screen` (the main subject frame — the default) or `camera` (the bubble/presentation slot, a media bubble shown over the recording behind it). Each slot needs a layout that renders it: the `screen` slot (default) requires a layout that shows the screen frame — not `camera-only` or any `*-camera-only-*` variant — and the `camera` slot requires a layout that renders the camera (e.g. `camera-bubble`, `side-by-side`, `tv-presenter`). Pointing media at a slot the layout doesn't show is rejected. One call fills one slot; a section can hold media in both slots by adding to one slot here and the other with `update_layout`.

    Example:

    ```json theme={null}
    {
      "videoId": "vid_…",
      "id": "cl_…",
      "layout": {"kind": "side-by-side", "position": "left", "style": "regular"},
      "startTimeMs": 5000,
      "durationMs": 4000
    }
    ```
  </Accordion>

  <Accordion title="update_layout">
    Update a layout on a clip. Only provided fields change.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `layoutId` (required): Layout ID (use `base` for the clip-spanning layout)
    * `layout` (optional): New structured layout
    * `startTimeMs` (optional): New start time in ms on the clip's playback timeline (cuts applied)
    * `durationMs` (optional): New duration in ms. Minimum 200ms.
    * `transitionStyle` (optional): `spring` or `hardCut`. Rejected on the base layout.
    * `media` (optional): Set the layout's B-roll content for one slot. Rejected on the base layout. Same shape as `add_layout`'s `media`, including the optional `slot` field. Additive per slot: media in the other slot is preserved, and media already in the targeted slot is replaced.
  </Accordion>

  <Accordion title="remove_layout">
    Remove a non-base layout from a clip. Use `update_layout` to change the base layout.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `layoutId` (required): Layout ID
  </Accordion>

  <Accordion title="generate_auto_layouts">
    Let AI watch the clip and lay it out — the same as the editor's "Auto layouts". The AI picks an editing style (or follows `style`), sets the clip's base layout, and adds time-ranged layout changes. Generation watches the actual video, so this can take on the order of a minute for longer clips.

    <Warning>
      This replaces the clip's existing layouts with the generated ones.
    </Warning>

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `style` (optional): Editing style guiding the AI's layout choices — one of `product-demo-portrait`, `product-demo-round`, `product-demo-square`, `product-demo-wide`, `presentation-tv-show`, `presentation-portrait`, `tutorial-round`, `tutorial-square`, `intro`, `outro`, `intro-and-outro`. Omit to let the AI pick one based on the clip's content.
    * `instructions` (optional): Free-form guidance for the AI, e.g. "keep the camera visible the whole time" or "punch in whenever a menu is opened".
  </Accordion>
</AccordionGroup>

### Background Music

Set one story-level audio track that loops over the whole video and is included in exports. This is separate from per-clip sound effects. Volume ranges from 0 to 1 and defaults to 0.2.

<AccordionGroup>
  <Accordion title="set_background_music">
    Set or replace the video's background music track. Call `create_source` first (`kind: "audio"` with the track's duration), `PUT` the audio bytes to the returned `uploadUrl`, then pass the `sourceId` here.

    **Parameters:**

    * `videoId` (required): Video ID
    * `sourceId` (required): Audio source ID from `create_source` (`kind: "audio"`)
    * `volume` (optional): Volume from 0 to 1 (default 0.2)
    * `name` (optional): Display name for the track
  </Accordion>

  <Accordion title="remove_background_music">
    Remove the video's background music track.

    **Parameters:**

    * `videoId` (required): Video ID
  </Accordion>
</AccordionGroup>

### Zooms

Zoom into a specific point on the clip's screen layer for a time range (the camera is unaffected). Use `manualZoom` for a fixed focus point or `trackingZoom` to follow the cursor in the screen recording. Times are in ms on the clip's playback timeline (cuts applied).

<AccordionGroup>
  <Accordion title="list_zooms">
    List zooms on a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="add_zoom">
    Add a zoom to a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `type` (required): `manualZoom` or `trackingZoom`
    * `startTimeMs` (required): Start time in ms on the clip's playback timeline (cuts applied)
    * `durationMs` (required): Duration in ms
    * `focusPoint`: Required for `manualZoom` (rejected if missing); ignored for `trackingZoom`. Percentages — `{xPct, yPct}` (0-100).
    * `scale` (required): Magnification factor (1 = no zoom, 3.5 = max). A zoom without a scale cannot be rendered.
  </Accordion>

  <Accordion title="update_zoom">
    Update an existing zoom. Only provided fields change.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `zoomId` (required): Zoom ID
    * `type` (optional): `manualZoom` or `trackingZoom`
    * `startTimeMs` (optional): New start time in ms on the clip's playback timeline (cuts applied)
    * `durationMs` (optional): New duration
    * `focusPoint` (optional): New focus point
    * `scale` (optional): New magnification
  </Accordion>

  <Accordion title="remove_zoom">
    Remove a zoom from a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `zoomId` (required): Zoom ID
  </Accordion>

  <Accordion title="generate_auto_zooms">
    Automatically generate tracking zooms from the mouse clicks in the clip's screen recording — the same as the editor's "Generate zooms". Every click opens a zoom window; nearby windows are merged. Requires the clip to contain a screen recording; returns an empty list when it has no usable mouse clicks.

    <Warning>
      By default this removes the clip's existing tracking zooms first, so repeated calls regenerate instead of stacking. Pass `replaceExisting: false` to keep them. Manual zooms are never touched.
    </Warning>

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `intensity` (optional): How aggressively to zoom — `slow`, `medium` (default), or `fast`. `slow` opens fewer, longer windows; `fast` opens shorter, tighter ones at a higher scale.
    * `scale` (optional): Override the magnification factor (1 = no zoom, 3.5 = max). Defaults to the intensity preset's scale (1.5 for slow/medium, 2 for fast).
    * `replaceExisting` (optional): Defaults to `true` — existing tracking zooms are removed first. Pass `false` to keep them.
  </Accordion>
</AccordionGroup>

### Blurs and highlights

Mask a rectangular region of the clip's screen layer to either blur it (hide sensitive info) or highlight it (draw attention). Both share the same shape: a rectangle defined by a top-left `point` and `dimensions`, both in percentages. Times are in ms on the clip's playback timeline (cuts applied).

<AccordionGroup>
  <Accordion title="list_blurs / list_highlights">
    List blur or highlight masks on a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="add_blur / add_highlight">
    Add a blur or highlight mask to a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `startTimeMs` (required): Start time in ms on the clip's playback timeline (cuts applied)
    * `durationMs` (required): Duration in ms
    * `point` (required): Top-left corner — `{xPct, yPct}` (0-100)
    * `dimensions` (required): Rectangle size — `{widthPct, heightPct}` (0-100)
  </Accordion>

  <Accordion title="update_blur / update_highlight">
    Update an existing blur or highlight.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `maskId` (required): Mask ID
    * `startTimeMs` (optional): New start time in ms on the clip's playback timeline (cuts applied)
    * `durationMs` (optional): New duration
    * `point` (optional): New top-left
    * `dimensions` (optional): New size
  </Accordion>

  <Accordion title="remove_blur / remove_highlight">
    Remove a blur or highlight from a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `maskId` (required): Mask ID
  </Accordion>
</AccordionGroup>

### Overlays

Place an image or video on top of a clip for a time range. Both reference a `sourceId` from `create_source` (`kind: "image"` or `kind: "video"` — upload the bytes first); the overlay type follows the source's kind. Position uses a percentage `point` (so it survives ratio changes) and a pixel `dimensions` box (so the shape never distorts).

<Note>
  `startTimeMs` and `durationMs` are milliseconds on the clip's **playback timeline** (with cuts applied) — the same timeline as the cut transcript. A start at or past the end of the clip is rejected with a `400`.
</Note>

<AccordionGroup>
  <Accordion title="list_overlays">
    List the image and video overlays on a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="add_overlay">
    Add an image or video overlay on top of a clip. Call `create_source` first (`kind: "image"` or `kind: "video"`), `PUT` the bytes, then pass the returned `sourceId` — the overlay type follows the source's kind. When `point`/`dimensions` are omitted, a centered box is computed from the source.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `startTimeMs` (required): Start time in ms on the clip playback timeline (with cuts applied), the same timeline as the cut transcript
    * `durationMs` (required): Duration in ms
    * `sourceId` (required): Source ID from `create_source`
    * `name` (optional): Overlay name
    * `point` (optional): Top-left corner — `{xPct, yPct}` (0-100), as a percentage of the story canvas
    * `dimensions` (optional): Overlay size in artboard pixels — `{width, height}`
  </Accordion>

  <Accordion title="update_overlay">
    Update an existing overlay. Only provided fields change.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `overlayId` (required): Overlay ID
    * `startTimeMs` (optional): New start time in ms on the clip playback timeline (with cuts applied)
    * `durationMs` (optional): New duration in ms
    * `name` (optional): Overlay name
    * `point` (optional): New top-left — `{xPct, yPct}` (0-100)
    * `dimensions` (optional): New size in artboard pixels — `{width, height}`
  </Accordion>

  <Accordion title="remove_overlay">
    Remove an overlay from a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `overlayId` (required): Overlay ID
  </Accordion>
</AccordionGroup>

### Sound effects

Add an audio clip to play over a clip for a time range. Upload the audio with `create_source` first (`kind: "audio"` with the audio's duration), then reference the returned `sourceId`. Upload the audio file as-is — MP3, WAV, M4A, and other common formats are accepted; no need to wrap it in an MP4.

<Note>
  `startTimeMs` and `durationMs` are milliseconds on the clip's **playback timeline** (with cuts applied) — the same timeline as the cut transcript. A start at or past the end of the clip is rejected with a `400`.
</Note>

<AccordionGroup>
  <Accordion title="list_sound_effects">
    List the sound effects on a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
  </Accordion>

  <Accordion title="add_sound_effect">
    Add a sound effect to a clip from an uploaded audio source. Call `create_source` first (`kind: "audio"` with the audio's duration), `PUT` the audio file as-is (MP3, WAV, M4A, …) to the returned `uploadUrl`, then pass the `sourceId` here.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `sourceId` (required): Source ID for the audio (from `create_source`)
    * `startTimeMs` (required): Start time in ms on the clip playback timeline (with cuts applied), the same timeline as the cut transcript
    * `durationMs` (required): Duration in ms
    * `name` (optional): Sound effect name
    * `volume` (optional): Playback volume (0 = muted, 1 = original). Defaults to 1.
  </Accordion>

  <Accordion title="update_sound_effect">
    Update an existing sound effect. Only provided fields change.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `soundEffectId` (required): Sound effect ID
    * `startTimeMs` (optional): New start time in ms on the clip playback timeline (with cuts applied)
    * `durationMs` (optional): New duration in ms
    * `name` (optional): Sound effect name
    * `volume` (optional): Playback volume (0 = muted, 1 = original)
  </Accordion>

  <Accordion title="remove_sound_effect">
    Remove a sound effect from a clip.

    **Parameters:**

    * `videoId` (required): Video ID
    * `id` (required): Clip ID
    * `soundEffectId` (required): Sound effect ID
  </Accordion>
</AccordionGroup>

## Example prompts

Once connected, you can ask your AI assistant things like:

* "List all my Tella videos"
* "Get the transcript for video xyz"
* "Create a playlist called 'Product Updates' and add my latest 3 videos"
* "Create a 'Customer facing' tag and add it to my demo videos"
* "Show me all videos tagged 'Onboarding'"
* "Make my onboarding video public and enable downloads"
* "Trim the first 5 seconds off the intro clip"
* "Add a side-by-side layout to the demo clip from 10s to 20s with the camera on the left"
* "Add a B-roll image of our product logo at 30s for 4 seconds"
* "Upload this video file as a new clip at the end of my onboarding video"
* "Add this product demo clip as B-roll between 12s and 20s of the intro"
* "Find the silent gaps in my latest clip longer than 1.5 seconds"
* "Remove all the filler words from the keynote clip"
* "Blur the email address in the bottom-left of the screen between 12s and 18s"
* "Zoom into the top-right of the screen at 25s for 3 seconds"
* "Generate zooms for my demo clip wherever I clicked"
* "Auto-layout my tutorial clip and keep the camera visible the whole time"
* "Turn on Studio Sound for my latest video"
* "Add my logo as an overlay in the top-right corner from 5s to 10s"
* "Play this whoosh sound effect at 8s when the transition happens"
* "List my webhook subscriptions"
* "Pause delivery on my webhook endpoint"
* "Update my webhook to only send video.created and export.ready events"

## Troubleshooting

### Authentication issues

If you're having trouble authenticating, try clearing your MCP auth cache:

```bash theme={null}
rm -rf ~/.mcp-auth
```

Then reconnect to trigger a fresh OAuth flow.

### Connection issues

Ensure you're using a compatible MCP client. The server uses HTTP transport.
