#!/usr/bin/env node
import puppeteer from 'puppeteer';
import { execSync } from 'child_process';
import path from 'path';
import { fileURLToPath } from 'url';
import fs from 'fs';
import { mcpConfigs } from './mcp-configs.js';
const __dirname = path.dirname(fileURLToPath(import.meta.url));
// Only render these 3 MCPs
const targetIds = ['lightspeed', 'bigcommerce', 'toast'];
const configs = mcpConfigs.filter(c => targetIds.includes(c.id));
function generateHTML(config) {
const { name, color, question, statLabel, statValue, statLabel2, statValue2, rows, insight } = config;
return `
${name} MCP Demo
${question}
Here's what I found:
${name.substring(0, 2).toLowerCase()}
${name}
${statValue}
${statLabel}
${statValue2}
${statLabel2}
${rows[0].label}
${rows[0].value}
${rows[1].label}
${rows[1].value}
${rows[2].label}
${rows[2].value}
๐ก Recommendation
${insight}
`;
}
async function renderVideo(config, browser) {
const htmlPath = path.join(__dirname, 'output', `${config.id}.html`);
const framesDir = path.join(__dirname, 'output', `${config.id}-frames`);
const mp4Path = path.join(__dirname, 'output', `${config.id}.mp4`);
// Generate HTML
fs.writeFileSync(htmlPath, generateHTML(config));
// Create frames directory
fs.rmSync(framesDir, { recursive: true, force: true });
fs.mkdirSync(framesDir, { recursive: true });
// Capture frames
const page = await browser.newPage();
await page.setViewport({ width: 1920, height: 1080 });
await page.goto(`file://${htmlPath}`, { waitUntil: 'networkidle0' });
await new Promise(r => setTimeout(r, 300));
const fps = 30;
const totalFrames = 300; // 10 seconds
for (let i = 0; i < totalFrames; i++) {
const scroll = i / (totalFrames - 1);
await page.evaluate(s => window.updateScene(s), scroll);
await new Promise(r => setTimeout(r, 15));
await page.screenshot({
path: path.join(framesDir, `frame-${String(i+1).padStart(4,'0')}.png`),
type: 'png'
});
}
await page.close();
// Encode video
execSync(`ffmpeg -y -framerate ${fps} -i "${framesDir}/frame-%04d.png" -c:v libx264 -pix_fmt yuv420p -crf 20 "${mp4Path}"`, { stdio: 'pipe' });
// Cleanup frames
fs.rmSync(framesDir, { recursive: true, force: true });
return mp4Path;
}
async function main() {
console.log(`\n๐ฌ Generating ${configs.length} MCP videos...\n`);
const browser = await puppeteer.launch({
headless: true,
executablePath: '/Applications/Google Chrome.app/Contents/MacOS/Google Chrome',
args: ['--no-sandbox']
});
const results = [];
for (let i = 0; i < configs.length; i++) {
const config = configs[i];
const start = Date.now();
process.stdout.write(`[${i+1}/${configs.length}] ${config.name}... `);
try {
await renderVideo(config, browser);
console.log(`โ (${Math.round((Date.now() - start) / 1000)}s)`);
results.push({ name: config.name, id: config.id, success: true });
} catch (err) {
console.log(`โ ${err.message}`);
results.push({ name: config.name, id: config.id, success: false, error: err.message });
}
}
await browser.close();
console.log(`\nโ Done! Videos saved to output/\n`);
return results;
}
main().catch(console.error);