Guide · AWS Transcribe · Custom Vocabulary
AWS Transcribe Custom Vocabulary in MCP Servers — CreateVocabularyCommand, Vocabulary Filters, TSV Format
AWS Transcribe custom vocabularies let you teach the speech model domain-specific words — product names, technical jargon, rare proper nouns — that it would otherwise mishear or silently drop. A vocabulary is a list of words paired with pronunciation hints in IPA or phonetic spellings (SoundsLike), and an optional DisplayAs override that controls how the word appears in the final transcript. Separately, vocabulary filters mask, remove, or tag specific words in the output transcript — useful for profanity filtering or redacting branded competitor names. Both are created once, compile to a READY state (minutes), and are referenced by name in subsequent transcription jobs or streaming sessions. This guide covers the TSV file format, the vocabulary compile-wait pattern, filter methods, and combining both in an MCP tool.
TL;DR
Create a custom vocabulary with CreateVocabularyCommand using either a simple Phrases array for single-word entries or a VocabularyFileUri pointing to an S3 TSV file with four columns: Phrase, IPA (IPA pronunciation, can be empty), SoundsLike (phonetic spelling, can be empty), DisplayAs (output form, can be empty). Poll GetVocabularyCommand until VocabularyState === 'READY' before using it. Create a vocabulary filter with CreateVocabularyFilterCommand (a Words array or an S3 word-list file) and choose a method: mask (replace with ***), remove (omit the word), or tag (keep but mark VocabularyFilterMatch: true on the item). Reference both by name in Settings.VocabularyName and Settings.VocabularyFilterName when starting a transcription job.
Custom vocabulary — simple Phrases array
import {
TranscribeClient,
CreateVocabularyCommand,
GetVocabularyCommand,
VocabularyState,
UpdateVocabularyCommand,
DeleteVocabularyCommand,
} from '@aws-sdk/client-transcribe';
const transcribe = new TranscribeClient({ region: 'us-east-1' });
// Simple vocabulary — word list only, no pronunciation hints
// Good for product names and acronyms that differ only in capitalisation/spacing
await transcribe.send(new CreateVocabularyCommand({
VocabularyName: 'mcp-tools-vocab',
LanguageCode: 'en-US',
Phrases: [
'AliveMCP',
'MCP', // forces capitalised output even when spoken as a word
'WebSocket',
'OAuth',
'k8s', // Kubernetes shorthand
'kubectl',
'Zod',
'Prisma',
'Drizzle',
],
}));
// Wait for READY state before using in a job (usually 1–5 minutes)
await waitForVocabularyReady('mcp-tools-vocab');
async function waitForVocabularyReady(name: string, maxAttempts = 30): Promise<void> {
for (let i = 0; i < maxAttempts; i++) {
await new Promise(r => setTimeout(r, 10_000)); // 10-second poll
const { VocabularyState: state } = await transcribe.send(
new GetVocabularyCommand({ VocabularyName: name })
);
if (state === VocabularyState.READY) return;
if (state === VocabularyState.FAILED) {
const { FailureReason } = await transcribe.send(
new GetVocabularyCommand({ VocabularyName: name })
);
throw new Error(`Vocabulary ${name} failed: ${FailureReason}`);
}
}
throw new Error(`Vocabulary ${name} did not reach READY state within ${maxAttempts * 10}s`);
}
The Phrases array accepts single words and hyphenated multi-word phrases. Each entry is case-sensitive in the vocabulary file but Transcribe applies it case-insensitively when matching audio — it then outputs the word in the casing you specified. Limit the vocabulary to words the model genuinely fails on; adding thousands of common English words worsens accuracy rather than improving it.
TSV file format with pronunciation hints
For words with unusual pronunciation, upload a TSV file to S3 and pass the URI as VocabularyFileUri. The file must have a header row and exactly four tab-separated columns:
# vocabulary.tsv — tab-separated, UTF-8, header row required
Phrase IPA SoundsLike DisplayAs
AliveMCP uh-lyve-em-see-pee AliveMCP
Kubernetes kju.bɚˈneɪ.tɪs Kubernetes
kubectl kube-cuttle kubectl
OAuth oh-auth OAuth
gRPC gee-are-pee-see gRPC
Zod zahd Zod
tsconfig tee-es-config tsconfig
Prisma priz-muh Prisma
Column rules:
- Phrase — the word or hyphenated phrase as it should appear in the transcript. Required. Up to 256 characters.
- IPA — International Phonetic Alphabet transcription. Preferred for exact pronunciation control. Use an IPA chart or the AWS documentation for valid symbols. Leave empty if using
SoundsLike. - SoundsLike — an approximate phonetic spelling using English letter combinations. Simpler than IPA but less precise. Provide either
IPAorSoundsLike, not both. Leave both empty to use only the display form without pronunciation override. - DisplayAs — how the word appears in the final transcript. Optional. Use for acronyms (IPA pronunciation but output as "API"), hyphenated compounds, or words that should include digits (
OAuth2).
// Upload vocabulary TSV to S3 and create vocabulary from it
import { PutObjectCommand } from '@aws-sdk/client-s3';
const tsvContent = [
'Phrase\tIPA\tSoundsLike\tDisplayAs',
'AliveMCP\t\tuh-lyve-em-see-pee\tAliveMCP',
'kubectl\t\tkube-cuttle\tkubectl',
'gRPC\t\tgee-are-pee-see\tgRPC',
].join('\n');
await s3.send(new PutObjectCommand({
Bucket: 'my-transcribe-assets',
Key: 'vocabularies/mcp-tools-vocab.tsv',
Body: tsvContent,
ContentType: 'text/plain',
}));
await transcribe.send(new CreateVocabularyCommand({
VocabularyName: 'mcp-tools-vocab-detailed',
LanguageCode: 'en-US',
VocabularyFileUri: 's3://my-transcribe-assets/vocabularies/mcp-tools-vocab.tsv',
}));
await waitForVocabularyReady('mcp-tools-vocab-detailed');
Updating an existing vocabulary
// UpdateVocabularyCommand replaces the entire vocabulary — not a merge/append
// The vocabulary re-enters PENDING state and must be re-waited before use
await transcribe.send(new UpdateVocabularyCommand({
VocabularyName: 'mcp-tools-vocab',
LanguageCode: 'en-US',
// Add new terms to the existing list — must include ALL desired phrases
Phrases: [
'AliveMCP', 'MCP', 'WebSocket', 'OAuth', 'k8s', 'kubectl', 'Zod',
'Prisma', 'Drizzle',
'Hono', // ← newly added
'Fastify', // ← newly added
'Vite', // ← newly added
],
}));
// Must wait again after update
await waitForVocabularyReady('mcp-tools-vocab');
Vocabulary updates are destructive replacements, not patches. Maintain the canonical phrase list in your own configuration store (e.g., a JSON file in S3 or a database column) and always pass the full desired list when calling UpdateVocabularyCommand. Keep a versioned copy of your TSV in S3 — Transcribe deletes the previous file from its managed store when you update. The vocabulary enters PENDING state during the update and returns to READY once compiled. Any jobs started while the vocabulary is PENDING will fail if they reference this vocabulary by name — gate job submission on vocabulary state.
Vocabulary filters
import {
CreateVocabularyFilterCommand,
VocabularyFilterMethod,
} from '@aws-sdk/client-transcribe';
// Create a profanity filter using a simple word list
await transcribe.send(new CreateVocabularyFilterCommand({
VocabularyFilterName: 'profanity-en-us',
LanguageCode: 'en-US',
Words: ['word1', 'word2', 'word3'], // replace with actual words to filter
}));
// Vocabulary filters do NOT have a compile phase — they're available immediately
// after creation (unlike vocabularies which require READY polling)
Vocabulary filters have three methods that control how matched words are handled in the transcript output:
| VocabularyFilterMethod | Effect on matched word | Use case |
|---|---|---|
mask |
Replaces matched word with *** |
Visible profanity filter — users can see a word was filtered but not what it was |
remove |
Omits the word entirely from the transcript | Clean transcripts for downstream parsing; filler words (um, uh, like) for readability |
tag |
Keeps the word but adds VocabularyFilterMatch: true on the item |
Audit logging — keep the full text for compliance but flag matched words for review |
The mask and remove methods affect results.transcripts[0].transcript (the full text string). The tag method does not affect the text — only the results.items[N].VocabularyFilterMatch flag. Use tag when you need the original words in a secure audit log but want the downstream agent to see a sanitized version built by filtering tagged items client-side.
Combining vocabulary and filter in a transcription job
import { StartTranscriptionJobCommand } from '@aws-sdk/client-transcribe';
await transcribe.send(new StartTranscriptionJobCommand({
TranscriptionJobName: `mcp-${Date.now()}`,
Media: { MediaFileUri: 's3://recordings-bucket/meeting-2026-11-04.ogg' },
LanguageCode: 'en-US',
OutputBucketName: 'transcripts-bucket',
OutputKey: `output/meeting-2026-11-04.json`,
Settings: {
// Custom vocabulary — improves recognition of your domain terms
VocabularyName: 'mcp-tools-vocab',
// Vocabulary filter — masks profanity in the output
VocabularyFilterName: 'profanity-en-us',
VocabularyFilterMethod: 'mask',
// Speaker diarization (optional — combine with both above)
ShowSpeakerLabels: true,
MaxSpeakerLabels: 3,
},
}));
Both a vocabulary and a vocabulary filter can be active on the same job. The vocabulary improves recognition first (before the transcript is written), and the filter is applied to the output. You cannot use two vocabularies or two filters simultaneously on a single job — if you need multiple domain sets, merge them into a single vocabulary file. For streaming transcription, the same field names apply directly on StartStreamingTranscriptionCommand (without the Settings wrapper — they're top-level parameters).
Vocabulary limits and naming rules
| Limit | Value |
|---|---|
| Maximum vocabularies per account | 100 (default; request increase via Service Quotas) |
| Maximum phrases per vocabulary | 256 KB TSV file size (not phrase count) |
| Maximum phrase length | 256 characters |
| Vocabulary compile time | Typically 1–5 minutes; large files may take 15–20 minutes |
| VocabularyName pattern | ^[0-9a-zA-Z._-]+$ — alphanumeric plus ., _, - |
| VocabularyFilterName pattern | Same as above |
| Vocabulary filters per account | 100 (same default limit) |
Vocabulary names are global per account per region. If your MCP server supports multi-tenant workloads, prefix vocabulary names with the tenant or project identifier (e.g., acme-corp-vocab) to avoid name collisions. The vocabulary compile time means that vocabularies must be provisioned asynchronously — not on first request — in production systems. Build vocabulary management into your infrastructure provisioning flow, not into your MCP tool's hot path.