minimax-mcp-tools
Version:
Async MCP server with Minimax API integration for image generation and text-to-speech
148 lines • 6.06 kB
JavaScript
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { ConfigManager } from './config/config-manager.js';
import { imageGenerationSchema, textToSpeechSchema, taskBarrierSchema, validateImageParams, validateTTSParams, validateTaskBarrierParams } from './config/schemas.js';
import { ImageGenerationService } from './services/image-service.js';
import { TextToSpeechService } from './services/tts-service.js';
import { RateLimitedTaskManager } from './core/task-manager.js';
import { ErrorHandler } from './utils/error-handler.js';
let config;
let imageService;
let ttsService;
let taskManager;
try {
config = ConfigManager.getInstance();
config.validate();
imageService = new ImageGenerationService();
ttsService = new TextToSpeechService();
taskManager = new RateLimitedTaskManager();
}
catch (error) {
console.error("❌ Failed to initialize:", ErrorHandler.formatErrorForUser(error));
process.exit(1);
}
const server = new McpServer({
name: "minimax-mcp-tools",
version: "2.2.0",
description: "Async Minimax AI integration for image generation and text-to-speech"
});
server.registerTool("submit_image_generation", {
title: "Submit Image Generation Task",
description: "Generate images asynchronously. RECOMMENDED: Submit multiple tasks in batch to saturate rate limits, then call task_barrier once to wait for all completions. Returns task ID only - actual files available after task_barrier.",
inputSchema: imageGenerationSchema.shape
}, async (params) => {
try {
const validatedParams = validateImageParams(params);
const { taskId } = await taskManager.submitImageTask(async () => {
return await imageService.generateImage(validatedParams);
});
return {
content: [{
type: "text",
text: `Task ${taskId} submitted`
}]
};
}
catch (error) {
ErrorHandler.logError(error, { tool: 'submit_image_generation', params });
return {
content: [{
type: "text",
text: `❌ Failed to submit image generation task: ${ErrorHandler.formatErrorForUser(error)}`
}]
};
}
});
server.registerTool("submit_speech_generation", {
title: "Submit Speech Generation Task",
description: "Convert text to speech asynchronously. RECOMMENDED: Submit multiple tasks in batch to saturate rate limits, then call task_barrier once to wait for all completions. Returns task ID only - actual files available after task_barrier.",
inputSchema: textToSpeechSchema.shape
}, async (params) => {
try {
const validatedParams = validateTTSParams(params);
const { taskId } = await taskManager.submitTTSTask(async () => {
return await ttsService.generateSpeech(validatedParams);
});
return {
content: [{
type: "text",
text: `Task ${taskId} submitted`
}]
};
}
catch (error) {
ErrorHandler.logError(error, { tool: 'submit_speech_generation', params });
return {
content: [{
type: "text",
text: `❌ Failed to submit TTS task: ${ErrorHandler.formatErrorForUser(error)}`
}]
};
}
});
server.registerTool("task_barrier", {
title: "Wait for Task Completion",
description: "Wait for ALL submitted tasks to complete and retrieve results. Essential for batch processing - submit multiple tasks first, then call task_barrier once to collect all results efficiently. Clears completed tasks.",
inputSchema: taskBarrierSchema.shape
}, async (params) => {
try {
validateTaskBarrierParams(params);
const { completed, results } = await taskManager.barrier();
if (completed === 0) {
return {
content: [{
type: "text",
text: "ℹ️ No tasks were submitted before this barrier."
}]
};
}
const resultSummaries = results.map(({ taskId, success, result, error }) => {
if (!success) {
return `❌ Task ${taskId}: FAILED - ${error?.message || 'Unknown error'}`;
}
if (result?.files) {
const warnings = result.warnings ? ` (${result.warnings.length} warnings)` : '';
return `✅ Task ${taskId}: Generated ${result.count} image(s)${warnings}`;
}
else if (result?.audioFile) {
const subtitles = result.subtitleFile ? ` + subtitles` : '';
const warnings = result.warnings ? ` (${result.warnings.length} warnings)` : '';
return `✅ Task ${taskId}: Generated speech${subtitles}${warnings}`;
}
else {
return `✅ Task ${taskId}: Completed successfully`;
}
});
const summary = resultSummaries.join('\n');
taskManager.clearCompletedTasks();
return {
content: [{
type: "text",
text: summary
}]
};
}
catch (error) {
ErrorHandler.logError(error, { tool: 'task_barrier' });
return {
content: [{
type: "text",
text: `❌ Task barrier failed: ${ErrorHandler.formatErrorForUser(error)}`
}]
};
}
});
process.on('SIGINT', () => {
console.error("🛑 Shutting down gracefully...");
taskManager.clearCompletedTasks();
process.exit(0);
});
process.on('SIGTERM', () => {
console.error("🛑 Received SIGTERM, shutting down...");
taskManager.clearCompletedTasks();
process.exit(0);
});
const transport = new StdioServerTransport();
await server.connect(transport);
//# sourceMappingURL=index.js.map