MCP server for VoxAI macOS app providing live meeting transcripts, text-to-speech, voice input, and meeting management capabilities
VoxAI MCP defines 14 tools with complete JSON schemas and detailed descriptions. Naming follows verb_noun convention (speak, list_voices, get_meeting, push_advice). Descriptions are comprehensive (100-400 chars), exceeding the 10-1024 baseline. All parameters have type definitions and descriptions. However, output schemas are not documented, LLMs cannot predict response structure for downstream chaining. Error handling is absent: no guidance on retryability, user-fixable vs fatal errors, or recovery paths. Parameters lack enums where appropriate (e.g., 'kind' in push_advice accepts free-form strings instead of constrained values). Some parameters accept opaque IDs (meeting_id) without accepting human-readable alternatives. Overall solid definition quality with gaps in output documentation and error guidance.
Fetch speech the user spoke into the dialog overlay (voice-direct input), incrementally. This turns the user's voice into your input: treat new segments as messages the user typed to you. Voice-dialog protocol: when the user asks to enter voice dialog, first call once and use the returned nextIndex as your cursor (skip history — only react to what's new), then poll every few seconds. Merge segments arriving close together, ignore echoes of your own TTS, reply in chat AND speak() a short summary. Stop after ~10 min of silence and say so.
Get the live transcript of the meeting currently in progress, with incremental fetching. Listening-mode usage: call in a loop, passing the previous call's nextIndex as after_index so only new segments are returned; empty segments means nothing new yet. Speaker labels lag 30-60s behind the text (filled in by a background worker), and the incremental cursor never re-sends old segments — so every response also carries `speakers`: the CURRENT label map for ALL segments ({"<index>": "<speaker>"}, 0-based global indices, unlabeled segments omitted). On every poll, refresh the labels of segments you already fetched from this map. Values are canonical: "me" / "partner" / "speaker_N" / custom name. When live is False: no meeting is in progress; lastMeetingId / lastMeetingTitle of the most recent finished meeting are included for post-meeting retrieval via get_meeting(). The scenario field ("meeting" / "call" / "in_person") should tune your advice style.
Get the full content of a meeting, including all transcript segments and speaker labels. Speaker values are canonical: "me" / "partner" (call mode), "speaker_1", "speaker_2", ... (voice diarization), a custom name assigned by the user, or "" when not yet labeled.
Output schemas not documented. LLMs cannot predict response structure (fields, types, pagination) for downstream tool chaining. E.g., list_meetings returns 'id, title, scenario, startTime, endTime, isLive, segmentCount, preview' but this is only in the description, not a formal schema.
No error handling guidance. Tools lack recovery hints, retryability classification, or actionable error messages. E.g., if get_meeting(meeting_id='invalid') fails, the LLM receives no guidance on whether to retry, call list_meetings() first, or ask the user.
Free-form string parameters where enums are appropriate. 'kind' in push_advice accepts 'check|ask|risk|strategy|emotion|action|note' but is defined as a string with no enum constraint. LLMs may hallucinate invalid values like 'warning' or 'alert'.
Inferred effective spec: <=2025-11-25.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 56 | <=2025-11-25 | v2 |
Install the optional Qwen3-TTS voice component — six natural Chinese voices that run fully on-device, a big step up from the built-in system voice. Tell the user the cost before you call this: a ~190MB download now, plus a ~2GB model on the first local speak(). It is optional — the system voice keeps working without it. After installing, switch to it with update_voice_config(cn_engine: "local", cn_voice: "Vivian"). Voices: Vivian, Aiden, Dylan (Beijing accent), Serena, Eric, Ryan. Safe to call again to repair or update an existing install.
List summary info for all recorded meetings. Returns one entry per meeting: id, title, scenario ("meeting" / "call" / "in_person"), startTime, endTime, isLive, segmentCount, preview (excerpt of the first segment).
List all available voice IDs, grouped by engine.
Push a live-copilot advice item to the meeting in progress; it shows up in real time in the app's advice panel and floating card. Use while listening in on a live meeting: fact-check results, follow-up questions, risk alerts, negotiation strategy, emotion observations, action items. Advice is archived with the meeting and appended after the transcript on export.
Send a reply back to the voice-dialog overlay. The app plays it aloud via TTS and shows it in the dialog transcript.
Rename a recorded session. Use a short, descriptive title (≤60 chars) in the user's language — e.g. "定价谈判 · 蓝湖科技" instead of a timestamp. Good moments to rename: right after a session ends (users rarely rename the default timestamp titles themselves), or when the user asks. Don't rename a title the user clearly set by hand unless asked. The app applies the change within a few seconds.
Convert text to speech and play it aloud on the user's Mac. Language is auto-detected and the engine is chosen from the current config (system voice, or Azure with the user's own key).
Check in as the live-listening agent. The app's advice panel then shows "<name> · Listening" so the user can see their AI is actually connected. Call this once when entering listening mode; after that, every get_live_transcript() poll refreshes the heartbeat automatically (the app treats a heartbeat older than ~3 minutes as disconnected, so keep polling every 30-60s).
Check out of listening mode. The app immediately shows "AI Not Connected" instead of waiting for the heartbeat to expire.
Immediately stop any speech currently playing.
Update voice settings (persisted to config). cn_engine: Chinese engine — "system" (built-in macOS voice, offline), "azure" (official API, requires the user's own key configured in the app's Settings; do not set keys here), or "local" (Qwen3-TTS, six on-device voices, needs install_voice_component first). cn_voice: Chinese voice ID (azure: xiaoxiao/yunxi/xiaoyi; local: Vivian/Aiden/Dylan/Serena/Eric/Ryan). en_voice: English voice ID (any name from `say -v '?'`). speed: Speech rate, 0.5-2.0, 1.0 = normal. language: Language mode "auto"/"zh"/"en".
Opaque ID-only parameters without human-readable alternatives. meeting_id requires the exact ID from list_meetings(); no fallback to search by title or date. Forces extra lookup calls and breaks the chat data model where users say 'the pricing meeting' not 'meeting_abc123'.
No pagination or result limits documented. list_meetings and list_voices may return unbounded results. If a user has 1000 meetings, returning all could exhaust context. No mention of limit, offset, or next_cursor parameters.