AI Frontier Post
Tutorials

Let an AI agent cut your video: hands-on with Concat, the free local CapCut replacement

CapCut's price is not money: it is an account, an upload, and a watermark on the free tier. Concat removes all three — a free, local editor — and its 0.2.8 release adds the part that makes it interesting to anyone who already lives in a coding agent: a built-in MCP server that lets an agent do the cutting. This guide proves it, command by command, on version 0.2.8.

TL;DR: Concat is a free, open-source CapCut replacement that runs locally with no account, no upload and no watermark, and its 0.2.8 release ships a built-in MCP server. This guide installs 0.2.8 with its checksum verified, builds a two-clip edit with a title through MCP tool calls, and exports a real H.264 file — including the one failure (a missing Vulkan renderer on a GPU-less server) and its one-package fix.
  • Who: Concat (jub0t/Concat on GitHub), a free, open-source video editor.
  • What: a local CapCut replacement — multi-track editing, keyframes, effects, titles, local captions and voices — whose 0.2.8 release ships a built-in MCP server, so a coding agent can create projects, cut, title and export through typed tool calls.
  • When: version 0.2.8, released 20 October 2026, is the version verified in this guide; the repository showed 12,028 stars and 1,484 forks at writing.
  • Requirements: Linux, macOS or Windows; about 155 MB of download; Python 3 for the client script. No API key, no account, and no model downloads for anything in this guide. A GPU, or Mesa's lavapipe driver on GPU-less Linux, for exports.
  • Licence: GNU Affero General Public License v3.0.
  • Verified: 0.2.8 Linux package installed with its SHA256 checksum checked; MCP headless server probed (42 tools, apiVersion 0.2, mode headless); a 640×360 project at 30 fps built from two clips plus a title (7.0-second timeline, confirmed by timeline_read); export finished 210 of 210 frames as H.264/AAC, 7.0 seconds, 186,592 bytes; preview frame and an exported frame inspected (the title is painted on the video). Two first attempts failed and their errors are quoted: a wrong assumption about project_create's return shape, and a missing Vulkan renderer fixed by installing mesa-vulkan-drivers.
An AI agent at a console connected by a glowing pipeline to a video editing timeline
Your agent is the editor now. Image: AI Frontier Post.

Why this project, why now

CapCut is where most short-form video gets cut, and its price is not money: it is an account, an upload, and a watermark on the free tier. Concat removes all three. It is a free, open-source video editor for macOS, Windows, Linux and Android that runs entirely on your own machine — no account, no subscription, no upload, no watermark on any export, up to 4K at 60 frames per second. The editor itself is a native Rust application with a GPU compositor, multi-track timelines, keyframes, effects and titles, local auto-captions, text-to-speech, voice cloning and background removal.

That alone would make it one more CapCut alternative. The reason it is blowing up now is the second half of the pitch: the editor is scriptable. Version 0.2.8, released 20 October 2026, ships a built-in MCP server, so a coding agent can drive the same engine the window uses — create projects, import media, cut, title, keyframe, apply effects and export — without opening a window and without an account. When this guide was written, the repository (jub0t/Concat on GitHub) showed 12,028 stars and 1,484 forks, after being created on 25 August 2026, and October's monthly trending board showed it up about 7,900 stars on the month. The agent-editable editor is what that spike is about, and this guide tests exactly that: every command below was run, and every output quoted is the output the run produced.

What you'll need

  • Linux, macOS or Windows. The walkthrough below runs on Linux; the macOS and Windows paths are noted where they differ.
  • About 155 MB of download for the Linux package, plus ffmpeg if you want to generate test footage (or two video files of your own).
  • No account, no API key, and no model downloads for anything in this guide. The in-app AI features (captions, voices, cutout) download their models once from Settings and then work offline — they are documented here, not run here.
  • A GPU, or a software Vulkan renderer. On a GPU-less Linux machine the export fails until you install Mesa's lavapipe driver — step 5 shows the exact error and the one-package fix. macOS and Windows machines use their native GPU path.
  • Python 3 for the small MCP client in step 3. No Python packages beyond the standard library.

Step 1: Install Concat 0.2.8 and check the version

Download the release for your platform from the project's GitHub Releases page. On Linux x86_64 that is Concat-0.2.8-linux-x86_64.deb (154,767,456 bytes), alongside .rpm and .AppImage builds, an ARM build, and a SHA256SUMS file. Verify the download before you install it:

