Guide · AWS Polly · Engine Selection

AWS Polly Neural, Long-Form and Generative Engines in MCP Servers — Voice Selection, Engine Comparison

AWS Polly offers four synthesis engines — standard, neural, long-form, and generative — each with a different quality, latency, cost, and character-limit profile; choosing the right engine for the content type is the highest-leverage Polly decision an MCP server team makes. The standard engine uses concatenative synthesis: fast, cheap, and robotic-sounding. The neural engine uses deep learning to produce more natural prosody and intonation, at ~4× the cost per character. The long-form engine extends neural synthesis for documents and articles up to 100,000 characters with a subset of voices optimised for narration. The generative engine uses a large language model to produce the most human-like speech currently available from Polly, but supports only a handful of voices and carries the highest per-character cost. This guide explains how to select an engine, query the voice catalog with GetVoicesCommand, and structure your MCP tool to pick the right tier for each request.

TL;DR

Use Engine: 'neural' for most MCP server voice tools — it delivers natural-sounding speech for conversational and notification content at a moderate cost. Switch to 'long-form' (voices: Ruth, Stephen) when synthesizing documents or articles exceeding a few sentences: it was trained on narration-style content and handles paragraph-length input better than neural. Use 'standard' only for high-volume, cost-sensitive applications where audio quality is secondary. Reserve 'generative' for cases where human-likeness is a product differentiator — it incurs significantly higher cost and only a few voices support it. Call GetVoicesCommand with an Engine filter to list voices available for a given tier before hardcoding a VoiceId.

Engine comparison

EngineQualityLatencyCost (relative)Character limit per callSSML
standard Robotic, concatenative Lowest 1× 3,000 (text) / 6,000 (SSML) Full support
neural Natural, expressive Low–medium 4× 3,000 (text) / 6,000 (SSML) Full support
long-form Natural, narration-tuned Medium 4× (same as neural) 3,000 (text) / 6,000 (SSML); use async task for up to 100,000 Subset of SSML tags
generative Highly human-like Highest ~20–30× 3,000 (text); no SSML Not supported

The long-form engine is priced the same as neural per character, so the choice between neural and long-form is purely about which one sounds better for your content — not cost. For single-sentence notifications or UI feedback, neural is fine. For multi-paragraph summaries, narrated articles, or document read-aloud, long-form with Ruth or Stephen produces markedly better pacing. The generative engine is typically 20–30× the per-character cost of standard — verify current pricing in your AWS region before enabling it in a high-volume path.

Discovering available voices with GetVoicesCommand

import {
  PollyClient,
  GetVoicesCommand,
  Engine,
  LanguageCode,
} from '@aws-sdk/client-polly';

const polly = new PollyClient({ region: 'us-east-1' });

async function getVoicesForEngine(engine: Engine, languageCode?: LanguageCode) {
  const { Voices } = await polly.send(new GetVoicesCommand({
    Engine: engine,
    LanguageCode: languageCode,       // omit to fetch all languages
    IncludeAdditionalLanguageCodes: true, // include voices that support secondary languages
  }));

  return (Voices ?? []).map(v => ({
    id: v.Id,
    name: v.Name,
    gender: v.Gender,
    language: v.LanguageCode,
    language_name: v.LanguageName,
    supported_engines: v.SupportedEngines,
    additional_languages: v.AdditionalLanguageCodes,
  }));
}

// Usage: list all neural voices for US English
const neuralUsVoices = await getVoicesForEngine(Engine.NEURAL, LanguageCode.EN_US);
// Returns: Joanna, Matthew, Ivy, Kendra, Kimberly, Salli, Joey, Justin, Kevin

// List long-form voices (English only as of late 2026)
const longFormVoices = await getVoicesForEngine(Engine.LONG_FORM);
// Returns: Ruth (en-US), Stephen (en-US)

Cache the GetVoicesCommand result at application startup — the voice catalog changes infrequently. If you need to validate a user-supplied VoiceId at request time, check it against your cached set rather than making a live API call per request. The SupportedEngines field on each voice is authoritative — not every voice listed for one engine is available for another. Calling SynthesizeSpeechCommand with an engine that the requested voice doesn't support raises InvalidParameterValueException: Engine not supported by voice.

