Guide · Amazon Translate
MCP Server AWS Translate — TranslateTextCommand, auto-detect, language codes
Amazon Translate lets an MCP server tool translate text between 75+ language pairs in a single API call. The SDK entry point is TranslateTextCommand from @aws-sdk/client-translate: you supply SourceLanguageCode, TargetLanguageCode, and up to 10,000 bytes of Text. Setting SourceLanguageCode to "auto" delegates language detection to Amazon Comprehend — a convenient shortcut when user-supplied text may arrive in unknown languages. The three things to get right before the first call succeeds are region alignment (Translate has limited regional availability — not every region supports every language pair), IAM (translate:TranslateText on * — the action does not support resource-level restrictions), and text size (10,000-byte limit per call — split longer documents before calling).
TL;DR
Import TranslateClient and TranslateTextCommand from @aws-sdk/client-translate. Send { Text, SourceLanguageCode: 'auto', TargetLanguageCode: 'es' }. Read response.TranslatedText for the result and response.SourceLanguageCode to learn what language was detected. IAM needs translate:TranslateText (no resource-level scope — use *). Text limit is 10,000 bytes per call — buffer and chunk longer inputs. For custom domain vocabulary, pass TerminologyNames pointing to a terminology file you imported with ImportTerminologyCommand.
Installing the SDK and constructing the client
Amazon Translate uses the @aws-sdk/client-translate package. It is separate from Comprehend (language detection) and Polly (text-to-speech) — install only what your MCP server needs.
npm install @aws-sdk/client-translate
Construct the TranslateClient once at module initialization. It is stateless and safe to share across concurrent tool calls.
import { TranslateClient } from '@aws-sdk/client-translate';
const translate = new TranslateClient({
region: process.env.AWS_REGION ?? 'us-east-1',
// Credentials resolve from the standard chain:
// 1. AWS_ACCESS_KEY_ID / AWS_SECRET_ACCESS_KEY env vars
// 2. ~/.aws/credentials
// 3. ECS task role / Lambda execution role / EC2 instance profile (preferred)
});
Translate is not available in every AWS region. As of 2026, the full language pair coverage is widest in us-east-1, us-west-2, eu-west-1, and ap-northeast-1. If your MCP server already runs in another region, check the Amazon Translate endpoints documentation before assuming your target language pair is available locally.
Translating text with TranslateTextCommand
TranslateTextCommand is synchronous and returns in under a second for typical short strings. The Text field accepts plain text only — no HTML tags unless you are using the document translation endpoint (TranslateDocumentCommand). HTML entities in plain text mode are passed through literally and may appear in the translation output.
import {
TranslateClient,
TranslateTextCommand,
type TranslateTextCommandInput,
type TranslateTextCommandOutput,
} from '@aws-sdk/client-translate';
const translate = new TranslateClient({ region: process.env.AWS_REGION ?? 'us-east-1' });
export async function translateText(
text: string,
targetLang: string,
sourceLang = 'auto',
): Promise<{ translated: string; detectedSource: string }> {
const input: TranslateTextCommandInput = {
Text: text,
SourceLanguageCode: sourceLang, // 'auto' triggers Amazon Comprehend language detection
TargetLanguageCode: targetLang, // BCP 47 code — 'es', 'fr', 'zh', 'ja', etc.
// TerminologyNames: ['my-domain-terms'], // optional — custom vocabulary
// Settings: { Formality: 'FORMAL', Profanity: 'MASK' }, // optional
};
const response: TranslateTextCommandOutput = await translate.send(
new TranslateTextCommand(input),
);
return {
translated: response.TranslatedText ?? '',
detectedSource: response.SourceLanguageCode ?? sourceLang,
};
}
// Example MCP tool call:
// const { translated, detectedSource } = await translateText(tool.input.text, 'es');
// → { translated: 'Hola, mundo', detectedSource: 'en' }
When SourceLanguageCode is "auto", the response SourceLanguageCode tells you what Translate detected. This is reliable for 50+ character inputs. For very short strings (fewer than 20 characters), detection accuracy drops — consider requiring users to supply the source language explicitly for short fragments, or using Amazon Comprehend DetectDominantLanguage as a pre-step with confidence scoring before calling Translate.
Language code reference
Amazon Translate uses IETF BCP 47 language tags. The table below covers the most common codes. For the full 75+ language list, see the Amazon Translate documentation — additional languages are added periodically.
| Language | Code | Notes |
|---|---|---|
| English | en | Most supported as source/target pair |
| Spanish | es | Includes es-MX, es-ES variants via formality |
| French | fr | Formal/informal formality supported |
| German | de | Formal/informal formality supported |
| Japanese | ja | Formal/informal formality supported |
| Chinese Simplified | zh | Mainland Chinese |
| Chinese Traditional | zh-TW | Taiwan/Hong Kong variant |
| Korean | ko | Formal/informal formality supported |
| Portuguese | pt | Use pt-PT for European, pt for Brazilian |
| Italian | it | — |
| Arabic | ar | Right-to-left; check UI rendering |
| Hindi | hi | — |
| Russian | ru | — |
| Dutch | nl | Formal/informal formality supported |
| Turkish | tr | — |
| Auto-detect | auto | Source only — not valid as TargetLanguageCode |
Not all language pairs are directly supported — some pairs route through English as a pivot language internally. The UnsupportedLanguagePairException is returned when you request a pair that Translate cannot bridge, even via pivot.
Formality and profanity settings
The optional Settings object controls two quality dimensions: Formality (formal vs informal register) and Profanity (whether to mask profane words). Both are language-dependent — not every target language supports formality control.
import { TranslateTextCommand } from '@aws-sdk/client-translate';
// Formal translation — appropriate for business communications
const formalResult = await translate.send(new TranslateTextCommand({
Text: "Hey, what's up? Can you send me the report?",
SourceLanguageCode: 'en',
TargetLanguageCode: 'de',
Settings: {
Formality: 'FORMAL', // 'FORMAL' | 'INFORMAL' — not all languages support this
Profanity: 'MASK', // 'MASK' — replaces profanity with **** in output
},
}));
// German formal: "Guten Tag, wie geht es Ihnen? Könnten Sie mir bitte den Bericht zusenden?"
// Check if formality was actually applied (not all language pairs support it)
// response.AppliedSettings?.Formality tells you whether it was honored
const appliedFormality = formalResult.AppliedSettings?.Formality;
if (appliedFormality !== 'FORMAL') {
// Language pair did not support formality — log a warning
console.warn('Formality setting not applied for target language:', formalResult.TargetLanguageCode);
}
Formality is supported for German (de), French (fr), Italian (it), Spanish (es), Japanese (ja), Korean (ko), Dutch (nl), Portuguese (pt), and Hindi (hi) as target languages. Requesting FORMAL for an unsupported pair returns a successful translation without an error — check AppliedSettings.Formality in the response to confirm the setting was honored.
Chunking text that exceeds the 10,000-byte limit
Amazon Translate enforces a 10,000-byte limit per TranslateTextCommand call (bytes, not characters — UTF-8 multi-byte characters count toward the limit). Longer inputs must be chunked. The chunking boundary should respect sentence boundaries rather than arbitrary byte offsets to preserve translation quality.
const TRANSLATE_BYTE_LIMIT = 9500; // leave headroom below the 10,000-byte hard limit
function chunkBySentence(text: string, byteLimit: number): string[] {
const encoder = new TextEncoder();
const chunks: string[] = [];
// Split on sentence-ending punctuation followed by whitespace
const sentences = text.split(/(?<=[.!?。!?])\s+/u);
let current = '';
for (const sentence of sentences) {
const candidate = current ? current + ' ' + sentence : sentence;
if (encoder.encode(candidate).length > byteLimit) {
if (current) chunks.push(current.trim());
current = sentence;
} else {
current = candidate;
}
}
if (current.trim()) chunks.push(current.trim());
return chunks;
}
export async function translateLongText(
text: string,
targetLang: string,
sourceLang = 'auto',
): Promise<string> {
const chunks = chunkBySentence(text, TRANSLATE_BYTE_LIMIT);
const results = await Promise.all(
chunks.map(chunk =>
translate.send(new TranslateTextCommand({
Text: chunk,
SourceLanguageCode: sourceLang,
TargetLanguageCode: targetLang,
}))
)
);
return results.map(r => r.TranslatedText ?? '').join(' ');
}
For documents exceeding a few thousand words, consider asynchronous batch translation (StartTextTranslationJobCommand) instead — it handles multi-file, multi-MB jobs via S3 and avoids chunking logic in the calling code entirely. For structured documents (HTML, Word .docx), use TranslateDocumentCommand which preserves formatting across a 100KB size limit.
IAM: minimal permissions for a Translate caller
TranslateText does not support resource-level IAM restrictions — the resource must be *. If you use custom terminologies, add translate:GetTerminology and scope the resource to the terminology ARN.
{
"Version": "2012-10-17",
"Statement": [
{
"Sid": "AllowTranslateText",
"Effect": "Allow",
"Action": [
"translate:TranslateText",
"translate:TranslateDocument"
],
"Resource": "*"
},
{
"Sid": "AllowTerminologyRead",
"Effect": "Allow",
"Action": [
"translate:GetTerminology",
"translate:ListTerminologies"
],
"Resource": "*"
},
{
"Sid": "AllowComprehendForAutoDetect",
"Effect": "Allow",
"Action": [
"comprehend:DetectDominantLanguage"
],
"Resource": "*"
}
]
}
The comprehend:DetectDominantLanguage permission is only required when SourceLanguageCode: 'auto' is used — Translate calls Comprehend internally. Without this permission, auto-detect requests fail with an AccessDeniedException from the Comprehend side. Add it whenever auto-detect is needed.
Error handling reference
| Error | Cause | Fix |
|---|---|---|
| UnsupportedLanguagePairException | Source/target combination not supported — even via pivot | Check the Amazon Translate supported language pairs list; consider translating through en manually in two steps |
| TextSizeLimitExceededException | Input text exceeds 10,000 bytes | Chunk input before calling; use batch job for large documents |
| AccessDeniedException | Missing translate:TranslateText or comprehend:DetectDominantLanguage (for auto) | Add both actions to the IAM policy with Resource: * |
| InvalidRequestException | TargetLanguageCode set to 'auto', or unknown language code | 'auto' is source-only; verify language code against the Amazon Translate docs |
| ResourceNotFoundException | TerminologyNames references a terminology that does not exist | Verify terminology name with ListTerminologiesCommand; check region match |
| TooManyRequestsException | Exceeded service throughput quota (default 100 TPS per account) | Implement exponential backoff; request quota increase via AWS Service Quotas |
| ServiceUnavailableException | Transient service error | Retry with jittered backoff; check AWS Service Health Dashboard |