openshot-mcp
openshot-mcp
An MCP server that lets an AI agent (Claude Code, Codex, or any other MCP client) build a video edit inside an OpenShot 4.0 project file.
You give it a music track and a folder of clips. The agent finds the tempo, lays the clips out on the beat grid, drops a marker at every section change, and saves the project. You then open the project in OpenShot and finish the edit by hand, with everything already lined up to the music.
It was built for game trailers, where the raw material is a pile of short screen captures and a score with a clear beat, but nothing in it is game specific.
What it does
Imports media. Any file ffprobe can read becomes an OpenShot file entry with the right frame rate, duration, and stream metadata.
Turns a music file into a beat grid. Tempo and beat phase are detected with librosa and refined by searching for the grid that lines up best with the audio's onsets. Section changes are estimated from energy and onset density. If you already know the BPM and sections from your DAW, pass them in and skip detection.
Places clips on the grid. Every cut lands on a beat, quantized to the project's frame rate with exact fractions so adjacent clips never drift apart.
Adds section markers so you can see the musical structure on the OpenShot timeline.
Saves safely. It backs up the project first, writes atomically, refuses to write while OpenShot has the project open, and refuses if the file changed on disk since it was loaded.
What it does not do
Drive the running OpenShot application. OpenShot has no API for that, so this server edits the project file. Close the project in OpenShot before saving, then reopen it.
Render or export video. Use OpenShot for that.
Make editorial decisions. The server exposes primitives; which clip goes where is up to the agent or the person driving it.
Effects, transitions, titles, or keyframe animation (planned for a later version).
Requirements
Python 3.12 or newer
ffmpeg (for the
ffprobecommand) on your PATHOpenShot 4.0.x to open the results. The project file format is checked on load and other versions are refused.
Developed and tested on Windows. The code has no Windows-only dependencies except the check that detects an open OpenShot window, which is skipped on other platforms.
Install
git clone https://github.com/alecray/openshot-mcp.git
cd openshot-mcp
python -m venv .venv
.venv/Scripts/pip install -e ".[dev]" # on macOS/Linux: .venv/bin/pip
.venv/Scripts/python -m pytest -q # should report all tests passingRegister the server with your MCP client. For Claude Code, add this to the project's .mcp.json
(adjust the path to wherever you cloned the repo):
{
"mcpServers": {
"openshot": {
"command": "/path/to/openshot-mcp/.venv/Scripts/python.exe",
"args": ["-m", "openshot_mcp"]
}
}
}Typical session
An agent working through the tools looks like this:
open_projecton an existing.osp, ornew_projectto start a 720p 30 fps one.import_mediawith the music file and every clip.analyze_musicon the music file. It returns the BPM, the offset of the first beat, and a list of section start times, and sets that grid as active. Or callset_gridwith values you know.place_sequencewith a list of clips and how many beats each should last. Clips are laid back to back starting at beat zero. Useadd_clipfor anything that needs a specific spot.get_timelineto review the result, thensave_project.Open the project in OpenShot.
Tools
Tool | Purpose |
| Load an existing OpenShot 4.0 project |
| Start a new HD 720p 30 fps project (written on save) |
| Probe files and add them to the project. Importing the same path twice returns the same id |
| List imported files and their ids |
| Detect the beat grid from an audio file. Explicit values override detection |
| Set the beat grid from known values |
| Return the active grid, including beat times |
| Place one clip. |
| Place several clips back to back on the grid |
| Move, trim, or retitle a clip |
| Delete a clip |
| Manage timeline markers |
| List clips in order, with any gaps or overlaps |
| Check ids, references, bounds, and overlaps |
| Write the project file |
Layers use OpenShot's numeric ids: 1000000 for L1 through 5000000 for L5.
How the project file is written
Existing content is left untouched. New files, clips, and markers are appended.
New clips carry the full set of default properties captured from a real OpenShot 4.0 project (
templates/clip_defaults.json), so OpenShot treats them exactly like clips it created itself.Media paths follow OpenShot's own rules: relative to the project folder when on the same drive,
@assets/...for files in the project's assets folder, absolute otherwise.Thumbnails and waveform caches are not written. OpenShot regenerates them when it opens the project.
Marker titles are stored but OpenShot does not display them; markers appear as icons.
Accuracy notes
Tempo and beat phase detection is reliable on anything with a steady pulse. On the first real
trailer score it recovered 88.0 BPM exactly with an 18 ms offset. Section detection is a rough
guess and works best as a starting point; pass sections from your DAW when you know them.
Repository layout
src/openshot_mcp/ server.py (MCP tools), project.py (project model and save logic),
media.py (ffprobe), grid.py (beat grid and audio analysis), lock.py
templates/ empty project and default clip properties captured from OpenShot 4.0
tests/ pytest suite with generated fixtures
docs/ design notes, decision records, specs, and reviewsContributing
Issues and pull requests are welcome. Run the tests before opening a PR. Commit messages follow
the Conventional Commits format (feat:, fix:, docs:, and so on), enforced by the hook in
.githooks once you run git config core.hooksPath .githooks.
License
MIT. See LICENSE.