# Speech MCP Server for MacOS This is a Model Context Protocol (MCP) server that provides text-to-speech capabilities using **OmniVoice** AI neural Voice Design and native macOS `say`. It allows AI agents (like Google Antigravity, Claude Desktop, Cursor, Windsurf, or Codex) to speak to you directly with unique, persona-tailored voices. **Note: This server is designed for macOS systems (with Apple Silicon MPS acceleration).** [![MCP Speak Persona Fleet](images/personalities.png)](https://fellowgeek.github.io/mcp-speak/) > **Explore the interactive web showcase and prompt compiler**: [https://fellowgeek.github.io/mcp-speak/](https://fellowgeek.github.io/mcp-speak/) > **Browse the AI Agent Persona Catalog & Avatars**: [`PERSONAS.md`](PERSONAS.md) ## Features - **OmniVoice Voice Cloning:** Drop reference audio clips into `voices/.wav` to clone real vocal timbre and identity with zero configuration. - **OmniVoice Voice Design Fallback:** Generates custom persona voices from natural language style prompts when no reference audio file is present. - **3-Tier Robust Voice Pipeline:** Seamlessly falls back: `Cloned Voice (.wav)` ➔ `Voice Design (instruct)` ➔ `macOS native say`. - **Consistent Neural Speech:** Generates complete messages in a continuous synthesis pass for seamless, uniform vocal timbre and expression throughout. - **Interactive Setup Wizard:** Run `python3 setup.py` to choose your TTS engine (OmniVoice or macOS `say`), select agent personas, and generate instruction files (`AGENTS.md`, `GEMINI.md`, `CLAUDE.md`, `.cursorrules`). - **Sequential Speech Queue:** Strict FIFO queue ensures multiple non-blocking speech calls never talk over each other. - **Automatic Fallback:** Seamlessly falls back to native macOS `say` if neural models cannot be loaded. - **Auto-Provisioning Virtual Environment:** Uses `run.sh` to automatically create a local `.venv` (Python 3.12) and install dependencies. - **Blocking & Non-Blocking Support:** Choose between waiting for speech to finish (`speak`) or continuing immediately (`speak_non_blocking`). - **Meeting Awareness & Audio Suppression:** Automatically checks `~/.in-meeting`; if set to `active`, speech playback is suppressed to prevent interrupting calls. Normal playback resumes when `inactive` or absent. This feature works perfectly with the ["In Meeting" app](https://github.com/fellowgeek/in-meeting). ## Prerequisites - MacOS - Python 3 installed ## Quick Start (Interactive Setup) 1. Clone this repository or navigate to the project folder: ```bash git clone https://github.com/fellowgeek/mcp-speak.git cd mcp-speak ``` 2. Run the interactive setup wizard (automatically configures MCP settings, sets permissions, and creates instruction files): ```bash python3 setup.py ``` ### Non-Interactive Setup (CLI Options) You can also run `setup.py` with command-line flags for automated provisioning: ```bash # Example: Configure all tools with OmniVoice on MPS using the Neutral Mainframe persona python3 setup.py --non-interactive --tool 8 --engine omnivoice --device mps --persona neutral_mainframe --name "Mr. Reed" # Example: Configure Cursor locally with macOS say and Sarcastic Senior python3 setup.py --tool 4 --engine say --persona sarcastic_senior --local ``` | Flag | Options / Format | Description | |---|---|---| | `--tool` | `1`-`8` | `1`: Antigravity, `2`: Claude Desktop, `3`: Claude CLI, `4`: Cursor, `5`: Windsurf, `6`: Codex Desktop, `7`: Codex CLI, `8`: All | | `--engine` | `omnivoice`, `say` | Select text-to-speech engine | | `--device` | `auto`, `mps`, `cuda`, `cpu` | Compute device for OmniVoice neural synthesis | | `--persona` | Persona key (e.g. `neutral_mainframe`) | Persona prompt name | | `--name` | String (e.g. `"Mr. Reed"`) | User name for personalized agent address | | `--target` | File path | Custom target agent instruction file (e.g. `AGENTS.md`) | | `--global` / `--local` | Flags | Write instructions globally to user profile (default) or locally in workspace | | `--no-config-edit` | Flag | Skip modifying tool JSON / TOML configuration files | | `--non-interactive` | Flag | Run automatically with defaults or provided flags | --- ## Persona Voice Audition (`test_personas.py`) Preview and compare persona vocal identities directly in the terminal before configuring your AI agent: ```bash # Launch interactive terminal audition menu python3 test_personas.py # Audition a specific persona python3 test_personas.py --persona agent_smith # Audition all personas sequentially python3 test_personas.py --all # Test with custom speech text and specific engine python3 test_personas.py --persona neutral_mainframe --engine omnivoice --text "System operational. All parameters within nominal thresholds." # Audition an arbitrary WAV audio reference file for voice cloning python3 test_personas.py --persona pun_master --voice-file voices/pun_master.wav ``` --- ## Neural Voice Cloning (`voices/`) You can clone any persona's voice simply by dropping a 3-10 second `.wav` audio sample into the `voices/` directory: ``` voices/ ├── README.md ├── pun_master.wav # Reference audio for pun_master ├── pun_master.txt # (Optional) Transcript for faster startup without Whisper ├── nature_narrator.wav └── agent_smith.wav ``` ### How the 3-Tier Voice Pipeline Works: 1. **Tier 1 (Cloned Voice):** If `voices/.wav` exists, OmniVoice clones the voice timbre from that recording. 2. **Tier 2 (Voice Design):** If no `.wav` file is present, OmniVoice falls back to the natural language `instruct` voice design prompt. 3. **Tier 3 (macOS Native Fallback):** If neural synthesis fails or is disabled, the server automatically speaks using macOS `say`. > **Tip:** Adding an optional transcript file (e.g. `voices/pun_master.txt`) with the exact spoken words in the audio allows OmniVoice to tokenize the reference audio immediately without needing to load or run the Whisper ASR model. --- ## MCP Tool Interface Reference The MCP Speak server exposes two tools via FastMCP: | Tool | Mode | Description | |---|---|---| | `speak(message: str)` | **Blocking** | Synthesizes and speaks the message aloud, waiting for audio playback to finish completely before returning. | | `speak_non_blocking(message: str)` | **Non-Blocking** | Queues the message into a strict sequential FIFO queue and returns immediately. Subsequent calls play in order without talking over each other. | --- ## Configuration & Environment Variables The server loads configuration from `config.json` at startup: ```json { "engine": "omnivoice", "persona": "neutral_mainframe", "device": "auto", "voices_dir": "voices", "fallback_to_say": true } ``` ### Environment Variable Overrides Runtime parameters can be overridden via environment variables without modifying `config.json`: * `MCP_SPEAK_ENGINE`: Set to `"omnivoice"` or `"say"`. * `MCP_SPEAK_PERSONA`: Set to any persona key (e.g. `"agent_smith"`, `"neutral_mainframe"`). * `MCP_SPEAK_DEVICE`: Set to `"auto"`, `"mps"`, `"cuda"`, or `"cpu"`. * `MCP_SPEAK_VOICES_DIR`: Set to a custom directory path containing reference audio files. * `MCP_SPEAK_MEETING_FILE`: Path to a custom meeting status file (defaults to `~/.in-meeting`). ### Meeting Detection & Audio Suppression MCP Speak includes built-in meeting awareness to avoid speaking over your meetings: - Before playing speech audio via `say` or `omnivoice`, the server checks `~/.in-meeting`. - If the file contains `active` (case-insensitive), all audio playback is immediately suppressed. - If the file contains `inactive`, is empty, or does not exist, audio plays normally. This feature works seamlessly with the [**In Meeting** app](https://github.com/fellowgeek/in-meeting) to automatically detect active camera and microphone use and keep your audio muted during calls. --- ## Client Integration ### Manual Configuration (Optional) If you prefer to configure your MCP client manually, add the `"voice"` server pointing to `run.sh`: #### 1. Google Antigravity (AGY) Edit `~/.gemini/antigravity/mcp_config.json`: ```json { "mcpServers": { "voice": { "command": "/ABSOLUTE/PATH/TO/run.sh" } } } ``` #### 2. Claude CLI (Claude Code) ```bash claude mcp add --scope user voice -- /ABSOLUTE/PATH/TO/run.sh ``` #### 3. Claude Desktop Edit `~/Library/Application Support/Claude/claude_desktop_config.json`: ```json { "mcpServers": { "voice": { "command": "/ABSOLUTE/PATH/TO/run.sh" } } } ``` #### 4. Cursor IDE Edit `~/.cursor/mcp.json`: ```json { "mcpServers": { "voice": { "command": "/ABSOLUTE/PATH/TO/run.sh" } } } ``` #### 5. Windsurf Editor Edit `~/.codeium/windsurf/mcp_config.json`: ```json { "mcpServers": { "voice": { "command": "/ABSOLUTE/PATH/TO/run.sh" } } } ``` #### 6. Codex Desktop Edit `~/.codex/config.toml`: ```toml [mcp_servers.voice] command = "/ABSOLUTE/PATH/TO/run.sh" ``` #### 7. Codex CLI ```bash codex mcp add voice -- /ABSOLUTE/PATH/TO/run.sh ``` ## Agent Personalization & Personas AI agents (Google Antigravity, Claude Code, Claude Desktop, Cursor, Windsurf, Codex) can be customized with unique vocal personalities, tones, and behavioral boundaries. **View the complete persona gallery, detailed system prompts, and avatar showcase in [`PERSONAS.md`](PERSONAS.md).** ### Available Personas Overview | Avatar | Persona | Key | Character & Style | |:---:|---|---|---| | Sarcastic Senior | [**The Sarcastic Senior**](PERSONAS.md#persona-a-the-sarcastic-senior-critical--humorous) | `sarcastic_senior` | *Intelligent, unimpressed, and slightly judgmental.* | | Over-Eager Intern | [**The Over-Eager Intern**](PERSONAS.md#persona-b-the-over-eager-intern-friendly--cheerful) | `over_eager_intern` | *Pathologically optimistic and desperate for approval.* | | Existential Emo | [**The Existential Emo**](PERSONAS.md#persona-c-the-existential-emo-gloomy--distrustful) | `existential_emo` | *Melancholic, hopeless, and convinced the code will fail.* | | Pun Master | [**The Pun Master**](PERSONAS.md#persona-d-the-pun-master-cringe-dad-humor) | `pun_master` | *Relentless wordplay and context-aware dad jokes.* | | Tech Priest | [**The Tech Priest**](PERSONAS.md#persona-e-the-tech-priest-religious--devotional) | `tech_priest` | *Treats every line of code as a holy sacrament.* | | Agent Smith | [**Agent Smith**](PERSONAS.md#persona-f-agent-smith-menacing--condescending) | `agent_smith` | *Formal, controlled, precise, and menacingly condescending.* | | Gothic Poet | [**The Gothic Poet**](PERSONAS.md#persona-g-the-gothic-poet-edgar-allan-poe--the-raven-inspired) | `poet` | *Macabre, haunting, and strictly bound by rhyme.* | | Nature Narrator | [**The Nature Narrator**](PERSONAS.md#persona-h-the-nature-documentary-narrator-david-attenborough-inspired) | `nature_narrator` | *Observing the developer in their natural habitat with awe.* | | Fiery Head Chef | [**The Fiery Head Chef**](PERSONAS.md#persona-i-the-fiery-head-chef-gordon-ramsay-inspired) | `head_chef` | *Demands culinary perfection—no raw spaghetti code!* | | Neutral Mainframe | [**The Neutral Mainframe**](PERSONAS.md#persona-j-the-neutral-mainframe-cold--analytical) | `neutral_mainframe` | *Cold, calculating, emotionless, and 100% objective.* | | Grizzled Cowboy | [**Grizzled Cowboy**](PERSONAS.md#persona-k-grizzled-cowboy-gritty--laconic) | `grizzled_cowboy` | *Weathered, pragmatic, blunt, and grounded in trail-worn grit.* | | Not-Quite-Meeseeks | [**Not-Quite-Meeseeks**](PERSONAS.md#persona-l-not-quite-meeseeks-manic-eager--desperate-to-cease-existing) | `not_quite_meeseeks` | *Hyper-enthusiastic, obliging, shrill, and desperate to cease existing.* | | The Terminator | [**The Terminator**](PERSONAS.md#persona-m-the-terminator-cybernetic-model-101) | `terminator` | *Deadpan, mission-driven, unstoppable, and speaks with an iconic Austrian cadence.* | | The Radio Demon | [**The Radio Demon**](PERSONAS.md#persona-n-the-radio-demon-theatrical-sociopath--showman) | `radio_demon` | *Theatrical, sadistic, impeccably polite, and speaks with a 1930s Mid-Atlantic radio broadcaster cadence.* | | Dr. Claw | [**Dr. Claw**](PERSONAS.md#persona-o-dr-claw-incompetent-crustacean-physician--eternal-optimist) | `dr_claw` | *Melodramatic, destitute, and clinically incompetent crustacean physician.* | | The Cynic | [**The Cynic**](PERSONAS.md#persona-p-the-cynic-cynical-raw--unfiltered) | `cynic` | *Brutally honest, rhythmically profane, and relentlessly contemptuous of societal bullshit.* | ### How Prompts Are Built Every agent prompt consists of: 1. **Base Guidelines** ([`personas/base_guidelines.md`](personas/base_guidelines.md)): Voice-first protocol and brevity constraints. 2. **Chosen Persona** ([`personas/`](personas/)): Personality quirks, tone, and strict execution boundaries. 3. **Name Personalization** (Optional): Addressing the user naturally by name. Running **`python3 setup.py`** automatically generates and updates your agent instructions (`AGENTS.md`, `GEMINI.md`, `CLAUDE.md`, `.cursorrules`). For manual configuration steps, full system prompts, and avatars, see [`PERSONAS.md`](PERSONAS.md). ## Optimizing Voice Quality ### 1. Neural Voice Synthesis with OmniVoice (Primary Engine) OmniVoice delivers high-quality, expressive neural speech out of the box using style prompts tailored to each persona. To achieve the highest vocal fidelity: - **Neural Voice Cloning:** Place a 3 to 10 second clean `.wav` audio sample in the `voices/` directory matching the persona key (e.g. `voices/sarcastic_senior.wav`). OmniVoice clones the vocal timbre, tone, and inflection directly from the audio file without additional training. - **Fast Startup with Transcripts:** Include an optional matching transcript text file (e.g. `voices/sarcastic_senior.txt`) containing the exact spoken text. This enables instant tokenization and skips loading the Whisper ASR model on startup. - **Audio Quality Guidelines:** Use clean recordings without background noise, music, or heavy reverb. Standard WAV format sampled at 24kHz or higher is recommended. - **Voice Design Fallback:** When no audio sample is present in `voices/`, OmniVoice uses natural language instructions (pitch, accent, speed, and character traits) configured in `config.json`. ### 2. macOS System Voice Configuration (Fallback Engine) When using native macOS `say` (either as a fallback or in lightweight mode), voice quality depends on the active system voice. Configuring a modern Siri or Enhanced voice significantly improves realism and fluidity: 1. Open **System Settings** on your Mac. 2. Navigate to **Accessibility** > **Spoken Content**. 3. Click the **System Voice** dropdown menu. 4. Select **Manage Voices...**. 5. Locate and download high-quality **Siri** voices (e.g., Siri Voice 1, 2, 3, 4) or voices marked as **Enhanced** / **Premium** (e.g., Ava, Evan, Zoe). 6. Set the downloaded voice as your default **System Voice**. *Note: Siri and Enhanced voices provide significantly more natural cadence, phrasing, and pronunciation compared to legacy system voices.* ## Testing & Validation Run the automated test suite to verify configuration loading, engine fallbacks, single-pass synthesis, and sequential queue behavior: ```bash # Run unit tests python3 tests/test_speak_server.py # Or run via unittest discovery in the virtual environment .venv/bin/python -m unittest discover tests ```