Skip to main content
Glama
index.ts39.2 kB
#!/usr/bin/env node /** * MCP Server for MusicGPT API * * Provides AI-powered audio generation and processing capabilities including: * - Music generation from text prompts * - Voice conversion and cloning * - Audio processing and enhancement * - Audio transcription and analysis */ import { Server } from "@modelcontextprotocol/sdk/server/index.js"; import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js"; import { ListToolsRequestSchema, CallToolRequestSchema, ErrorCode, McpError, } from "@modelcontextprotocol/sdk/types.js"; import axios, { AxiosInstance, AxiosError } from "axios"; // ============================================================================ // Configuration // ============================================================================ interface ServerConfig { apiKey?: string; baseUrl: string; timeout?: number; } // ============================================================================ // Tool Definitions // ============================================================================ const TOOLS = [ // Helper Tools { name: "get_conversion_by_id", description: "Get details of a conversion by its task ID or conversion ID. Returns status, audio URL, and metadata.", inputSchema: { type: "object" as const, properties: { conversionType: { type: "string", description: "Type of conversion (must match MusicGPT API conversion types)", enum: [ "MUSIC_AI", "TEXT_TO_SPEECH", "VOICE_CONVERSION", "COVER", "EXTRACTION", "DENOISING", "DEECHO", "DEREVERB", "SOUND_GENERATOR", "AUDIO_TRANSCRIPTION", "AUDIO_SPEED_CHANGER", "AUDIO_MASTERING", "AUDIO_CUTTER", "REMIX", "FILE_CONVERT", "KEY_BPM_EXTRACTION", "AUDIO_TO_MIDI", "EXTEND", "INPAINT", "SING_OVER_INSTRUMENTAL", "LYRICS_GENERATOR", "STEMS_SEPARATION", "VOCAL_EXTRACTION" ], }, task_id: { type: "string", description: "Task ID associated with the conversion (provide either task_id or conversion_id)", }, conversion_id: { type: "string", description: "Conversion ID to fetch details (provide either task_id or conversion_id)", }, }, required: ["conversionType"], }, }, { name: "get_all_voices", description: "Get a paginated list of all available voices for voice conversion and TTS", inputSchema: { type: "object" as const, properties: { limit: { type: "number", description: "Maximum number of voices per page (default: 20)", default: 20, }, page: { type: "number", description: "Page number for pagination (default: 0)", default: 0, }, }, }, }, { name: "search_voices", description: "Search for voices by name", inputSchema: { type: "object" as const, properties: { voice_name: { type: "string", description: "Name of the voice to search for", }, }, required: ["voice_name"], }, }, // Music Generation Tools { name: "generate_music", description: "Generate custom music from a text prompt using AI. Can create songs with or without lyrics, instrumental tracks, or vocal-only versions.", inputSchema: { type: "object" as const, properties: { prompt: { type: "string", description: "Natural language prompt for music generation (keep under 280 characters for best results)", }, music_style: { type: "string", description: "Style of music to generate (e.g., Rock, Pop, Jazz, Hip-Hop)", }, lyrics: { type: "string", description: "Custom lyrics for the generated music", }, make_instrumental: { type: "boolean", description: "Whether to make the music instrumental (no vocals)", default: false, }, vocal_only: { type: "boolean", description: "Whether to generate only vocals of output audio", default: false, }, voice_id: { type: "string", description: "Voice model ID to use for vocals (use get_all_voices to find IDs)", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["prompt"], }, }, { name: "create_cover_song", description: "Create a cover version of a song with a different voice or style", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the original audio file", }, voice_id: { type: "string", description: "Voice model ID to use for the cover (use get_all_voices to find IDs)", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url", "voice_id"], }, }, { name: "generate_sound_effect", description: "Generate sound effects from a text description", inputSchema: { type: "object" as const, properties: { prompt: { type: "string", description: "Description of the sound effect to generate", }, duration: { type: "number", description: "Duration of the sound effect in seconds", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["prompt"], }, }, // Voice and Speech Tools { name: "voice_changer", description: "Convert audio from one voice to another using AI voice models", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to convert", }, voice_id: { type: "string", description: "Target voice model ID (use get_all_voices to find IDs)", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url", "voice_id"], }, }, { name: "text_to_speech", description: "Convert text to speech using AI voices", inputSchema: { type: "object" as const, properties: { text: { type: "string", description: "Text to convert to speech", }, voice_id: { type: "string", description: "Voice model ID to use (use get_all_voices to find IDs)", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["text", "voice_id"], }, }, // Audio Extraction and Separation { name: "extract_audio", description: "Extract vocals, instruments, or specific stems from audio", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to process", }, extraction_type: { type: "string", description: "Type of extraction to perform", enum: ["vocals", "instrumental", "drums", "bass", "piano", "other"], }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url", "extraction_type"], }, }, // Audio Enhancement { name: "denoise_audio", description: "Remove background noise from audio", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to denoise", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "deecho_audio", description: "Remove echo from audio", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to process", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "dereverb_audio", description: "Remove reverb from audio", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to process", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, // Audio Manipulation { name: "convert_audio_format", description: "Convert audio file to a different format", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to convert", }, output_format: { type: "string", description: "Desired output format", enum: ["mp3", "wav", "flac", "ogg", "m4a"], }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url", "output_format"], }, }, { name: "cut_audio", description: "Cut or trim audio to a specific duration", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to cut", }, start_time: { type: "number", description: "Start time in seconds", }, end_time: { type: "number", description: "End time in seconds", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url", "start_time", "end_time"], }, }, { name: "change_audio_speed", description: "Change the playback speed of audio", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to process", }, speed_factor: { type: "number", description: "Speed multiplier (e.g., 1.5 for 1.5x speed, 0.75 for 0.75x speed)", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url", "speed_factor"], }, }, { name: "master_audio", description: "Apply professional audio mastering to improve sound quality", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to master", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "remix_audio", description: "Create a remix of an audio track", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to remix", }, remix_style: { type: "string", description: "Style of remix to create", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "extend_audio", description: "Extend an audio track using AI to generate continuation", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to extend", }, extension_duration: { type: "number", description: "Duration to extend in seconds", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "inpaint_audio", description: "Fill in missing or corrupted parts of audio using AI", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file with gaps to fill", }, start_time: { type: "number", description: "Start time of the section to inpaint in seconds", }, end_time: { type: "number", description: "End time of the section to inpaint in seconds", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url", "start_time", "end_time"], }, }, { name: "sing_over_instrumental", description: "Add AI-generated vocals over an instrumental track", inputSchema: { type: "object" as const, properties: { instrumental_url: { type: "string", description: "URL of the instrumental audio file", }, lyrics: { type: "string", description: "Lyrics to sing", }, voice_id: { type: "string", description: "Voice model ID to use for singing (use get_all_voices to find IDs)", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["instrumental_url", "lyrics", "voice_id"], }, }, // Analysis Tools { name: "transcribe_audio", description: "Transcribe speech from audio to text", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to transcribe", }, language: { type: "string", description: "Language code (e.g., 'en', 'es', 'fr')", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "extract_key_bpm", description: "Extract musical key and BPM (tempo) from audio", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to analyze", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "audio_to_midi", description: "Convert audio to MIDI format", inputSchema: { type: "object" as const, properties: { audio_url: { type: "string", description: "URL of the audio file to convert to MIDI", }, webhook_url: { type: "string", description: "URL for callback upon completion", }, }, required: ["audio_url"], }, }, { name: "generate_lyrics", description: "Generate song lyrics based on a theme or prompt", inputSchema: { type: "object" as const, properties: { prompt: { type: "string", description: "Theme or prompt for lyrics generation", }, genre: { type: "string", description: "Music genre for the lyrics", }, }, required: ["prompt"], }, }, ]; // ============================================================================ // Main Server Class // ============================================================================ class MusicGPTMCPServer { private server: Server; private axiosInstance: AxiosInstance; private config: ServerConfig; constructor(config: ServerConfig) { this.config = config; // Initialize axios instance with authentication this.axiosInstance = axios.create({ baseURL: config.baseUrl, timeout: config.timeout || 60000, // 60 seconds for audio processing headers: { "Content-Type": "application/json", ...(config.apiKey && { Authorization: config.apiKey, }), }, }); // Initialize MCP server this.server = new Server( { name: "mcp-server-musicgpt", version: "1.0.0", }, { capabilities: { tools: {}, }, } ); this.setupHandlers(); this.setupErrorHandling(); } /** * Set up request handlers for the MCP server */ private setupHandlers(): void { // Handle tool listing this.server.setRequestHandler( ListToolsRequestSchema, async () => ({ tools: TOOLS, }) ); // Handle tool execution this.server.setRequestHandler( CallToolRequestSchema, async (request) => { const { name, arguments: args } = request.params; try { switch (name) { // Helper endpoints case "get_conversion_by_id": return await this.handleGetConversionById(args); case "get_all_voices": return await this.handleGetAllVoices(args); case "search_voices": return await this.handleSearchVoices(args); // Music generation case "generate_music": return await this.handleGenerateMusic(args); case "create_cover_song": return await this.handleCreateCover(args); case "generate_sound_effect": return await this.handleGenerateSoundEffect(args); // Voice and speech case "voice_changer": return await this.handleVoiceChanger(args); case "text_to_speech": return await this.handleTextToSpeech(args); // Audio extraction case "extract_audio": return await this.handleExtractAudio(args); // Audio enhancement case "denoise_audio": return await this.handleDenoiseAudio(args); case "deecho_audio": return await this.handleDeechoAudio(args); case "dereverb_audio": return await this.handleDereverbAudio(args); // Audio manipulation case "convert_audio_format": return await this.handleConvertAudioFormat(args); case "cut_audio": return await this.handleCutAudio(args); case "change_audio_speed": return await this.handleChangeAudioSpeed(args); case "master_audio": return await this.handleMasterAudio(args); case "remix_audio": return await this.handleRemixAudio(args); case "extend_audio": return await this.handleExtendAudio(args); case "inpaint_audio": return await this.handleInpaintAudio(args); case "sing_over_instrumental": return await this.handleSingOverInstrumental(args); // Analysis tools case "transcribe_audio": return await this.handleTranscribeAudio(args); case "extract_key_bpm": return await this.handleExtractKeyBpm(args); case "audio_to_midi": return await this.handleAudioToMidi(args); case "generate_lyrics": return await this.handleGenerateLyrics(args); default: throw new McpError( ErrorCode.MethodNotFound, `Unknown tool: ${name}` ); } } catch (error) { if (error instanceof McpError) { throw error; } if (axios.isAxiosError(error)) { throw this.handleAxiosError(error); } throw new McpError( ErrorCode.InternalError, `Tool execution failed: ${error instanceof Error ? error.message : String(error)}` ); } } ); } /** * Set up global error handling */ private setupErrorHandling(): void { this.server.onerror = (error) => { console.error("[MCP Error]", error); }; process.on("SIGINT", async () => { await this.server.close(); process.exit(0); }); } // ========================================================================== // Helper Endpoint Handlers // ========================================================================== private async handleGetConversionById(args: any) { if (!args.conversionType) { throw new McpError(ErrorCode.InvalidParams, "conversionType is required"); } if (!args.task_id && !args.conversion_id) { throw new McpError(ErrorCode.InvalidParams, "Either task_id or conversion_id is required"); } const params: any = { conversionType: args.conversionType }; if (args.task_id) params.task_id = args.task_id; if (args.conversion_id) params.conversion_id = args.conversion_id; const response = await this.axiosInstance.get("/byId", { params }); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } private async handleGetAllVoices(args: any) { const params = { limit: args.limit || 20, page: args.page || 0, }; const response = await this.axiosInstance.get("/getAllVoices", { params }); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } private async handleSearchVoices(args: any) { if (!args.voice_name) { throw new McpError(ErrorCode.InvalidParams, "voice_name is required"); } const response = await this.axiosInstance.get("/searchVoices", { params: { voice_name: args.voice_name }, }); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } // ========================================================================== // Music Generation Handlers // ========================================================================== private async handleGenerateMusic(args: any) { if (!args.prompt) { throw new McpError(ErrorCode.InvalidParams, "prompt is required"); } const response = await this.axiosInstance.post("/MusicAI", { prompt: args.prompt, music_style: args.music_style, lyrics: args.lyrics, make_instrumental: args.make_instrumental || false, vocal_only: args.vocal_only || false, voice_id: args.voice_id, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Music generation started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id or conversion_id to check the status.`, }, ], }; } private async handleCreateCover(args: any) { if (!args.audio_url || !args.voice_id) { throw new McpError(ErrorCode.InvalidParams, "audio_url and voice_id are required"); } const response = await this.axiosInstance.post("/cover", { audio_url: args.audio_url, voice_id: args.voice_id, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Cover song creation started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleGenerateSoundEffect(args: any) { if (!args.prompt) { throw new McpError(ErrorCode.InvalidParams, "prompt is required"); } const response = await this.axiosInstance.post("/soundgenerator", { prompt: args.prompt, duration: args.duration, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Sound effect generation started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } // ========================================================================== // Voice and Speech Handlers // ========================================================================== private async handleVoiceChanger(args: any) { if (!args.audio_url || !args.voice_id) { throw new McpError(ErrorCode.InvalidParams, "audio_url and voice_id are required"); } const response = await this.axiosInstance.post("/voicetovoice", { audio_url: args.audio_url, voice_id: args.voice_id, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Voice conversion started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleTextToSpeech(args: any) { if (!args.text || !args.voice_id) { throw new McpError(ErrorCode.InvalidParams, "text and voice_id are required"); } const response = await this.axiosInstance.post("/TextToSpeech", { text: args.text, voice_id: args.voice_id, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Text-to-speech conversion started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } // ========================================================================== // Audio Extraction Handler // ========================================================================== private async handleExtractAudio(args: any) { if (!args.audio_url || !args.extraction_type) { throw new McpError(ErrorCode.InvalidParams, "audio_url and extraction_type are required"); } const response = await this.axiosInstance.post("/extraction", { audio_url: args.audio_url, extraction_type: args.extraction_type, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio extraction started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } // ========================================================================== // Audio Enhancement Handlers // ========================================================================== private async handleDenoiseAudio(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/denoise", { audio_url: args.audio_url, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio denoising started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleDeechoAudio(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/deecho", { audio_url: args.audio_url, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio de-echo started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleDereverbAudio(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/dereverb", { audio_url: args.audio_url, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio de-reverb started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } // ========================================================================== // Audio Manipulation Handlers // ========================================================================== private async handleConvertAudioFormat(args: any) { if (!args.audio_url || !args.output_format) { throw new McpError(ErrorCode.InvalidParams, "audio_url and output_format are required"); } const response = await this.axiosInstance.post("/fileconvert", { audio_url: args.audio_url, output_format: args.output_format, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio format conversion started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleCutAudio(args: any) { if (!args.audio_url || args.start_time === undefined || args.end_time === undefined) { throw new McpError(ErrorCode.InvalidParams, "audio_url, start_time, and end_time are required"); } const response = await this.axiosInstance.post("/audio_cutter", { audio_url: args.audio_url, start_time: args.start_time, end_time: args.end_time, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio cutting started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleChangeAudioSpeed(args: any) { if (!args.audio_url || !args.speed_factor) { throw new McpError(ErrorCode.InvalidParams, "audio_url and speed_factor are required"); } const response = await this.axiosInstance.post("/audio_speed_changer", { audio_url: args.audio_url, speed_factor: args.speed_factor, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio speed change started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleMasterAudio(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/audio_mastering", { audio_url: args.audio_url, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio mastering started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleRemixAudio(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/remix", { audio_url: args.audio_url, remix_style: args.remix_style, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio remix started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleExtendAudio(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/extend", { audio_url: args.audio_url, extension_duration: args.extension_duration, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio extension started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleInpaintAudio(args: any) { if (!args.audio_url || args.start_time === undefined || args.end_time === undefined) { throw new McpError(ErrorCode.InvalidParams, "audio_url, start_time, and end_time are required"); } const response = await this.axiosInstance.post("/inpaint", { audio_url: args.audio_url, start_time: args.start_time, end_time: args.end_time, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio inpainting started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleSingOverInstrumental(args: any) { if (!args.instrumental_url || !args.lyrics || !args.voice_id) { throw new McpError(ErrorCode.InvalidParams, "instrumental_url, lyrics, and voice_id are required"); } const response = await this.axiosInstance.post("/sing_over_instrumental", { instrumental_url: args.instrumental_url, lyrics: args.lyrics, voice_id: args.voice_id, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Singing over instrumental started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } // ========================================================================== // Analysis Tool Handlers // ========================================================================== private async handleTranscribeAudio(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/audiotranscribe", { audio_url: args.audio_url, language: args.language, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio transcription started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleExtractKeyBpm(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/extract_key_bpm", { audio_url: args.audio_url, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Key and BPM extraction started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleAudioToMidi(args: any) { if (!args.audio_url) { throw new McpError(ErrorCode.InvalidParams, "audio_url is required"); } const response = await this.axiosInstance.post("/audio_to_midi", { audio_url: args.audio_url, webhook_url: args.webhook_url, }); return { content: [ { type: "text", text: `Audio to MIDI conversion started!\n\n${JSON.stringify(response.data, null, 2)}\n\nUse get_conversion_by_id with the task_id to check the status.`, }, ], }; } private async handleGenerateLyrics(args: any) { if (!args.prompt) { throw new McpError(ErrorCode.InvalidParams, "prompt is required"); } const response = await this.axiosInstance.get("/lyrics_generator", { params: { prompt: args.prompt, genre: args.genre, }, }); return { content: [ { type: "text", text: JSON.stringify(response.data, null, 2), }, ], }; } // ========================================================================== // Utility Methods // ========================================================================== private handleAxiosError(error: AxiosError): McpError { const status = error.response?.status; const message = (error.response?.data as any)?.message || error.message || "API request failed"; if (status === 401 || status === 403) { return new McpError( ErrorCode.InvalidRequest, `Authentication failed: ${message}. Check your MUSICGPT_API_KEY.` ); } if (status === 404) { return new McpError( ErrorCode.InvalidRequest, `Resource not found: ${message}` ); } if (status === 429) { return new McpError( ErrorCode.InternalError, `Rate limit exceeded: ${message}` ); } if (status === 400) { return new McpError( ErrorCode.InvalidParams, `Invalid request: ${message}` ); } if (status && status >= 500) { return new McpError( ErrorCode.InternalError, `Server error: ${message}` ); } return new McpError( ErrorCode.InternalError, `API request failed: ${message}` ); } /** * Start the MCP server */ async run(): Promise<void> { const transport = new StdioServerTransport(); await this.server.connect(transport); console.error("MusicGPT MCP Server running on stdio"); } } // ============================================================================ // Main Execution // ============================================================================ const config: ServerConfig = { apiKey: process.env.MUSICGPT_API_KEY, baseUrl: process.env.MUSICGPT_BASE_URL || "https://api.musicgpt.com/api/public/v1", timeout: process.env.MUSICGPT_TIMEOUT ? parseInt(process.env.MUSICGPT_TIMEOUT, 10) : 60000, }; if (!config.apiKey) { console.error( "Error: MUSICGPT_API_KEY environment variable is required" ); process.exit(1); } const server = new MusicGPTMCPServer(config); server.run().catch((error) => { console.error("Fatal error:", error); process.exit(1); });

Implementation Reference

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/pasie15/mcp-server-musicgpt'

If you have feedback or need assistance with the MCP directory API, please join our Discord server