# Cassette-Editor/oh-my-cassette [Health: Active]

**Category:** 🎥 Multimedia Process  
**Repository:** https://github.com/Cassette-Editor/oh-my-cassette  
**GitHub Stars:** 140  
**Views:** 0  
**Installs:** 0  
**Upvotes:** 0  
**Directory Page:** https://allmcps.com/mcp/cassette-editor-oh-my-cassette

## Description
Chat raw clips into a finished cut.

## Tools
Capabilities this server exposes over MCP:

- **cassette_ingest_media** — Safely ingest trusted project media into an isolated session
- **cassette_list_assets** — Read the session's media manifest
- **cassette_make_prompt** — (legacy) Build a full edit brief — superseded by verbatim relay
- **cassette_match_bgm** — Match Free To Use background music
- **cassette_match_exact_bgm** — Match a specific title and artist
- **jamendo_music_matcher** — Match structured Jamendo preferences
- **cassette_jamendo_setup** — Verify and privately store this machine's Jamendo Client ID
- **cassette_answer_question** — Answer a guided question or resume a paused job
- **cassette_run_job** — Run one conversational turn (`message` = the user's verbatim words); `export=true` renders
- **cassette_job_status** — Resume a deliberately detached or interrupted job call
- **cassette_review_completion** — Review completion and explicitly approve export
- **cassette_cancel_job** — Request cooperative cancellation
- **cassette_timeline** — Read the live project timeline as a bounded text digest (+ optional contact sheet)
- **cassette_edit** — Surgical no-LLM edit / undo through the manual command lane (`CASSETTE_DIRECT_EDIT=1`)
- **cassette_config** — Get/set the session's model + thinking level (static product list, applies next turn)
- **cassette_login** — Verify and privately store this machine's credentials, or request a new emailed password (MCP hosts only)

## Claude Desktop Quick Installation
Remote MCP endpoint (confidence: high). Install path detected from listing signals. Add as a URL/SSE server in your client:

```json
"mcpServers": {
  "oh-my-cassette": {
    "url": "https://trycassette.online/"
  }
}
```

## Documentation & README

<p align="center">
  <img src="assets/banner.jpg" width="80%" alt="Oh My Cassette banner" />
</p>

<h1 align="center">
Oh My <a href="https://trycassette.online/">Cassette</a>: Chat Your Raw Clips Into a Finished Cut
</h1>

<p align="center">
  <a href="https://github.com/Cassette-Editor/oh-my-cassette/releases">
    <img src="https://img.shields.io/github/v/release/Cassette-Editor/oh-my-cassette?style=flat-square&color=blue" alt="Version">
  </a>
  <a href="https://github.com/Cassette-Editor/oh-my-cassette/blob/main/LICENSE">
    <img src="https://img.shields.io/github/license/Cassette-Editor/oh-my-cassette?style=flat-square" alt="License">
  </a>
  <a href="https://github.com/Cassette-Editor/oh-my-cassette/stargazers">
    <img src="https://img.shields.io/github/stars/Cassette-Editor/oh-my-cassette?style=flat-square&logo=github" alt="Stars">
  </a>
  <a href="https://github.com/Cassette-Editor/oh-my-cassette/fork">
    <img src="https://img.shields.io/github/forks/Cassette-Editor/oh-my-cassette?style=flat-square&logo=github" alt="Forks">
  </a>
  <img src="https://img.shields.io/badge/Python-3.11--3.13-purple.svg" alt="Python">
  <a href="https://github.com/Cassette-Editor/oh-my-cassette/actions/workflows/ci.yml">
    <img src="https://img.shields.io/github/actions/workflow/status/Cassette-Editor/oh-my-cassette/ci.yml?branch=main&style=flat-square&label=CI" alt="CI">
  </a>
  <a href="https://trycassette.online">
    <img src="https://img.shields.io/badge/Cassette-Compatible-1f6feb?style=flat-square&logo=data%3Aimage%2Fwebp%3Bbase64%2CUklGRhgHAABXRUJQVlA4WAoAAAAQAAAANwAANwAAQUxQSMkCAAABkIVt29lI75hr27Yx9tq2bdu2bdu2bdt23e4kfQ86TZN%2FeRgRE4DfcIX9Oyr%2BCuE7ZFJeX0a0oK0SE7SsKyNSmTU2KjQvKypKsTVWumhcVkSEIsuNVNG4KJ9WOabqqPK3yVm1yDnjCzX8OCWLWhknfabG7yZkUiPz5HcU8HWvVK5kGPuegj4blEqJV%2B9XFPhpZ09nmU0UWi7gLKteLHtRBTqx5CIKvotl%2B%2B%2F2tNfgd7%2FC6%2BHpgCwTv4r2YVQGJJhn2meRPo3PCoU5lhhE%2BT41F1wsssoswo%2FphaFiycVGrfQLC0Dl0ssNqugSMq0uAQ0LrbQlJLlkXVUSGgdttjnISr6T8TuCIGDQVom0FVa0PxSCRu2Ltwcp2pdOFLe2P1nKWX6LXnc7nRhR53mvuaezEpyU58d0AdwaRG1mX18oDOcQLH%2FqizEjUmpSZidHrXuarXmggmAOQ9IYb78HnK1BwVVm2%2BTUG%2BON31IryGgZCwBh8bzqBZ9EqhSNvkauB7Zwb2ko9Dh8PnFMfowmdVkDDjzq5eVS7lm25S1aSKuBHS%2Fdsk%2FP4QylDA%2BstxOfJO2xOWWZ892VpR3zlXvyIqO0Btj2bZnZmFMBYh9xS7YfJEcmM02rLVeFl7uTNPlXkFc8gBwOW8g1BaE4WViShiS5F4tf5ti%2FK%2FPpU9UdkvR4t7pZ45dn4GTV%2FvJwPebx7J3S67Sprjyr07oz7ewA78b3ebI8cO%2B8g7weSOUBNVO4YxS7HYq6%2BiBxNBvOMmYJ4svGALzvO%2BSUxkLDXN9HfRuX4vMMr9t7MusGhHFwxtLOAuL8tUDRwgPs01%2Fe8i6v31u%2FehTfG5c6XIKIAQvJo42XNbWuRhylDVkA7yfbhQBippUKY%2BVKrBbOCShbAx5VUgsCwHvvp1HSjDCumsnVEDvzKXJSOCmtyC4YfFpOyRhnnF8Mv2TaYviLBABWUDggKAQAAPAYAJ0BKjgAOAA%2BTSCLRCKiIRgN%2FgAoBMS0htgATIB%2FRvwg8Cf6r%2BM37Vdtj559kc4Z9Yvtn5Gfkzzg7Sf%2BC%2FKTgCePfx3%2FVfmBzAfTz%2B7epP%2BKf2X8wvYDuAPUA%2Flv9E%2F2P3O%2FDz%2FeeSD8v%2FuHsB%2Fxz%2Bef6r%2B7fu%2Fxm36xJmyyFkngkpmRPYsV6xmH5QF4lb%2Fl9hSgk5D3PqHNQLXrlv7NrItI%2FEHrxBF2jRuhKjEBVmBB10mJvHzkR2Q2arL3GqP10RyllNZGUghwhZxRilAA%2Fuw6mSpSUmz%2BC9%2BKh7D7qK39xAKDdKGxdWNME94DsfqLZMWOWeIeTypqgNWds0IPEbdim9%2FrYj%2BEd8AhWw%2F%2Bzi%2FwdIkNi7bJQyZ55GARC1%2FHJxJRDbUPxx7XEJNOqXwK2TTvcPfg%2FB1Sxtu3%2F%2BqXZV7Ea27hgAqAwjmpzYt%2FUnFY%2BC0oW45G3Loc8bVetEXzeXitUxCUpACSaawCSSfLqRe1eG3zUIXqEc82GnC78rFUxGMUSd%2FG9fuFsdPGi96Lm3KIZEKgeEYxznL%2BMFlhtO40ZN%2FW3ejoFZfedwy4KUpiGElmS9DdaNzh40f3qq2ox2aJonnJDtPET3GC8%2FxLitJKLpjNVH%2Fs2f5UmAx%2B5LKDbHdllFCDWoI3Pp%2FVHmOId6JL4jNCWVVHcq5vtCed3YAxjpLyN7Mu2NYOtVFR9IFyf7Vd5AztRBIGFD79z80kLXz%2F%2F%2BfO3t2lkek5ydWcB0weeaYMf%2Fwaie0%2Bighho%2Bx1LSEM4hwlpukY3yztN8wyYCF8fSe9AWia9TSnFp57nMo5YifIm9RF6TM%2Fvo6yBTI0Pi%2FGt%2F%2F6mnejWgvH1%2F%2F%2BhS5iYOwLt%2BHG5ouw5J01Fd%2F1WfcWXdBL9MEwe3fGBYZUYUOiM3FvKWH0ikirZAvG0xfpgf%2FgnXTZaTTeyzpnuvF289BWGVcYoRTe2gBbGUT00tzQ4XuCIo9OV7ysRBBK4PTfYTi%2FntO%2FGJkP7JHWEAf7RfyPpf2z%2F3gmxcwD3IXTbEB9aXF%2BVQYVzi8x%2FWj1eluYh3G6xEWj5aKxK09fn3AaJy6LKTQjgk8209Tv7brUzGKt26W4a5uQVRnc6cAzlXdvZB3nrAUJ4SsTWRbCM9m6TDQ7A0dctlIEQzv%2FUTvpPTnyuaOqiVP5PsHXhi9x0791xIp4e1U8sUVgkxVIgE2QYZjQXnY9Kuv0ciSsqTTzWZ6it0Z1Y9qOMi%2F%2BDwjsHCI%2FTwjcrXfrv2jVjya%2Fh3h%2FRzBb7Mszb6Pr54Dpg%2B6OQeVLDvWMOcd9P7x1uSSp%2FrzOS7zB9i5HJvNTeTjIi%2Be5Xf9eSzCoym6aKUC6i6Gkf7O96HwkdmR9TOx3ZuNOJY%2F%2BdF%2F%2F%2FnRxgu4Mj%2F29%2FT%2FJ34nP%2F6r9U3fQV%2BjAyqrfV23tQAAA&logoColor=white" alt="Cassette Compatible">
  </a>
  <a href="https://github.com/nousresearch/hermes-agent">
    <img src="https://img.shields.io/badge/Hermes%20Agent-Compatible-7c3aed?style=flat-square&logo=data%3Aimage%2Fsvg%2Bxml%3Bbase64%2CPHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNCAyNCI%2BPHBhdGggZD0iTTUgNGgzdjZoOFY0aDN2MTZoLTN2LTdIOHY3SDV6IiBmaWxsPSJ3aGl0ZSIvPjxjaXJjbGUgY3g9IjEyIiBjeT0iMTIiIHI9IjIiIGZpbGw9IiMxMTEiLz48L3N2Zz4%3D&logoColor=white" alt="Hermes Agent Compatible">
  </a>
  <a href="https://github.com/openai/codex">
    <img src="https://img.shields.io/badge/Codex-Compatible-000000?style=flat-square&logo=openai&logoColor=white" alt="Codex Compatible">
  </a>
  <a href="https://claude.com/claude-code">
    <img src="https://img.shields.io/badge/Claude%20Code-Compatible-d97757?style=flat-square&logo=anthropic&logoColor=white" alt="Claude Code Compatible">
  </a>
  <a href="https://opencode.ai">
    <img src="https://img.shields.io/badge/OpenCode-Compatible-0b0b0b?style=flat-square&logo=opencode&logoColor=white" alt="OpenCode Compatible">
  </a>
  <a href="https://discord.gg/qd9NY4k8d7">
    <img src="https://img.shields.io/discord/1514649803626250452?style=flat-square&logo=discord&logoColor=white&label=Discord&color=5865F2" alt="Discord">
  </a>
  <a href="https://glama.ai/mcp/servers/Cassette-Editor/oh-my-cassette">
    <img src="https://glama.ai/mcp/servers/Cassette-Editor/oh-my-cassette/badges/score.svg" alt="oh-my-cassette MCP server">
  </a>
</p>

<p align="center">
  <a href="./README.zh-cn.md">简体中文</a> | <b>English</b>
</p>

<table align="center" width="82%">
<tr>
<td>
  <video src="https://github.com/user-attachments/assets/efcfda75-f09b-4bde-8a69-e84410f28e10" controls width="100%"></video>
</td>
</tr>
</table>

<h3 align="center">
  <em>
  Point <a href="https://claude.com/claude-code">Claude Code</a>, <a href="https://github.com/openai/codex">Codex</a>, or <a href="https://github.com/nousresearch/hermes-agent">Hermes</a> at a folder of raw clips.<br />
  Describe the video you want. Get back a finished, beat-synced cut.
  </em>
</h3>

<p align="center">
  <b>14 clips in. One prompt. ~13 minutes to a rendered file.</b><br />
  <sub>No timeline. No editing software. No GPU.</sub>
</p>

## ⚡ Install in 30 seconds

```bash
# Claude Code
claude plugin marketplace add Cassette-Editor/oh-my-cassette
claude plugin install oh-my-cassette@cassette-editor
```

```bash
# Codex
codex plugin marketplace add https://github.com/Cassette-Editor/oh-my-cassette.git
codex plugin add oh-my-cassette@cassette-editor
```

Restart your agent, then say: *"Edit the clips in ./footage into a 30-second travel vlog with beat-synced cuts."*

Needs Python 3.11–3.13, `ffmpeg`, and a [Cassette account](https://trycassette.online/signup/). Full setup — including [OpenCode](#opencode), [Hermes](#hermes), and any other MCP host — is in [Quick Start](#-quick-start). Want to try it first? [Run it in your browser](#-try-without-installation) — no install, no account.

# 🎥 Overview

**Oh My Cassette** is an open-source AI video editing plugin and local MCP server for [Claude Code](https://claude.com/claude-code), [Codex](https://github.com/openai/codex), [Hermes Agent](https://github.com/nousresearch/hermes-agent), and [OpenCode](https://opencode.ai). It turns natural-language chat into finished montage videos on [Cassette](https://trycassette.online) — beat-synced cuts, auto-matched music, subtitles, transitions, and picture-in-picture — with minimal token overhead.

The agent does the parts that make editing tedious: it watches every clip, picks the shots, plans the cut, syncs it to the beat, and renders — while you stay in chat. Because it runs through your agent, you can do all of it from your phone.

<table>
  <tr>
    <td align="center" width="33%">
      <h3>💬 Chat-to-Edit</h3>
      <p>Describe the video in plain language — the agent handles shot selection, pacing, and the timeline, so you never open an editor.</p>
    </td>
    <td align="center" width="33%">
      <h3>🎵 Smart Music Matching</h3>
      <p>Finds and syncs music to the mood and rhythm of your footage, so the cut lands on the beat without you marking a single one.</p>
    </td>
    <td align="center" width="33%">
      <h3>👀 Nothing Renders Until You Say So</h3>
      <p>Every turn returns a timeline digest and a contact sheet you can review in seconds — you approve the plan before a frame is rendered.</p>
    </td>
  </tr>
</table>

## 🖥️ How it works

Upload your clips, describe the video you want, and the agent edits it.

<table width="100%">
<tr><td>
  <video src="https://github.com/user-attachments/assets/28792f85-a468-4f8b-a5c6-c7f550e724a3" controls width="100%"></video>
</td></tr>
</table>

| Time | What happens |
| --- | --- |
| 0:09 | The brief — one line, style left entirely to the agent, typed into Claude Code |
| 0:22 | 15 files upload to Cassette; every clip is analyzed for scene content |
| 0:35 | The agent edits on its own — shot selection, title card, lower-third, grading, beat-synced cuts |
| 0:58 | The timeline comes back as a readable digest, with a clickable contact-sheet link |
| 1:03 | One `cmd+click` opens the real contact sheet — one frame per clip, zero render |
| 1:12 | The contact sheet itself: what the cut looks like before a single frame is rendered |
| 1:44 | The export lands, and the runtime measures it — duration, black frames, audio levels |
| 1:52 | The finished cut |

**The cut it produced**

<table align="center" width="70%">
<tr><td>
  <video src="https://github.com/user-attachments/assets/54df6c4d-ba5f-4b01-af83-62f214dc6249" controls width="100%"></video>
</td></tr>
</table>

<sub>The screen recording is compressed to keep this page light, so the terminal text looks softer here than it does on your machine; the cut below it is the full-quality render. Prompt to rendered file took 9 minutes 53 seconds of real time, sped up above. A person's face is pixelated in both the screen recording and the cut it produced, for privacy — that blur is not something the agent added.</sub>

<sub>**Token cost:** that session used 55K output tokens and 2.33M billed input tokens (2.07M of them cache reads) on Claude Opus 5 — roughly **$4 at API list price**, measured from the Claude Code transcript of this exact recording. One brief, one turn, start to exported file. The editing itself runs on Cassette, so the agent only pays for the brief and the timeline digests, not for the footage.</sub>

## 🎬 Case Videos

Every case below was edited end-to-end by an AI agent through Oh My Cassette, from the exact prompt shown — real inputs, real processing times, and the output is what the agent delivered.

<table width="100%">
<tr>
<td width="33%" valign="top">
  <video src="https://github.com/user-attachments/assets/b85285a9-30ed-4f9b-b314-538bfd9dbdd6" controls width="100%"></video>
  <h3>Travel Vlog</h3>
  <p>
    <sub>🎞️ Input: 13 video clips · 1 audio track</sub><br />
    <sub>⏱️ Processing Time: 15 mins</sub>
  </p>
  <p><b>Prompt</b><br />
  Edit a 30-second travel vlog using all materials with a clear viewing flow. Add the title subtitle "KOTA KINABALU" at the beginning and end. Include the vlog shooting time, overlay the character video onto similar scenic shots as a picture-in-picture effect, keep the rhythm light and memorable, and use hard cuts plus music beat sync for a fresh vacation feeling.</p>
  <p><code>Travel</code> <code>Vlog</code> <code>Beat Sync</code> <code>PiP</code></p>
</td>
<td width="33%" valign="top">
  <video src="https://github.com/user-attachments/assets/97fdfe3a-f420-4135-8c62-494bbb7ea436" controls width="100%"></video>
  <h3>Cooking Tutorial</h3>
  <p>
    <sub>🎞️ Input: 12 video clips · 1 audio track</sub><br />
    <sub>⏱️ Processing Time: 8 mins</sub>
  </p>
  <p><b>Prompt</b><br />
  Use these materials to edit a cooking video for stir-fried pork with green peppers. Keep the steps smooth, preserve the cooking sounds, and add simple subtitles. The subtitles can be generated based on the visuals to help viewers understand each step. Keep the style clean and natural without covering the ingredients.</p>
  <p><code>Food</code> <code>Tutorial</code> <code>Explainer</code></p>
</td>
<td width="33%" valign="top">
  <video src="https://github.com/user-attachments/assets/8da38e40-7876-4743-a32d-5fd95265f37e" controls width="100%"></video>
  <h3>Commercial</h3>
  <p>
    <sub>🎞️ Input: 15 video clips · 15 sound effects · 1 audio track</sub><br />
    <sub>⏱️ Processing Time: 13 mins</sub>
  </p>
  <p><b>Prompt</b><br />
  Use these materials to edit a 15-second product commercial. Start with opening the bottle cap, add bubbles, water splashes, ice cubes, and product details in the middle, and end with the full product and brand. Keep it refreshing, thirst-quenching, and ad-like.</p>
  <p><code>Product</code> <code>Short</code> <code>Commercial</code></p>
</td>
</tr>
</table>

<p align="center">
  <strong><a href="./docs/showcase.md">→ See all six cases</a> — daily vlog, cinematic short, and game highlights, each with its exact prompt.</strong><br />
  <sub>★ More cases are on the way. Star the project to follow along.</sub>
</p>

# 🏄 Try without installation

Upload a clip, type an edit, watch it happen — from a desktop or mobile browser, with no agent installed locally.

<h3><a href="http://43.134.224.156:8080/">▶ Open the live demo</a></h3>

> [!WARNING]
> **Evaluation demo — unauthenticated and public. Don't upload anything sensitive, private, or copyrighted.**

<details>
<summary>Full demo terms — data handling, third-party processing, and availability</summary>

- Uploaded files, prompts, generated outputs, job state, and troubleshooting metadata may be processed by this demo server, Cassette, DeepSeek, and third-party music/search providers used by the BGM flow. Treat anything you upload as visible to the demo operator and the external services involved in the workflow.
- The demo may be reset, rate-limited, unavailable, or changed at any time. We do not guarantee data retention, deletion timing, confidentiality, fitness for production use, output quality, copyright compliance of generated/editing results, or uninterrupted service. You are responsible for the content you upload and for reviewing any generated output before sharing it.
- Browser refreshes, tab closes, or navigation away from the demo start a new web session and best-effort cleanup the previous session's temporary uploads, chat history, and finished job files.
- By default the demo uses the server-side DeepSeek configuration when available. You can open **Settings** in the web UI and provide your own DeepSeek API key for testing. Your key is sent only with requests to this demo server and is not written to this repository or server-side disk by the web app, but it still transits the public demo server; use a key you can rotate and monitor.

</details>

The demo is a separate deployment with its own repository and its own transport; this repository is the plugin only.

# 🚀 Quick Start

## Before You Start 🎬

Oh My Cassette connects Codex, Claude, or Hermes to the <a href="https://trycassette.online/agent">Cassette Agent</a>. You need:

* Codex, Claude Code, or a working Hermes Agent installation.
* A Cassette account.
* For Hermes only, a configured gateway such as QQ or Telegram.

> [!TIP]
> **Apply for a Cassette account here:** [**Cassette Sign Up**](https://trycassette.online/signup/)

If you plan to use Hermes and it is not installed yet:

```bash
curl -fsSL https://raw.githubusercontent.com/NousResearch/hermes-agent/main/scripts/install.sh | bash
```

If the Hermes Agent gateway is not configured yet:

```bash
hermes gateway setup
```

Oh My Cassette currently supports QQ and Telegram gateways.


## Requirements

- macOS, Linux, or Windows for the local MCP plugin (Codex, Claude Code, or OpenCode). The Hermes Agent gateway path is macOS/Linux only.
- Codex, Claude Code, or OpenCode for the local MCP plugin, or Hermes Agent with its gateway configured.
- Python 3.11–3.13.
- `ffmpeg`, required for Hermes gateway normalization and optional API export thumbnails.

Install system tools:

```bash
# macOS
brew install uv ffmpeg
```

```bash
# Debian/Ubuntu Linux
sudo apt-get update
sudo apt-get install -y ffmpeg
curl -LsSf https://astral.sh/uv/install.sh | sh
```

```powershell
# Windows (PowerShell)
winget install Python.Python.3.13
winget install Gyan.FFmpeg
```

## Install

### Codex

```bash
codex plugin marketplace add https://github.com/Cassette-Editor/oh-my-cassette.git
codex plugin add oh-my-cassette@cassette-editor
```

Start a new Codex task after installation so plugin discovery runs again. The plugin contributes the host-neutral `cassette-video-edit` and `cassette-model` skills plus a local MCP process named `cassette`. Fresh editing sessions use GPT-5.6 Luna with Extra High thinking; invoke `$cassette-model` only when you want to inspect or change that session setting.

### Claude Code

```bash
claude plugin marketplace add Cassette-Editor/oh-my-cassette
claude plugin install oh-my-cassette@cassette-editor
```

Restart Claude Code after installation. You can verify the installation with:

```bash
claude plugin details oh-my-cassette@cassette-editor
```

Claude Code can keep the plugin up to date on its own once you enable auto-update for the marketplace — see [Update](#update).

Fresh editing sessions use GPT-5.6 Luna with Extra High thinking. Invoke `/cassette-model` when you want to inspect or change the current session's model and thinking level.

### OpenCode

OpenCode's plugin manager installs npm packages only, and its plugins cannot contribute MCP servers, so there is no marketplace entry to add. One command instead:

```bash
curl -fsSL https://raw.githubusercontent.com/Cassette-Editor/oh-my-cassette/release/scripts/install_opencode.py | python3 -
```

That downloads the current release, writes the `cassette` server into `~/.config/opencode/opencode.json` (merging with any servers and settings already there), installs the host-neutral skills into `~/.config/opencode/skills/`, and installs `/cassette-model` into `~/.config/opencode/commands/`. Restart OpenCode afterwards. Fresh editing sessions use GPT-5.6 Luna with Extra High thinking; the command changes the setting only when you invoke it.

**Re-run the same command to update.** Only `git` is not required — the release tarball is fetched with Python's standard library.

Cassette credentials are shared across Codex, Claude Code, OpenCode, and Hermes, so if you have already set up another host there is nothing more to do. Otherwise the installer prints the `setup_local_mcp.py` command to finish authentication. Jamendo uses the host-specific BYOK setup described below.

The plugin tree lands in `~/.oh-my-cassette` (override with `OMC_HOME`). `--dry-run` previews the changes. If you prefer a git checkout, clone it and run the installer from there — it registers that tree and leaves it alone, and `--sync` fast-forwards it to the release channel.

### Any other MCP host

The runtime is host-neutral, so any client that launches a local stdio MCP server can use it. Point the client at `scripts/run_local_mcp.py` (run with `python3`, or `python` on Windows) and set `CASSETTE_RUNTIME_ADAPTER=mcp`. The server ships full workflow guidance in its MCP `instructions`, and every tool returns a typed `phase`/`next_action` so a host without the packaged skill can still drive the flow. For the best experience, also install the `cassette-video-edit` and `cassette-model` skills (or equivalent system prompts). Generic clients can call `cassette_config` or ask in natural language to change the current session's model.

### Hermes

Install through the Hermes plugin manager (recommended):

```bash
hermes plugins install Cassette-Editor/oh-my-cassette
```

The Hermes installer prompts for your Cassette account email and password and saves them to `~/.hermes/.env`. Then run the setup finisher — it configures the same stdio MCP server and canonical editing skill used by the other hosts, sets Hermes's tool timeout to 1800 seconds, detects `ffmpeg`/`ffprobe`, and lets you pick the Cassette region — and enable the thin gateway plugin:

```bash
python3 ~/.hermes/plugins/cassette/scripts/install_plugin.py --setup-only
hermes plugins enable cassette
hermes gateway restart
```

You can check the install status anytime from the Diagnose section.

<details>
<summary>Alternative: guided installer from a git checkout (for development)</summary>

```bash
git clone https://github.com/Cassette-Editor/oh-my-cassette.git
cd oh-my-cassette
python3 scripts/install_plugin.py
```

Run the installer and follow the prompts to set up Oh My Cassette with your Cassette account.

The installer:

- installs the plugin into `~/.hermes/plugins/cassette` as a symlink by default;
- writes the shared Cassette stdio server into `~/.hermes/config.yaml` with an 1800-second tool timeout;
- asks whether to enable the plugin with `hermes plugins enable cassette`;
- asks which Cassette URL to use:
  - `https://sg.trycassette.online/agent` (Asia, default)
  - `https://trycassette.online/agent` (America)
- optionally verifies and saves a Jamendo Client ID into `~/.hermes/.env`;
- detects `ffmpeg` and `ffprobe` paths for service environments;
- restarts the Hermes gateway service.

To copy files instead of creating a symlink:

```bash
python3 scripts/install_plugin.py --copy --force
```

For non-interactive installs:

```bash
python3 scripts/install_plugin.py \
  --skip-plugin-enable \
  --skip-cassette-url \
  --skip-cassette-auth \
  --skip-jamendo-auth
```
</details>

## Use with agent clients over local MCP

Codex, Claude Code, OpenCode, and Hermes use the same self-contained runtime. In this README, **MCP server** means a local child process connected over stdin/stdout: it opens no port and does not depend on the FastAPI web-demo service. The separate Cassette backend remains the editing engine and continues to handle authentication, media processing, agent runs, project state, and rendering.

The web demo is intentionally different. Browsers still need the retained FastAPI server for uploads, chat sessions, and frontend endpoints; none of that behavior is removed by the local MCP plugin.

### First-run authentication

Cassette passwords are generated by the server and emailed to you. You never choose one, and the plugin never invents one.

Missing credentials do not prevent the MCP process from starting. There are two ways to hand the password over.

**In the conversation.** Paste the password from your Cassette email and ask the agent to sign in; it calls the `cassette_login` tool, which verifies the account against Cassette before writing anything and then stores it privately. Nothing else is needed — no terminal, no browser. The trade-off is explicit: the password lands in your agent host's transcript on disk and is sent to the model provider for the rest of that conversation. If that is not acceptable to you, use the terminal instead.

**In a private terminal.** The command keeps the password out of the transcript entirely — it prompts with `getpass`, verifies the account before writing anything, and stores credentials in the platform-standard Oh My Cassette config directory. Every `auth_required` envelope carries the exact command for your install; from a git checkout it is:

```bash
python3 scripts/setup_local_mcp.py --email you@example.com
```

Credentials may also come from process environment variables. Environment values take precedence over protected local config, so `cassette_login` refuses to write a file that would be shadowed. Importing an existing Hermes `.env` is explicit and optional:

```bash
python3 scripts/setup_local_mcp.py --import-hermes
```

Either route creates config directories with mode `0700` and credential files with mode `0600`, rejects symlinks and permissive files, and never persists access or refresh tokens.

### Jamendo BYOK setup

Jamendo music matching is strictly bring-your-own-key for every local agent host. Create a read-only application in the [Jamendo developer portal](https://devportal.jamendo.com/) and copy its Client ID. A Client Secret is neither needed nor accepted.

**In the conversation.** Ask the agent to configure Jamendo, paste your Client ID, and it calls `cassette_jamendo_setup`. The tool verifies a minimal Tracks request before writing anything. The trade-off is the same as chat sign-in: the ID reaches the host transcript and model provider for that conversation.

**In a private terminal.** Keep the ID out of the conversation entirely:

```bash
python3 scripts/setup_local_mcp.py --jamendo
```

Codex, Claude Code, and OpenCode store it in the protected `settings.json`; Hermes stores it in `~/.hermes/.env`. `JAMENDO_CLIENT_ID` in the process environment remains highest precedence. A failed validation preserves the previous working value.

BYOK assigns API access and quota to your Jamendo application; it does not grant commercial rights to selected music. Review the returned track URL, license URL, download eligibility, and attribution requirements before publishing or commercial use.

### Resetting the password

You cannot pick a replacement — Cassette generates one and emails it to the account address. Ask the agent for a new password and it calls `cassette_login` with `request_new_password` and `confirm_replace`; or from a terminal:

```bash
python3 scripts/setup_local_mcp.py --reset-password
```

Both routes ask you to confirm first, because the request is irreversible: **it replaces the account password everywhere, including on your other machines**, it is limited to a few attempts an hour, and the replacement happens before the email is sent — so even a delivery failure kills the old password. Check your inbox before retrying.

The reset only mails the new password; paste it back (in the conversation, or at the terminal prompt) to finish signing this machine in. The verified password is stored in `credentials.json`:

- macOS: `~/Library/Application Support/Oh My Cassette/credentials.json`
- Linux: `~/.config/oh-my-cassette/credentials.json` (or under `XDG_CONFIG_HOME` when set)
- Windows: `%APPDATA%\Oh My Cassette\credentials.json`

To make this machine forget the stored password without touching the account:

```bash
python3 scripts/setup_local_mcp.py --logout
```

### Usage: point it at your clips

1. **Start your agent in the folder that holds the clips** — or in any parent of it. That folder is the trusted media root (`CASSETTE_PROJECT_ROOT`, set to the host's project directory), and everything beneath it is ingestible, so `~/videos/trip/raw/*.mp4` works when you start in `~/videos/trip`. Clips somewhere else? Register that directory once:

   ```bash
   python3 scripts/setup_local_mcp.py --allowed-root /absolute/path/to/media
   ```

   Ingesting a file outside every trusted root fails with `source_path_not_allowed`.

2. **Say what you want, in one message, naming the folder.** No upload step to run yourself — the agent ingests the files it needs.

   > Edit the clips in ./footage into a 30-second travel vlog with beat-synced cuts. Add the title "KOTA KINABALU" at the start and end, and keep the rhythm light.

3. **Keep going in the same conversation.** Each turn commits the edit and returns a timeline digest plus a contact-sheet JPEG saved locally with a clickable link — nothing renders. Hermes labels this as the thumbnail and uses the same saved file; no editor deep link is exposed. "Make the intro shorter", "swap the music for something calmer", and "undo that" all continue the same session.

4. **Say "export" when you're happy.** That's the only thing that starts a render; the finished file lands in `cassette/exports/<job_id>/`.

Supported inputs are video, image, and audio files (`.mp4`, `.mov`, `.jpg`, `.png`, `.mp3`, `.wav`, and friends). Mixed folders are fine — send the footage and the music track together.

### Guided editing flow

What the plugin does under the hood on each of those turns:

1. Ask your agent client to edit one or more media files in the current project.
2. The plugin ingests only media inside the active project or another explicitly trusted root. It canonicalizes paths and rejects traversal and symlink escapes.
3. Describe the edit, answer any guided choices, and start the job. `cassette_run_job` is the wait: the host calls it exactly once for that user turn while the runtime streams progress notifications.
4. When that call settles, follow its typed `phase` and `next_action`; do not start a status-poll loop or retry the edit in the same user turn. `cassette_job_status` is reserved for a deliberately detached or interrupted call.
5. If Cassette needs a real user decision, answer it with the returned job ID. On hosts that support MCP elicitation, `cassette_job_status` collects the answer inline and returns the already-resumed status; other hosts use the `cassette_answer_question` round-trip. API jobs persist their private continuation metadata across host restarts.
6. When editing completes, review the result. Rendering starts only after an explicit `export` decision.
7. The result contains validated absolute paths, file URIs, MIME type, size, and an MCP resource link for each exported artifact. Large media bytes are never embedded in the tool response.

When a background job reaches a terminal state (finished, needs input, failed, or cancelled), the MCP runtime posts a best-effort local desktop notification — `osascript` on macOS, `notify-send` on Linux — so you learn a long render is done even after the monitor budget hands the job back. Set `CASSETTE_MCP_NOTIFY=0` to disable it.

Sessions are isolated by a cryptographically random session ID. Codex, Claude Code, OpenCode, and Hermes share host-neutral storage, so you can deliberately hand a session or job ID from one host to another; nothing is shared implicitly.

Additional trusted media directories can be registered during setup:

```bash
python3 scripts/setup_local_mcp.py --allowed-root /absolute/path/to/media
```

Exports stay under the shared Oh My Cassette data directory at `cassette/exports/<job_id>/`. Only files contained in that job-specific directory can be returned.

### How the plugin reaches Cassette

One way: direct calls to the separate Cassette backend. Authentication retries once after a `401`, access tokens are kept in memory only, and continuation metadata is persisted, so a paused job resumes after the agent client restarts.

There is no browser to install, drive, or keep alive. The Playwright transport that used to sit behind `CASSETTE_TRANSPORT=browser` has been removed; a leftover setting is reported once on stderr and ignored.

### MCP tools

The local MCP runtime exposes the same 16 tool names as Hermes:

| Tool | Purpose |
|---|---|
| `cassette_ingest_media` | Safely ingest trusted project media into an isolated session |
| `cassette_list_assets` | Read the session's media manifest |
| `cassette_make_prompt` | (legacy) Build a full edit brief — superseded by verbatim relay |
| `cassette_match_bgm` | Match Free To Use background music |
| `cassette_match_exact_bgm` | Match a specific title and artist |
| `jamendo_music_matcher` | Match structured Jamendo preferences |
| `cassette_jamendo_setup` | Verify and privately store this machine's Jamendo Client ID |
| `cassette_answer_question` | Answer a guided question or resume a paused job |
| `cassette_run_job` | Run one conversational turn (`message` = the user's verbatim words); `export=true` renders |
| `cassette_job_status` | Resume a deliberately detached or interrupted job call |
| `cassette_review_completion` | Review completion and explicitly approve export |
| `cassette_cancel_job` | Request cooperative cancellation |
| `cassette_timeline` | Read the live project timeline as a bounded text digest (+ optional contact sheet) |
| `cassette_edit` | Surgical no-LLM edit / undo through the manual command lane (`CASSETTE_DIRECT_EDIT=1`) |
| `cassette_config` | Get/set the session's model + thinking level (static product list, applies next turn) |
| `cassette_login` | Verify and privately store this machine's credentials, or request a new emailed password (MCP hosts only) |

Every tool returns a structured envelope with `ok`, typed `data` or `error`, `session_id`, `job_id`, the current phase, and a runtime-derived `next_action`.

### Local preview links and plan review

The runtime returns **no editor deep link**. A `…?projectSessionId=<id>&chatSessionId=<uuid>` URL is a bearer capability: the backend binds no owner to a scratch session, so the only checks on that route are "signed in" and "knows the id" — any authenticated account that sees the link can open the project *and* run edits on the thread. Tool output ends up in chat transcripts, logs and screen recordings, so the runtime no longer emits one, and the skills instruct the agent not to construct one. Previews are the timeline digest, the contact sheet, and the export.

This narrows exposure rather than closing it: the route still resolves for anyone who reconstructs the URL. Binding a session to its owner has to happen server-side.

Two concurrency semantics worth knowing: a plugin turn never cancels a run started from the open editor tab (it fails typed as `thread_busy` instead — wait and retry), while typing a fresh message in the tab DOES cancel an in-flight plugin turn (the tab takes over; existing product behavior).

**Behavior change (0.4.14):** the agent receives the user's `message` verbatim (no brief wrapper), sessions are multi-turn on one thread, and a turn ends with the edit committed but **nothing rendered** — the envelope carries `timeline_delta`, `quality.timeline_ctl`, and a contact-sheet preview instead; pass `export=true` on the turn where the user asks to finish. A fresh session starts immediately with GPT-5.6 Luna and Extra High thinking; model selection is opt-in through `$cassette-model` in Codex, `/cassette-model` in Claude Code/OpenCode, `/cassette_model` in Hermes, or an explicit natural-language request. The saved session preference applies from the next turn.

**Behavior change (0.4.0):** on MCP hosts, `edit_plan_review` now surfaces as a real question by default (`CASSETTE_PLAN_REVIEW=user`) instead of being silently auto-approved — answer with `approve`, `revise <feedback>`, or `reject`, in chat or in the open editor tab (first answer wins). Set `CASSETTE_UNATTENDED=1` to restore the previous fully headless behavior. Status envelopes additionally carry `timeline_delta` (what changed) and `plan_progress`, fed by the run's SSE event stream (`CASSETTE_API_STREAM=0` disables).

## Ready to Use with Hermes 📼
**Now you can pick up your phone and DM your agent! Don't forget to keep your agent alive and network connected.**

In QQ or Telegram:

1. Send one or more video, image, or audio files.
2. Wait for the saved-material acknowledgement.
3. Send the edit instruction in the same conversation or prefix it with `/edit`. The session starts with GPT-5.6 Luna and Extra High thinking; send `/cassette_model` only when you want to change them. Your words go to the Cassette agent verbatim; optimization and BGM remain explicit via `/refine` and `/music`.
4. Each turn ends with the edit saved (timeline delta + contact-sheet preview, no render). Depending on the client, the preview is delivered as an image or a labeled local thumbnail link. Keep editing in the same conversation. Say "export" when you want the video, and the plugin renders and delivers it through the gateway when supported.

| Command | Explanation |
|---|---|
| `/new` or `/reset` | Clear your assets and start a new conversation with Hermes |
| `/edit <instruction>` | Edit the current video based on your instruction. |
| `/refine <instruction>` | Refine your edit instruction and start editing. |
| `/music <BGM request>` | Match and add a BGM to your assets based on your request. |
| `/cut` | Stop the current Cassette edit. |
| `/check_assets` | Check the uploaded assets and their status. |
| `/cassette_model` | Select the current Cassette model and thinking level. |
| `/cassette language zh` | Set Cassette’s response language to Chinese. |
| `/cassette language en` | Set Cassette’s response language to English. |
| `/cassette status <job_id>` | Check the status of a specific job. |
| `/cassette cancel <job_id>` | Cancel a specific job. |

* Assets and video state are preserved within the same conversation session. You can send additional messages in the same session to further modify the edited video results.

* Use `/new` or `/reset` to start a fresh Hermes session and clear the live Cassette session and your assets for that conversation.

* QQ is set to Chinese and Telegram is set to English by default, you can set language by command `/cassette language zh/en` manually.

## Update

The runtime checks the release channel once a day and, when a newer version exists, tells your agent — which mentions it once and offers to run the command below for you. Set `CASSETTE_UPDATE_CHECK=0` to turn that check off.

| Host | Automatic | Manual |
| --- | --- | --- |
| Claude Code | yes, once enabled (below) | `claude plugin marketplace update cassette-editor && claude plugin update oh-my-cassette@cassette-editor` |
| Codex | marketplace snapshot only | `codex plugin add oh-my-cassette@cassette-editor` |
| Hermes | no | `hermes plugins update cassette && hermes gateway restart` |
| OpenCode | no | re-run the install command |

### Claude Code: automatic updates

Claude Code checks for marketplace and plugin updates after your session starts, with a random delay of up to ten minutes, so the session you are in keeps the version it launched with — you are prompted to run `/reload-plugins`, or the new version loads next launch. It is off by default for third-party marketplaces, so turn it on once — `scripts/setup_local_mcp.py` offers to do this during setup (skip with `--no-auto-update`), or do it yourself in `/plugin` → **Marketplaces** → `cassette-editor` → **Enable auto-update**, or in `~/.claude/settings.json`:

```json
{
  "extraKnownMarketplaces": {
    "cassette-editor": {
      "source": { "source": "github", "repo": "Cassette-Editor/oh-my-cassette" },
      "autoUpdate": true
    }
  }
}
```

Declaring it in `settings.json` wins over the `/plugin` toggle — Claude Code syncs the declared value into its marketplace state and then points you back at the settings file to change it.

Because the setup prompt only runs at setup, an install that predates it — or one where it was declined — would never hear about the toggle again. So on Claude Code the runtime also reads that setting at startup and, while auto-update is off, asks your agent to mention it at most once per session and point you at the toggle. Your agent will not edit your Claude configuration itself. `CASSETTE_UPDATE_CHECK=0` silences this along with the release check.

Claude Code's `DISABLE_AUTOUPDATER` turns off all automatic updates including plugins; pair it with `FORCE_AUTOUPDATE_PLUGINS=1` to keep plugin updates while managing Claude Code itself manually.

### Codex

Codex refreshes configured git marketplace snapshots on its own, but installed plugins are cached per version, so one command applies the new one:

```bash
codex plugin marketplace upgrade cassette-editor   # only needed if the snapshot is stale
codex plugin add oh-my-cassette@cassette-editor
```

### OpenCode

Re-run the install command — it is the same command for installs and updates:

```bash
curl -fsSL https://raw.githubusercontent.com/Cassette-Editor/oh-my-cassette/release/scripts/install_opencode.py | python3 -
```

The local launcher updates its locked, plugin-managed virtual environment automatically on the next start after any of these.

### Hermes

If installed through the Hermes plugin manager:

```bash
hermes plugins update cassette
hermes gateway restart
```

If installed from a git checkout (symlink install), update the checkout:

```bash
git pull --ff-only
hermes gateway restart
```

If the plugin was installed with --copy, reinstall the copied plugin after pulling:
```bash
git pull --ff-only
python3 scripts/install_plugin.py --copy --force
hermes gateway restart
```

See [CHANGELOG.md](./CHANGELOG.md) for what changed in each release.

<details>
<summary>Migrating a symlink install to the Hermes plugin manager (optional)</summary>

Existing symlink installs keep working — migration is optional. To switch:

```bash
rm ~/.hermes/plugins/cassette        # removes only the symlink, not your checkout
hermes plugins install Cassette-Editor/oh-my-cassette
hermes gateway restart
```

Your credentials in `~/.hermes/.env` and the plugin's enabled state carry over;
already-set values are not prompted again. Don't run
`hermes plugins install --force` on top of a symlink — it fails with a
confusing error instead of replacing it.
</details>

# ❓ FAQ

### Which AI agents does Oh My Cassette work with?

Claude Code, Codex, OpenCode, and Hermes Agent are supported out of the box, and any other MCP host can connect to the local `cassette` MCP server. Sessions live in a host-agnostic data directory, so you can start an edit in one host and continue it in another.

### Does it edit videos locally or in the cloud?

The plugin runs locally beside your agent and handles media ingestion, edit planning, and job supervision. The actual editing and rendering happen on [Cassette](https://trycassette.online), so you don't need a GPU or any editing software installed.

### Do I need a Cassette account?

Yes. The installer asks for your Cassette account email and password on first run and stores them locally; the plugin authenticates with Cassette on your behalf.

### What kinds of videos can it make?

Montage and story edits — vlogs, travel videos, music-driven shorts, cooking tutorials, product commercials, and game highlights — with beat-synced cuts, subtitles, picture-in-picture, transitions, and auto-matched background music. See [Case Videos](#-case-videos) for real examples with prompts and processing times.

### Can I see what the agent is doing mid-edit?

Yes. After each editing turn you get a timeline digest and a contact-sheet JPEG saved locally. Terminal clients present a clickable local thumbnail link, and supported gateway clients can deliver the preview as an image. Before the edit runs, the agent can also surface the plan as a storyboard sheet (one source frame per planned beat) for review. The plugin intentionally does not expose an editor deep link.

### Can I try it without installing anything?

Yes — the [public web demo](https://trycassette.online/agent-demo) runs the full workflow in your browser. It is unauthenticated and for evaluation only, so don't upload sensitive content.

### Is Oh My Cassette free and open source?

This plugin is free and open source under the MIT license — all of it, including the MCP server and the skill.

Rendering runs on [Cassette](https://trycassette.online), a separate hosted service that requires an account. See [Cassette's pricing](https://trycassette.online) for what an account costs. You can try the whole workflow with no account at all through the [web demo](#-try-without-installation).

# 🔨 Development & Troubleshooting

Setup for contributors, the full configuration reference, transport internals, the diagnostic
scripts, and answers to common runtime problems all live in
**[docs/development.md](./docs/development.md)**.

Quick diagnostic — run this first when something misbehaves:

```bash
python3 scripts/diagnose_local_mcp.py   # Codex / Claude / OpenCode
python3 scripts/diagnose_install.py     # Hermes
```

Both report bootstrap, config, transport, and media-root state without printing credentials.

# 💬 Community

- **[Discussions](https://github.com/Cassette-Editor/oh-my-cassette/discussions)** — ask questions, share the cuts you made, and see what's planned.
- **[Discord](https://discord.gg/qd9NY4k8d7)** — chat with contributors and other users.
- **[Issues](https://github.com/Cassette-Editor/oh-my-cassette/issues)** — bug reports and feature requests. Issues labelled `good first issue` are a good place to start.
- **[CONTRIBUTING.md](./CONTRIBUTING.md)** — how to propose a change.

Made something you like with it? Post it in Discussions — we feature the best cuts in the showcase.


## License

MIT. See [LICENSE](LICENSE).

