Minimax MCP Tools
by PsychArch
Verified
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { generateImage } from "./minimax-api.js";
import { generateSpeech } from "./minimax-tts-api.js";
import path from 'path';
import { fileURLToPath } from 'url';
import { dirname } from 'path';
const __filename = fileURLToPath(import.meta.url);
const __dirname = dirname(__filename);
// Create a new MCP server
const server = new McpServer({
name: "minimax-mcp-tools",
version: "1.0.0"
});
// Define an image generation tool using Minimax API
server.tool(
"generate_image",
{
prompt: z.string().describe("Description of the image to generate"),
aspectRatio: z.enum(["1:1", "16:9", "4:3", "3:2", "2:3", "3:4", "9:16", "21:9"]).optional().describe("Aspect ratio of the image"),
outputFile: z.string().optional().describe("Absolute path to save the generated image file"),
n: z.number().min(1).max(9).optional().describe("Number of images to generate (1-9)")
},
async ({ prompt, aspectRatio, outputFile, n }) => {
// Get API key from environment variable
const apiKey = process.env.MINIMAX_API_KEY;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: MINIMAX_API_KEY environment variable is not set. Please set it in the MCP server configuration."
}]
};
}
// Set default output directory if not provided
const outputDirectory = outputFile ? path.dirname(outputFile) : 'generated-images';
try {
// Call the Minimax API to generate the image
const result = await generateImage(prompt, apiKey, {
aspectRatio: aspectRatio || "1:1",
n: n || 1
}, outputDirectory, outputFile);
if (!result.success) {
return {
content: [{
type: "text",
text: `Error generating image: ${result.error}`
}]
};
}
// Prepare the response with image information
const imageDetails = result.images.map(img => {
const absolutePath = outputFile ? outputFile : img.localPath;
return `- Image saved to: ${absolutePath}\n URL: ${img.url}`;
}).join('\n');
return {
content: [{
type: "text",
text: `Successfully generated ${result.images.length} image(s):\n${imageDetails}`
}]
};
} catch (error) {
console.error('Error in generate_image tool:', error);
return {
content: [{
type: "text",
text: `Error generating image: ${error.message}`
}]
};
}
}
);
// Voice ID descriptions for better user experience
const voiceIdDescription = `Voice ID to use. Options include:
- Male voices: male-qn-qingse (青涩青年), male-qn-jingying (精英青年), male-qn-badao (霸道青年), male-qn-daxuesheng (青年大学生)
- Female voices: female-shaonv (少女), female-yujie (御姐), female-chengshu (成熟女性), female-tianmei (甜美女性)
- Presenters: presenter_male (男性主持人), presenter_female (女性主持人)
- Audiobooks: audiobook_male_1 (男性有声书1), audiobook_male_2 (男性有声书2), audiobook_female_1 (女性有声书1), audiobook_female_2 (女性有声书2)
- Beta voices: male-qn-qingse-jingpin (青涩青年-beta), female-shaonv-jingpin (少女音色-beta)
- Character voices: clever_boy (聪明男童), cute_boy (可爱男童), lovely_girl (萌萌女童), cartoon_pig (卡通猪小琪),
bingjiao_didi (病娇弟弟), junlang_nanyou (俊朗男友), chunzhen_xuedi (纯真学弟), lengdan_xiongzhang (冷淡学长),
badao_shaoye (霸道少爷), tianxin_xiaoling (甜心小玲), qiaopi_mengmei (俏皮萌妹), wumei_yujie (妩媚御姐),
diadia_xuemei (嗲嗲学妹), danya_xuejie (淡雅学姐)
- Western characters: Santa_Claus, Grinch, Rudolph, Arnold, Charming_Santa, Charming_Lady, Sweet_Girl, Cute_Elf, Attractive_Girl, Serene_Woman`;
// Define a text-to-speech tool using Minimax API
server.tool(
"generate_speech",
{
text: z.string().describe("Text to convert to speech"),
model: z.enum(["speech-01-turbo", "speech-01-240228", "speech-01-turbo-240228", "speech-01-hd"]).optional().describe("Model version to use for speech generation"),
voiceId: z.string().optional().describe(voiceIdDescription),
speed: z.number().min(0.5).max(2).optional().describe("Speech speed (0.5-2.0)"),
volume: z.number().min(0.1).max(10).optional().describe("Speech volume (0.1-10.0)"),
pitch: z.number().min(-12).max(12).optional().describe("Speech pitch (-12 to 12)"),
emotion: z.enum(["happy", "sad", "angry", "fearful", "disgusted", "surprised", "neutral"]).optional().describe("Emotion of the speech"),
format: z.enum(["mp3", "pcm", "flac", "wav"]).optional().describe("Audio format"),
outputFile: z.string().optional().describe("Absolute path to save the generated audio file"),
sampleRate: z.enum([8000, 16000, 22050, 24000, 32000, 44100]).optional().describe("Sample rate of the generated audio"),
bitrate: z.enum([32000, 64000, 128000, 256000]).optional().describe("Bitrate of the generated audio (for MP3 only)"),
channel: z.enum([1, 2]).optional().describe("Number of audio channels (1=mono, 2=stereo)"),
latexRead: z.boolean().optional().describe("Whether to read LaTeX formulas"),
pronunciationDict: z.array(z.string()).optional().describe("List of pronunciation replacements"),
timberWeights: z.array(
z.object({
voice_id: z.string(),
weight: z.number().min(1).max(100)
})
).optional().describe("Voice timber weights for voice mixing"),
stream: z.boolean().optional().describe("Whether to use streaming mode"),
languageBoost: z.enum([
"Chinese", "Chinese,Yue", "English", "Arabic", "Russian", "Spanish",
"French", "Portuguese", "German", "Turkish", "Dutch", "Ukrainian",
"Vietnamese", "Indonesian", "Japanese", "Italian", "Korean", "auto"
]).optional().describe("Enhance recognition of specific languages"),
subtitleEnable: z.boolean().optional().describe("Whether to enable subtitle generation")
},
async ({
text, model, voiceId, speed, volume, pitch, emotion, format, outputFile,
sampleRate, bitrate, channel, latexRead, pronunciationDict, timberWeights,
stream, languageBoost, subtitleEnable
}) => {
// Get API key and group ID from environment variables
const apiKey = process.env.MINIMAX_API_KEY;
const groupId = process.env.MINIMAX_GROUP_ID;
if (!apiKey) {
return {
content: [{
type: "text",
text: "Error: MINIMAX_API_KEY environment variable is not set. Please set it in the MCP server configuration."
}]
};
}
if (!groupId) {
return {
content: [{
type: "text",
text: "Error: MINIMAX_GROUP_ID environment variable is not set. Please set it in the MCP server configuration."
}]
};
}
// Set default output directory if not provided
const outputDirectory = outputFile ? path.dirname(outputFile) : 'generated-audio';
try {
// Call the Minimax API to generate speech
const result = await generateSpeech(text, apiKey, groupId, {
model,
voiceId: voiceId || "male-qn-qingse",
speed: speed || 1.0,
volume: volume || 1.0,
pitch: pitch || 0,
emotion: emotion || "neutral",
format: format || "mp3",
sampleRate,
bitrate,
channel,
latexRead,
pronunciationDict,
timberWeights,
stream,
languageBoost,
subtitleEnable
}, outputDirectory, outputFile);
if (!result.success) {
return {
content: [{
type: "text",
text: `Error generating speech: ${result.error}`
}]
};
}
// Prepare the response with audio information
const audioInfo = result.audioInfo;
const durationSeconds = audioInfo.length / 1000; // Convert milliseconds to seconds
const fileSizeKB = Math.round(audioInfo.size / 1024); // Convert bytes to KB
let responseText = `Successfully generated speech:\n- Audio saved to: ${outputFile || audioInfo.localPath}\n- Format: ${audioInfo.format}\n- Duration: ${durationSeconds.toFixed(2)} seconds\n- File size: ${fileSizeKB} KB`;
// Add subtitle information if available
if (audioInfo.subtitlePath) {
responseText += `\n- Subtitle file: ${audioInfo.subtitlePath}`;
}
return {
content: [{
type: "text",
text: responseText
}]
};
} catch (error) {
console.error('Error in generate_speech tool:', error);
return {
content: [{
type: "text",
text: `Error generating speech: ${error.message}`
}]
};
}
}
);
// Log to stderr for debugging
console.error("Minimax MCP Tools Server starting...");
// Set up stdio transport and connect
const transport = new StdioServerTransport();
await server.connect(transport);
console.error("MCP Server connected and ready to receive commands");