@snahrup/notion-hero-image-mcp
Version:
AI-powered animated hero banner generator for Notion pages. Integrates DALL-E 3, Google Veo 3, Cloudinary CDN, and Notion API.
102 lines • 4.67 kB
JavaScript
import OpenAI from 'openai';
import fetch from 'node-fetch';
import { writeFileSync } from 'fs';
import { join } from 'path';
import { tmpdir } from 'os';
const openai = new OpenAI({
apiKey: process.env.OPENAI_API_KEY,
});
const definition = {
name: 'generate_image',
description: 'Generate a hero still image using DALL-E 3 with detailed Veo animation script',
inputSchema: {
type: 'object',
properties: {
prompt: {
type: 'string',
description: 'Visual description for the hero image'
},
style: {
type: 'string',
description: 'Art style (cinematic, illustration, photorealistic)',
default: 'cinematic'
}
},
required: ['prompt']
}
};
const generateAnimationBeats = (prompt, style) => {
// Generate timestamped animation beats for 8-second Veo video
const beats = [
{ timeRange: "00:00 – 00:15", description: "Ambient low-light, subtle atmospheric introduction" },
{ timeRange: "00:16 – 02:00", description: "Primary elements begin gentle movement, energy builds" },
{ timeRange: "02:01 – 04:00", description: "Peak animation moment, focal point pulses or glows" },
{ timeRange: "04:01 – 06:00", description: "Secondary elements respond, energy flows outward" },
{ timeRange: "06:01 – 07:15", description: "Movement gradually settles, returning to rest state" },
{ timeRange: "07:16 – 08:00", description: "Final frames match opening for seamless loop" }
];
return beats;
};
const handler = {
'tools/call': async (request) => {
if (request.params.name !== 'generate_image')
return null;
const { prompt, style = 'cinematic' } = request.params.arguments;
// Generate detailed animation script with GPT-4
const scriptResponse = await openai.chat.completions.create({
model: 'gpt-4-turbo-preview',
messages: [{
role: 'system',
content: `Create a detailed 8-second animation plan for a Notion hero banner.
Format your response as JSON with these fields:
- title: Creative title for the animation
- scene_description: Detailed static scene description (no movement)
- animation_beats: Array of {timeRange, description} for 6 key moments
- style_tags: Comma-separated style descriptors
Focus on subtle, elegant movement suitable for a professional banner.`
}, {
role: 'user',
content: `Create animation plan for: "${prompt}". Visual style: ${style}`
}],
response_format: { type: "json_object" },
max_tokens: 500
});
const animationPlan = JSON.parse(scriptResponse.choices[0].message.content || '{}');
// Generate image with DALL-E 3
const imageResponse = await openai.images.generate({
model: 'dall-e-3',
prompt: `${style} style: ${prompt}. High quality, 16:9 aspect ratio, suitable for animation. ${animationPlan.scene_description || ''}`,
size: '1792x1024',
quality: 'hd',
n: 1
});
if (!imageResponse.data || !imageResponse.data[0]?.url) {
throw new Error('Failed to generate image');
}
const imageUrl = imageResponse.data[0].url;
// Download image locally for processing
const response = await fetch(imageUrl);
const buffer = await response.buffer();
const imagePath = join(tmpdir(), `hero-${Date.now()}.png`);
writeFileSync(imagePath, buffer);
// Prepare animation beats if not properly generated
const animationBeats = animationPlan.animation_beats || generateAnimationBeats(prompt, style);
return {
content: [{
type: 'text',
text: JSON.stringify({
image_url: imageUrl,
local_path: imagePath,
title: animationPlan.title || `${style} Banner Animation`,
scene_description: animationPlan.scene_description || prompt,
animation_beats: animationBeats,
style_tags: animationPlan.style_tags || `${style}, banner-friendly, seamless-loop, subtle-motion`,
prompt: prompt,
style: style
})
}]
};
}
};
export default { definition, handler };
//# sourceMappingURL=generate-image.js.map