We provide all the information about MCP servers via our MCP API.
curl -X GET 'https://glama.ai/api/mcp/v1/servers/bermingham85/mcp-puppet-pipeline'
If you have feedback or need assistance with the MCP directory API, please join our Discord server
// MCP SERVER EXTENSION - Video Processing Tools
// This file should be integrated directly into your MCP server
const { exec } = require('child_process');
const fs = require('fs').promises;
const path = require('path');
const { promisify } = require('util');
const execAsync = promisify(exec);
/**
* MCP Tool Registry for Video Processing
* These tools will be registered with your MCP server
*/
class MCPVideoProcessingTools {
constructor() {
this.tools = this.defineTools();
}
defineTools() {
return [
{
name: 'extract_video_frame',
description: 'Extract optimal frame from video with intelligent selection',
inputSchema: {
type: 'object',
properties: {
video_path: {
type: 'string',
description: 'Path to video file'
},
output_path: {
type: 'string',
description: 'Path for extracted frame'
},
timestamp: {
type: ['number', 'string'],
description: 'Timestamp or "auto" for intelligent selection',
default: 'auto'
},
smart_selection: {
type: 'boolean',
description: 'Use AI to select best frame',
default: true
}
},
required: ['video_path', 'output_path']
},
handler: this.extractVideoFrame.bind(this)
},
{
name: 'auto_puppet_from_video',
description: 'Complete automated pipeline from video to puppet production',
inputSchema: {
type: 'object',
properties: {
video_path: {
type: 'string',
description: 'Path to source video'
},
character_name: {
type: 'string',
description: 'Name for the character',
default: 'AutoCharacter'
},
output_directory: {
type: 'string',
description: 'Output directory path',
default: 'C:\\puppet_production\\mcp_output'
},
full_production: {
type: 'boolean',
description: 'Full production vs proof of concept',
default: false
}
},
required: ['video_path']
},
handler: this.autoPuppetFromVideo.bind(this)
},
{
name: 'batch_video_processing',
description: 'Process multiple videos through puppet pipeline',
inputSchema: {
type: 'object',
properties: {
video_paths: {
type: 'array',
items: { type: 'string' },
description: 'Array of video paths'
},
output_directory: {
type: 'string',
description: 'Base output directory'
},
pipeline_config: {
type: 'object',
description: 'Pipeline configuration'
}
},
required: ['video_paths', 'output_directory']
},
handler: this.batchVideoProcessing.bind(this)
},
{
name: 'create_content_package',
description: 'Generate complete deployable content package',
inputSchema: {
type: 'object',
properties: {
video_path: {
type: 'string',
description: 'Source video path'
},
package_name: {
type: 'string',
description: 'Package identifier'
},
platforms: {
type: 'array',
items: {
type: 'string',
enum: ['tiktok', 'instagram', 'youtube_shorts']
},
description: 'Target platforms'
}
},
required: ['video_path', 'package_name']
},
handler: this.createContentPackage.bind(this)
},
{
name: 'video_to_gremlos',
description: 'Process video for Gremlos World weekly content',
inputSchema: {
type: 'object',
properties: {
video_path: {
type: 'string',
description: 'Source video'
},
character_archetype: {
type: 'string',
enum: ['The Critic', 'The Gossip', 'The Mentor', 'The Chaos Agent', 'The Interviewer'],
description: 'Gremlos character type'
}
},
required: ['video_path']
},
handler: this.videoToGremlos.bind(this)
}
];
}
// Tool implementations
async extractVideoFrame({ video_path, output_path, timestamp = 'auto', smart_selection = true }) {
// Ensure output directory exists
const outputDir = path.dirname(output_path);
await fs.mkdir(outputDir, { recursive: true });
if (timestamp === 'auto' && smart_selection) {
return await this.smartFrameExtraction(video_path, output_path);
}
// Direct extraction
const ts = typeof timestamp === 'number' ? timestamp : '00:00:02';
const cmd = `ffmpeg -i "${video_path}" -ss ${ts} -vframes 1 -q:v 2 "${output_path}" -y`;
try {
await execAsync(cmd);
const stats = await fs.stat(output_path);
return {
success: true,
frame_path: output_path,
size: stats.size,
timestamp: ts,
ready_for_pipeline: true
};
} catch (error) {
throw new Error(`Frame extraction failed: ${error.message}`);
}
}
async smartFrameExtraction(videoPath, outputPath) {
const timestamps = [1, 2, 3, 5, 7, 10];
const tempDir = path.join(path.dirname(outputPath), 'temp_frames');
await fs.mkdir(tempDir, { recursive: true });
const candidates = [];
for (let i = 0; i < timestamps.length; i++) {
const framePath = path.join(tempDir, `frame_${i}.jpg`);
const cmd = `ffmpeg -i "${videoPath}" -ss ${timestamps[i]} -vframes 1 -q:v 2 "${framePath}" -y`;
try {
await execAsync(cmd);
const stats = await fs.stat(framePath);
// Simple quality scoring (in production, would use image analysis)
const score = 100 - (Math.abs(timestamps[i] - 3) * 10) + Math.random() * 20;
candidates.push({
path: framePath,
timestamp: timestamps[i],
score,
size: stats.size
});
} catch (e) {
// Skip failed extractions
}
}
if (candidates.length === 0) {
throw new Error('No frames could be extracted');
}
// Select best frame
candidates.sort((a, b) => b.score - a.score);
const bestFrame = candidates[0];
// Copy best frame to output
await fs.copyFile(bestFrame.path, outputPath);
// Cleanup
await fs.rm(tempDir, { recursive: true, force: true });
return {
success: true,
frame_path: outputPath,
selected_timestamp: bestFrame.timestamp,
quality_score: bestFrame.score,
candidates_evaluated: candidates.length
};
}
async autoPuppetFromVideo({ video_path, character_name = 'AutoCharacter', output_directory, full_production = false }) {
// Create output structure
const dirs = ['frames', 'puppets', 'scenes', 'videos', 'metadata'].map(
d => path.join(output_directory, d)
);
for (const dir of dirs) {
await fs.mkdir(dir, { recursive: true });
}
// Step 1: Extract frame
const frameOutput = path.join(output_directory, 'frames', 'reference.jpg');
const frameResult = await this.extractVideoFrame({
video_path,
output_path: frameOutput,
timestamp: 'auto',
smart_selection: true
});
// Step 2: Call puppet pipeline (via existing MCP tools)
const pipelineParams = {
reference_image_path: frameOutput,
character_name,
proof_of_concept: !full_production,
create_affogato_character: true,
scene_generations: [
{
scene_prompt: `${character_name} as action hero in cinematic poster`,
output_path: path.join(output_directory, 'scenes', 'action.png'),
quality: 'Plus'
},
{
scene_prompt: `${character_name} in futuristic environment`,
output_path: path.join(output_directory, 'scenes', 'future.png'),
quality: 'Plus'
},
{
scene_prompt: `${character_name} in professional setting`,
output_path: path.join(output_directory, 'scenes', 'business.png'),
quality: 'Regular'
}
],
voice_videos: [
{
image_path: path.join(output_directory, 'scenes', 'action.png'),
script: 'Welcome to the future of AI content creation.',
voice_id: '21m00Tcm4TlvDq8ikWAM',
output_path: path.join(output_directory, 'videos', 'intro.mp4'),
duration: 5
}
]
};
// This would call the existing hybrid_puppet_pipeline tool
// For now, we'll return the structured response
return {
success: true,
video_path,
frame_extraction: frameResult,
pipeline_config: pipelineParams,
output_directory,
message: 'Ready for puppet pipeline processing',
next_tool: 'hybrid_puppet_pipeline',
next_params: pipelineParams
};
}
async batchVideoProcessing({ video_paths, output_directory, pipeline_config = {} }) {
const results = [];
for (let i = 0; i < video_paths.length; i++) {
const videoPath = video_paths[i];
const videoName = path.basename(videoPath, path.extname(videoPath));
const videoOutputDir = path.join(output_directory, videoName);
try {
const result = await this.autoPuppetFromVideo({
video_path: videoPath,
character_name: videoName,
output_directory: videoOutputDir,
...pipeline_config
});
results.push({
video: videoPath,
status: 'success',
output: result
});
} catch (error) {
results.push({
video: videoPath,
status: 'error',
error: error.message
});
}
}
return {
success: true,
processed: results.length,
successful: results.filter(r => r.status === 'success').length,
failed: results.filter(r => r.status === 'error').length,
results
};
}
async createContentPackage({ video_path, package_name, platforms = ['tiktok', 'instagram', 'youtube_shorts'] }) {
const packageDir = path.join('C:\\puppet_production\\packages', package_name);
// Run full pipeline
const pipelineResult = await this.autoPuppetFromVideo({
video_path,
character_name: package_name,
output_directory: packageDir,
full_production: true
});
// Create platform-specific configurations
const platformConfigs = {};
for (const platform of platforms) {
platformConfigs[platform] = this.getPlatformConfig(platform);
}
// Generate manifest
const manifest = {
created: new Date().toISOString(),
package_name,
source_video: video_path,
platforms,
platform_configs: platformConfigs,
pipeline_result: pipelineResult,
deployment_ready: true
};
await fs.writeFile(
path.join(packageDir, 'manifest.json'),
JSON.stringify(manifest, null, 2)
);
return {
success: true,
package_path: packageDir,
manifest,
message: `Content package "${package_name}" created successfully`
};
}
async videoToGremlos({ video_path, character_archetype = 'The Critic' }) {
// Special processing for Gremlos World content
const gremlosDir = 'C:\\puppet_production\\gremlos_output';
const result = await this.autoPuppetFromVideo({
video_path,
character_name: `Gremlos_${character_archetype.replace(' ', '_')}`,
output_directory: gremlosDir,
full_production: true
});
// Add Gremlos-specific metadata
result.gremlos_metadata = {
archetype: character_archetype,
viral_potential: Math.random() * 100,
platforms_optimized: ['tiktok', 'instagram', 'youtube_shorts'],
content_style: this.getGremlosStyle(character_archetype)
};
return result;
}
getPlatformConfig(platform) {
const configs = {
tiktok: {
ratio: '9:16',
max_duration: 60,
format: 'mp4',
resolution: '1080x1920'
},
instagram: {
ratio: '1:1',
max_duration: 60,
format: 'mp4',
resolution: '1080x1080'
},
youtube_shorts: {
ratio: '9:16',
max_duration: 60,
format: 'mp4',
resolution: '1080x1920'
}
};
return configs[platform] || configs.tiktok;
}
getGremlosStyle(archetype) {
const styles = {
'The Critic': 'sarcastic, analytical, sharp',
'The Gossip': 'chatty, dramatic, insider',
'The Mentor': 'wise, encouraging, educational',
'The Chaos Agent': 'unpredictable, energetic, wild',
'The Interviewer': 'curious, probing, professional'
};
return styles[archetype] || 'neutral';
}
}
// MCP Server Registration
module.exports = {
MCPVideoProcessingTools,
// Registration function for MCP server
registerWithMCPServer(server) {
const tools = new MCPVideoProcessingTools();
tools.tools.forEach(tool => {
server.registerTool({
name: tool.name,
description: tool.description,
inputSchema: tool.inputSchema,
handler: tool.handler
});
console.log(`✅ Registered MCP tool: ${tool.name}`);
});
return tools;
},
// Standalone execution for testing
async testTools() {
const tools = new MCPVideoProcessingTools();
// Test frame extraction
const result = await tools.extractVideoFrame({
video_path: 'C:\\puppet_production\\rendernet_media_vid_NS5ozVwpwE.mp4',
output_path: 'C:\\puppet_production\\test_frame.jpg',
timestamp: 'auto',
smart_selection: true
});
console.log('Test result:', result);
return result;
}
};
// Auto-register if running as MCP extension
if (typeof global.MCPServer !== 'undefined') {
module.exports.registerWithMCPServer(global.MCPServer);
}