server.ts•55.9 kB
import { McpServer, ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
ListPromptsRequestSchema,
GetPromptRequestSchema,
LoggingLevel,
SetLevelRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
import { Creatify, Types } from "@tsavo/creatify-api-ts";
import { z } from "zod";
/**
* Creatify MCP Server implementation
* Exposes Creatify AI API capabilities as MCP tools and resources
*/
export class CreatifyMcpServer {
private creatify: Creatify;
private logLevel: LoggingLevel = "info";
constructor(apiId: string, apiKey: string) {
this.creatify = new Creatify({
apiId,
apiKey,
});
}
/**
* Send a log message to the client
*/
private async log(server: McpServer, level: LoggingLevel, message: string, data?: any) {
const levelPriority = {
debug: 0,
info: 1,
notice: 2,
warning: 3,
error: 4,
critical: 5,
alert: 6,
emergency: 7
};
if (levelPriority[level] >= levelPriority[this.logLevel]) {
await server.notification({
method: "notifications/message",
params: {
level,
logger: "creatify-mcp",
data: {
message,
...data
}
}
});
}
}
/**
* Set up all MCP tools and resources on the server
*/
async setupServer(server: McpServer): Promise<void> {
// Set up logging
await this.setupLogging(server);
// Set up prompts
await this.setupPrompts(server);
// Set up resources
await this.setupResources(server);
// Set up tools
await this.setupTools(server);
}
/**
* Set up logging capabilities
*/
private async setupLogging(server: McpServer): Promise<void> {
// Handle log level setting
server.setRequestHandler(SetLevelRequestSchema, async (request) => {
this.logLevel = request.params.level;
await this.log(server, "info", `Log level set to ${this.logLevel}`);
return {};
});
await this.log(server, "info", "Creatify MCP Server initialized", {
capabilities: ["tools", "resources", "prompts", "logging"]
});
}
/**
* Set up MCP prompts (reusable templates)
*/
private async setupPrompts(server: McpServer): Promise<void> {
const prompts = [
{
name: "create-product-demo",
description: "Create a professional product demonstration video",
arguments: [
{
name: "productName",
description: "Name of the product to demonstrate",
required: true
},
{
name: "keyFeatures",
description: "Key features to highlight (comma-separated)",
required: true
},
{
name: "targetAudience",
description: "Target audience for the demo",
required: false
},
{
name: "duration",
description: "Desired video duration in seconds",
required: false
}
]
},
{
name: "create-social-content",
description: "Create engaging social media content",
arguments: [
{
name: "platform",
description: "Target platform (TikTok, Instagram, YouTube)",
required: true
},
{
name: "topic",
description: "Content topic or theme",
required: true
},
{
name: "tone",
description: "Content tone (energetic, professional, casual, funny)",
required: false
},
{
name: "callToAction",
description: "Call to action for viewers",
required: false
}
]
},
{
name: "create-educational-video",
description: "Create an educational or tutorial video",
arguments: [
{
name: "subject",
description: "Subject or topic to teach",
required: true
},
{
name: "difficulty",
description: "Difficulty level (beginner, intermediate, advanced)",
required: true
},
{
name: "learningObjectives",
description: "What viewers should learn (comma-separated)",
required: true
},
{
name: "includeQuiz",
description: "Whether to include quiz questions",
required: false
}
]
},
{
name: "create-marketing-campaign",
description: "Create a marketing campaign video",
arguments: [
{
name: "campaignGoal",
description: "Primary goal (awareness, conversion, engagement)",
required: true
},
{
name: "brandMessage",
description: "Core brand message to communicate",
required: true
},
{
name: "targetDemographic",
description: "Target demographic details",
required: true
},
{
name: "budget",
description: "Campaign budget tier (low, medium, high)",
required: false
}
]
},
{
name: "analyze-video-performance",
description: "Analyze and optimize video performance",
arguments: [
{
name: "videoUrl",
description: "URL of the video to analyze",
required: true
},
{
name: "metrics",
description: "Metrics to focus on (views, engagement, conversion)",
required: false
},
{
name: "improvementAreas",
description: "Areas to improve (audio, visual, script, pacing)",
required: false
}
]
}
];
// List prompts
server.setRequestHandler(ListPromptsRequestSchema, async () => {
await this.log(server, "debug", "Listing available prompts", { count: prompts.length });
return { prompts };
});
// Get specific prompt
server.setRequestHandler(GetPromptRequestSchema, async (request) => {
const promptName = request.params.name;
const args = request.params.arguments || {};
await this.log(server, "debug", `Getting prompt: ${promptName}`, { arguments: args });
switch (promptName) {
case "create-product-demo":
return {
description: "Professional product demonstration video workflow",
messages: [
{
role: "user",
content: {
type: "text",
text: `Create a professional product demonstration video for "${args.productName || '[PRODUCT_NAME]'}" highlighting these key features: ${args.keyFeatures || '[KEY_FEATURES]'}.\n\nTarget audience: ${args.targetAudience || 'General consumers'}\nDesired duration: ${args.duration || '60-90'} seconds\n\nPlease:\n1. First, use the 'generate_ai_script' tool to create an engaging script\n2. Then use 'create_avatar_video' to bring it to life\n3. Consider the target audience and keep the tone professional yet engaging\n4. Include a clear call-to-action at the end`
}
}
]
};
case "create-social-content":
const aspectRatio = args.platform === 'TikTok' || args.platform === 'Instagram' ? '9:16' : '16:9';
return {
description: "Engaging social media content creation workflow",
messages: [
{
role: "user",
content: {
type: "text",
text: `Create engaging ${args.platform || '[PLATFORM]'} content about "${args.topic || '[TOPIC]'}" with a ${args.tone || 'energetic'} tone.\n\nCall to action: ${args.callToAction || 'Like and follow for more!'}\n\nWorkflow:\n1. Use 'create_ai_shorts' for ${aspectRatio} short-form content\n2. Keep it under 60 seconds for maximum engagement\n3. Make the first 3 seconds attention-grabbing\n4. Include trending elements for the platform\n5. End with a clear call-to-action`
}
}
]
};
case "create-educational-video":
return {
description: "Educational video creation workflow",
messages: [
{
role: "user",
content: {
type: "text",
text: `Create an educational video about "${args.subject || '[SUBJECT]'}" for ${args.difficulty || 'beginner'} level learners.\n\nLearning objectives: ${args.learningObjectives || '[LEARNING_OBJECTIVES]'}\nInclude quiz: ${args.includeQuiz || 'No'}\n\nEducational workflow:\n1. Generate a structured script with 'generate_ai_script'\n2. Break content into digestible segments\n3. Use 'create_avatar_video' with a professional, teaching tone\n4. Include examples and practical applications\n5. Summarize key points at the end\n${args.includeQuiz === 'Yes' ? '6. Add quiz questions for knowledge check' : ''}`
}
}
]
};
case "create-marketing-campaign":
return {
description: "Marketing campaign video creation workflow",
messages: [
{
role: "user",
content: {
type: "text",
text: `Create a marketing campaign video with goal: ${args.campaignGoal || '[CAMPAIGN_GOAL]'}\n\nBrand message: "${args.brandMessage || '[BRAND_MESSAGE]'}"\nTarget demographic: ${args.targetDemographic || '[TARGET_DEMOGRAPHIC]'}\nBudget tier: ${args.budget || 'medium'}\n\nMarketing workflow:\n1. Use 'generate_ai_script' with persuasive, brand-aligned messaging\n2. Create compelling visuals with 'create_url_to_video' if you have a landing page\n3. Use 'create_avatar_video' for personal connection\n4. Focus on emotional appeal and clear value proposition\n5. Include strong call-to-action for ${args.campaignGoal}\n6. Optimize for the target demographic`
}
}
]
};
case "analyze-video-performance":
return {
description: "Video performance analysis and optimization workflow",
messages: [
{
role: "user",
content: {
type: "text",
text: `Analyze and optimize this video: ${args.videoUrl || '[VIDEO_URL]'}\n\nFocus metrics: ${args.metrics || 'engagement, retention'}\nImprovement areas: ${args.improvementAreas || 'overall performance'}\n\nAnalysis workflow:\n1. Use 'create_ai_edited_video' to enhance the existing video\n2. Apply editing style based on performance goals\n3. Consider A/B testing different versions\n4. Focus on the specified improvement areas\n5. Generate recommendations for future content\n6. Create optimized version with better pacing and engagement`
}
}
]
};
default:
throw new Error(`Prompt not found: ${promptName}`);
}
});
await this.log(server, "info", "Prompts initialized", { count: prompts.length });
}
/**
* Set up MCP resources (read-only data)
*/
private async setupResources(server: McpServer): Promise<void> {
// Avatars resource
server.resource(
"avatars",
"creatify://avatars",
async () => {
try {
const avatars = await this.creatify.avatar.getAvatars();
return {
contents: [{
uri: "creatify://avatars",
mimeType: "application/json",
text: JSON.stringify(avatars, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch avatars: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
// Voices resource
server.resource(
"voices",
"creatify://voices",
async () => {
try {
const voices = await this.creatify.avatar.getVoices();
return {
contents: [{
uri: "creatify://voices",
mimeType: "application/json",
text: JSON.stringify(voices, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch voices: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
// Custom templates resource
server.resource(
"templates",
"creatify://templates",
async () => {
try {
const templates = await this.creatify.customTemplates.getCustomTemplateList();
return {
contents: [{
uri: "creatify://templates",
mimeType: "application/json",
text: JSON.stringify(templates, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch templates: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
// Workspace credits resource
server.resource(
"credits",
"creatify://credits",
async () => {
try {
const credits = await this.creatify.workspace.getRemainingCredits();
return {
contents: [{
uri: "creatify://credits",
mimeType: "application/json",
text: JSON.stringify(credits, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch credits: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
// Music library resource
server.resource(
"music",
"creatify://music",
async () => {
try {
const music = await this.creatify.musics.getMusics();
return {
contents: [{
uri: "creatify://music",
mimeType: "application/json",
text: JSON.stringify(music, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch music: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
// Dynamic resource for individual avatar details
server.resource(
"avatar-details",
new ResourceTemplate("creatify://avatar/{avatarId}", { list: undefined }),
async (uri, { avatarId }) => {
try {
// Note: Individual avatar details not available in current API
// Return basic info that avatar exists
const avatars = await this.creatify.avatar.getAvatars();
const avatar = avatars.find(a => a.avatar_id === avatarId);
if (!avatar) {
throw new Error(`Avatar ${avatarId} not found`);
}
return {
contents: [{
uri: uri.href,
mimeType: "application/json",
text: JSON.stringify(avatar, null, 2)
}]
};
} catch (error) {
throw new Error(`Failed to fetch avatar ${avatarId}: ${error instanceof Error ? error.message : String(error)}`);
}
}
);
}
/**
* Set up MCP tools (actions that can be performed)
*/
private async setupTools(server: McpServer): Promise<void> {
// Create Avatar Video tool
server.tool(
"create_avatar_video",
{
text: z.string().describe("The text to be spoken by the avatar"),
avatarId: z.string().describe("The ID of the avatar to use"),
aspectRatio: z.enum(["16:9", "9:16", "1:1"]).describe("Video aspect ratio"),
voiceId: z.string().optional().describe("Optional voice ID for the avatar"),
name: z.string().optional().describe("Optional name for the video"),
greenScreen: z.boolean().optional().describe("Whether to use green screen background"),
noCaptions: z.boolean().optional().describe("Whether to disable captions"),
noMusic: z.boolean().optional().describe("Whether to disable background music"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for video completion before returning")
},
async ({ text, avatarId, aspectRatio, voiceId, name, greenScreen, noCaptions, noMusic, webhookUrl, waitForCompletion }) => {
try {
await this.log(server, "info", "Creating avatar video", {
avatarId,
aspectRatio,
textLength: text.length,
waitForCompletion
});
const params: any = {
text,
creator: avatarId,
aspect_ratio: aspectRatio,
};
if (voiceId) params.accent = voiceId;
if (name) params.name = name;
if (greenScreen !== undefined) params.green_screen = greenScreen;
if (noCaptions !== undefined) params.no_caption = noCaptions;
if (noMusic !== undefined) params.no_music = noMusic;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
await this.log(server, "info", "Waiting for avatar video completion...");
result = await this.creatify.avatar.createAndWaitForLipsync(params);
await this.log(server, "info", "Avatar video completed", { videoId: result.id });
} else {
result = await this.creatify.avatar.createLipsync(params);
await this.log(server, "info", "Avatar video creation started", { videoId: result.id });
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
await this.log(server, "error", "Avatar video creation failed", {
error: error instanceof Error ? error.message : String(error),
avatarId,
aspectRatio
});
return {
content: [{
type: "text",
text: `Error creating avatar video: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Create URL to Video tool
server.tool(
"create_url_to_video",
{
url: z.string().url().describe("The URL to convert to video"),
visualStyle: z.string().optional().describe("Visual style for the video (e.g., 'DynamicProductTemplate')"),
scriptStyle: z.string().optional().describe("Script style for narration (e.g., 'EnthusiasticWriter')"),
aspectRatio: z.enum(["16:9", "9:16", "1:1"]).optional().describe("Video aspect ratio"),
language: z.string().optional().default("en").describe("Language for the script and voiceover"),
videoLength: z.number().optional().describe("Desired video length in seconds"),
targetAudience: z.string().optional().describe("Target audience for the video"),
targetPlatform: z.string().optional().describe("Target platform (e.g., 'YouTube', 'TikTok')"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for video completion before returning")
},
async ({ url, visualStyle, scriptStyle, aspectRatio, language, videoLength, targetAudience, targetPlatform, webhookUrl, waitForCompletion }) => {
try {
// First create a link from the URL
const linkResponse = await this.creatify.urlToVideo.createLink({ url });
// Then create video from the link
const videoParams: any = {
link: linkResponse.id,
language: language || "en"
};
if (visualStyle) videoParams.visual_style = visualStyle;
if (scriptStyle) videoParams.script_style = scriptStyle;
if (aspectRatio) videoParams.aspect_ratio = aspectRatio;
if (videoLength) videoParams.video_length = videoLength;
if (targetAudience) videoParams.target_audience = targetAudience;
if (targetPlatform) videoParams.target_platform = targetPlatform;
if (webhookUrl) videoParams.webhook_url = webhookUrl;
let result;
result = await this.creatify.urlToVideo.createVideoFromLink(videoParams);
if (waitForCompletion) {
// Poll for completion
let attempts = 0;
const maxAttempts = 120; // 10 minutes
while (attempts < maxAttempts && result.status !== 'done' && result.status !== 'error') {
await new Promise(resolve => setTimeout(resolve, 5000));
result = await this.creatify.urlToVideo.getVideo(result.id);
attempts++;
}
}
return {
content: [{
type: "text",
text: JSON.stringify({ link: linkResponse, video: result }, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating URL to video: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Generate Text-to-Speech tool
server.tool(
"generate_text_to_speech",
{
text: z.string().describe("The text to convert to speech"),
voiceId: z.string().describe("The voice ID to use for speech generation"),
name: z.string().optional().describe("Optional name for the TTS task"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for audio completion before returning")
},
async ({ text, voiceId, name, webhookUrl, waitForCompletion }) => {
try {
const params: any = {
script: text,
accent: voiceId
};
if (name) params.name = name;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
result = await this.creatify.textToSpeech.createAndWaitForTextToSpeech(params);
} else {
result = await this.creatify.textToSpeech.createTextToSpeech(params);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error generating text-to-speech: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Create Multi-Avatar Conversation tool
server.tool(
"create_multi_avatar_conversation",
{
conversation: z.array(z.object({
avatarId: z.string().describe("Avatar ID for this part of conversation"),
text: z.string().describe("Text for this avatar to speak"),
voiceId: z.string().optional().describe("Optional voice ID for this avatar"),
backgroundUrl: z.string().optional().describe("Optional background image URL")
})).describe("Array of conversation parts with different avatars"),
aspectRatio: z.enum(["16:9", "9:16", "1:1"]).describe("Video aspect ratio"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for video completion before returning")
},
async ({ conversation, aspectRatio, webhookUrl, waitForCompletion }) => {
try {
const videoInputs = conversation.map((part, index) => {
const input: any = {
character: {
type: "avatar",
avatar_id: part.avatarId,
avatar_style: "normal"
},
voice: {
type: "text",
input_text: part.text
}
};
if (part.voiceId) {
input.voice.voice_id = part.voiceId;
}
if (part.backgroundUrl) {
input.background = {
type: "image",
url: part.backgroundUrl
};
}
return input;
});
const params: any = {
video_inputs: videoInputs,
aspect_ratio: aspectRatio
};
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
result = await this.creatify.avatar.createMultiAvatarLipsync(params);
if (waitForCompletion) {
// Poll for completion
let attempts = 0;
const maxAttempts = 120; // 10 minutes
while (attempts < maxAttempts && result.status !== 'done' && result.status !== 'error') {
await new Promise(resolve => setTimeout(resolve, 5000));
result = await this.creatify.avatar.getLipsync(result.id);
attempts++;
}
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating multi-avatar conversation: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Get Video Status tool
server.tool(
"get_video_status",
{
videoId: z.string().describe("The ID of the video to check status for"),
videoType: z.enum(["lipsync", "url-to-video", "text-to-speech", "multi-avatar", "ai-editing", "custom-template"]).describe("The type of video/task to check")
},
async ({ videoId, videoType }) => {
try {
let result;
switch (videoType) {
case "lipsync":
result = await this.creatify.avatar.getLipsync(videoId);
break;
case "url-to-video":
result = await this.creatify.urlToVideo.getVideo(videoId);
break;
case "text-to-speech":
result = await this.creatify.textToSpeech.getTextToSpeech(videoId);
break;
case "multi-avatar":
result = await this.creatify.avatar.getLipsync(videoId); // Multi-avatar uses same endpoint
break;
case "ai-editing":
result = await this.creatify.aiEditing.getAiEditing(videoId);
break;
case "custom-template":
result = await this.creatify.customTemplates.getCustomTemplate(videoId);
break;
default:
throw new Error(`Unknown video type: ${videoType}`);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error getting video status: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Create Custom Template Video tool
server.tool(
"create_custom_template_video",
{
templateId: z.string().describe("The ID or name of the custom template to use"),
data: z.record(z.any()).describe("Template data as key-value pairs (varies by template)"),
aspectRatio: z.enum(["16:9", "9:16", "1:1"]).optional().describe("Video aspect ratio"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for video completion before returning")
},
async ({ templateId, data, aspectRatio, webhookUrl, waitForCompletion }) => {
try {
const params: any = {
visual_style: templateId,
data
};
if (aspectRatio) params.aspect_ratio = aspectRatio;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
result = await this.creatify.customTemplates.createAndWaitForCustomTemplate(params);
} else {
result = await this.creatify.customTemplates.createCustomTemplate(params);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating custom template video: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// How to Use tool - AI can call this to understand parameters better
server.tool(
"how_to_use",
{
toolName: z.string().describe("Name of the tool to get usage information for"),
includeExamples: z.boolean().optional().default(true).describe("Whether to include usage examples")
},
async ({ toolName, includeExamples }) => {
try {
const toolGuides = {
"create_avatar_video": {
description: "Create AI avatar videos with lip-sync technology",
requiredParams: {
text: "Text for the avatar to speak (max 1000 characters)",
avatarId: "ID of avatar (get from creatify://avatars resource)",
aspectRatio: "Video format: '16:9' (landscape), '9:16' (portrait), '1:1' (square)"
},
optionalParams: {
voiceId: "Voice ID (get from creatify://voices resource)",
name: "Custom name for the video",
greenScreen: "true/false - use green screen background",
noCaptions: "true/false - disable captions",
noMusic: "true/false - disable background music",
waitForCompletion: "true/false - wait for video to complete before returning"
},
examples: includeExamples ? [
{
description: "Simple avatar video",
code: `{
"text": "Hello! Welcome to our product demo.",
"avatarId": "anna_costume1_cameraA",
"aspectRatio": "16:9"
}`
},
{
description: "Avatar video with custom voice and green screen",
code: `{
"text": "This is a professional presentation.",
"avatarId": "john_suit_cameraB",
"aspectRatio": "16:9",
"voiceId": "en-US-GuyNeural",
"greenScreen": true,
"waitForCompletion": true
}`
}
] : []
},
"create_url_to_video": {
description: "Convert websites into professional promotional videos",
requiredParams: {
url: "Website URL to convert (must be publicly accessible)"
},
optionalParams: {
visualStyle: "Template style (e.g., 'DynamicProductTemplate', 'MinimalClean')",
scriptStyle: "Narration style (e.g., 'EnthusiasticWriter', 'ProfessionalNarrator')",
aspectRatio: "Video format: '16:9', '9:16', '1:1'",
language: "Language code (default: 'en')",
videoLength: "Desired length in seconds",
targetAudience: "Target audience description",
targetPlatform: "Platform optimization ('YouTube', 'TikTok', 'Instagram')"
},
examples: includeExamples ? [
{
description: "Convert product page to YouTube video",
code: `{
"url": "https://example.com/product",
"visualStyle": "DynamicProductTemplate",
"targetPlatform": "YouTube",
"aspectRatio": "16:9"
}`
}
] : []
},
"generate_text_to_speech": {
description: "Generate natural-sounding speech from text",
requiredParams: {
text: "Text to convert to speech",
voiceId: "Voice ID (get from creatify://voices resource)"
},
optionalParams: {
name: "Custom name for the audio file",
waitForCompletion: "true/false - wait for audio to complete"
},
examples: includeExamples ? [
{
description: "Generate professional narration",
code: `{
"text": "Welcome to our comprehensive guide.",
"voiceId": "en-US-AriaNeural",
"name": "intro-narration"
}`
}
] : []
},
"create_multi_avatar_conversation": {
description: "Create videos with multiple avatars having conversations",
requiredParams: {
conversation: "Array of conversation parts with avatarId and text",
aspectRatio: "Video format: '16:9', '9:16', '1:1'"
},
optionalParams: {
waitForCompletion: "true/false - wait for video to complete"
},
examples: includeExamples ? [
{
description: "Two-person conversation",
code: `{
"conversation": [
{
"avatarId": "anna_costume1_cameraA",
"text": "Hi! Let me introduce our new feature.",
"voiceId": "en-US-AriaNeural"
},
{
"avatarId": "john_suit_cameraB",
"text": "That sounds amazing! Tell me more.",
"voiceId": "en-US-GuyNeural"
}
],
"aspectRatio": "16:9"
}`
}
] : []
},
"create_custom_template_video": {
description: "Generate videos using pre-designed custom templates",
requiredParams: {
templateId: "Template ID (get from creatify://templates resource)",
data: "Template data as key-value pairs (varies by template)"
},
optionalParams: {
aspectRatio: "Video format override",
waitForCompletion: "true/false - wait for video to complete"
},
examples: includeExamples ? [
{
description: "Product showcase template",
code: `{
"templateId": "product-showcase-template",
"data": {
"productName": "Amazing Widget",
"productDescription": "Revolutionary new widget",
"price": "$99.99",
"features": ["Feature 1", "Feature 2"]
}
}`
}
] : []
},
"create_ai_edited_video": {
description: "Automatically edit and enhance existing videos using AI",
requiredParams: {
videoUrl: "URL to video file to be edited",
editingStyle: "Editing style ('film', 'commercial', 'social', 'vlog')"
},
optionalParams: {
name: "Custom name for the edited video",
waitForCompletion: "true/false - wait for editing to complete"
},
examples: includeExamples ? [
{
description: "Edit raw footage into commercial",
code: `{
"videoUrl": "https://example.com/raw-footage.mp4",
"editingStyle": "commercial",
"name": "product-commercial"
}`
}
] : []
},
"get_video_status": {
description: "Check status and progress of video generation tasks",
requiredParams: {
videoId: "ID of the video/task to check",
videoType: "Type: 'lipsync', 'url-to-video', 'text-to-speech', 'multi-avatar', 'ai-editing', 'custom-template'"
},
examples: includeExamples ? [
{
description: "Check avatar video status",
code: `{
"videoId": "video_abc123",
"videoType": "lipsync"
}`
}
] : []
},
"create_ai_shorts": {
description: "Create short-form videos using AI (perfect for TikTok, Instagram Reels, YouTube Shorts)",
requiredParams: {
prompt: "Text prompt describing the short video content"
},
optionalParams: {
aspectRatio: "Video format (default: '9:16' for shorts)",
duration: "Duration in seconds (typically 15-60 for shorts)",
style: "Visual style for the video",
waitForCompletion: "true/false - wait for video completion"
},
examples: includeExamples ? [
{
description: "Create a TikTok-style short",
code: `{
"prompt": "A quick tutorial on making coffee with energetic music",
"aspectRatio": "9:16",
"duration": 30,
"style": "energetic"
}`
}
] : []
},
"generate_ai_script": {
description: "Generate AI-powered scripts for videos",
requiredParams: {
topic: "Topic or subject for the script"
},
optionalParams: {
scriptType: "Type of script ('commercial', 'educational', 'entertainment')",
duration: "Target duration in seconds",
tone: "Tone of script ('professional', 'casual', 'enthusiastic')",
targetAudience: "Target audience description",
waitForCompletion: "true/false - wait for script completion"
},
examples: includeExamples ? [
{
description: "Generate educational script",
code: `{
"topic": "Introduction to renewable energy",
"scriptType": "educational",
"duration": 120,
"tone": "professional",
"targetAudience": "college students"
}`
}
] : []
},
"create_custom_avatar": {
description: "Design and create your own custom avatar (DYOA - Design Your Own Avatar)",
requiredParams: {
description: "Detailed description of the avatar to create"
},
optionalParams: {
gender: "Gender ('male', 'female', 'non-binary')",
ageRange: "Age range (e.g., '20-30', '40-50')",
ethnicity: "Ethnicity or appearance description",
clothing: "Clothing style description",
background: "Background setting description",
waitForCompletion: "true/false - wait for avatar creation"
},
examples: includeExamples ? [
{
description: "Create professional business avatar",
code: `{
"description": "Professional businesswoman with confident demeanor",
"gender": "female",
"ageRange": "30-40",
"clothing": "Navy blue business suit",
"background": "Modern office setting"
}`
}
] : []
},
"manage_music": {
description: "Manage music files for video backgrounds",
requiredParams: {
action: "Action to perform ('list', 'upload', 'delete', 'get')"
},
optionalParams: {
musicId: "Music ID (required for 'delete' and 'get' actions)",
musicUrl: "URL to music file (required for 'upload' action)",
name: "Name for the music (optional for 'upload' action)"
},
examples: includeExamples ? [
{
description: "List all available music",
code: `{
"action": "list"
}`
},
{
description: "Upload new background music",
code: `{
"action": "upload",
"musicUrl": "https://example.com/background-music.mp3",
"name": "Upbeat Background Track"
}`
}
] : []
},
"create_advanced_lipsync": {
description: "Create advanced lip-sync videos with enhanced emotion and gesture control",
requiredParams: {
text: "Text to be spoken by the avatar",
avatarId: "ID of the avatar to use",
voiceId: "Voice ID for the avatar",
aspectRatio: "Video aspect ratio"
},
optionalParams: {
emotionIntensity: "Emotion intensity (0-1)",
gestureIntensity: "Gesture intensity (0-1)",
backgroundMusic: "Background music ID",
name: "Custom name for the video",
waitForCompletion: "true/false - wait for video completion"
},
examples: includeExamples ? [
{
description: "Create expressive avatar video",
code: `{
"text": "I'm so excited to share this amazing news with you!",
"avatarId": "anna_costume1_cameraA",
"voiceId": "en-US-AriaNeural",
"aspectRatio": "16:9",
"emotionIntensity": 0.8,
"gestureIntensity": 0.7
}`
}
] : []
}
};
const guide = toolGuides[toolName as keyof typeof toolGuides];
if (!guide) {
return {
content: [{
type: "text",
text: `Tool '${toolName}' not found. Available tools: ${Object.keys(toolGuides).join(', ')}`
}],
isError: true
};
}
let response = `# ${toolName}\n\n${guide.description}\n\n`;
response += "## Required Parameters:\n";
for (const [param, desc] of Object.entries(guide.requiredParams)) {
response += `- **${param}**: ${desc}\n`;
}
if (Object.keys(guide.optionalParams).length > 0) {
response += "\n## Optional Parameters:\n";
for (const [param, desc] of Object.entries(guide.optionalParams)) {
response += `- **${param}**: ${desc}\n`;
}
}
if (includeExamples && guide.examples && guide.examples.length > 0) {
response += "\n## Examples:\n";
guide.examples.forEach((example, index) => {
response += `\n### ${example.description}:\n\`\`\`json\n${example.code}\n\`\`\`\n`;
});
}
response += "\n## Tips:\n";
response += "- Use creatify://avatars resource to get available avatar IDs\n";
response += "- Use creatify://voices resource to get available voice IDs\n";
response += "- Use creatify://credits resource to check remaining credits\n";
response += "- Set waitForCompletion=true for synchronous operation\n";
response += "- Use get_video_status to monitor long-running tasks\n";
return {
content: [{
type: "text",
text: response
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error getting usage information: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// AI Editing tool
server.tool(
"create_ai_edited_video",
{
videoUrl: z.string().url().describe("URL to the video file to be edited"),
editingStyle: z.string().describe("Editing style (e.g., 'film', 'commercial', 'social', 'vlog')"),
name: z.string().optional().describe("Optional name for the editing task"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for editing completion before returning")
},
async ({ videoUrl, editingStyle, name, webhookUrl, waitForCompletion }) => {
try {
const params: any = {
video_url: videoUrl,
editing_style: editingStyle
};
if (name) params.name = name;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
result = await this.creatify.aiEditing.createAndWaitForAiEditing(params);
} else {
result = await this.creatify.aiEditing.createAiEditing(params);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating AI edited video: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// AI Shorts tool
server.tool(
"create_ai_shorts",
{
prompt: z.string().describe("Text prompt describing the short video to create"),
aspectRatio: z.enum(["16:9", "9:16", "1:1"]).optional().default("9:16").describe("Video aspect ratio (shorts are typically 9:16)"),
duration: z.number().optional().describe("Desired duration in seconds (typically 15-60 for shorts)"),
style: z.string().optional().describe("Visual style for the short video"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for video completion before returning")
},
async ({ prompt, aspectRatio, duration, style, webhookUrl, waitForCompletion }) => {
try {
const params: any = {
prompt,
aspect_ratio: aspectRatio
};
if (duration) params.duration = duration;
if (style) params.style = style;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
result = await this.creatify.aiShorts.createAndWaitForAiShorts(params);
} else {
result = await this.creatify.aiShorts.createAiShorts(params);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating AI shorts: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// AI Scripts tool
server.tool(
"generate_ai_script",
{
topic: z.string().describe("Topic or subject for the script"),
scriptType: z.string().optional().describe("Type of script (e.g., 'commercial', 'educational', 'entertainment')"),
duration: z.number().optional().describe("Target duration in seconds"),
tone: z.string().optional().describe("Tone of the script (e.g., 'professional', 'casual', 'enthusiastic')"),
targetAudience: z.string().optional().describe("Target audience for the script"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for script completion before returning")
},
async ({ topic, scriptType, duration, tone, targetAudience, webhookUrl, waitForCompletion }) => {
try {
const params: any = {
topic
};
if (scriptType) params.script_type = scriptType;
if (duration) params.duration = duration;
if (tone) params.tone = tone;
if (targetAudience) params.target_audience = targetAudience;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
result = await this.creatify.aiScripts.createAndWaitForAiScript(params);
} else {
result = await this.creatify.aiScripts.createAiScript(params);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error generating AI script: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Design Your Own Avatar (DYOA) tool
server.tool(
"create_custom_avatar",
{
description: z.string().describe("Description of the avatar to create"),
gender: z.enum(["male", "female", "non-binary"]).optional().describe("Gender of the avatar"),
ageRange: z.string().optional().describe("Age range (e.g., '20-30', '40-50')"),
ethnicity: z.string().optional().describe("Ethnicity or appearance description"),
clothing: z.string().optional().describe("Clothing style description"),
background: z.string().optional().describe("Background setting description"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for avatar creation before returning")
},
async ({ description, gender, ageRange, ethnicity, clothing, background, webhookUrl, waitForCompletion }) => {
try {
const params: any = {
description
};
if (gender) params.gender = gender;
if (ageRange) params.age_range = ageRange;
if (ethnicity) params.ethnicity = ethnicity;
if (clothing) params.clothing = clothing;
if (background) params.background = background;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
result = await this.creatify.dyoa.createAndWaitForDyoa(params);
} else {
result = await this.creatify.dyoa.createDyoa(params);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating custom avatar: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Music Management tool
server.tool(
"manage_music",
{
action: z.enum(["list", "upload", "delete", "get"]).describe("Action to perform: list, upload, delete, or get music"),
musicId: z.string().optional().describe("Music ID (required for delete and get actions)"),
musicUrl: z.string().optional().describe("URL to music file (required for upload action)"),
name: z.string().optional().describe("Name for the music (optional for upload action)")
},
async ({ action, musicId, musicUrl, name }) => {
try {
let result;
switch (action) {
case "list":
result = await this.creatify.musics.getMusics();
break;
case "upload":
if (!musicUrl) {
throw new Error("musicUrl is required for upload action");
}
const uploadParams: any = { music_url: musicUrl };
if (name) uploadParams.name = name;
result = await this.creatify.musics.createMusic(uploadParams);
break;
case "delete":
if (!musicId) {
throw new Error("musicId is required for delete action");
}
result = await this.creatify.musics.deleteMusic(musicId);
break;
case "get":
if (!musicId) {
throw new Error("musicId is required for get action");
}
result = await this.creatify.musics.getMusic(musicId);
break;
default:
throw new Error(`Unknown action: ${action}`);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error managing music: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
// Advanced Lipsync V2 tool
server.tool(
"create_advanced_lipsync",
{
text: z.string().describe("Text to be spoken by the avatar"),
avatarId: z.string().describe("ID of the avatar to use"),
voiceId: z.string().describe("Voice ID for the avatar"),
aspectRatio: z.enum(["16:9", "9:16", "1:1"]).describe("Video aspect ratio"),
emotionIntensity: z.number().optional().describe("Emotion intensity (0-1)"),
gestureIntensity: z.number().optional().describe("Gesture intensity (0-1)"),
backgroundMusic: z.string().optional().describe("Background music ID"),
name: z.string().optional().describe("Optional name for the video"),
webhookUrl: z.string().optional().describe("Optional webhook URL for completion notification"),
waitForCompletion: z.boolean().optional().default(false).describe("Whether to wait for video completion before returning")
},
async ({ text, avatarId, voiceId, aspectRatio, emotionIntensity, gestureIntensity, backgroundMusic, name, webhookUrl, waitForCompletion }) => {
try {
const params: any = {
text,
avatar_id: avatarId,
voice_id: voiceId,
aspect_ratio: aspectRatio
};
if (emotionIntensity !== undefined) params.emotion_intensity = emotionIntensity;
if (gestureIntensity !== undefined) params.gesture_intensity = gestureIntensity;
if (backgroundMusic) params.background_music = backgroundMusic;
if (name) params.name = name;
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
if (waitForCompletion) {
result = await this.creatify.lipsyncV2.createAndWaitForLipsyncV2(params);
} else {
result = await this.creatify.lipsyncV2.createLipsyncV2(params);
}
return {
content: [{
type: "text",
text: JSON.stringify(result, null, 2)
}]
};
} catch (error) {
return {
content: [{
type: "text",
text: `Error creating advanced lipsync: ${error instanceof Error ? error.message : String(error)}`
}],
isError: true
};
}
}
);
}
}