curl -sSL -o Concat-0.2.8-linux-x86_64.deb \
  https://github.com/jub0t/Concat/releases/download/v0.2.8/Concat-0.2.8-linux-x86_64.deb
curl -sSL -o SHA256SUMS \
  https://github.com/jub0t/Concat/releases/download/v0.2.8/SHA256SUMS
sha256sum -c <(grep "Concat-0.2.8-linux-x86_64.deb" SHA256SUMS)

The check prints:

Concat-0.2.8-linux-x86_64.deb: OK

Install it with your package manager (sudo apt install ./Concat-0.2.8-linux-x86_64.deb on Debian/Ubuntu), or, if you only want to try it without installing, extract it with dpkg-deb -x Concat-0.2.8-linux-x86_64.deb extracted and run extracted/opt/concat/concat directly — the /usr/bin/concat wrapper simply execs /opt/concat/concat, so the extracted binary behaves the same. Either way, confirm the version:

concat --version
concat 0.2.8

Two things worth knowing before you go further. First, Concat is in beta: it works, and it still has edges — the release notes say so plainly, and Windows builds are not code-signed yet, so SmartScreen asks you to choose More info, then Run anyway. Second, concat --help is not a flag here; the binary's usage line is concat [--project <PROJECT_DIRECTORY> | --mcp [auto|window|headless] | --version | --smoke | --whisper-cpu]. The --mcp flag is the one this guide is about.

Step 2: Make two test clips (or use your own footage)

The edit below needs two video files. If you have footage, skip this step and use your own paths. Otherwise generate two plain clips with ffmpeg — a 4-second dark-blue clip and a 3-second dark-red clip, each with a tone so you can hear the cut as well as see it:

ffmpeg -y -v error -f lavfi -i color=c=0x1a3a5c:s=640x360:d=4 \
  -f lavfi -i sine=frequency=440:d=4 -shortest clip1.mp4
ffmpeg -y -v error -f lavfi -i color=c=0x5c1a1a:s=640x360:d=3 \
  -f lavfi -i sine=frequency=660:d=3 -shortest clip2.mp4

Confirm the durations — 4 seconds and 3 seconds — because the timeline math in step 4 depends on them:

ffprobe -v error -show_entries format=duration -of csv=p=0 clip1.mp4
4.000000

Step 3: The client script that talks to Concat's MCP server

Concat's MCP server speaks newline-delimited JSON-RPC 2.0 over stdio. An agent — Claude Code, Claude Desktop, or any MCP client — starts it as concat --mcp and nothing extra needs installing. The server's own concat_version tool reports what it speaks; on the verified run it answered:

{
  "apiVersion": "0.2",
  "capabilities": ["events", "mcp"],
  "concat": "0.2.8",
  "mcp": {"mode": "headless", "writesUnder": ["<your work folder>"]}
}

Two details in that answer matter. The mode is headless: with no window open, the server runs the editor engine itself. And writesUnder is the sandbox rule — the agent writes projects, exports and frames only under your home folder unless you set CONCAT_MCP_ROOTS (a colon-separated path list) to allow other folders in headless mode. Keep your demo project under your home folder and you need no extra configuration. The server exposes 42 tools, named for what they do: project_create, media_import, clips_add_sequence, title_add, timeline_read, project_save, export_start, export_wait, preview_frame, and the rest — effects, keyframes, masks, stabilization, templates, undo and redo. Times are seconds on the timeline; sizes and offsets are fractions of the frame.

Save the following script as concat_mcp_edit.py. It is the exact script the verified run used: it handshakes, creates a 640×360 project at 30 frames per second, imports two clips, lays them end to end, adds a title, saves, exports, and grabs a preview frame, printing the server's answer at each step. One correction is already baked in, and it is worth knowing about: project_create returns a summary with no folder field, so the script takes the folder as location plus project name rather than reading a path out of the answer — the first version of this script assumed a path field and failed with a KeyError before importing anything.

#!/usr/bin/env python3
"""Drive Concat 0.2.8's built-in MCP server over stdio.

Builds a short edit with no window and no account: a project, two imported
clips laid end to end, a title over the cut, then an export to MP4 and a
preview frame. Every step prints the server's own answer.

Usage:
    python3 concat_mcp_edit.py --binary /usr/bin/concat \
        --work ~/concat-demo --clips clip1.mp4 clip2.mp4

On a GPU-less Linux server the export needs a software Vulkan renderer:
    sudo apt install mesa-vulkan-drivers   # provides lavapipe
Projects the agent writes must live under your home folder unless you set
CONCAT_MCP_ROOTS to allow another root.
"""
import argparse
import json
import os
import select
import subprocess
import sys
import time


