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.

LanguageCodeNotes
EnglishenMost supported as source/target pair
SpanishesIncludes es-MX, es-ES variants via formality
FrenchfrFormal/informal formality supported
GermandeFormal/informal formality supported
JapanesejaFormal/informal formality supported
Chinese SimplifiedzhMainland Chinese
Chinese Traditionalzh-TWTaiwan/Hong Kong variant
KoreankoFormal/informal formality supported
PortugueseptUse pt-PT for European, pt for Brazilian
Italianit—
ArabicarRight-to-left; check UI rendering
Hindihi—
Russianru—
DutchnlFormal/informal formality supported
Turkishtr—
Auto-detectautoSource 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

ErrorCauseFix
UnsupportedLanguagePairExceptionSource/target combination not supported — even via pivotCheck the Amazon Translate supported language pairs list; consider translating through en manually in two steps
TextSizeLimitExceededExceptionInput text exceeds 10,000 bytesChunk input before calling; use batch job for large documents
AccessDeniedExceptionMissing translate:TranslateText or comprehend:DetectDominantLanguage (for auto)Add both actions to the IAM policy with Resource: *
InvalidRequestExceptionTargetLanguageCode set to 'auto', or unknown language code'auto' is source-only; verify language code against the Amazon Translate docs
ResourceNotFoundExceptionTerminologyNames references a terminology that does not existVerify terminology name with ListTerminologiesCommand; check region match
TooManyRequestsExceptionExceeded service throughput quota (default 100 TPS per account)Implement exponential backoff; request quota increase via AWS Service Quotas
ServiceUnavailableExceptionTransient service errorRetry with jittered backoff; check AWS Service Health Dashboard