Turn a screenshot into a cinematic promo.
An agent takes a screenshot or a screen recording, layers it, keys a move, captions it, and hands back a still, a GIF, or a video. Same compositor the Mac app ships. No GUI.
Rendered headless from ProductCard.promo — 1440×810 MP4, 30 fps. Still at 3 seconds below shows a 2.5D device slab. An agent validated the project, then called promo_render_video and promo_render_still.


The job
- Point an agent at a product screenshot or a screen recording — not at a blank timeline.
- It authors a
.promofolder: layers, motion, caption. A 2.5D device slab is optional — since 0.2.2 headless CLI/MCP bakes bezel, extruded depth, optional tilt, and contact shadow at stage time. Validate before render.okmeans it will render. - It writes a still to look, then a GIF or video to share. Paths come back, never bytes. Outputs land in the project's
Exports/.
Agents do not discover the GitHub repo by themselves. Install the skill, then the tools. Recipes that already ran:
Product card
Screenshot → 2.5D device slab + caption. Still at 3s and a looping video.
Footage first
Probe, filmstrip, silences, scene cuts — then compose. Some clips have no silence gaps.
Install the binaries
Latest release is v0.2.2. Each tarball is promo plus promoshot-mcp — put both on PATH or keep them side by side (--promo is optional when the CLI sits next to the server). Video needs ffmpeg / ffprobe. Linux also wants a Vulkan ICD (mesa-vulkan-drivers is enough).
# Linux x64
curl -fsSL -o promoshot.tar.gz \
https://github.com/GarAlex/promoshot/releases/download/v0.2.2/promoshot-v0.2.2-linux-x64.tar.gz
tar -xzf promoshot.tar.gz
install -m 0755 promo promoshot-mcp ~/.local/bin/
# macOS Apple silicon
curl -fsSL -o promoshot.tar.gz \
https://github.com/GarAlex/promoshot/releases/download/v0.2.2/promoshot-v0.2.2-macos-arm64.tar.gz
tar -xzf promoshot.tar.gz
install -m 0755 promo promoshot-mcp ~/.local/bin/Source and image: github.com/GarAlex/promoshot. Releases: github.com/GarAlex/promoshot/releases. Docker and a GPU-less Linux box: Linux & CI →
mcp.json
--workspace is where new projects go. --root fences which folders the server will touch. Pointing both at one directory is the tidy setup.
{
"mcpServers": {
"promoshot": {
"command": "~/.local/bin/promoshot-mcp",
"args": [
"--workspace", "/ABS/PATH/Promo",
"--root", "/ABS/PATH/Promo"
]
}
}
}# Claude Code
claude mcp add promoshot ~/.local/bin/promoshot-mcp
# Grok Build
grok mcp add promoshot -- ~/.local/bin/promoshot-mcp \
--workspace /ABS/PATH/Promo --root /ABS/PATH/Promo
grok inspectHandshake that actually ran — validate, then a still:
{
"jsonrpc": "2.0",
"id": 1,
"method": "initialize",
"params": { "protocolVersion": "2025-06-18" }
}
{ "jsonrpc": "2.0", "method": "notifications/initialized" }
{
"jsonrpc": "2.0",
"id": 2,
"method": "tools/call",
"params": {
"name": "promo_validate",
"arguments": { "project": "/ABS/PATH/Promo/ProductCard.promo" }
}
}
{
"jsonrpc": "2.0",
"id": 3,
"method": "tools/call",
"params": {
"name": "promo_render_still",
"arguments": { "project": "/ABS/PATH/Promo/ProductCard.promo", "time": 3.0 }
}
}Replies: ok — nothing the renderer would quietly correct, then a path under Exports/. Inspect lists layer ids (promo inspect /ABS/PATH/Project.promo --json — flag after the path).
Skill — agents will not find this alone
Same file everywhere: skill/SKILL.md in GarAlex/promoshot. Drift-tested against the server's tool list.
REPO=https://github.com/GarAlex/promoshot
git clone --depth 1 $REPO /tmp/promoshot
mkdir -p ~/.claude/skills/promoshot
cp /tmp/promoshot/skill/SKILL.md ~/.claude/skills/promoshot/SKILL.md
mkdir -p ~/.grok/skills/promoshot
cp /tmp/promoshot/skill/SKILL.md ~/.grok/skills/promoshot/SKILL.md
mkdir -p ~/.agents/skills/promoshot
cp /tmp/promoshot/skill/SKILL.md ~/.agents/skills/promoshot/SKILL.md
# Cursor: in the repo the person is editing, not the engine repo
mkdir -p .cursor/rules
cp /tmp/promoshot/skill/SKILL.md .cursor/rules/promoshot.mdstdio vs the Mac app
Headless promoshot-mcp is stdio JSON-RPC only — the client spawns the process; no port, no token. The Mac app is a separate HTTP MCP (Settings → Automation) that shares the core names and adds app-only abilities.
| Headless promoshot-mcp | Mac app MCP | |
|---|---|---|
| Transport | stdio JSON-RPC | HTTP from Settings → Automation |
| Tools | 18 — schema, validate, inspect, render, workspace, media, init, upsert, speak | Same core names plus promo_open (and app speech / Keychain) |
| Watermark | None. CLI overlay is None — the CLI is the oracle and renders clean | Free-tier renders through the app carry the PromoShot watermark |
| Fence | --root refuses projects outside the tree | Per-folder access; access_required until the person approves |
| Workspace | --workspace / $PROMOSHOT_WORKSPACE / XDG | promo_workspace is a pre-approved folder |
Want the GUI on this Mac? Download PromoShot. Want CI? Headless Linux →
Eighteen tools
Measured tools/list on v0.2.2 = 18. No MCP resources. The server owns no renderer — every validate / inspect / still / GIF / video shells out to promo.
| Tool | What it does |
|---|---|
| promo_schema | Authoring subset + four validated recipes. Read once before authoring. |
| promo_schema_full | Whole .promo format (same compiled-in file as the parser). |
| promo_schema_types | Types-only JSON Schema generated from the parser structs. |
| promo_validate | Renderer's own parser. ok means it will render. Optional preview → Exports/preview.png. |
| promo_inspect | Canvas, duration, layers with ids, undefined colours, missing media. |
| promo_render_still | One PNG at a time. Returns the path. Default Exports/still-<time>s.png. |
| promo_render_frames | PNG per frame over a range — a contact sheet before a full render. |
| promo_render_video | mp4, audio mixed. Needs ffmpeg. Default Exports/export.mp4. |
| promo_render_gif | Looping GIF, no ffmpeg. Default 12 fps, Exports/export.gif. |
| promo_workspace | Folder for new assistant-authored projects. |
| promo_media_probe | Distilled ffprobe: container, duration, streams. |
| promo_media_filmstrip | Contact sheet of a source clip; sampled times per cell. Default 12 frames, max 48. |
| promo_media_silences | Silence spans and their inverse (sound spans). Default −35 dB, min 0.35 s. |
| promo_media_scenes | Scene-change cuts and the shots between them. Default threshold 0.4. Use this when a clip has no silence gaps. |
| promo_init | Create a .promo folder (canvas, palette, bg layer). Never overwrites. |
| promo_upsert_layer | Scaffold image / video / caption with placement, fadeIn, device frame. Since 0.2.2, headless bakes the 2.5D slab (bezel, extruded depth, optional tilt, contact shadow) at stage time. |
| promo_upsert_keyframe | Create or merge one keyframe — a second placement is a push-in, viewport is a Ken Burns ride. |
| promo_speak | TTS for speech.text using the person's own key. Without a key, drop a voice file into Resources/. |
App-only (not in this binary): promo_open.
Mac GUI, or headless from here.
The app is the editor. This MCP is how an agent ships the same pixels.