class Mcp:
    def __init__(self, binary, env_extra):
        env = dict(os.environ)
        env.update(env_extra)
        self.proc = subprocess.Popen(
            [binary, "--mcp", "headless"],
            stdin=subprocess.PIPE, stdout=subprocess.PIPE,
            stderr=subprocess.DEVNULL, env=env, text=True, bufsize=1)
        self.next_id = 0

    def handshake(self):
        self._send({"jsonrpc": "2.0", "id": 0, "method": "initialize",
                    "params": {"protocolVersion": "2025-06-18", "capabilities": {},
                               "clientInfo": {"name": "concat-demo", "version": "0.1"}}})
        self._read_id(0, timeout=60)
        self._send({"jsonrpc": "2.0", "method": "notifications/initialized"})

    def call(self, name, args, timeout=120):
        self.next_id += 1
        rid = self.next_id
        self._send({"jsonrpc": "2.0", "id": rid, "method": "tools/call",
                    "params": {"name": name, "arguments": args}})
        msg = self._read_id(rid, timeout=timeout)
        result = msg["result"]
        if result.get("isError"):
            raise RuntimeError(f"{name} failed: "
                               f"{result['content'][0]['text']}")
        return result["content"][0]["text"]

    def _send(self, obj):
        self.proc.stdin.write(json.dumps(obj) + "\n")
        self.proc.stdin.flush()

    def _read_id(self, rid, timeout):
        end = time.time() + timeout
        while time.time() < end:
            ready, _, _ = select.select([self.proc.stdout], [], [], 0.5)
            if ready:
                line = self.proc.stdout.readline()
                if not line:
                    break
                try:
                    msg = json.loads(line)
                except json.JSONDecodeError:
                    continue
                if msg.get("id") == rid:
                    return msg
        raise TimeoutError(f"no reply to request {rid}")

    def close(self):
        try:
            self.proc.stdin.close()
        except BrokenPipeError:
            pass
        time.sleep(1)
        self.proc.terminate()
        try:
            self.proc.wait(timeout=5)
        except subprocess.TimeoutExpired:
            self.proc.kill()


def main():
    ap = argparse.ArgumentParser()
    ap.add_argument("--binary", default="/usr/bin/concat")
    ap.add_argument("--work", required=True, help="directory for the project")
    ap.add_argument("--clips", nargs=2, required=True)
    ap.add_argument("--title", default="Built by an agent")
    args = ap.parse_args()
    work = os.path.abspath(os.path.expanduser(args.work))
    os.makedirs(work, exist_ok=True)

    mcp = Mcp(args.binary, {})
    try:
        mcp.handshake()
        version = json.loads(mcp.call("concat_version", {}))
        print("server:", version["concat"], "| mode:", version["mcp"]["mode"])
        project = json.loads(mcp.call("project_create", {
            "location": work, "name": "mcp-short",
            "video": {"width": 640, "height": 360, "rateNum": 30, "rateDen": 1}}))
        # The summary carries no folder field: the folder is location + name.
        path = os.path.join(work, "mcp-short")
        print("project:", project["project"]["name"],
              project["project"]["width"], "x", project["project"]["height"],
              "@", project["project"]["fps"], "fps ->", path)
        ids = []
        for clip in args.clips:
            imported = json.loads(mcp.call("media_import", {
                "path": path, "file": os.path.abspath(clip)}))
            ids.append(imported["createdId"])
            print("imported:", clip, "->", imported["createdId"])
        mcp.call("clips_add_sequence",
                 {"path": path, "mediaIds": ids, "start": 0})
        mcp.call("title_add", {"path": path, "start": 0.5, "duration": 3,
                               "style": {"content": args.title}, "above": True})
        timeline = json.loads(mcp.call("timeline_read", {"path": path}))
        clips = [(c["id"], c["kind"], c["start"], c["end"])
                 for c in timeline["timeline"]["clips"]]
        print("timeline:", timeline["timeline"]["duration"], "seconds,", clips)
        mcp.call("project_save", {"path": path})
        out = os.path.join(work, "mcp-short.mp4")
        job = json.loads(mcp.call("export_start", {"path": path, "output": out}))
        waited = json.loads(mcp.call("export_wait",
                                      {"job": job["job"], "timeoutSeconds": 300},
                                      timeout=330))
        print("export:", waited["state"], waited["frame"], "of",
              waited["total"], "frames ->", out)
        if waited["state"] != "done":
            sys.exit(f"export did not finish: {waited}")
        frame = json.loads(mcp.call("preview_frame",
                                    {"path": path, "time": 1.0,
                                     "output": os.path.join(work, "frame.png"),
                                     "width": 640, "height": 360}))
        print("preview frame:", frame["path"], frame["width"], "x", frame["height"])
    finally:
        mcp.close()


