@amg555/frameforge-mcp 2.3.5 → 2.3.7
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +47 -0
- package/README.md +7 -5
- package/package.json +3 -3
- package/plugins/video-pipeline/skills/video-pipeline/SKILL.md +70 -14
- package/scripts/deep-validate.js +5 -3
- package/src/cli/make.js +6 -0
- package/src/mcp-server/server.js +166 -14
- package/src/video-editor/editor.js +852 -0
- package/src/video-renderer/renderer.js +129 -31
- package/src/video-renderer/scene-animator.js +1033 -251
- package/src/worker/server-render-free.js +5 -0
- package/src/worker/server.js +6 -0
- package/src/workflows/assets.js +25 -3
- package/src/workflows/render.js +26 -3
- package/src/workflows/script.js +2 -0
package/CHANGELOG.md
CHANGED
|
@@ -4,6 +4,53 @@ All notable changes to FrameForge are documented here.
|
|
|
4
4
|
|
|
5
5
|
---
|
|
6
6
|
|
|
7
|
+
## [2.3.7] — 2026-11-09
|
|
8
|
+
|
|
9
|
+
### Added
|
|
10
|
+
- **Autonomous Local Video Editor (`src/video-editor/editor.js`)**:
|
|
11
|
+
- Full-featured local FFmpeg video editing engine for autonomous agent workflows (inspired by `video-agent-skill`).
|
|
12
|
+
- `probeMedia`: `ffprobe` JSON parsing for duration, dimensions, fps, streams, codecs, and channels.
|
|
13
|
+
- `parseTimestamp`: Flexible timestamp parsing supporting numeric seconds, `"01:23.45"`, `"83.45s"`, `"01:02:03"`.
|
|
14
|
+
- `editClip`: Frame-accurate trimming, speed ramping (`0.25x` to `4.0x` with chained `atempo` pitch preservation), aspect ratio conforming (`16:9` landscape, `9:16` portrait, `1:1` square), color grading, and HarfBuzz captions.
|
|
15
|
+
- Cinematic color grading presets: `cinematic`, `warm`, `cool`, `vintage`, `bw`, `vibrant`, and `cyberpunk`.
|
|
16
|
+
- `concatClipsWithTransitions`: Seamless multi-clip transitions using FFmpeg `xfade` (`fade`, `dissolve`, `wipeleft`, `wiperight`, `slideup`, `slidedown`, `circleopen`, `smoothleft`) and `acrossfade`.
|
|
17
|
+
- `editAndAssemble`: End-to-end autonomous assembly with ambient clip audio volume, master voiceover overlay, and ducked background music with LUFS normalisation.
|
|
18
|
+
- Auto-fit mode: Automatically calculates clip durations so video length perfectly matches master voiceover length.
|
|
19
|
+
- `applyOperations`: Imperative operation dispatcher supporting `cut`, `trim`, `speed`, `color`, `caption`, `mute`, `transition`, and `assemble`.
|
|
20
|
+
- **`edit_video` MCP Tool (`src/mcp-server/server.js`)**:
|
|
21
|
+
- Exposes dedicated autonomous video editor tool to Claude Code, Cursor, Windsurf, Bob, and Codex.
|
|
22
|
+
- Local fallback in `external_platform_call` for `action === 'edit'`: runs local `VideoEditor` instead of erroring out when no external platform is configured.
|
|
23
|
+
- Enhanced `create_video_from_script` and `VideoRenderer.renderScene()` to support `startTime`, `endTime`, `speed`, `clipVolume`, and `colorGrade` for user clips (`video_path`).
|
|
24
|
+
- **Worker & Remote MCP Integration**:
|
|
25
|
+
- Registered `edit_video` in Cloudflare Remote MCP (`cloudflare-mcp/src/index.js`).
|
|
26
|
+
- Added `edit_video` job execution in background worker servers (`src/worker/server.js`, `src/worker/server-render-free.js`).
|
|
27
|
+
|
|
28
|
+
---
|
|
29
|
+
|
|
30
|
+
## [2.3.6] — 2026-11-09
|
|
31
|
+
|
|
32
|
+
### Added
|
|
33
|
+
- **Full-Canvas Cinematic Procedural Entities** (`src/video-renderer/scene-animator.js`):
|
|
34
|
+
- Completely eliminated the rigid PPT card slideshow pattern. Every scene now renders a living, full-canvas animated universe.
|
|
35
|
+
- 3D Celestial Orbital Simulation: Earth with rotating longitude/latitude wireframes, tilted elliptical orbits, orbiting satellites with solar panels, expanding spherical radio wavefront pulses, and ground target beacon.
|
|
36
|
+
- 3D Rotating DNA Double-Helix: Sinusoidal backbones with perspective depth, nucleotide base-pair rungs, and molecular spore drift.
|
|
37
|
+
- Full-Canvas Financial Candlesticks: Realistic Japanese candlesticks with upper/lower wicks, glowing Exponential Moving Average (EMA) ribbon curve, volume bars, and price tickers.
|
|
38
|
+
- 3D Tactical Radar Globe: Rotating spherical grid with 360-degree radar sweep sector and pinging incident coordinates.
|
|
39
|
+
- 3D Wireframe Polyhedra: Pure mathematical 3D perspective projection (octahedron/cubes) with coordinate axes.
|
|
40
|
+
- Historical Milestone Roadmaps: Flowing chronological paths with golden era seals and progressive illumination pulses.
|
|
41
|
+
- Full-Canvas Neural Mesh: 4-tier neural network with glowing energy packets traveling along active synapses.
|
|
42
|
+
- **Direct Agent Code & HTML Execution** (`src/video-renderer/renderer.js`, `src/mcp-server/server.js`, `src/workflows/render.js`, `src/workflows/assets.js`):
|
|
43
|
+
- Added support for `html` (complete page conforming to `window.DURATION` and `window.seek(t)`) and `code` (canvas drawing instructions for `t`) across `create_video_from_script`, `render_from_scenes`, and workflow pipelines.
|
|
44
|
+
- Enables Claude Code, Opus 5.5, Cursor, and IBM Bob to write custom animation programs that FrameForge renders directly without template overrides.
|
|
45
|
+
- Added `wrapCodeInHarnessHtml()` helper to embed pure Canvas drawing code into a compliant virtual-clock harness page.
|
|
46
|
+
- **Cinematic Broadcast Lower-Third Overlays**:
|
|
47
|
+
- Replaced repetitive center-card text with glassmorphic lower-third broadcast banners leaving the upper 75% of the frame completely open for motion graphics.
|
|
48
|
+
- Accent color left tab, tag pills, high-contrast drop-shadow typography, and subtle bottom progress bar.
|
|
49
|
+
- **Multilingual Typography Integration**:
|
|
50
|
+
- Full native DirectWrite/HarfBuzz font support for Malayalam (`Nirmala UI`, `Noto Sans Malayalam`), Hindi/Devanagari, Tamil, Telugu, Arabic, and CJK with zero broken glyphs.
|
|
51
|
+
|
|
52
|
+
---
|
|
53
|
+
|
|
7
54
|
## [2.3.5] — 2026-11-09
|
|
8
55
|
|
|
9
56
|
### Added
|
package/README.md
CHANGED
|
@@ -9,7 +9,7 @@
|
|
|
9
9
|
[](https://www.npmjs.com/package/@amg555/frameforge-mcp)
|
|
10
10
|
[](LICENSE)
|
|
11
11
|
[](https://nodejs.org/)
|
|
12
|
-
[](CHANGELOG.md)
|
|
13
13
|
[](https://modelcontextprotocol.io/)
|
|
14
14
|
[](#testing)
|
|
15
15
|
|
|
@@ -52,7 +52,7 @@ Add to your IDE's MCP config (works in Bob, Cursor, Windsurf, Codex, Claude Code
|
|
|
52
52
|
}
|
|
53
53
|
```
|
|
54
54
|
|
|
55
|
-
That's it. Your agent now has
|
|
55
|
+
That's it. Your agent now has 19 video production & autonomous editing tools. FFmpeg and TTS run on your machine — no cloud render costs.
|
|
56
56
|
|
|
57
57
|
---
|
|
58
58
|
|
|
@@ -174,13 +174,14 @@ Any combination of `image_path`, `video_path`, `audio_path` works — FrameForge
|
|
|
174
174
|
|
|
175
175
|
## Tools
|
|
176
176
|
|
|
177
|
-
|
|
177
|
+
19 tools, grouped by what you're doing:
|
|
178
178
|
|
|
179
179
|
**Make a video from your own script (no LLM key)**
|
|
180
180
|
|
|
181
181
|
| Tool | What it does |
|
|
182
182
|
|------|-------------|
|
|
183
|
-
| `create_video_from_script` | Full render from scenes you provide — voice,
|
|
183
|
+
| `create_video_from_script` | Full render from scenes you provide — voice, procedural motion graphics / custom code, FFmpeg, quality gate |
|
|
184
|
+
| `edit_video` | Autonomous local video editor — cut, trim, speed-ramp, color-grade, reframe (16:9 / 9:16 / 1:1), add captions, apply xfade transitions, and mix audio |
|
|
184
185
|
| `ingest_media` | Import your own clips, images, and audio into a scenes manifest |
|
|
185
186
|
| `edit_scenes` | Update captions, swap media files, or append scenes to a manifest |
|
|
186
187
|
| `render_from_scenes` | Re-render from an existing manifest, skipping research and scripting |
|
|
@@ -189,7 +190,8 @@ Any combination of `image_path`, `video_path`, `audio_path` works — FrameForge
|
|
|
189
190
|
| `multi_voice_tts` | Override TTS provider per call without touching `.env` |
|
|
190
191
|
| `generate_scene_image` | Generate one scene image via the configured image provider |
|
|
191
192
|
| `publish_video` | Deliver the finished video via file copy or HTTP webhook |
|
|
192
|
-
| `external_platform_call` |
|
|
193
|
+
| `external_platform_call` | Bridge to ElevenLabs, Runway, VoxForge, or local FFmpeg editor fallback |
|
|
194
|
+
| `export_to_editor` | Bridge/import into DaVinci Resolve, Premiere Pro, After Effects, Remotion, or CapCut |
|
|
193
195
|
|
|
194
196
|
**Auto-research pipeline (needs a free LLM key)**
|
|
195
197
|
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@amg555/frameforge-mcp",
|
|
3
|
-
"version": "2.3.
|
|
4
|
-
"description": "FrameForge — AI video production engine for agentic IDEs. npx @amg555/frameforge-mcp. FFmpeg + TTS run on your machine.
|
|
3
|
+
"version": "2.3.7",
|
|
4
|
+
"description": "FrameForge — AI video production engine for agentic IDEs. npx @amg555/frameforge-mcp. FFmpeg + TTS run on your machine. 19 MCP tools.",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"start:local-render": "cross-env RENDER_MODE=full WORKER_PORT=3211 node src/worker/server-render-free.js",
|
|
31
31
|
"db:migrate": "node scripts/db-migrate.js",
|
|
32
32
|
"validate": "node scripts/deep-validate.js",
|
|
33
|
-
"test": "node --test src/pipeline/retry-policy.test.js src/pipeline/telemetry.test.js src/pipeline/artifact-manifest.test.js src/pipeline/artifact-retention.test.js src/pipeline/external-adapter.test.js src/workflows/publishing.test.js src/workflows/script.test.js src/animation/animation-system.test.js src/fact-checker/review-gate.test.js src/fact-checker/checker.test.js src/utils/log-redaction.test.js src/worker/server-render-free.test.js src/worker/server.test.js src/mcp-server/server.test.js src/quality-gate/gate.test.js src/shorts-extractor/extractor.test.js src/audio-mixer/mixer.test.js src/video-renderer/scene-animator.test.js src/video-renderer/renderer.test.js plugins/video-pipeline/skills/video-pipeline/assets/project/reel/engine.test.js src/harness/lib/pixels.test.js src/harness/lib/verify-purity.test.js src/harness/harness.test.js src/integration.test.js",
|
|
33
|
+
"test": "node --test src/pipeline/retry-policy.test.js src/pipeline/telemetry.test.js src/pipeline/artifact-manifest.test.js src/pipeline/artifact-retention.test.js src/pipeline/external-adapter.test.js src/workflows/publishing.test.js src/workflows/script.test.js src/animation/animation-system.test.js src/fact-checker/review-gate.test.js src/fact-checker/checker.test.js src/utils/log-redaction.test.js src/worker/server-render-free.test.js src/worker/server.test.js src/mcp-server/server.test.js src/quality-gate/gate.test.js src/shorts-extractor/extractor.test.js src/audio-mixer/mixer.test.js src/video-renderer/scene-animator.test.js src/video-renderer/renderer.test.js src/video-editor/editor.test.js plugins/video-pipeline/skills/video-pipeline/assets/project/reel/engine.test.js src/harness/lib/pixels.test.js src/harness/lib/verify-purity.test.js src/harness/harness.test.js src/integration.test.js",
|
|
34
34
|
"lint": "echo 'No linter configured — add eslint for production'",
|
|
35
35
|
"check-credentials": "node src/utils/log-redaction.js",
|
|
36
36
|
"generate-assets": "node scripts/generate-initial-assets.js",
|
|
@@ -26,11 +26,11 @@ IDE Agent (IBM Bob / Codex / opencode / Cursor / Windsurf / Claude Code)
|
|
|
26
26
|
│
|
|
27
27
|
▼ Cloudflare Remote MCP (recommended) ─────────────────────────────
|
|
28
28
|
│ https://video-mcp.YOUR_SUBDOMAIN.workers.dev/mcp
|
|
29
|
-
│
|
|
29
|
+
│ 19 tools: create_video_from_script · edit_video · render_from_scenes
|
|
30
30
|
│ ingest_media · edit_scenes · render_tts · multi_voice_tts
|
|
31
31
|
│ generate_scene_image · assemble_video · publish_video
|
|
32
32
|
│ research_topic · write_script · external_platform_call
|
|
33
|
-
│ queue_full_pipeline · get_job_status
|
|
33
|
+
│ queue_full_pipeline · get_job_status · export_to_editor
|
|
34
34
|
│ scaffold_animation_project · validate_storyboard · render_animation
|
|
35
35
|
│
|
|
36
36
|
▼ OR — Local stdio / HTTP MCP ────────────────────────────────────
|
|
@@ -137,21 +137,36 @@ npm run setup
|
|
|
137
137
|
|
|
138
138
|
## Workflow (step-by-step)
|
|
139
139
|
|
|
140
|
-
### Path 1 — Agent-First (
|
|
140
|
+
### Path 1 — Agent-First (Code-Driven Motion Graphics or Direct Custom Code)
|
|
141
141
|
|
|
142
|
-
**You are already running an LLM
|
|
143
|
-
them to FrameForge for rendering. No
|
|
144
|
-
FrameForge
|
|
142
|
+
**You are already running an LLM (Claude Code / Opus 5.5 / Cursor / Bob) — use it.**
|
|
143
|
+
Write the scenes yourself and hand them to FrameForge for rendering. No external API key needed.
|
|
144
|
+
FrameForge renders full-canvas, broadcast-quality motion graphics (24fps pure function of `t`) with zero slideshow/card patterns:
|
|
145
145
|
|
|
146
|
-
|
|
146
|
+
- **Option A — Natural Description:** Provide `visual_description` (e.g. "Earth rotating with 4 satellite orbits and microwave beams intersecting"). FrameForge automatically synthesizes a full-canvas procedural 3D/2D animation entity (orbital mechanics, 3D DNA helix, candlestick charts, neural mesh, 3D wireframe polyhedra).
|
|
147
|
+
- **Option B — Direct Agent Code:** Provide `html` (complete page with `window.DURATION` & `window.seek(t)`) or `code` (canvas drawing instructions for `t`). FrameForge executes your program directly via headless Chromium.
|
|
148
|
+
- **Option C — Multilingual & Regional Scripts:** Pass `language: "ml"` (Malayalam), `"hi"` (Hindi), etc. Native DirectWrite/HarfBuzz font stacks (`Nirmala UI`, `Noto Sans Malayalam`) render conjuncts without broken glyphs.
|
|
149
|
+
|
|
150
|
+
```js
|
|
147
151
|
create_video_from_script({
|
|
152
|
+
topic: "GPS Satellites and Orbital Mechanics",
|
|
153
|
+
style: "cosmic_space",
|
|
154
|
+
language: "en",
|
|
148
155
|
scenes: [
|
|
149
|
-
{
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
visual_description: "
|
|
156
|
+
{
|
|
157
|
+
narration: "GPS uses trilateration from atomic clock satellites orbiting Earth.",
|
|
158
|
+
caption: "TRILATERATION",
|
|
159
|
+
visual_description: "3D Earth rotating with 4 satellite orbits and microwave beams converging",
|
|
160
|
+
// Optional: you can pass your own Canvas code or complete HTML:
|
|
161
|
+
// code: "ctx.fillStyle = '#0c1116'; ctx.fillRect(0,0,W,H); ..."
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
narration: "Relativistic time dilation requires nanosecond daily corrections.",
|
|
165
|
+
caption: "TIME DILATION",
|
|
166
|
+
visual_description: "Quantum atomic clock oscillation with compounding nanosecond curve"
|
|
167
|
+
}
|
|
153
168
|
],
|
|
154
|
-
title: "How
|
|
169
|
+
title: "How GPS Actually Works"
|
|
155
170
|
})
|
|
156
171
|
```
|
|
157
172
|
|
|
@@ -160,8 +175,6 @@ Then deliver it:
|
|
|
160
175
|
publish_video({ productionId, masterPath, channels: ["file"] })
|
|
161
176
|
```
|
|
162
177
|
|
|
163
|
-
> **Image generation fallback:** If the configured image provider is unavailable, FrameForge automatically falls back to a dark-background placeholder per scene — the render completes. Switch to `PROVIDER_IMAGE=falai` for a more reliable free provider.
|
|
164
|
-
|
|
165
178
|
### Path 2 — Code-Driven Animated Video (Playwright + ffmpeg, no AI images)
|
|
166
179
|
|
|
167
180
|
Use when: the user wants Remotion-style motion graphics, image gen is unavailable, or they ask for "animated video" or "make it look good". Every pixel is a pure function of time `t`.
|
|
@@ -216,6 +229,49 @@ for research and scripting, so `PROVIDER_LLM` + the matching API key are require
|
|
|
216
229
|
4. publish_video({ productionId, masterPath, channels: ["file"] })
|
|
217
230
|
```
|
|
218
231
|
|
|
232
|
+
### Path 4 — Autonomous Local Video Editing (`edit_video`)
|
|
233
|
+
|
|
234
|
+
When the user provides video clips (B-roll, screen recordings, camera footage) and audio/narration, FrameForge autonomously cuts, trims, color-grades, reframes, captions, and stitches them with seamless transitions locally:
|
|
235
|
+
|
|
236
|
+
```js
|
|
237
|
+
edit_video({
|
|
238
|
+
clips: [
|
|
239
|
+
{
|
|
240
|
+
video_path: "/path/to/clip1.mp4",
|
|
241
|
+
start_time: "00:05.5",
|
|
242
|
+
end_time: "00:15.0",
|
|
243
|
+
speed: 1.0,
|
|
244
|
+
color_grade: "cinematic",
|
|
245
|
+
caption: "AUTONOMOUS EDITING",
|
|
246
|
+
transition: "dissolve",
|
|
247
|
+
transition_duration: 0.5
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
video_path: "/path/to/clip2.mp4",
|
|
251
|
+
start_time: 0,
|
|
252
|
+
duration: 6.0,
|
|
253
|
+
speed: 0.8, // subtle slow-mo
|
|
254
|
+
color_grade: "cinematic",
|
|
255
|
+
caption: "SEAMLESS TRANSITIONS",
|
|
256
|
+
clip_volume: 0.3 // duck ambient sound
|
|
257
|
+
}
|
|
258
|
+
],
|
|
259
|
+
audio_path: "/path/to/master_voiceover.m4a",
|
|
260
|
+
music_path: "/path/to/background_track.mp3",
|
|
261
|
+
music_volume: 0.07,
|
|
262
|
+
orientation: "landscape", // or "portrait" for Reels / TikTok / Shorts
|
|
263
|
+
auto_fit: true // automatically slice clips to fit total voiceover duration
|
|
264
|
+
})
|
|
265
|
+
```
|
|
266
|
+
|
|
267
|
+
Supported features:
|
|
268
|
+
- **Trimming & Cutting:** Accurate timestamp parsing (`"01:23.45"`, `"83.45s"`, numeric seconds).
|
|
269
|
+
- **Speed Ramping:** `0.25x` to `4.0x` with chained `atempo` filters preserving pitch.
|
|
270
|
+
- **Cinematic Color Grading:** `cinematic`, `warm`, `cool`, `vintage`, `bw`, `vibrant`, `cyberpunk`.
|
|
271
|
+
- **Canvas Reframe:** `landscape` (1920×1080), `portrait` (1080×1920), `square` (1080×1080).
|
|
272
|
+
- **Transitions:** `fade`, `dissolve`, `wipeleft`, `wiperight`, `slideup`, `slidedown`, `circleopen`, `smoothleft`.
|
|
273
|
+
- **Audio Mixing & Ducking:** Original ambient audio level control, master voice track, background music with sidechain ducking and LUFS normalisation.
|
|
274
|
+
|
|
219
275
|
### Voice Synthesis (per call)
|
|
220
276
|
|
|
221
277
|
```
|
package/scripts/deep-validate.js
CHANGED
|
@@ -20,9 +20,11 @@ check('batch handling', code.includes('Array.isArray(body)'));
|
|
|
20
20
|
check('ping handler', code.includes("method === 'ping'"));
|
|
21
21
|
check('notifications/init', code.includes("notifications/initialized"));
|
|
22
22
|
check('body size limit', code.includes('MAX_BODY_BYTES'));
|
|
23
|
-
check('SERVER_INFO version 2.3.
|
|
23
|
+
check('SERVER_INFO version 2.3.7', code.includes("version: '2.3.7'"));
|
|
24
24
|
check('18 tools defined (incl get_job_status)', code.includes('get_job_status'));
|
|
25
25
|
check('export_to_editor tool defined', code.includes('export_to_editor'));
|
|
26
|
+
check('edit_video tool defined', code.includes('edit_video'));
|
|
27
|
+
check('video-editor exists', fs.existsSync('./src/video-editor/editor.js'));
|
|
26
28
|
check('voxforge TTS provider', code.includes('voxforge'));
|
|
27
29
|
|
|
28
30
|
// ── No execSync with shell strings in renderer or extractor ───────────────
|
|
@@ -205,7 +207,7 @@ check('render-free: default channel is file', renderFreeJs.includes("
|
|
|
205
207
|
// ── server.js — queue_full_pipeline + tool completeness ──────────────────
|
|
206
208
|
console.log('\n=== server.js tool completeness ===');
|
|
207
209
|
const serverjs = fs.readFileSync('./src/mcp-server/server.js', 'utf8');
|
|
208
|
-
check('server.js exposes
|
|
210
|
+
check('server.js exposes tools (header)', serverjs.includes('video production'));
|
|
209
211
|
check('server.js publish_video calls runPublishing', serverjs.includes("require('../workflows/publishing')"));
|
|
210
212
|
check('server.js has queue_full_pipeline tool', serverjs.includes('queue_full_pipeline'));
|
|
211
213
|
check('server.js has get_job_status tool', serverjs.includes('get_job_status'));
|
|
@@ -293,7 +295,7 @@ check('cf-worker has checkRateLimit', cfWorker.includes('checkRateLimit'));
|
|
|
293
295
|
check('cf-worker hashes token (SHA-256)', cfWorker.includes('SHA-256'));
|
|
294
296
|
check('cf-worker uses RATE_LIMITS KV', cfWorker.includes('env.RATE_LIMITS'));
|
|
295
297
|
check('cf-worker returns 429 on limit', cfWorker.includes('429'));
|
|
296
|
-
check('cf-worker version 2.3.
|
|
298
|
+
check('cf-worker version 2.3.7', cfWorker.includes("'2.3.7'"));
|
|
297
299
|
check('wrangler.toml has kv_namespaces',fs.readFileSync('./cloudflare-mcp/wrangler.toml','utf8').includes('kv_namespaces'));
|
|
298
300
|
|
|
299
301
|
// ── plugins/skills present ────────────────────────────────────────────────
|
package/src/cli/make.js
CHANGED
|
@@ -410,6 +410,9 @@ async function runSingleVideo(args, topic, outDir, workflows) {
|
|
|
410
410
|
skipImages,
|
|
411
411
|
skipTts: args.noTts,
|
|
412
412
|
enableAiImages,
|
|
413
|
+
topic,
|
|
414
|
+
title,
|
|
415
|
+
style: args.style || '',
|
|
413
416
|
});
|
|
414
417
|
log('Assets', `✅ ${assetsResult.sceneCount} scenes prepared`);
|
|
415
418
|
|
|
@@ -418,6 +421,9 @@ async function runSingleVideo(args, topic, outDir, workflows) {
|
|
|
418
421
|
const renderResult = await runRender({
|
|
419
422
|
productionId,
|
|
420
423
|
sceneAssets: assetsResult.sceneAssets,
|
|
424
|
+
topic,
|
|
425
|
+
title,
|
|
426
|
+
style: args.style || '',
|
|
421
427
|
});
|
|
422
428
|
if (!renderResult.passed) {
|
|
423
429
|
throw new Error(`Quality gate FAILED: ${JSON.stringify(renderResult.qcReport?.checks?.filter(c => !c.passed).map(c => c.name))}`);
|
package/src/mcp-server/server.js
CHANGED
|
@@ -1,13 +1,14 @@
|
|
|
1
1
|
/**
|
|
2
2
|
* FrameForge MCP Server — MCP 2024-11-05 spec compliant
|
|
3
3
|
*
|
|
4
|
-
* Exposes
|
|
5
|
-
* Works with IBM Bob, Cursor, Windsurf, Codex, opencode, or any MCP-capable IDE.
|
|
4
|
+
* Exposes 19 video production & autonomous editing tools via MCP.
|
|
5
|
+
* Works with IBM Bob, Cursor, Windsurf, Codex, opencode, Claude Code, or any MCP-capable IDE.
|
|
6
6
|
* Primary connection: Cloudflare Remote MCP (cloudflare-mcp/).
|
|
7
7
|
* Alternative: stdio (src/mcp-server/stdio.js) or HTTP (this file).
|
|
8
8
|
*
|
|
9
9
|
* Primary path for agentic IDEs:
|
|
10
10
|
* create_video_from_script — IDE writes scenes, FrameForge renders (no internal LLM)
|
|
11
|
+
* edit_video — Autonomous local video editor (cut, trim, speed, grade, transition, mix)
|
|
11
12
|
* multi_voice_tts — per-call TTS platform override
|
|
12
13
|
*
|
|
13
14
|
* Full pipeline path (FrameForge does the research + scripting):
|
|
@@ -15,7 +16,7 @@
|
|
|
15
16
|
* assemble_video, publish_video
|
|
16
17
|
*
|
|
17
18
|
* Edit path:
|
|
18
|
-
* ingest_media, edit_scenes, render_from_scenes, external_platform_call
|
|
19
|
+
* edit_video, ingest_media, edit_scenes, render_from_scenes, external_platform_call, export_to_editor
|
|
19
20
|
*
|
|
20
21
|
* Start: node src/mcp-server/server.js (or: npm start)
|
|
21
22
|
* Port: MCP_PORT env (default 3210)
|
|
@@ -349,9 +350,14 @@ const TOOLS = {
|
|
|
349
350
|
skipTts: { type: 'boolean', default: false, description: 'Skip TTS synthesis (use audio_path in scenes)' },
|
|
350
351
|
withMusic: { type: 'boolean', default: true },
|
|
351
352
|
musicVolume: { type: 'number', default: 0.07 },
|
|
353
|
+
topic: { type: 'string', description: 'Overall topic/genre for visual theme and aesthetic selection' },
|
|
354
|
+
style: { type: 'string', description: 'Visual style/theme override (e.g. cinematic_documentary, finance_executive, cyber_tech, retro_synthwave, nature_organic, hype_sports, minimal_luxury, breaking_news, cosmic_space)' },
|
|
355
|
+
theme: { type: 'string', description: 'Theme preset alias' },
|
|
356
|
+
title: { type: 'string', description: 'Video title' },
|
|
357
|
+
language: { type: 'string', description: 'BCP-47 language hint (e.g. ml, hi, en, ta)' },
|
|
352
358
|
},
|
|
353
359
|
},
|
|
354
|
-
handler: async ({ productionId, scenes, orientation = 'landscape', skipImages = false, skipTts = false, withMusic = true, musicVolume = 0.07 }) => {
|
|
360
|
+
handler: async ({ productionId, scenes, orientation = 'landscape', skipImages = false, skipTts = false, withMusic = true, musicVolume = 0.07, topic = '', style = '', theme = '', title = '', language = '' }) => {
|
|
355
361
|
const syncFs = require('fs');
|
|
356
362
|
const { runAssets } = require('../workflows/assets');
|
|
357
363
|
const { runRender } = require('../workflows/render');
|
|
@@ -392,6 +398,11 @@ const TOOLS = {
|
|
|
392
398
|
parallelism: 4,
|
|
393
399
|
skipImages,
|
|
394
400
|
skipTts,
|
|
401
|
+
topic,
|
|
402
|
+
style,
|
|
403
|
+
theme,
|
|
404
|
+
title,
|
|
405
|
+
language,
|
|
395
406
|
imageWidth: renderWidth,
|
|
396
407
|
imageHeight: renderHeight,
|
|
397
408
|
});
|
|
@@ -404,6 +415,11 @@ const TOOLS = {
|
|
|
404
415
|
musicVolume,
|
|
405
416
|
renderWidth,
|
|
406
417
|
renderHeight,
|
|
418
|
+
topic,
|
|
419
|
+
style,
|
|
420
|
+
theme,
|
|
421
|
+
title,
|
|
422
|
+
language,
|
|
407
423
|
});
|
|
408
424
|
|
|
409
425
|
if (!renderResult.passed) {
|
|
@@ -467,8 +483,8 @@ Named presets (set EXTERNAL_PLATFORM_NAME to one of these and omit platformUrl):
|
|
|
467
483
|
|
|
468
484
|
const adapter = new ExternalAdapter(adapterConfig);
|
|
469
485
|
|
|
470
|
-
// If no URL was provided via param or env, give
|
|
471
|
-
if (!adapter.isConfigured()) {
|
|
486
|
+
// If no URL was provided via param or env and action !== 'edit', give clear setup message
|
|
487
|
+
if (!adapter.isConfigured() && action !== 'edit') {
|
|
472
488
|
throw new Error(
|
|
473
489
|
'No platform URL configured. Either:\n' +
|
|
474
490
|
' • Pass platformUrl: "http://localhost:8080" (connects any custom app immediately)\n' +
|
|
@@ -485,12 +501,28 @@ Named presets (set EXTERNAL_PLATFORM_NAME to one of these and omit platformUrl):
|
|
|
485
501
|
// Validate operations
|
|
486
502
|
const ops = operations || [];
|
|
487
503
|
if (ops.length === 0) throw new Error('action=edit requires at least one operation in the operations array');
|
|
488
|
-
const validOps = ['cut','trim','transition','color','caption','mute','speed','swap'];
|
|
504
|
+
const validOps = ['cut','trim','transition','color','caption','mute','speed','swap','assemble'];
|
|
489
505
|
for (const [i, o] of ops.entries()) {
|
|
490
506
|
if (!o.op) throw new Error(`operations[${i}] missing "op" field`);
|
|
491
507
|
if (!validOps.includes(o.op)) throw new Error(`operations[${i}].op "${o.op}" not valid. Use: ${validOps.join(', ')}`);
|
|
492
508
|
}
|
|
493
|
-
|
|
509
|
+
|
|
510
|
+
if (adapter.isConfigured()) {
|
|
511
|
+
return adapter.renderScenes({ scenes: [], productionId: 'edit', options: { operations: ops, ...(metadata || {}) } });
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
// Fallback: Local autonomous VideoEditor execution
|
|
515
|
+
const { VideoEditor } = require('../video-editor/editor');
|
|
516
|
+
const localEditor = new VideoEditor({ filesDir: FILES_DIR });
|
|
517
|
+
return localEditor.applyOperations({
|
|
518
|
+
operations: ops,
|
|
519
|
+
files: files || [],
|
|
520
|
+
outputPath,
|
|
521
|
+
orientation: metadata?.orientation || 'landscape',
|
|
522
|
+
musicPath: metadata?.musicPath || metadata?.music_path,
|
|
523
|
+
musicVolume: metadata?.musicVolume != null ? metadata?.musicVolume : 0.08,
|
|
524
|
+
audioPath: metadata?.audioPath || metadata?.audio_path,
|
|
525
|
+
});
|
|
494
526
|
}
|
|
495
527
|
|
|
496
528
|
case 'render':
|
|
@@ -518,8 +550,9 @@ Named presets (set EXTERNAL_PLATFORM_NAME to one of these and omit platformUrl):
|
|
|
518
550
|
description: `Agent-first video creation. YOU (the IDE agent) provide the complete scenes array — FrameForge renders it.
|
|
519
551
|
Features:
|
|
520
552
|
- Code-driven animation and kinetic typography by default (broadcast quality, zero AI artifacts).
|
|
553
|
+
- Supports direct agent/user code: pass "html" (complete page with window.seek(t)) or "code" (canvas drawing instructions for t).
|
|
554
|
+
- Procedural full-canvas cinematic animations (celestial orbits, DNA helices, candlestick charts, neural nets, 3D wireframe geometry).
|
|
521
555
|
- Supports manual user clips (video_path) and manual images (image_path).
|
|
522
|
-
- Optional AI diffusion generation via enableAiImages: true.
|
|
523
556
|
- No internal LLM or research required. Primary path for Claude Code, IBM Bob, Cursor, and any agentic IDE.
|
|
524
557
|
|
|
525
558
|
Example scene:
|
|
@@ -527,6 +560,8 @@ Example scene:
|
|
|
527
560
|
"narration": "The text spoken aloud in this scene",
|
|
528
561
|
"caption": "OVERLAY TEXT",
|
|
529
562
|
"visual_description": "Description of the scene visual / animation",
|
|
563
|
+
"html": "<!doctype html>... window.seek = async (t) => { ... }",
|
|
564
|
+
"code": "/* canvas drawing code for t */",
|
|
530
565
|
"camera_hint": "zoom_in",
|
|
531
566
|
"music_mood": "energetic",
|
|
532
567
|
"image_path": "/optional/your/own/image.jpg",
|
|
@@ -537,7 +572,7 @@ Example scene:
|
|
|
537
572
|
type: 'object',
|
|
538
573
|
required: ['scenes'],
|
|
539
574
|
properties: {
|
|
540
|
-
scenes: { type: 'array', description: 'Complete scenes array written by the agent. Each scene: { narration, caption, visual_description, camera_hint?, music_mood?, image_path?, video_path?, audio_path?, voice? }' },
|
|
575
|
+
scenes: { type: 'array', description: 'Complete scenes array written by the agent. Each scene: { narration, caption, visual_description, html?, code?, camera_hint?, music_mood?, image_path?, video_path?, audio_path?, voice? }' },
|
|
541
576
|
title: { type: 'string', description: 'Video title' },
|
|
542
577
|
orientation: { type: 'string', enum: ['landscape', 'portrait'], default: 'landscape', description: 'Output orientation. "landscape" = 1920×1080 (16:9, YouTube/desktop). "portrait" = 1080×1920 (9:16, TikTok/Reels/Shorts).' },
|
|
543
578
|
voice: { type: 'string', description: 'edge-tts voice ID for all scenes, e.g. "ml-IN-MidhunNeural" (Malayalam male), "en-US-BrianNeural" (English). Per-scene voice field overrides this. Full list: edge-tts --list-voices' },
|
|
@@ -546,10 +581,13 @@ Example scene:
|
|
|
546
581
|
parallelism: { type: 'number', default: 1, description: 'Concurrent TTS synthesis requests (default: 1).' },
|
|
547
582
|
withMusic: { type: 'boolean', default: true },
|
|
548
583
|
musicVolume: { type: 'number', default: 0.07 },
|
|
584
|
+
topic: { type: 'string', description: 'Overall topic/genre for visual aesthetic and background motion (e.g. "Kerala history", "crypto finance", "AI robotics")' },
|
|
585
|
+
style: { type: 'string', description: 'Visual style/theme override (e.g. cinematic_documentary, finance_executive, cyber_tech, retro_synthwave, nature_organic, hype_sports, minimal_luxury, breaking_news, cosmic_space)' },
|
|
586
|
+
theme: { type: 'string', description: 'Theme preset alias' },
|
|
549
587
|
productionId: { type: 'string', description: 'Optional — reuse an existing productionId' },
|
|
550
588
|
},
|
|
551
589
|
},
|
|
552
|
-
handler: async ({ scenes, title = 'FrameForge Video', orientation = 'landscape', voice, language, skipTts = false, parallelism = 1, withMusic = true, musicVolume = 0.07, productionId }) => {
|
|
590
|
+
handler: async ({ scenes, title = 'FrameForge Video', orientation = 'landscape', voice, language, skipTts = false, parallelism = 1, withMusic = true, musicVolume = 0.07, topic = '', style = '', theme = '', productionId }) => {
|
|
553
591
|
const { runAssets } = require('../workflows/assets');
|
|
554
592
|
const { runRender } = require('../workflows/render');
|
|
555
593
|
|
|
@@ -595,6 +633,12 @@ Example scene:
|
|
|
595
633
|
video_path: s.video_path || null,
|
|
596
634
|
audio_path: s.audio_path || null,
|
|
597
635
|
voice: s.voice || null, // per-scene voice override
|
|
636
|
+
html: s.html || null,
|
|
637
|
+
code: s.code || s.animation_code || null,
|
|
638
|
+
topic: s.topic || topic || '',
|
|
639
|
+
style: s.style || style || '',
|
|
640
|
+
theme: s.theme || theme || '',
|
|
641
|
+
language: s.language || language || '',
|
|
598
642
|
character: s.character || { character_id: 'none', pose: 'none', emotion: 'neutral', position: { x: 0.5, y: 0.8, scale: 1.0 } },
|
|
599
643
|
focal_point: s.focal_point || { x: 0.5, y: 0.35, weight: 0.8 },
|
|
600
644
|
overlay: s.overlay || { type: 'none' },
|
|
@@ -608,8 +652,31 @@ Example scene:
|
|
|
608
652
|
const renderWidth = orientation === 'portrait' ? 1080 : 1920;
|
|
609
653
|
const renderHeight = orientation === 'portrait' ? 1920 : 1080;
|
|
610
654
|
|
|
611
|
-
const assetsResult = await runAssets({
|
|
612
|
-
|
|
655
|
+
const assetsResult = await runAssets({
|
|
656
|
+
productionId: pid,
|
|
657
|
+
scenes: processedScenes,
|
|
658
|
+
parallelism,
|
|
659
|
+
skipTts,
|
|
660
|
+
voice: resolvedVoice,
|
|
661
|
+
topic,
|
|
662
|
+
style,
|
|
663
|
+
theme,
|
|
664
|
+
title,
|
|
665
|
+
language,
|
|
666
|
+
});
|
|
667
|
+
const renderResult = await runRender({
|
|
668
|
+
productionId: pid,
|
|
669
|
+
sceneAssets: assetsResult.sceneAssets,
|
|
670
|
+
withMusic,
|
|
671
|
+
musicVolume,
|
|
672
|
+
renderWidth,
|
|
673
|
+
renderHeight,
|
|
674
|
+
topic,
|
|
675
|
+
style,
|
|
676
|
+
theme,
|
|
677
|
+
title,
|
|
678
|
+
language,
|
|
679
|
+
});
|
|
613
680
|
|
|
614
681
|
if (!renderResult.passed) {
|
|
615
682
|
return { productionId: pid, passed: false, qcReport: renderResult.qcReport };
|
|
@@ -1366,13 +1433,98 @@ Operations (used with action="edit"):
|
|
|
1366
1433
|
},
|
|
1367
1434
|
},
|
|
1368
1435
|
|
|
1436
|
+
// ── Tool 19: Autonomous Local Video Editor ──────────────────────────────
|
|
1437
|
+
// Edit, cut, trim, speed-ramp, color-grade, reframe, add captions, and transition
|
|
1438
|
+
// multiple video clips, and mix with voiceover & ducked background music locally.
|
|
1439
|
+
edit_video: {
|
|
1440
|
+
description: `Autonomous local video editor. Edit, cut, trim, speed-ramp, color-grade, reframe (16:9 landscape / 9:16 portrait / 1:1 square), add captions, apply xfade transitions, and mix clip audio with voiceover & ducked background music locally using FFmpeg. Fully autonomous — zero external APIs or subscriptions needed.
|
|
1441
|
+
|
|
1442
|
+
Features:
|
|
1443
|
+
- Fast & frame-accurate trimming (start_time, end_time, duration).
|
|
1444
|
+
- Speed ramping (0.25x to 4.0x) with audio pitch preserving (atempo).
|
|
1445
|
+
- Cinematic color grading (cinematic, warm, cool, vintage, bw, vibrant, cyberpunk).
|
|
1446
|
+
- Broadcast-quality captions with HarfBuzz text shaping & multilingual Indic/Malayalam support.
|
|
1447
|
+
- Transitions: fade, dissolve, wipeleft, wiperight, slideup, slidedown, circleopen, smoothleft, etc.
|
|
1448
|
+
- Multi-track audio: clip ambient audio volume control, master voiceover, and background music with sidechain ducking.
|
|
1449
|
+
- Auto-fit mode: automatically calculates clip durations so video perfectly matches master audio length.`,
|
|
1450
|
+
inputSchema: {
|
|
1451
|
+
type: 'object',
|
|
1452
|
+
required: ['clips'],
|
|
1453
|
+
properties: {
|
|
1454
|
+
clips: {
|
|
1455
|
+
type: 'array',
|
|
1456
|
+
description: 'Array of clip objects. Each clip: { video_path, start_time?, end_time?, duration?, speed?, clip_volume?, color_grade?, caption?, transition?, transition_duration?, audio_path? }',
|
|
1457
|
+
items: {
|
|
1458
|
+
type: 'object',
|
|
1459
|
+
required: ['video_path'],
|
|
1460
|
+
properties: {
|
|
1461
|
+
video_path: { type: 'string', description: 'Path to source video clip' },
|
|
1462
|
+
start_time: { type: ['number', 'string'], description: 'Start time offset (e.g. 5, "00:05", "01:23.45")' },
|
|
1463
|
+
end_time: { type: ['number', 'string'], description: 'End time offset (e.g. 15, "00:15")' },
|
|
1464
|
+
duration: { type: ['number', 'string'], description: 'Desired duration in seconds' },
|
|
1465
|
+
speed: { type: 'number', default: 1.0, description: 'Speed multiplier (e.g. 0.5 for 2x slow-mo, 1.5 for fast)' },
|
|
1466
|
+
clip_volume: { type: 'number', default: 1.0, description: 'Volume of original clip audio (0 to mute, 1.0 normal)' },
|
|
1467
|
+
color_grade: { type: 'string', enum: ['none', 'cinematic', 'warm', 'cool', 'vintage', 'bw', 'vibrant', 'cyberpunk'], description: 'Color grading preset' },
|
|
1468
|
+
caption: { type: 'string', description: 'Caption text to burn into the lower third of this clip' },
|
|
1469
|
+
transition: { type: 'string', default: 'fade', description: 'Transition into next clip (fade, dissolve, wipeleft, wiperight, slideup, slidedown, circleopen, none)' },
|
|
1470
|
+
transition_duration: { type: 'number', default: 0.5, description: 'Duration of transition in seconds (default 0.5)' },
|
|
1471
|
+
audio_path: { type: 'string', description: 'Optional scene-specific audio/voice file' },
|
|
1472
|
+
},
|
|
1473
|
+
},
|
|
1474
|
+
},
|
|
1475
|
+
audio_path: { type: 'string', description: 'Master voiceover / narration audio track across whole video' },
|
|
1476
|
+
music_path: { type: 'string', description: 'Background music track' },
|
|
1477
|
+
music_volume: { type: 'number', default: 0.08, description: 'Background music volume level (default 0.08)' },
|
|
1478
|
+
orientation: { type: 'string', enum: ['landscape', 'portrait', 'square'], default: 'landscape', description: 'Target canvas orientation' },
|
|
1479
|
+
width: { type: 'number', description: 'Custom canvas width (default 1920 for landscape, 1080 for portrait/square)' },
|
|
1480
|
+
height: { type: 'number', description: 'Custom canvas height (default 1080 for landscape, 1920 for portrait, 1080 for square)' },
|
|
1481
|
+
color_grade: { type: 'string', enum: ['none', 'cinematic', 'warm', 'cool', 'vintage', 'bw', 'vibrant', 'cyberpunk'], description: 'Global color grade fallback for all clips' },
|
|
1482
|
+
transition: { type: 'string', default: 'fade', description: 'Global transition fallback between clips' },
|
|
1483
|
+
transition_duration: { type: 'number', default: 0.5, description: 'Global transition duration in seconds' },
|
|
1484
|
+
auto_fit: { type: 'boolean', default: false, description: 'Auto-adjust clip durations to match audio_path duration' },
|
|
1485
|
+
output_path: { type: 'string', description: 'Destination file path for the assembled MP4' },
|
|
1486
|
+
},
|
|
1487
|
+
},
|
|
1488
|
+
handler: async ({
|
|
1489
|
+
clips = [],
|
|
1490
|
+
audio_path = null,
|
|
1491
|
+
music_path = null,
|
|
1492
|
+
music_volume = 0.08,
|
|
1493
|
+
orientation = 'landscape',
|
|
1494
|
+
width,
|
|
1495
|
+
height,
|
|
1496
|
+
color_grade = null,
|
|
1497
|
+
transition = 'fade',
|
|
1498
|
+
transition_duration = 0.5,
|
|
1499
|
+
auto_fit = false,
|
|
1500
|
+
output_path = null,
|
|
1501
|
+
}) => {
|
|
1502
|
+
const { VideoEditor } = require('../video-editor/editor');
|
|
1503
|
+
const editor = new VideoEditor({ filesDir: FILES_DIR });
|
|
1504
|
+
return editor.editAndAssemble({
|
|
1505
|
+
clips,
|
|
1506
|
+
audioPath: audio_path,
|
|
1507
|
+
musicPath: music_path,
|
|
1508
|
+
musicVolume: music_volume,
|
|
1509
|
+
orientation,
|
|
1510
|
+
width,
|
|
1511
|
+
height,
|
|
1512
|
+
colorGrade: color_grade,
|
|
1513
|
+
transition,
|
|
1514
|
+
transitionDuration: transition_duration,
|
|
1515
|
+
autoFit: auto_fit,
|
|
1516
|
+
outputPath: output_path,
|
|
1517
|
+
});
|
|
1518
|
+
},
|
|
1519
|
+
},
|
|
1520
|
+
|
|
1369
1521
|
};
|
|
1370
1522
|
|
|
1371
1523
|
// ── MCP protocol handler (MCP 2024-11-05 spec) ───────────────────────────
|
|
1372
1524
|
|
|
1373
1525
|
const SERVER_INFO = {
|
|
1374
1526
|
name: 'video-pipeline',
|
|
1375
|
-
version: '2.3.
|
|
1527
|
+
version: '2.3.7',
|
|
1376
1528
|
};
|
|
1377
1529
|
|
|
1378
1530
|
const CAPABILITIES = {
|