Skip to main content
Glama

generate_podcast_audio

Convert podcast text content into audio using FlowSpeech technology, with optional custom scripts to modify speaker dialogue before audio generation.

Instructions

Generate audio for a podcast episode that already has text content. This is the second stage of two-stage generation. The episode must have contentStatus=text-success. You can optionally provide customScripts parameter to override the generated scripts when generating audio. NOTE: This is the ONLY way to use modified scripts - there is no separate tool to edit scripts after generation.

Input Schema

TableJSON Schema
NameRequiredDescriptionDefault
episodeIdYesThe complete 24-character episode ID from the previous create_podcast_text_only response. IMPORTANT: Must be exactly 24 characters. Copy the FULL Episode ID - do not truncate or shorten it.
customScriptsNoOptional custom scripts to override generated ones
waitForCompletionNoWhether to wait for audio generation to complete

Implementation Reference

  • The execute function implementing the tool logic: checks episode contentStatus is 'text-success', submits audio generation request using client.podcast.generatePodcastAudio (with optional customScripts), polls status until complete using pollUntilComplete utility, handles timeout/errors, and returns formatted podcast episode details.
    async execute(args, {log}: {log: any}) { try { log.info('Generating podcast audio', { episodeId: args.episodeId, hasCustomScripts: Boolean(args.customScripts), }); // Check episode status first const statusResponse = await client.podcast.getEpisodeStatus( args.episodeId, ); if (statusResponse.code !== 0) { return `Failed to query episode: ${statusResponse.message ?? 'Unknown error'}`; } const episode = statusResponse.data; if (!episode) { return 'Episode not found'; } if (episode.contentStatus !== 'text-success') { return `Cannot generate audio: Episode contentStatus is ${episode.contentStatus ?? 'unknown'}, must be text-success`; } const requestData = args.customScripts ? {scripts: args.customScripts} : undefined; const submitResponse = await client.podcast.generatePodcastAudio( args.episodeId, requestData, ); if (submitResponse.code !== 0) { return `Failed to start audio generation: ${submitResponse.message ?? 'Unknown error'}`; } log.info(`Audio generation task submitted`, { episodeId: args.episodeId, }); if (!args.waitForCompletion) { return `Audio generation started\n\nEpisode ID: ${args.episodeId}\nStatus: pending\n\nUse get_podcast_status to check progress.`; } const result = await pollUntilComplete( async () => { const response = await client.podcast.getEpisodeStatus( args.episodeId, ); if (response.code !== 0) { throw new Error(response.message ?? 'Failed to query status'); } if (!response.data) { throw new Error('No episode data returned'); } return response.data; }, { pollInterval: 5000, maxRetries: 60, onProgress(status, retry) { log.debug(`Audio generation status: ${status}`, { episodeId: args.episodeId, retry: `${retry}/60`, }); }, }, ); if (!result.success) { if (result.error) { log.error('Audio generation failed', { episodeId: args.episodeId, error: result.error, }); return `Audio generation failed: ${result.error}`; } log.warn('Audio generation timeout', { episodeId: args.episodeId, lastStatus: result.lastStatus, }); return `Task is still processing after 5 minutes.\n\nEpisode ID: ${args.episodeId}\nLast Status: ${result.lastStatus}\n\nThe task is running in the background. Use get_podcast_status tool with this Episode ID to check progress.`; } const finalEpisode = result.data!; log.info('Audio generation completed', {episodeId: args.episodeId}); const hasOutline = Boolean(finalEpisode.outline); const hasScripts = Boolean( finalEpisode.scripts && finalEpisode.scripts.length > 0, ); return `Podcast Audio Generation Completed Content Included: - Audio Files: ${finalEpisode.audioUrl ? 'Yes' : 'No'} - Outline: ${hasOutline ? 'Yes (see below)' : 'No'} - Scripts: ${hasScripts ? 'Yes (see below)' : 'No'} ${formatPodcastEpisode(finalEpisode)}`; } catch (error) { const errorMessage = formatError(error); log.error('Failed to generate podcast audio', {error: errorMessage}); return `Failed to generate podcast audio: ${errorMessage}`; } },
  • Zod input schema definition for the tool parameters used in FastMCP tool registration.
    parameters: z.object({ episodeId: z .string() .min(24) .max(24) .describe( 'The complete 24-character episode ID from the previous create_podcast_text_only response. IMPORTANT: Must be exactly 24 characters. Copy the FULL Episode ID - do not truncate or shorten it.', ), customScripts: z .array( z.object({ content: z.string().describe('Script text content'), speakerId: z.string().describe('Speaker ID'), }), ) .optional() .describe('Optional custom scripts to override generated ones'), waitForCompletion: z .boolean() .default(true) .describe('Whether to wait for audio generation to complete'), }),
  • The FastMCP server.addTool call registering the 'generate_podcast_audio' tool with name, description, input schema, annotations, and handler.
    name: 'generate_podcast_audio', description: 'Generate audio for a podcast episode that already has text content. This is the second stage of two-stage generation. The episode must have contentStatus=text-success. You can optionally provide customScripts parameter to override the generated scripts when generating audio. NOTE: This is the ONLY way to use modified scripts - there is no separate tool to edit scripts after generation.', parameters: z.object({ episodeId: z .string() .min(24) .max(24) .describe( 'The complete 24-character episode ID from the previous create_podcast_text_only response. IMPORTANT: Must be exactly 24 characters. Copy the FULL Episode ID - do not truncate or shorten it.', ), customScripts: z .array( z.object({ content: z.string().describe('Script text content'), speakerId: z.string().describe('Speaker ID'), }), ) .optional() .describe('Optional custom scripts to override generated ones'), waitForCompletion: z .boolean() .default(true) .describe('Whether to wait for audio generation to complete'), }), annotations: { title: 'Generate Podcast Audio', openWorldHint: true, readOnlyHint: false, },
  • TypeScript interfaces for GeneratePodcastAudioRequest (maps to customScripts as scripts) and GeneratePodcastAudioResponse used by the API client methods.
    export type GeneratePodcastAudioRequest = { scripts?: Array<{ content: string; speakerId: string; }>; }; export type GeneratePodcastAudioResponse = { success: boolean; message: string; episodeId: string; status: string; contentStatus?: 'text-success' | 'text-fail' | 'audio-success' | 'audio-fail'; };
  • PodcastClient method to POST to the backend API endpoint for generating podcast audio, invoked by the tool handler at runtime.
    async generatePodcastAudio( episodeId: string, data?: GeneratePodcastAudioRequest, ): Promise<ApiResponse<GeneratePodcastAudioResponse>> { const response = await this.axiosInstance.post< ApiResponse<GeneratePodcastAudioResponse> >(`/v1/podcast/episodes/${episodeId}/audio`, data ?? {}); return response.data; }

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/marswaveai/listenhub-mcp-server'

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