if __name__ == "__main__":
    main()
Diagram of a coding agent sending commands through a protocol hub to a video editing engine
Agent, protocol, engine: the MCP server is the middle of the picture. Image: AI Frontier Post.

Step 4: Run the edit — two clips, one title, seven seconds

Run the script against your two clips. The --work folder must be under your home folder (or added to CONCAT_MCP_ROOTS); the run below used a folder under home:

python3 concat_mcp_edit.py --binary /usr/bin/concat \
  --work ~/concat-demo --clips clip1.mp4 clip2.mp4

The server's answers, as the verified run printed them:

server: 0.2.8 | mode: headless
project: mcp-short 640 x 360 @ 30.0 fps -> ~/concat-demo/mcp-short
imported: clip1.mp4 -> m1
imported: clip2.mp4 -> m2
timeline: 7.0 seconds, [('c3', 'video', 0.0, 4.0), ('c5', 'text', 0.5, 3.5), ('c4', 'video', 4.0, 7.0)]
export: done 210 of 210 frames -> ~/concat-demo/mcp-short.mp4
preview frame: ~/concat-demo/frame.png 640 x 360

Read that timeline line carefully, because it is the whole edit in one row. Clip c3 is the first clip on track T1 from 0 to 4 seconds; clip c4 is the second clip on the same track from 4 to 7 seconds; clip c5 is the title, a text clip on a lane above the video (T2) from 0.5 to 3.5 seconds, reading "Built by an agent". The two clips' durations add up to the 7-second timeline, and the title overlaps the first clip only. project_save wrote the project document to mcp-short/concat.json in the project folder — edits stay in memory until you save, exactly as in the window, so a script that crashes before saving keeps nothing.

Step 5: Check the export — and fix the one failure you will hit on a GPU-less server

The export is already done by step 4, so this step is verification. Read the file back with ffprobe:

ffprobe -v error -show_entries format=duration,size \
  -show_entries stream=codec_name,width,height,r_frame_rate \
  -of default=noprint_wrappers=1 ~/concat-demo/mcp-short.mp4
codec_name=h264
width=640
height=360
r_frame_rate=30/1
codec_name=aac
duration=7.000000
size=186592

That is a real, playable file: H.264 video with AAC audio, 640×360 at 30 frames per second, exactly 7 seconds, 186,592 bytes. The preview frame the script grabbed is a 640×360 PNG. Open it, or pull a frame out of the export yourself with ffmpeg -ss 1.5 -i ~/concat-demo/mcp-short.mp4 -frames:v 1 check.png — at 1.5 seconds you are inside the title's span, and the frame shows "Built by an agent" in white over the dark-blue clip. That frame is the proof the title is not metadata on a timeline but paint on the exported video.

Now the failure, because you will hit it if your machine has no GPU. The first export attempt in the verified run did not fail on the edit — it failed on the render, with this exact error from export_wait:

no GPU or software renderer is available to draw with - install Mesa's Vulkan drivers (lavapipe)

The fix is one package on Debian/Ubuntu, and it changes nothing else about the setup:

sudo apt install mesa-vulkan-drivers

After that install, the same project exported cleanly: 210 of 210 frames, state done, with no change to the project or the script. The same failure hits preview_frame, for the same reason — both need a renderer, and lavapipe is the software one. If you are on a laptop or desktop with a GPU, you will never see this error; it is specific to headless servers and virtual machines. Note also that export defaults are H.264 at CRF 20 with the medium preset and limited color range — pass codec, crf or preset to export_start if you want HEVC, AV1, or a different quality point.

Diagram of clips flowing through a compositor into a finished video file
Timeline in, finished file out: the export is the same engine the window uses. Image: AI Frontier Post.

Step 6: Hand the server to Claude Code (or any MCP client)

The script above proves the protocol; for daily use you hand the server to your agent instead of running a script. In Claude Code, one command registers it — use the binary path for your install:

claude mcp add concat -- /usr/bin/concat --mcp

On macOS that path is /Applications/Concat.app/Contents/MacOS/concat; on Windows, %LOCALAPPDATA%\Programs\Concat\concat.exe for a per-user install or C:\Program Files\Concat\concat.exe for an all-users or .msi install; on Linux with the AppImage, point at the .AppImage file itself. For Claude Desktop and most other clients, the same shape goes in the JSON config:

