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
| Engine | Quality | Latency | Cost (relative) | Character limit per call | SSML |
|---|---|---|---|---|---|
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.