Voice I/O layer for AI coding assistants — local TTS, STT, session booking. MCP server.
VoiceLayer defines 2 tools with comprehensive descriptions and well-structured JSON Schema input definitions. Both tools have extensive, LLM-optimized descriptions (450+ chars each) that explain WHAT the tool does, WHEN to use it, prerequisites, limits, and error recovery paths. All parameters include descriptions and type constraints (enums, patterns, min/max bounds). Tool naming is action-verb-first (voice_speak, voice_ask), which is excellent. Annotations are present and correct. However, output schemas are NOT documented, the response format is only described in prose. Parameter relationships are documented inline but could be more explicit. The descriptions, while comprehensive, are somewhat dense and could be more concise for LLM rapid parsing (arcade baseline: 194 chars; these run 450+). Neither tool is idempotent, which is acceptable for stateful voice I/O but should be called out. Error handling is described conceptually but lacks structured error response examples. For a specialized domain like voice interaction, this is strong work; for general MCP patterns, it's above-average but not exceptional.
Ask ONE short question aloud (≤600 chars — longer is refused). Waits for the user's voice response. BLOCKING: the question is spoken before the microphone opens, and the caller blocks for the entire playback plus the response. HARD LIMIT: 600 characters maximum; the effective per-request limit may be lower for short timeout_seconds values. Longer messages are refused, not truncated. That is roughly 45 seconds of speech, and it is a question, not a briefing. If content the user must understand or respond to is longer, split it into two or more sequential voice_ask calls. Each ask is a checkpoint: the user confirms they absorbed one part before the next is spoken. voice_speak is only for announcements or status updates that do not need a response. Auto-waits for any playing voice_speak audio to finish before speaking. Two recording modes: - VAD mode (default): Silero VAD detects speech, auto-stops on silence - Push-to-end (push_to_end=true; gated): DISABLES automatic silence detection. Recording will not stop when the user stops speaking; use only when the user explicitly asked for manual stop. User-controlled stop: touch /tmp/voicelayer.stop to end recording. Requires voice session booking — other sessions see 'line busy'. Captured user audio is always kept, including cancel, abort, and hard-timeout paths. If transcription cannot finish after audio exists, the non-fatal result includes an archive ID and audio path. Recover it with Re-transcribe in VoiceBar History; do not ask the user to repeat. Optional archive retranscription is explicit only and never automatic: call voice_ask with only retranscribe_archive_id from the preceding successful Ask. It binds to that exact receipt, sends the complete retained response audio to STT, does not speak and does not open the microphone, and returns the result only to this caller. Returns: transcribed text on success, recoverable archive details when captured audio needs retranscription, error if busy, if the prompt or the recorder never finished so the microphone never opened, or if the capture pipeline is stuck with zero recoverable audio. Prerequisites: sox (recording), whisper.cpp or Wispr Flow (STT), python3 + edge-tts (TTS).
Speak an announcement/status aloud or log a note silently. NON-BLOCKING — returns instantly. Spoken modes have a HARD LIMIT: 1,200 characters — longer messages are refused, not truncated. Use them only for announcements or status updates that do not require the user to understand or respond. voice_speak never waits for the user to absorb it — it has no acknowledgement gate, so an agent can queue the next utterance before the user has taken in the current one. Content the user must understand or respond to belongs in sequential voice_ask checkpoints. Modes (auto-detected from message if omitted): - announce: fast TTS for status updates (default for short messages) - brief: slower TTS for explanations (auto for messages > 280 chars) - consult: non-blocking spoken consultation; never waits for a response - think: silent markdown log, no audio (auto for 'insight:', 'note:', 'TODO:') Also supports: replay (index param) and toggle (enabled param). Stop playback: Voice Bar stop button or socket 'stop' command. Prerequisites: python3 + edge-tts, audio player (afplay on macOS).
Output schema is not documented. Descriptions mention return values in prose (e.g., 'transcribed text on success', 'archive details when captured audio needs retranscription') but there is no formal output schema definition. LLMs cannot reliably parse prose-only return types or plan downstream tool calls.
Parameter relationships are not formalized. voice_ask uses JSON Schema 'oneOf' to enforce mutual exclusion (message+voice+... vs retranscribe_archive_id only), which is correct, but this complex logic is buried in the schema. The description should explicitly state: 'Either provide message (new ask) OR retranscribe_archive_id (retry transcription), never both.' This aids LLM readability.
voice_speak description claims 'Required for speak/think modes, ignored for toggle/replay' for the message parameter, but the schema does not enforce this, message is optional (required: []). If an LLM calls voice_speak with mode='announce' and no message, the error handling is undefined. The description and schema are inconsistent.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-22 | C | 65 | <=2025-11-25 | v2 |
| 2026-03-09 | F | 39 | - | v1 |
Error recovery guidance is prose-only. voice_ask describes recovery paths ('Recover it with Re-transcribe in VoiceBar History; do not ask the user to repeat') but does not return structured error codes or recovery metadata that downstream tools can process. An LLM cannot programmatically detect which recovery path to take without parsing unstructured error text.
Example values embedded in descriptions. voice_speak mentions 'e.g. andrew' and 'e.g. en-US-AndrewNeural' for the voice parameter. LLMs frequently reuse these literal values. Replace with enum constraints if only a fixed set of voices is valid, or document in the description that these are just examples, not the only options.