splicedeck

splicedeck

Enables AI agents to edit videos locally by inspecting media, applying templates, cutting clips, and delivering finished files, with memory of human preferences.

Category
Visit Server

README

splicedeck

checks licence: Apache-2.0 Python 3.12+ runtime dependencies: 0

A video editor an AI agent drives, running on your own machine.

One call dresses a cut in a template: motion graphics, overlays, captions and a beat grid to cut against. One pass over a source gives you both a cleaned long-form master and vertical clips. And it remembers how each client, channel or show likes to be cut, so the next edit starts where the last one ended.

There is no timeline to drag and no account to create. Nothing is uploaded.


Status: the pipeline runs end to end, and memory reaches the cut

A source becomes a delivered file today. Measured on the reference machine (Windows 11, Python 3.13, ffmpeg 8.1.2) against a real 223 MB .mov:

inspect   2.6 s     draft  27 ms     splice  27 ms     verify  42 ms
deliver   157 s  ->  1920×1080 h264 + aac, -23.0 LUFS, decodes clean

python -m pytest reports 1425 passed, 2 skipped in about four minutes. Thirty-one verbs reach the CLI and eighteen of them reach an MCP server, both generated from one table so they cannot drift apart.

One headline feature does not work yet. Cutting by quoting needs a speech binary that no manifest can currently obtain. Read What does not work before you plan around it.


Install

You need Python 3.12 or newer and ffmpeg 8.x on your PATH. splicedeck neither installs nor bundles ffmpeg, and docs/first-run.md §4 explains why that is deliberate.

The setup script asks where the workspace goes, offers to install ffmpeg after showing you the exact command, scaffolds everything and writes an MCP config:

curl -fsSLO https://raw.githubusercontent.com/ihuzaifashoukat/splicedeck/main/install.sh
less install.sh && bash install.sh
irm https://raw.githubusercontent.com/ihuzaifashoukat/splicedeck/main/install.ps1 -OutFile install.ps1
notepad install.ps1; powershell -ExecutionPolicy Bypass -File install.ps1

Read it before you run it. A curl | bash one-liner would be a poor advertisement for a project whose README is largely about a threat model.

If you would rather do it yourself, or want the tests:

uv tool install splicedeck                    # or: pipx install splicedeck
git clone https://github.com/ihuzaifashoukat/splicedeck.git && cd splicedeck
python -m venv .venv
.venv/Scripts/python -m pip install -e ".[dev]"   # Windows
.venv/bin/python  -m pip install -e ".[dev]"      # macOS, Linux

Not on PyPI yet. There is no release, so uv tool install splicedeck will 404 until the first tag is pushed. Until then use the script, a clone, or uv tool install "git+https://github.com/ihuzaifashoukat/splicedeck.git".

docs/install.md has every route, the per-platform ffmpeg commands, the environment variables, and a prompt you can paste into an AI agent to have it install and wire up splicedeck for you.

Try it

The workspace root is whichever directory you run in, and spd init scaffolds one:

mkdir my-edit && cd my-edit
spd init                                      # bookmarks/ casebook/ elements/ ledger/ media/ profiles/ templates/
mkdir -p casebook/parties/demo
spd ready                                     # what is present, and what each gap blocks

init never overwrites. Re-running it after you have edited a profile fills in whatever is missing and leaves your edits alone. The files it writes are byte-identical to the ones this repository ships, and python -m checks.starter --check enforces that.

Then put your footage under media/ and cut:

spd inspect --path media/your-file.mov               # mints a source handle
spd draft --party demo --source s1 --bookmark baseline --profile wide-1080
spd apply --sheet c1 --template clean-master         # overlays, motion, beat grid
spd splice --sheet c1 --source a --in_ticks 0 --out_ticks 900000 \
           --source_in_ticks 0 --cause manual
spd verify --sheet c1
spd deliver --sheet c1

Sources must live inside the workspace. A path with a drive letter in it is refused PATH_OUTSIDE_WORKSPACE before anything is read.

A party is created by a human, by hand, on purpose. draft refuses UNKNOWN_PARTY until casebook/parties/<name>/ exists.

To drive it from an MCP-capable assistant, register the server:

{"mcpServers": {"splicedeck": {
  "command": "C:\\src\\splicedeck\\.venv\\Scripts\\python.exe",
  "args": ["-m", "splicedeck.surface.mcp"],
  "cwd": "C:\\src\\splicedeck"}}}

cwd must be the workspace, because the workspace root is the working directory and nothing else discovers it. python -m splicedeck.surface.mcp --tools prints the generated tool list and exits, which is how you tell a broken server from a broken host configuration. docs/mcp.md is the full guide.

Templates: the look in one call

apply dresses a cut sheet in a named template. It places the overlays, writes the beat grid the agent then cuts against, and records on the sheet which template it used.

