UNPKG

minimax-mcp-tools

Version:

Async MCP server with Minimax API integration for image generation and text-to-speech

148 lines 6.06 kB
#!/usr/bin/env node 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