@tsavo/creatify-mcp
Version:
MCP Server that exposes Creatify AI API capabilities as MCP tools for AI assistants and agents
1,330 lines (1,316 loc) • 55.6 kB
JavaScript
#!/usr/bin/env node
#!/usr/bin/env node
// src/index.ts
import { McpServer as McpServer2 } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
// src/server.ts
import { ResourceTemplate } from "@modelcontextprotocol/sdk/server/mcp.js";
import {
ListPromptsRequestSchema,
GetPromptRequestSchema,
SetLevelRequestSchema
} from "@modelcontextprotocol/sdk/types.js";
import { Creatify } from "@tsavo/creatify-api-ts";
import { z } from "zod";
var CreatifyMcpServer = class {
creatify;
logLevel = "info";
constructor(apiId, apiKey) {
this.creatify = new Creatify({
apiId,
apiKey
});
}
/**
* Send a log message to the client
*/
async log(server, level, message, data) {
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) {
await this.setupLogging(server);
await this.setupPrompts(server);
await this.setupResources(server);
await this.setupTools(server);
}
/**
* Set up logging capabilities
*/
async setupLogging(server) {
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)
*/
async setupPrompts(server) {
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
}
]
}
];
server.setRequestHandler(ListPromptsRequestSchema, async () => {
await this.log(server, "debug", "Listing available prompts", { count: prompts.length });
return { prompts };
});
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]"}.
Target audience: ${args.targetAudience || "General consumers"}
Desired duration: ${args.duration || "60-90"} seconds
Please:
1. First, use the 'generate_ai_script' tool to create an engaging script
2. Then use 'create_avatar_video' to bring it to life
3. Consider the target audience and keep the tone professional yet engaging
4. 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.
Call to action: ${args.callToAction || "Like and follow for more!"}
Workflow:
1. Use 'create_ai_shorts' for ${aspectRatio} short-form content
2. Keep it under 60 seconds for maximum engagement
3. Make the first 3 seconds attention-grabbing
4. Include trending elements for the platform
5. 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.
Learning objectives: ${args.learningObjectives || "[LEARNING_OBJECTIVES]"}
Include quiz: ${args.includeQuiz || "No"}
Educational workflow:
1. Generate a structured script with 'generate_ai_script'
2. Break content into digestible segments
3. Use 'create_avatar_video' with a professional, teaching tone
4. Include examples and practical applications
5. Summarize key points at the end
${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]"}
Brand message: "${args.brandMessage || "[BRAND_MESSAGE]"}"
Target demographic: ${args.targetDemographic || "[TARGET_DEMOGRAPHIC]"}
Budget tier: ${args.budget || "medium"}
Marketing workflow:
1. Use 'generate_ai_script' with persuasive, brand-aligned messaging
2. Create compelling visuals with 'create_url_to_video' if you have a landing page
3. Use 'create_avatar_video' for personal connection
4. Focus on emotional appeal and clear value proposition
5. Include strong call-to-action for ${args.campaignGoal}
6. 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]"}
Focus metrics: ${args.metrics || "engagement, retention"}
Improvement areas: ${args.improvementAreas || "overall performance"}
Analysis workflow:
1. Use 'create_ai_edited_video' to enhance the existing video
2. Apply editing style based on performance goals
3. Consider A/B testing different versions
4. Focus on the specified improvement areas
5. Generate recommendations for future content
6. 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)
*/
async setupResources(server) {
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)}`);
}
}
);
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)}`);
}
}
);
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)}`);
}
}
);
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)}`);
}
}
);
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)}`);
}
}
);
server.resource(
"avatar-details",
new ResourceTemplate("creatify://avatar/{avatarId}", { list: void 0 }),
async (uri, { avatarId }) => {
try {
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)
*/
async setupTools(server) {
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 = {
text,
creator: avatarId,
aspect_ratio: aspectRatio
};
if (voiceId) params.accent = voiceId;
if (name) params.name = name;
if (greenScreen !== void 0) params.green_screen = greenScreen;
if (noCaptions !== void 0) params.no_caption = noCaptions;
if (noMusic !== void 0) 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
};
}
}
);
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 {
const linkResponse = await this.creatify.urlToVideo.createLink({ url });
const videoParams = {
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) {
let attempts = 0;
const maxAttempts = 120;
while (attempts < maxAttempts && result.status !== "done" && result.status !== "error") {
await new Promise((resolve) => setTimeout(resolve, 5e3));
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
};
}
}
);
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 = {
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
};
}
}
);
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 = {
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 = {
video_inputs: videoInputs,
aspect_ratio: aspectRatio
};
if (webhookUrl) params.webhook_url = webhookUrl;
let result;
result = await this.creatify.avatar.createMultiAvatarLipsync(params);
if (waitForCompletion) {
let attempts = 0;
const maxAttempts = 120;
while (attempts < maxAttempts && result.status !== "done" && result.status !== "error") {
await new Promise((resolve) => setTimeout(resolve, 5e3));
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
};
}
}
);
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);
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
};
}
}
);
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 = {
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
};
}
}
);
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];
if (!guide) {
return {
content: [{
type: "text",
text: `Tool '${toolName}' not found. Available tools: ${Object.keys(toolGuides).join(", ")}`
}],
isError: true
};
}
let response = `# ${toolName}
${guide.description}
`;
response += "## Required Parameters:\n";
for (const [param, desc] of Object.entries(guide.requiredParams)) {
response += `- **${param}**: ${desc}
`;
}
if (Object.keys(guide.optionalParams).length > 0) {
response += "\n## Optional Parameters:\n";
for (const [param, desc] of Object.entries(guide.optionalParams)) {
response += `- **${param}**: ${desc}
`;
}
}
if (includeExamples && guide.examples && guide.examples.length > 0) {
response += "\n## Examples:\n";
guide.examples.forEach((example, index) => {
response += `
### ${example.description}:
\`\`\`json
${example.code}
\`\`\`
`;
});
}
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
};
}
}
);
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 = {
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
};
}
}
);
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 = {
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
};
}
}
);
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 = {
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
};
}
}
);
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 = {
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
};
}
}
);
server.tool(
"mana