/**
* separate_stems Tool
*
* Separates audio into vocals, drums, bass, and other stems.
*/
import { Tool } from '@modelcontextprotocol/sdk/types.js';
import { SeparateStemsOutput, StemSeparationResult, JobReference } from '../types/mcp-tools.js';
import {
IRCAM_API_CONFIG,
IrcamStemSeparatorResponse,
transformStemSeparatorResponse,
} from '../types/ircam-api.js';
import { httpPost, buildApiUrl } from '../utils/http.js';
import { validateAudioUrl } from '../utils/validation.js';
import { formatError } from '../utils/errors.js';
/**
* Tool definition for MCP registration
*/
export const separateStemsTool: Tool = {
name: 'separate_stems',
description:
'Separate an audio file into individual stems: vocals, drums, bass, and other instruments. ' +
'Accepts a public URL to an audio file (MP3, WAV, FLAC, OGG, M4A). ' +
'For longer files, returns a job_id for async processing - use check_job_status to monitor progress.',
inputSchema: {
type: 'object',
properties: {
audio_url: {
type: 'string',
description: 'Public URL to the audio file to separate',
},
},
required: ['audio_url'],
},
};
/**
* Handle separate_stems tool call
*/
export async function handleSeparateStems(
args: Record<string, unknown>
): Promise<SeparateStemsOutput> {
const audioUrl = args.audio_url as string;
// Validate input
const validation = validateAudioUrl(audioUrl);
if (!validation.valid) {
throw validation.error;
}
// Call IRCAM Stem Separator API
const url = buildApiUrl(IRCAM_API_CONFIG.ENDPOINTS.STEM_SEPARATOR);
const response = await httpPost<IrcamStemSeparatorResponse>(url, {
url: audioUrl,
});
if (!response.ok || !response.data) {
throw response.error || formatError('UNKNOWN_ERROR', 'Failed to separate stems');
}
// Transform response
const transformed = transformStemSeparatorResponse(response.data);
// Return either job reference or direct result
if (transformed.job_id) {
return { job_id: transformed.job_id } as JobReference;
}
return {
vocals_url: transformed.vocals_url!,
drums_url: transformed.drums_url!,
bass_url: transformed.bass_url!,
other_url: transformed.other_url!,
} as StemSeparationResult;
}