{
  "mcpServers": {
    "concat": {
      "command": "/usr/bin/concat",
      "args": ["--mcp"]
    }
  }
}

Three operating rules keep this safe. First, modes: concat --mcp defaults to auto — through the open window when one is running with Settings, Automation, Scripts and apps switched on, otherwise headless on its own. Force one with --mcp window (fail rather than run on its own) or --mcp headless. Second, locks: every Concat process locks the project folders it has open, so a project open in the window is refused to the agent with a note to use window mode, and vice versa — you cannot have the agent and the window saving over each other. Third, the agent edits projects of its own; it does not edit the project open in the window while you watch. That live mode is planned, not available. Treat the agent as a second editor with its own projects, and hand projects back and forth by closing one side first.

When to use this vs the alternatives

Against CapCut: pick Concat when the edit must stay on your machine, be repeatable by a script, or run without an account — and when you do not need CapCut's template and trending-effects library, which is far larger than anything Concat ships. If templates are the reason you edit in CapCut, stay there.

Against ffmpeg scripts: pick Concat when the edit has a timeline you want to see, revise, or hand to an agent — titles, layered tracks, keyframed moves. ffmpeg is still lighter for a single conversion or a fixed filter chain, and it needs no renderer at all.

Against a hosted AI video tool: pick Concat when the footage cannot leave your disk, or when you want the project file afterwards rather than a finished render. Hosted tools win when you want a clip generated from a prompt rather than cut from footage you own.

Against Premiere or DaVinci Resolve: pick Concat when free, local and agent-scriptable beats a mature effect library and a polished window. Concat is beta with a small team; the established editors are the safer choice for deadline work with clients.

Limits, stated plainly

Concat is beta, and the rough edges are real: Windows builds are unsigned, the window's GPU path on exotic hardware is still being worked out, and the AI features — local Whisper captions, text-to-speech, voice cloning, background removal — were not run in this guide, because they need model downloads from Settings first and this guide's bar is commands actually run. Voice cloning deserves one extra line: only clone a voice you have the right to clone; the tool will happily clone a sample, and consent is your call, not the software's. The graphical editor itself was also not driven in this guide's headless run — the verified path is the MCP engine, which is the same engine the window uses, but if you want to cut by hand, install the app, open it, drop footage on the timeline, and export from the Export sheet; that flow follows the project's own documentation, not a run in this guide. Finally, keep backups of anything important: a beta editor that locks project folders is not the place for the only copy of a project.

The takeaway

Concat's bet is that video editing should be a file format and a protocol, not just a window. The window is free and local; the engine behind it answers typed tool calls over stdio; and a seven-second edit built entirely by those calls exports as a real H.264 file with the title painted on it. If you cut short-form video and you already live in a coding agent, that combination — no account, no upload, no watermark, and an agent that can actually do the cutting — is worth an evening of testing on one real project. Start with the script above, then hand the server to your agent and let it do the second cut.

Questions, answered

Do I need a Concat account or an API key? No, for everything in this guide. Installing, creating, editing, exporting and the MCP server all run locally with no account. An account or provider key is only relevant to hosted AI features you opt into, which this guide did not run.

Can the MCP server run on a headless server? Yes — that is exactly how it was verified here, with --mcp headless. The one requirement is a renderer for exports and preview frames: a GPU, or Mesa's lavapipe driver (mesa-vulkan-drivers) on Linux.

Will files made here open in other editors? The exported MP4 is standard H.264/AAC and plays anywhere. The project folder (concat.json plus media) is Concat's own format — it is not a Premiere or Resolve project, and there is no export to those formats.

Is it really free, and under what licence? Yes: the editor is free with no watermark, no account and no paywall, under the GNU Affero General Public License v3.0. That licence matters if you build a hosted service on top of it — read it before you do.

Sources

  • Concat repository page (jub0t/Concat on GitHub) — star and fork counts, creation date, licence, and the 0.2.8 version badge quoted in this guide.
  • Concat docs/MCP.md — the MCP server setup, the per-platform binary paths, window vs headless modes, the project-lock rules, and the tool list.
  • Concat 0.2.8 release page — the release date, the Linux package names, and the SHA256SUMS file used for the checksum check.
  • The Concat binary's own --version output and the MCP server's responses — every command, flag, tool name, schema field, and output quoted in this guide, including the two failures and their fixes.