Four ship today:

Template What it is
clean-master A calm talking-head master carrying one lower-third and no beat grid
quick-beat A fast-cut vertical: three beat slots with a pulsing accent on each
bold-run A promo look: full-bleed intro and outro cards around two beat slots
bare-mark One small mark on screen and nothing else

A template's overlays animate when the optional motion tier is installed, and fall back to a still imprint when it is not. Six animated compositions ship in scenes/, written for this project and licensed with it.

Slots are enforced. verify refuses to pass a sheet with an unfilled slot, and a cut landing outside a slot's tolerance is refused SLOT_TOO_TIGHT with the nearest legal edges returned as ready-to-send calls. That is what lets an agent hit a rhythm it cannot see.

You can write your own. spd compose --kind template validates and writes a hand-authored template or element card. It is deliberately CLI-only: the MCP server may not write to templates/, and docs/templates.md §4 explains the reasoning rather than treating it as an oversight.

Why memory

An edit is a thousand small judgements and almost all of them repeat. How long to hold after a punchline. Whether this speaker's filler is noise or personality. How big captions have to be on a phone at arm's length. A stateless tool makes you re-supply that context every session, which is why "AI editing" so often produces something technically correct and tonally wrong.

Here, a decision you make once is recorded and reused:

subtitle.size_px = 74
  when {surface: vertical, frame: 1080x1920}
  set by  a render you shipped and kept, 2026-08-02
  before  66

That record lives in your repository as reviewable text. You can read the diff, correct a bad entry by editing a line, and git revert a change that made the edits worse. It is a behaviour changelog, kept in the same place as everything else you version.

Two rules keep it trustworthy:

  • Nothing durable is written by the model. A record describes something a human did: shipped a render and kept it, restored a moment the cut removed. The agent can point at what happened; it cannot compose what gets remembered.
  • Every write passes a human gate. No preference is learned silently.

That loop runs today. spd set, ship, keep, restore and discard append acts to a party's hash-chained ledger and stage a proposal from each one; an act cannot be appended to a chain that does not verify. spd review then asks for the value blind, showing the bounds and the shipped cut but never the number, and a matching answer becomes a sealed case and a regenerated findings.lock.txt. The next draft resolves against it: the bookmark opens the settings, the casebook overrides the ones a human settled, and the sheet records which lock it read.

Local first, and complete

A fresh clone with no API keys and no cloud account produces a finished, delivered file on your own machine. That is the baseline, not a degraded mode.

Cloud services can be switched on where they genuinely help, such as a hosted speech API for difficult audio or diarisation, but nothing becomes required and no deliverable depends on one. ffmpeg does the work as a child process. It is never vendored and never linked.

The project also refuses to guess about your hardware. Encoder support is proven by test-encoding rather than by reading a feature list, because feature lists lie. On the development machine ffmpeg -encoders advertises an NVIDIA encoder that fails at runtime, while the Intel one that actually works goes unmentioned in every guide.

What works

  • One analysis pass, two deliverables. Transcription and analysis run once per source. The long-form master and the vertical clips both read the same results.
  • Frame-accurate cutting with no audio drift. Audio stays PCM until the mux and is encoded once. The delivered samples are byte-identical to a reference assembly built in Python over 45 and 120 joins. Measured, not asserted.
  • Templates and motion in one call, with a beat grid the agent cuts against and a still fallback when the motion tier is absent.
  • Captions that stay legible. Size and contrast floors are enforced by the renderer, and text that would land under a platform's own interface is refused rather than drawn. Glyphs are shaped and rasterised by a pure-stdlib TrueType parser, so imprints are byte-reproducible and committed as goldens.
  • Vertical framing that admits uncertainty. When the subject cannot be tracked confidently it declines to auto-frame and says why. A confidently wrong crop is worse than an honest refusal, because nobody reviews the one that looked fine.
  • Rights that hold up. Music, effects and stock footage carry a record of where they came from and what the terms permit. A delivery refuses to run if any asset lacks one.
  • Typed refusals that carry their own correction. A refusal arrives with retry_with, a list of ready-to-send calls. There are 102 codes, each with a construction site and a test proving it is reachable.

What does not work

Stated plainly, because a status section that omits this is the reason the last one was worthless.

Not working Why Blocks
Cutting by quoting splicedeck/listen/fetchable.toml pins both download entries at a host that does not resolve, with placeholder all-zero digests. No route obtains the speech binary, including placing it by hand. hear, quote, captions from speech
Subject tracking by model No detector is pinned or shipped (docs/framing.md F5). The boundary is settled — a CLI child process, never an imported extension — but which binary fills it is not. watch --subject largest at the model tier
Cancelling from an MCP host The stdio loop is single-threaded, so nothing can arrive during a tools/call. cancel over MCP. The CLI and Ctrl-C are unaffected.

