#!/usr/bin/env node
/**
* ImaginePro MCP Server
*
* A Model Context Protocol (MCP) server that integrates ImaginePro AI image and video
* generation capabilities with AI assistants like Claude.
*
* Environment Variables:
* - IMAGINEPRO_API_KEY: Your ImaginePro API key (required)
* - IMAGINEPRO_BASE_URL: API base URL (optional, defaults to https://api.imaginepro.ai)
* - IMAGINEPRO_TIMEOUT: Request timeout in milliseconds (optional, defaults to 300000)
*
* @see https://github.com/imaginpro/imaginepro-mcp-server
*/
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import { StdioServerTransport } from '@modelcontextprotocol/sdk/server/stdio.js';
import { z } from 'zod';
import { readFileSync, existsSync } from 'fs';
import { join } from 'path';
import { homedir } from 'os';
// Dynamic import for ImaginePro SDK
let ImagineProSDK: any;
/**
* Configuration interface
*/
interface Config {
apiKey?: string;
baseUrl?: string;
timeout?: number;
}
/**
* Load configuration from file if it exists
* Checks for config in the following locations:
* 1. ~/.imaginepro/config.json
* 2. ./.imaginepro.json
*/
function loadConfigFile(): Config {
const configPaths = [
join(homedir(), '.imaginepro', 'config.json'),
join(process.cwd(), '.imaginepro.json'),
];
for (const configPath of configPaths) {
try {
if (existsSync(configPath)) {
const configContent = readFileSync(configPath, 'utf-8');
const config = JSON.parse(configContent);
console.error(`Loaded configuration from: ${configPath}`);
return config;
}
} catch (error) {
console.error(`Warning: Failed to load config from ${configPath}:`, error);
}
}
return {};
}
/**
* Initialize the ImaginePro SDK client
* Validates required environment variables and creates SDK instance
* Priority: Environment variables > Config file
*/
function initializeImaginePro() {
// Load config file first
const fileConfig = loadConfigFile();
// Environment variables take precedence
const apiKey = process.env.IMAGINEPRO_API_KEY || fileConfig.apiKey;
const baseUrl = process.env.IMAGINEPRO_BASE_URL || fileConfig.baseUrl || 'https://api.imaginepro.ai';
const timeout = parseInt(
process.env.IMAGINEPRO_TIMEOUT ||
(fileConfig.timeout ? String(fileConfig.timeout) : '300000')
);
// Validate API key
if (!apiKey) {
throw new Error(
'IMAGINEPRO_API_KEY is required. Set it as an environment variable or in a config file.\n' +
'Environment: export IMAGINEPRO_API_KEY="sk-your-key"\n' +
'Config file: ~/.imaginepro/config.json or ./.imaginepro.json'
);
}
console.error('ImaginePro MCP Server Configuration:');
console.error(` Base URL: ${baseUrl}`);
console.error(` Timeout: ${timeout}ms`);
console.error(` API Key: ${apiKey.substring(0, Math.min(7, apiKey.length))}...${apiKey.substring(Math.max(0, apiKey.length - 4))}`);
return new ImagineProSDK({
apiKey,
baseUrl,
timeout,
});
}
/**
* Standardize error responses
*/
function formatErrorResponse(error: any, context: string) {
const errorMessage = error?.message || 'Unknown error occurred';
const errorCode = error?.code || error?.statusCode;
let detailedMessage = `${context}: ${errorMessage}`;
if (errorCode) {
detailedMessage += ` (Code: ${errorCode})`;
}
console.error(`Error in ${context}:`, error);
return {
success: false,
error: detailedMessage,
};
}
/**
* Main server initialization and setup
*/
async function main() {
try {
// Dynamic import of ImaginePro SDK (CommonJS module)
const imagineProModule = await import('imaginepro') as any;
// Handle both CommonJS default export and named export
ImagineProSDK = imagineProModule.default?.default || imagineProModule.default || imagineProModule.ImagineProSDK;
if (!ImagineProSDK || typeof ImagineProSDK !== 'function') {
throw new Error('Failed to load ImagineProSDK. Please ensure the imaginepro package is installed correctly.');
}
// Initialize ImaginePro client
const imagineProClient = initializeImaginePro();
// Create MCP server instance
const server = new McpServer({
name: 'imaginepro-mcp-server',
version: '1.0.0',
});
/**
* Tool 1: Generate Image (Text-to-Image)
* Basic text-to-image generation using a text prompt
*/
server.registerTool(
'generate-image',
{
title: 'Generate Image',
description: 'Generate an AI image from a text prompt using ImaginePro',
inputSchema: {
prompt: z.string().describe('Detailed description of the image to generate'),
ref: z.string().optional().describe('Optional reference ID for tracking'),
webhookOverride: z.string().url().optional().describe('Optional webhook URL for async notifications'),
},
outputSchema: {
success: z.boolean(),
messageId: z.string().optional(),
imageUrl: z.string().optional(),
error: z.string().optional(),
},
},
async ({ prompt, ref, webhookOverride }) => {
try {
// Validate prompt
if (!prompt || prompt.trim().length === 0) {
throw new Error('Prompt cannot be empty');
}
const initiateResponse = await imagineProClient.imagine({
prompt,
ref,
webhookOverride,
});
// Wait for the image generation to complete
const response = await imagineProClient.fetchMessage(initiateResponse.messageId);
const output = {
success: true,
messageId: response.messageId,
imageUrl: response.uri,
status: response.status,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `✓ Image generated successfully!\nMessage ID: ${output.messageId}\nImage URL: ${output.imageUrl}\nStatus: ${output.status}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Image generation failed');
return {
content: [
{
type: 'text',
text: `✗ ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
/**
* Tool 2: Gemini Imagine (Multi-modal Generation)
* Generate images using both text and image inputs
*/
server.registerTool(
'gemini-imagine',
{
title: 'Gemini Multi-modal Image Generation',
description: 'Generate images using multi-modal inputs (text + images)',
inputSchema: {
contents: z.array(
z.object({
type: z.enum(['text', 'image']).describe('Content type'),
text: z.string().optional().describe('Text content'),
url: z.string().url().optional().describe('Image URL'),
})
).describe('Array of content items (text and/or images)'),
model: z.string().optional().describe('Model to use (default: gemini-2.5-flash-image-preview)'),
ref: z.string().optional().describe('Optional reference ID'),
webhookOverride: z.string().url().optional().describe('Optional webhook URL'),
},
outputSchema: {
success: z.boolean(),
messageId: z.string().optional(),
imageUrl: z.string().optional(),
status: z.string().optional(),
progress: z.number().optional(),
error: z.string().optional(),
},
},
async ({ contents, model, ref, webhookOverride }) => {
try {
const initiateResponse = await imagineProClient.geminiImagine({
contents,
model,
ref,
webhookOverride,
});
// Wait for the image generation to complete
const response = await imagineProClient.fetchMessage(initiateResponse.messageId);
const output = {
success: true,
messageId: response.messageId,
imageUrl: response.uri,
status: response.status,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `✓ Multi-modal image generated successfully!\nMessage ID: ${output.messageId}\nImage URL: ${output.imageUrl}\nStatus: ${output.status}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Gemini generation failed');
return {
content: [
{
type: 'text',
text: `Error with Gemini generation: ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
/**
* Tool 3: Generate Video
* Create video animations from start and end frames
*/
server.registerTool(
'generate-video',
{
title: 'Generate Video',
description: 'Generate a video animation from start and end frame images',
inputSchema: {
prompt: z.string().describe('Description of the video transition/animation'),
startFrameUrl: z.string().url().describe('URL of the starting frame image'),
endFrameUrl: z.string().url().describe('URL of the ending frame image'),
ref: z.string().optional().describe('Optional reference ID'),
webhookOverride: z.string().url().optional().describe('Optional webhook URL'),
},
outputSchema: {
success: z.boolean(),
messageId: z.string().optional(),
videoUrl: z.string().optional(),
status: z.string().optional(),
progress: z.number().optional(),
error: z.string().optional(),
},
},
async ({ prompt, startFrameUrl, endFrameUrl, ref, webhookOverride }) => {
try {
const initiateResponse = await imagineProClient.generateVideo({
prompt,
startFrameUrl,
endFrameUrl,
ref,
webhookOverride,
});
// Wait for the video generation to complete
const response = await imagineProClient.fetchMessage(initiateResponse.messageId);
const output = {
success: true,
messageId: response.messageId,
videoUrl: response.uri,
status: response.status,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `✓ Video generated successfully!\nMessage ID: ${output.messageId}\nVideo URL: ${output.videoUrl}\nStatus: ${output.status}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Video generation failed');
return {
content: [
{
type: 'text',
text: `Error generating video: ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
/**
* Tool 4: Upscale Image
* Enhance image resolution and quality
*/
server.registerTool(
'upscale-image',
{
title: 'Upscale Image',
description: 'Enhance image resolution and quality',
inputSchema: {
messageId: z.string().describe('Message ID of the image to upscale'),
ref: z.string().optional().describe('Optional reference ID'),
webhookOverride: z.string().url().optional().describe('Optional webhook URL'),
},
outputSchema: {
success: z.boolean(),
messageId: z.string().optional(),
imageUrl: z.string().optional(),
status: z.string().optional(),
progress: z.number().optional(),
error: z.string().optional(),
},
},
async ({ messageId, ref, webhookOverride }) => {
try {
const initiateResponse = await imagineProClient.upscale({
messageId,
ref,
webhookOverride,
});
// Wait for the upscaling to complete
const response = await imagineProClient.fetchMessage(initiateResponse.messageId);
const output = {
success: true,
messageId: response.messageId,
imageUrl: response.uri,
status: response.status,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `✓ Image upscaled successfully!\nMessage ID: ${output.messageId}\nUpscaled Image URL: ${output.imageUrl}\nStatus: ${output.status}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Image upscaling failed');
return {
content: [
{
type: 'text',
text: `Error upscaling image: ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
/**
* Tool 5: Create Variant
* Generate alternative versions of an existing image
*/
server.registerTool(
'create-variant',
{
title: 'Create Image Variant',
description: 'Generate alternative versions of an existing image',
inputSchema: {
messageId: z.string().describe('Message ID of the base image'),
ref: z.string().optional().describe('Optional reference ID'),
webhookOverride: z.string().url().optional().describe('Optional webhook URL'),
},
outputSchema: {
success: z.boolean(),
messageId: z.string().optional(),
imageUrl: z.string().optional(),
status: z.string().optional(),
progress: z.number().optional(),
error: z.string().optional(),
},
},
async ({ messageId, ref, webhookOverride }) => {
try {
const initiateResponse = await imagineProClient.variant({
messageId,
ref,
webhookOverride,
});
// Wait for the variant to complete
const response = await imagineProClient.fetchMessage(initiateResponse.messageId);
const output = {
success: true,
messageId: response.messageId,
imageUrl: response.uri,
status: response.status,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `✓ Variant created successfully!\nMessage ID: ${output.messageId}\nVariant Image URL: ${output.imageUrl}\nStatus: ${output.status}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Variant creation failed');
return {
content: [
{
type: 'text',
text: `Error creating variant: ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
/**
* Tool 6: Reroll Image
* Regenerate an image with the same prompt
*/
server.registerTool(
'reroll-image',
{
title: 'Reroll Image',
description: 'Regenerate an image using the same prompt',
inputSchema: {
messageId: z.string().describe('Message ID of the image to reroll'),
ref: z.string().optional().describe('Optional reference ID'),
webhookOverride: z.string().url().optional().describe('Optional webhook URL'),
},
outputSchema: {
success: z.boolean(),
messageId: z.string().optional(),
imageUrl: z.string().optional(),
status: z.string().optional(),
progress: z.number().optional(),
error: z.string().optional(),
},
},
async ({ messageId, ref, webhookOverride }) => {
try {
const initiateResponse = await imagineProClient.reroll({
messageId,
ref,
webhookOverride,
});
// Wait for the reroll to complete
const response = await imagineProClient.fetchMessage(initiateResponse.messageId);
const output = {
success: true,
messageId: response.messageId,
imageUrl: response.uri,
status: response.status,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `✓ Image rerolled successfully!\nMessage ID: ${output.messageId}\nNew Image URL: ${output.imageUrl}\nStatus: ${output.status}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Image reroll failed');
return {
content: [
{
type: 'text',
text: `Error rerolling image: ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
/**
* Tool 7: Inpaint Image
* Edit specific regions of an image
*/
server.registerTool(
'inpaint-image',
{
title: 'Inpaint Image',
description: 'Edit specific regions of an image using a mask',
inputSchema: {
messageId: z.string().describe('Message ID of the base image'),
maskUrl: z.string().url().describe('URL of the mask image (white areas will be edited)'),
prompt: z.string().describe('Description of what to generate in masked areas'),
ref: z.string().optional().describe('Optional reference ID'),
webhookOverride: z.string().url().optional().describe('Optional webhook URL'),
},
outputSchema: {
success: z.boolean(),
messageId: z.string().optional(),
imageUrl: z.string().optional(),
status: z.string().optional(),
progress: z.number().optional(),
error: z.string().optional(),
},
},
async ({ messageId, maskUrl, prompt, ref, webhookOverride }) => {
try {
const initiateResponse = await imagineProClient.inpainting({
messageId,
maskUrl,
prompt,
ref,
webhookOverride,
});
// Wait for the inpainting to complete
const response = await imagineProClient.fetchMessage(initiateResponse.messageId);
const output = {
success: true,
messageId: response.messageId,
imageUrl: response.uri,
status: response.status,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `✓ Image inpainted successfully!\nMessage ID: ${output.messageId}\nInpainted Image URL: ${output.imageUrl}\nStatus: ${output.status}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Image inpainting failed');
return {
content: [
{
type: 'text',
text: `Error inpainting image: ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
/**
* Tool 8: Fetch Message Status
* Check the status of an image/video generation task
*/
server.registerTool(
'fetch-status',
{
title: 'Fetch Generation Status',
description: 'Check the status of an image or video generation task',
inputSchema: {
messageId: z.string().describe('Message ID to check status for'),
},
outputSchema: {
success: z.boolean(),
status: z.string().optional(),
imageUrl: z.string().optional(),
videoUrl: z.string().optional(),
progress: z.number().optional(),
error: z.string().optional(),
},
},
async ({ messageId }) => {
try {
const response = await imagineProClient.fetchMessage(messageId);
const output = {
success: true,
status: response.status,
imageUrl: response.uri,
videoUrl: response.uri,
progress: response.progress,
};
return {
content: [
{
type: 'text',
text: `Status: ${output.status}\nProgress: ${output.progress || 'N/A'}%\nResult URL: ${output.imageUrl || 'Not ready'}`,
},
],
structuredContent: output,
};
} catch (error: any) {
const output = formatErrorResponse(error, 'Failed to fetch status');
return {
content: [
{
type: 'text',
text: `Error fetching status: ${output.error}`,
},
],
structuredContent: output,
isError: true,
};
}
}
);
// Connect to stdio transport
const transport = new StdioServerTransport();
await server.connect(transport);
console.error('ImaginePro MCP Server started successfully');
} catch (error: any) {
console.error('Failed to start ImaginePro MCP Server:', error.message);
process.exit(1);
}
}
// Start the server
main();