Selecting the right engine per content type

type ContentType = 'notification' | 'conversation' | 'article' | 'presentation';

function pickEngine(contentType: ContentType): { engine: string; voiceId: string } {
  switch (contentType) {
    case 'notification':
      // Short, single-sentence alerts — neural is natural enough; standard is an option to cut cost
      return { engine: 'neural', voiceId: 'Joanna' };

    case 'conversation':
      // Multi-turn dialogue, chatbot responses — neural with a consistent voice per persona
      return { engine: 'neural', voiceId: 'Matthew' };

    case 'article':
      // Blog posts, documents, > 3 paragraphs — long-form engine narrates better
      return { engine: 'long-form', voiceId: 'Ruth' };

    case 'presentation':
      // Slide narration or explainer video — long-form male voice
      return { engine: 'long-form', voiceId: 'Stephen' };
  }
}

Consistency matters more than perfection when building multi-step voice workflows. Pick one voice per persona and store it in configuration — switching voice IDs mid-session creates a jarring experience even if each individual voice sounds good in isolation. If your MCP server builds a "narrator" persona, choose Ruth or Stephen and use them for all long-form synthesis in that persona. For alert or notification voices, choose a gender-and-tone combination that matches your brand's written voice.

Multi-language voice strategy

Some Polly voices support a secondary language via the LanguageCode override parameter on SynthesizeSpeechCommand — a voice trained primarily on US English can synthesize French text with a French accent. This is not the same as a native French voice: pronunciation is accented. For best results with non-English content, use a voice whose primary LanguageCode matches the input text.

// Detect content language and select the appropriate native voice
function selectVoiceForLanguage(languageCode: string): { voiceId: string; engine: string } {
  const voiceMap: Record<string, { voiceId: string; engine: string }> = {
    'en-US': { voiceId: 'Joanna', engine: 'neural' },
    'en-GB': { voiceId: 'Amy',    engine: 'neural' },
    'fr-FR': { voiceId: 'Lea',    engine: 'neural' },
    'fr-CA': { voiceId: 'Gabrielle', engine: 'neural' },
    'de-DE': { voiceId: 'Vicki', engine: 'neural' },
    'es-ES': { voiceId: 'Lucia', engine: 'neural' },
    'es-US': { voiceId: 'Lupe',  engine: 'neural' },
    'ja-JP': { voiceId: 'Takumi', engine: 'neural' },
    'pt-BR': { voiceId: 'Camila', engine: 'neural' },
    'zh-CN': { voiceId: 'Zhiyu', engine: 'standard' }, // neural not available for zh-CN in all regions
  };

  return voiceMap[languageCode] ?? { voiceId: 'Joanna', engine: 'neural' };
}

Neural voice availability varies by language — many non-English languages only have standard voices or have limited neural coverage. Run GetVoicesCommand at startup and store the result to validate your language-to-voice map against what's actually available in your deployed region. The AdditionalLanguageCodes field on a voice object lists secondary languages the voice can synthesize with an accent — useful for mixed-language documents where a fully native voice isn't available.

Generative engine: when to use it

The generative engine produces the most human-like output but has significant restrictions: no SSML support, only a handful of voices (primarily Amy for en-GB and Ruth, Matthew for en-US), and cost roughly 20–30× that of the standard engine. Use it only when naturalness is the primary product differentiator — for example, a voice assistant product where users will notice robotic-sounding speech, or a content narration service where audio quality directly affects user retention.

// Generative engine — no SSML, plain text only, limited voice set
const { AudioStream } = await polly.send(new SynthesizeSpeechCommand({
  Text: 'Your order has shipped and will arrive on Thursday.',
  VoiceId: VoiceId.RUTH,
  OutputFormat: OutputFormat.MP3,
  Engine: Engine.GENERATIVE,
  // SampleRate omitted — uses default 22050 for mp3
  // TextType omitted — must be 'text', not 'ssml' (generative does not support SSML)
}));

If your application flow already uses SSML for pronunciation control, prosody, or pauses, the generative engine cannot be used — it ignores SSML tags and treats the input as plain text. In practice, the neural engine with well-crafted SSML often achieves results close to generative quality at a fraction of the cost. See the SSML guide for prosody and pronunciation techniques that close the quality gap.