skills/response-sanitization/SKILL.md
# Response Sanitization ## Capability Cleans and transforms MCP agent responses for optimal text-to-speech output, removing SSML/Markdown markup, handling special characters, truncating overly long responses, and ensuring voice-friendly formatting. ## MCP Tools | Tool | Input Schema | Output | Rate Limit | |------|-------------|--------|------------| | `sanitize.forTTS` | `z.object({ text: z.string(), options: SanitizeOptions.optional() })` | `{ sanitized: string, warnings: string[] }` | 100
npx skillsauth add reaatech/voice-agent-kit skills/response-sanitizationInstall this skill globally with one command. Works with Claude Code, Cursor, and Windsurf.
3 of 9 scanners reported clean
Some scanners were skipped, did not run, or reported a non-clean status. Review each row below.
Cleans and transforms MCP agent responses for optimal text-to-speech output, removing SSML/Markdown markup, handling special characters, truncating overly long responses, and ensuring voice-friendly formatting.
| Tool | Input Schema | Output | Rate Limit |
|------|-------------|--------|------------|
| sanitize.forTTS | z.object({ text: z.string(), options: SanitizeOptions.optional() }) | { sanitized: string, warnings: string[] } | 1000 RPM |
| sanitize.stripMarkdown | z.object({ text: z.string() }) | { text: string } | 1000 RPM |
| sanitize.stripSSML | z.object({ text: z.string() }) | { text: string } | 1000 RPM |
| sanitize.truncate | z.object({ text: z.string(), maxLength: z.number() }) | { text: string, truncated: boolean } | 1000 RPM |
| sanitize.validate | z.object({ text: z.string() }) | { valid: boolean, issues: string[] } | 1000 RPM |
{
"name": "sanitize.forTTS",
"arguments": {
"text": "I found **[3 articles](https://example.com)** about your query. The first one from *Nature* says..."
}
}
{
"sanitized": "I found 3 articles about your query. The first one from Nature says...",
"warnings": ["Removed markdown link syntax", "Removed italic markers"]
}
{
"name": "sanitize.stripMarkdown",
"arguments": {
"text": "Check out this **bold** and this *italic* text with [a link](https://example.com)"
}
}
{
"text": "Check out this bold and this italic text with a link"
}
{
"name": "sanitize.stripSSML",
"arguments": {
"text": "Hello <break time='500ms'/> welcome to <emphasis>our service</emphasis>"
}
}
{
"text": "Hello welcome to our service"
}
{
"name": "sanitize.truncate",
"arguments": {
"text": "Here are the main points from the article. First, climate change is affecting weather patterns globally. Second, renewable energy adoption is accelerating. Third, policy changes are needed...",
"maxLength": 150
}
}
{
"text": "Here are the main points from the article. First, climate change is affecting weather patterns globally. Second, renewable energy adoption is accelerating...",
"truncated": true
}
{
"name": "sanitize.validate",
"arguments": {
"text": "Your balance is $1,234.56 <special> and that's all"
}
}
{
"valid": false,
"issues": [
"Contains dollar sign (may cause TTS issues)",
"Contains angle brackets (SSML characters)"
]
}
| Failure | Cause | Recovery | |---------|-------|----------| | Empty text | No content to sanitize | Return empty string | | Invalid encoding | Non-UTF8 characters | Attempt to decode or reject | | Unbalanced tags | Malformed SSML | Strip to balanced state |
interface SanitizeOptions {
removeUrls: boolean; // Default: true
removeEmails: boolean; // Default: true
removePhoneNumbers: boolean; // Default: true
removeSpecialChars: boolean; // Default: false
maxLength: number; // Default: 500
}
| Pattern | Example | Action |
|---------|---------|--------|
| URLs | https://example.com | Remove or spell out |
| Emails | [email protected] | Remove or mask |
| Phone numbers | +1-555-123-4567 | Remove or mask |
| SSML tags | <break time='1s'/> | Strip to plain text |
| Special chars | & < > | Decode or remove |
// Before TTS
const optimizations: Record<string, string> = {
// Expand abbreviations
"Dr.": "Doctor",
"Mr.": "Mister",
"St.": "Street",
// Fix common TTS issues (apply only to standalone symbols)
" $": " dollars",
" %": " percent",
// Remove problematic characters
"*": "", // Markdown emphasis
"#": "", // Markdown headers
"> ": "", // Blockquotes (with trailing space to avoid mid-sentence)
};
// Split long sentences for natural pacing
function chunkForVoice(text: string, maxLength: number = 200): string[] {
const sentences = text.match(/[^.!?]+[.!?]+/g) || [text];
const chunks: string[] = [];
let current = "";
for (const sentence of sentences) {
if ((current + sentence).length > maxLength && current) {
chunks.push(current.trim());
current = sentence;
} else {
current += sentence;
}
}
if (current) chunks.push(current.trim());
return chunks;
}
// Adjust punctuation for natural speech
function adjustPunctuation(text: string): string {
return text
// Replace colons with pauses
.replace(/:/g, ", ")
// Reduce multiple exclamation marks
.replace(/!{2,}/g, "!")
// Remove semicolons (confuse TTS)
.replace(/;/g, ", ")
// Handle parentheses (often mispronounced)
.replace(/\(([^)]+)\)/g, "$1");
}
| Metric | Type | Description |
|--------|------|-------------|
| voice.sanitize.length_reduction | Histogram | Characters removed |
| voice.sanitize.truncated | Counter | Truncation events |
| voice.sanitize.warnings | Counter | Sanitization warnings |
| voice.sanitize.invalid | Counter | Invalid input detected |
| Span | Attributes |
|------|------------|
| voice.sanitize.full | input_length, output_length, warnings_count |
| voice.sanitize.truncate | original_length, max_length, truncated |
| voice.sanitize.strip | pattern_type, replacements |
tools
# Twilio Media Streams ## Capability Handles Twilio Media Streams WebSocket connections for real-time bidirectional audio communication, parsing inbound messages, encoding outbound audio, and managing call lifecycle events. ## MCP Tools | Tool | Input Schema | Output | Rate Limit | |------|-------------|--------|------------| | `twilio.handleStart` | `z.object({ message: z.object({ event: z.literal('start'), callSid: z.string(), streamSid: z.string(), format: z.string(), tracks: z.array(z.st
tools
# TTS Provider Interface ## Capability Provides a unified interface for text-to-speech (TTS) providers, enabling streaming audio synthesis with first-byte latency tracking, voice selection, and output format conversion. ## MCP Tools | Tool | Input Schema | Output | Rate Limit | |------|-------------|--------|------------| | `tts.synthesize` | `z.object({ text: z.string(), config: z.object({ provider: z.string(), voice: z.string().optional(), speed: z.number().optional() }) })` | `{ chunks: A
tools
# Telephony Lifecycle ## Capability Manages the complete lifecycle of voice calls from TwiML webhook initiation through call completion, including call connect, transfer, conference, and disconnect handling with proper session cleanup. ## MCP Tools | Tool | Input Schema | Output | Rate Limit | |------|-------------|--------|------------| | `telephony.generateTwiML` | `z.object({ sessionId: z.string(), wsUrl: z.string().url() })` | `{ twiml: string }` | 100 RPM | | `telephony.handleConnect` |
tools
# STT Provider Interface ## Capability Provides a unified interface for speech-to-text (STT) providers, enabling real-time streaming transcription with interim results, endpoint detection, and automatic reconnection handling. ## MCP Tools | Tool | Input Schema | Output | Rate Limit | |------|-------------|--------|------------| | `stt.connect` | `z.object({ provider: z.enum(['deepgram', 'aws-transcribe', 'google-cloud']), config: z.object({ apiKey: z.string().optional(), sampleRate: z.number