CHANGELOG.md carries the same list, and the two are meant to stay in sync.

Two tiers below the model one do work. subject: "centre" is geometric and needs nothing. SPD_SIGHT_LOCATOR=reduce selects a weights-free locator that finds the subject by temporal-median background subtraction in pure stdlib Python, no numpy and no compiled extension anywhere.

Checked against a face detector on the reference master, that locator's median agreed to within 0.1% of frame width. On the same footage it then reported certainty 0.26 and fitted no path at all, because a speaker who barely moves against a static background leaves nothing for background subtraction to hold onto. Both of those are the right answer: the arithmetic is sound, and the honest limit of a weights-free tier is a hole rather than a centred guess (docs/framing.md §7). Footage with a moving subject tracks fine.

How you drive it

Through an MCP server and Skills, so any MCP-capable assistant can use it, plus a CLI exposing exactly the same verbs. Both surfaces are generated from splicedeck/surface/verbs.py, and python -m checks.golden --check fails the build if they drift apart.

The server speaks five protocol revisions, 2024-11-05 through 2026-07-28, and answers both the initialize handshake and server/discover.

Failures are typed. A refusal carries its own correction as ready-to-send calls rather than prose an agent has to interpret, so recovery is one turn:

{"ok": false, "verb": "draft", "refused": "BOOKMARK_UNKNOWN",
 "plain": "No bookmark by that name is shipped.",
 "needs_human": false,
 "retry_with": [{"verb": "draft", "args": {"bookmark": "baseline", "party": "demo",
   "profile": "wide-1080", "situation": "default", "source": "s1"}}]}

Skills

Four skills teach an agent the verb order, the traps between verbs, and how to turn a refusal into the next correct call. They live in .claude/skills/, and a clone picks them up with no install at all.

Skill Fires when
cutting-a-deliverable Turning a source into a delivered file
cutting-vertical-clips Carving a 9:16 clip and keeping the subject in frame
recovering-from-a-refusal Any ok: false, or an spd command exiting 1
contributing-to-splicedeck Editing this codebase, or when two documents disagree

This repository is also a Claude Code plugin and its own marketplace:

claude plugin marketplace add ihuzaifashoukat/splicedeck
claude plugin install splicedeck@splicedeck

Or install the skills into any of the agents the skills CLI supports, including Codex, Cursor, OpenCode, Antigravity, Cline, Gemini CLI, Zed and Windsurf:

npx skills add ihuzaifashoukat/splicedeck            # add --list to look first

Both routes ship the skills only. They do not register the MCP server, because the server needs an absolute interpreter path and a cwd that neither a plugin nor a skill installer can know. install.sh writes that for you, and docs/mcp.md has it by hand.

Every other agent runtime reads AGENTS.md.

Design

The specification is written before the code, deliberately.

Document What it settles
AGENTS.md The contract every contributor and agent works under
docs/architecture.md The map: runtimes, packages, data flow
docs/first-run.md Clone to delivered file, and the Windows traps
docs/install.md Every install route, and a prompt for an AI agent
docs/mcp.md Driving splicedeck from an assistant
docs/cut-sheet.md The core artifact: integer-timed, diffable, human-readable
docs/templates.md Templates, slots, and what apply and compose do
docs/casebook.md How memory is stored, resolved and gated
docs/security.md The threat model, and why memory is an attack surface
docs/bookmarks.md Styles, and the axes they are points in
docs/agent-surface.md The verb table and the refusal catalogue
docs/roadmap.md The feature areas, and what each has to prove

Persistent memory in an agent is a security surface, not just a feature. Anything an attacker can write into it outlives the conversation that planted it. If you read one document, read docs/security.md.

Non-goals

Assembling a film from many sources. Generating video or music. A timeline GUI. Real-time collaboration. A hosted service. Automatically choosing which moments become clips, since it presents candidates and waits for a person.

Requirements

Python 3.12 or newer, and ffmpeg 8.x on your PATH. No compiled Python extension is used on any default path, so there is no build step and no platform runtime to install first. Windows, macOS and Linux; CI covers Ubuntu and Windows, and macOS is untested by machine.

The motion tier additionally needs Node and an npm install inside scenes/. It is optional, and a delivery without it falls back to still imprints.

Contributing

Issues and design critique are welcome. CONTRIBUTING.md is the front door: setup, the checks to run, how to add a verb or a refusal code, and the things that get a pull request rejected regardless of merit. Read AGENTS.md first. The twelve hard rules are load-bearing, and a change that breaks one is rejected on that basis alone.

By taking part you agree to the Code of Conduct.

Security

Please do not open a public issue for a vulnerability. SECURITY.md has the reporting route and what is in scope.

Licence

Apache-2.0. Copyright 2026 Huzaifa Shoukat.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured