midjourney-mcp
Version:
A Model Context Protocol server for Midjourney integration via MJ API
244 lines ⢠11 kB
JavaScript
/**
* Additional Midjourney tool handlers
*/
import { createTextContent, createErrorResult, createSuccessResult, isTaskCompleted, isTaskSuccessful, } from "../types/index.js";
/**
* Handle blend task
*/
export async function handleBlendTask(args, apiClient, _taskPoller) {
try {
const response = await apiClient.submitBlend(args.images);
if (response.code !== 1 || !response.result) {
return createErrorResult(`Failed to submit blend task: ${response.description}`);
}
const taskId = response.result;
return createSuccessResult(`šØ **Image Blending Started!**\n\n` +
`**Task ID:** ${taskId}\n` +
`**Images:** ${args.images.length} images to blend\n` +
`**Status:** Task submitted successfully\n\n` +
`š” **Next Steps:**\n` +
`⢠Use \`midjourney_get_task\` with task ID "${taskId}" to check progress\n` +
`⢠Blending typically takes 1-2 minutes\n` +
`⢠The result will combine elements from all input images\n\n` +
`ā±ļø **Estimated Time:** 60-120 seconds`);
}
catch (error) {
return createErrorResult(`Failed to submit blend task: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle describe task
*/
export async function handleDescribeTask(args, apiClient, _taskPoller) {
try {
const response = await apiClient.submitDescribe(args.image);
if (response.code !== 1 || !response.result) {
return createErrorResult(`Failed to submit describe task: ${response.description}`);
}
const taskId = response.result;
return createSuccessResult(`š **Image Description Started!**\n\n` +
`**Task ID:** ${taskId}\n` +
`**Status:** Task submitted successfully\n\n` +
`š” **Next Steps:**\n` +
`⢠Use \`midjourney_get_task\` with task ID "${taskId}" to check progress\n` +
`⢠Description typically takes 30-60 seconds\n` +
`⢠You'll get detailed text descriptions that can be used as prompts\n\n` +
`ā±ļø **Estimated Time:** 30-60 seconds`);
}
catch (error) {
return createErrorResult(`Failed to submit describe task: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle get task status
*/
export async function handleGetTask(args, _apiClient, taskPoller) {
try {
const task = await taskPoller.getTaskStatus(args.task_id);
if (!task) {
return createErrorResult(`Task not found: ${args.task_id}`);
}
const progress = taskPoller.getTaskProgress(task);
const statusText = taskPoller.formatTaskStatus(task);
const isCompleted = isTaskCompleted(task.status || 'NOT_START');
const isSuccessful = isTaskSuccessful(task.status || 'NOT_START');
// Calculate task duration
let durationInfo = '';
if (task.submitTime) {
const submitTime = new Date(task.submitTime);
const now = new Date();
const durationMs = now.getTime() - submitTime.getTime();
const durationMinutes = Math.floor(durationMs / 60000);
const durationSeconds = Math.floor((durationMs % 60000) / 1000);
durationInfo = `**Duration:** ${durationMinutes}m ${durationSeconds}s\n`;
}
let content = [
createTextContent(`š **Task Status Report**\n\n` +
`**Task ID:** ${args.task_id}\n` +
`**Action:** ${task.action || 'Unknown'}\n` +
`**Status:** ${statusText}\n` +
`**Progress:** ${progress}%\n` +
durationInfo)
];
if (task.prompt) {
content.push(createTextContent(`**Prompt:** ${task.prompt}`));
}
if (task.promptEn && task.promptEn !== task.prompt) {
content.push(createTextContent(`**English Prompt:** ${task.promptEn}`));
}
if (isCompleted) {
if (isSuccessful) {
content.push(createTextContent(`\nā
**Task Completed Successfully!**`));
if (task.imageUrl) {
// Display image using markdown format for direct rendering
content.push(createTextContent(`\nš¼ļø **Generated Image:**\n\n` +
`\n\n` +
`šø **Direct Link:** ${task.imageUrl}`));
}
if (task.buttons && task.buttons.length > 0) {
let actionsText = '\nš® **Available Actions:**\n';
const upscaleButtons = task.buttons.filter(b => b.label?.startsWith('U'));
const variationButtons = task.buttons.filter(b => b.label?.startsWith('V'));
const rerollButtons = task.buttons.filter(b => b.emoji === 'š');
if (upscaleButtons.length > 0) {
actionsText += '\n**š Upscale (Higher Resolution):**\n';
upscaleButtons.forEach(button => {
actionsText += `⢠**${button.label}**: Upscale image ${button.label.slice(1)}\n`;
});
}
if (variationButtons.length > 0) {
actionsText += '\n**š Variations (New Versions):**\n';
variationButtons.forEach(button => {
actionsText += `⢠**${button.label}**: Create variation of image ${button.label.slice(1)}\n`;
});
}
if (rerollButtons.length > 0) {
actionsText += '\n**š Reroll (Completely New):**\n';
actionsText += `⢠**Reroll**: Generate 4 new images with the same prompt\n`;
}
actionsText += '\nš” **How to use:** `midjourney_action` with task_id `' + args.task_id + '` and the action name (e.g., "U1", "V2", etc.)';
content.push(createTextContent(actionsText));
}
}
else {
content.push(createTextContent(`\nā **Task failed**`));
if (task.failReason) {
content.push(createTextContent(`**Failure Reason:** ${task.failReason}`));
}
}
}
else {
// Check if task appears stuck
const isStuck = taskPoller.isTaskStuck(task);
if (isStuck) {
content.push(createTextContent(`\nā ļø **Task appears to be stuck!**\n\n` +
`This task has been running for a long time. This could indicate:\n` +
`⢠The API service is experiencing delays\n` +
`⢠The task queue is busy\n` +
`⢠There may be an issue with the specific request\n\n` +
`**Suggestions:**\n` +
`⢠Wait a bit longer and check again\n` +
`⢠Try submitting a new task if this persists\n` +
`⢠Check the API service status`));
}
else {
content.push(createTextContent(`\nā³ Task is still processing. Check again in a few moments.\n\n` +
`š” **Tip:** Midjourney tasks typically take 1-3 minutes to complete.`));
}
}
// Add timing information if available
if (task.submitTime || task.startTime || task.finishTime) {
let timingInfo = '\nš
**Timing:**\n';
if (task.submitTime) {
timingInfo += `⢠Submitted: ${new Date(task.submitTime).toLocaleString()}\n`;
}
if (task.startTime) {
timingInfo += `⢠Started: ${new Date(task.startTime).toLocaleString()}\n`;
}
if (task.finishTime) {
timingInfo += `⢠Finished: ${new Date(task.finishTime).toLocaleString()}\n`;
}
content.push(createTextContent(timingInfo));
}
return {
content,
isError: false
};
}
catch (error) {
return createErrorResult(`Failed to get task status: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Handle action task (button actions)
*/
export async function handleActionTask(args, _apiClient, taskPoller) {
try {
// First get the task to find the button
const task = await taskPoller.getTaskStatus(args.task_id);
if (!task || !task.buttons) {
return createErrorResult(`Task not found or has no available actions: ${args.task_id}`);
}
// Find the button by label or customId
const button = task.buttons.find(btn => btn.label === args.action || btn.customId === args.action);
if (!button) {
const availableActions = task.buttons.map(btn => btn.label).join(', ');
return createErrorResult(`Action "${args.action}" not found. Available actions: ${availableActions}`);
}
// Submit the action using the button's customId
// Note: This would need to be implemented in the API client
// For now, return a placeholder response
return createSuccessResult(`ā
Action "${button.label}" would be executed on task ${args.task_id}\n\n` +
`**Button:** ${button.label} ${button.emoji}\n` +
`**Custom ID:** ${button.customId}\n\n` +
`Note: Button action execution is not yet implemented in the API client.`);
}
catch (error) {
return createErrorResult(`Failed to execute action: ${error instanceof Error ? error.message : String(error)}`);
}
}
/**
* Build prompt string with parameters
*/
export function buildPromptString(args) {
let prompt = args.prompt;
// Add aspect ratio
if (args.aspect_ratio) {
prompt += ` --ar ${args.aspect_ratio}`;
}
// Add quality
if (args.quality) {
const qualityMap = { low: '0.25', medium: '0.5', high: '1' };
prompt += ` --q ${qualityMap[args.quality] || '0.5'}`;
}
// Add style
if (args.style === 'raw') {
prompt += ` --style raw`;
}
// Add model
if (args.model && args.model !== 'midjourney') {
prompt += ` --${args.model}`;
}
// Add chaos
if (args.chaos !== undefined) {
prompt += ` --chaos ${args.chaos}`;
}
// Add stylize
if (args.stylize !== undefined) {
prompt += ` --stylize ${args.stylize}`;
}
// Add weird
if (args.weird !== undefined) {
prompt += ` --weird ${args.weird}`;
}
// Add seed
if (args.seed !== undefined) {
prompt += ` --seed ${args.seed}`;
}
// Add negative prompt
if (args.no) {
prompt += ` --no ${args.no}`;
}
return prompt;
}
//# sourceMappingURL=midjourney-handlers.js.map