UNPKG

@kajidog/mcp-tts-voicevox

Version:

VOICEVOX integration for MCP - Text-to-Speech server using VOICEVOX engine

134 lines (133 loc) 5.5 kB
#!/usr/bin/env node "use strict"; Object.defineProperty(exports, "__esModule", { value: true }); exports.VoicevoxError = exports.VoicevoxClient = void 0; const mcp_js_1 = require("@modelcontextprotocol/sdk/server/mcp.js"); const stdio_js_1 = require("@modelcontextprotocol/sdk/server/stdio.js"); const zod_1 = require("zod"); const voicevox_1 = require("./voicevox"); Object.defineProperty(exports, "VoicevoxClient", { enumerable: true, get: function () { return voicevox_1.VoicevoxClient; } }); const types_1 = require("./voicevox/types"); Object.defineProperty(exports, "VoicevoxError", { enumerable: true, get: function () { return types_1.VoicevoxError; } }); const server = new mcp_js_1.McpServer({ name: "MCP TTS Voicevox", version: "0.0.8", description: "A Voicevox server that converts text to speech for playback and saving.", }); // VoicevoxClientを一度だけインスタンス化 const voicevoxClient = new voicevox_1.VoicevoxClient({ url: process.env.VOICEVOX_URL ?? "http://localhost:50021", defaultSpeaker: Number(process.env.VOICEVOX_DEFAULT_SPEAKER || "1"), defaultSpeedScale: Number(process.env.VOICEVOX_DEFAULT_SPEED_SCALE || "1.0"), }); // 共通のエラーハンドリング関数 const handleError = (error) => { const errorMessage = error instanceof Error ? error.message : String(error); console.error("エラーが発生しました:", error); return { content: [{ type: "text", text: `エラー: ${errorMessage}` }], }; }; // テキストを音声に変換して再生 server.tool("speak", "Convert text to speech and play it", { text: zod_1.z .string() .describe("Text to be spoken (if both query and text are provided, query takes precedence)"), speaker: zod_1.z .number() .optional() .describe("Speaker ID (optional, used if text is provided)"), query: zod_1.z.string().optional().describe("Voice synthesis query"), speedScale: zod_1.z .number() .optional() .describe("Playback speed (optional, default is from environment variable)"), }, async ({ text, speaker, query, speedScale }) => { try { if (query) { // クエリが提供されている場合はそれを使用 const audioQuery = JSON.parse(query); if (speedScale !== undefined) { audioQuery.speedScale = speedScale; } const result = await voicevoxClient.enqueueAudioGeneration(audioQuery, speaker); return { content: [{ type: "text", text: result }], }; } else { // テキストからの通常の発話 const result = await voicevoxClient.speak(text, speaker, speedScale); return { content: [{ type: "text", text: result }], }; } } catch (error) { return handleError(error); } }); // クエリ生成ツール server.tool("generate_query", "Generate a query for voice synthesis", { text: zod_1.z.string().describe("Text for voice synthesis"), speaker: zod_1.z.number().optional().describe("Speaker ID (optional)"), speedScale: zod_1.z .number() .optional() .describe("Playback speed (optional, default is from environment variable)"), }, async ({ text, speaker, speedScale }) => { try { const query = await voicevoxClient.generateQuery(text, speaker, speedScale); return { content: [{ type: "text", text: JSON.stringify(query) }], }; } catch (error) { return handleError(error); } }); // 音声ファイル生成ツール server.tool("synthesize_file", "Generate an audio file and return its absolute path", { text: zod_1.z .string() .optional() .describe("Text for voice synthesis (if both query and text are provided, query takes precedence)"), query: zod_1.z.string().optional().describe("Voice synthesis query"), speaker: zod_1.z.number().optional().describe("Speaker ID (optional)"), output: zod_1.z.string().describe("Output path for the audio file"), speedScale: zod_1.z .number() .optional() .describe("Playback speed (optional, default is from environment variable)"), }, async ({ text, query, speaker, output, speedScale }) => { try { if (query) { // クエリが提供されている場合はそれを使用 const audioQuery = JSON.parse(query); if (speedScale !== undefined) { audioQuery.speedScale = speedScale; } const filePath = await voicevoxClient.generateAudioFile(audioQuery, output, speaker); return { content: [{ type: "text", text: filePath }], }; } else if (text) { // テキストから音声ファイルを生成 const filePath = await voicevoxClient.generateAudioFile(text, output, speaker, speedScale); return { content: [{ type: "text", text: filePath }], }; } else { throw new Error("queryパラメータとtextパラメータのどちらかを指定してください"); } } catch (error) { return handleError(error); } }); server.connect(new stdio_js_1.StdioServerTransport()).catch((error) => { console.error("Error connecting to MCP transport:", error); process.exit(1); });