Claude Voice
БесплатноНе проверенProvides bidirectional local voice for Claude Code on Apple Silicon, enabling hands-free conversation and spoken replies using local Whisper STT and Kokoro TTS,
Описание
Provides bidirectional local voice for Claude Code on Apple Silicon, enabling hands-free conversation and spoken replies using local Whisper STT and Kokoro TTS, with optional ElevenLabs backend and a Stop hook for automatic speech.
README
Bidirectional local voice for Claude Code on Apple Silicon, built on
mlx-audio. Talk to Claude, and Claude
talks back — automatically, guaranteed by a Claude Code Stop hook rather
than by Claude remembering to call a speak() tool. Runs 100% locally and
free by default (Whisper + Kokoro), with an optional ElevenLabs backend for
more realistic voices.
Contents: Two ways to use this · Commands · Why a Stop hook · Setup · Scope · Configuration · Architecture
Two ways to use this
Option 1 — Claude Code's native dictation + /talkback for replies.
Claude Code has its own built-in push-to-talk dictation (tap Space in the
chat box) that types your speech into the input box — that's a separate
system from this project, not something we built. Use it for input, and run
/talkback on so this MCP speaks Claude's replies back via Kokoro. Our own
mic/STT pipeline (listen, /talk) is never used in this mode — only the TTS
half. Simplest option if you're happy with Claude Code's own dictation and
just want spoken replies on top of it.
Option 2 — /talk for a fully local, hands-free conversation.
/talk arms hands_free mode and starts recording through our own local
pipeline (Whisper in, Kokoro out): it records until you stop talking,
transcribes it, Claude responds, the Stop hook speaks the reply, then it
automatically starts recording again — a continuous loop with no typing and
no dictation button, until you say "stop listening", go quiet past
hands_free_idle_seconds, or run /talk off. Entirely local end-to-end.
/talkonly arms listening. If you've ever turned spoken replies off with/talkback off,/talkalone won't bring them back — run/talkback ontoo, or you'll be transcribed and continued but never hear a reply. Both default to on, so a fresh install gets the full experience automatically.
The two options mix freely — e.g. native dictation most of the time, /talk
when you want your hands off the keyboard.
Commands
| Command | Does |
|---|---|
/talk |
Toggles hands_free (arms it and starts listening if off; disarms if on) |
/talk on |
Arms hands_free and starts listening, regardless of current state |
/talk off |
Disarms hands_free, regardless of current state — the quick way back after it auto-disarms |
/talk <seconds> |
One-shot timed recording; doesn't touch hands_free either way |
/talkback |
Toggles auto_speak (spoken replies) on/off |
/talkback on / off |
Sets auto_speak explicitly |
/talkback full |
Speaks the entire reply, no truncation |
/talkback brief |
Speaks a short summary only, truncated to brief_max_chars (default, 320 chars) |
/voice |
Lists all 54 voices, grouped by language |
/voice <id> |
Switches to that voice (e.g. /voice af_bella) |
/vocab add <word> |
Teach Whisper a word/name it tends to mishear (app names, jargon, rare terms) |
/vocab remove <word> |
Remove a word from the taught vocabulary |
/vocab / /vocab list |
Show the current taught vocabulary |
Say "stop listening" any time to end hands-free mode by voice instead of typing /talk off.
Why a Stop hook instead of a speak() tool?
Tool-call-based auto-speak (the common approach) only works if the model
chooses to call the tool after every reply — it can forget, get distracted,
or skip it under load. This project instead wires a Stop hook
(hooks/speak_on_stop.py) that Claude Code invokes after every turn,
independent of the MCP server and independent of Claude's cooperation. It
reads Claude's own final message for the turn, strips code/markdown/paths,
and speaks it (a short summary by default, or the whole thing with
/talkback full). Everything about this is tunable live via the
voice_config MCP tool or a config file, no restart required.
The same hook is also what makes hands-free mode work: when armed, it listens
again after speaking and feeds what you say back in via its decision: "block" output, so Claude Code continues the conversation without you
retyping anything.
Other capabilities
stop_speaking()— barge-in: interrupt playback mid-sentence.list_voices()— same as/voice, callable directly.voice_config()— get/set any setting live from Claude's console (see Configuration), persisted to~/.claude-voice-mcp/config.json(or a project-local.voice-mcp.json).vocabulary()//vocab— teach Whisper words it mishears (app names, jargon, rare terms), two layers deep: words are fed to it as aninitial_prompton every transcription (a standard Whisper biasing technique), and a fuzzy-correction pass (vocab_correct.py) catches near-misses the prompt alone didn't fix (e.g. "bido app" heard for "bedouapp") and swaps in the exact taught spelling. Neither layer is a hard guarantee, but together they measurably help with made-up names and jargon Whisper otherwise guesses wrong.- Hallucination guard (
stt_guard.py) — Whisper occasionally hallucinates a repeating phrase from silence/noise (a known failure mode). Detected and trimmed automatically before it reaches the conversation. - Echo guard (
stt_guard.is_echo_of) — in hands-free mode, the mic can pick up Claude's own TTS reply through the speakers and transcribe it back as if it were new user input, which without a check would let Claude reply to its own echo indefinitely. The hook compares what was just spoken against what the mic just heard and silently discards a match; a short settle delay before re-listening reduces how often this happens at all. - Duplicate-hook guard — if the Stop hook ends up registered in more than
one scope at once (e.g. both this project's
.claude/settings.jsonand a global~/.claude/settings.json, which both fire when working inside this repo), Claude Code runs every registered copy, and Claude would audibly speak the same reply twice. The hook atomically claims each turn'sprompt_idso only the first invocation for a given turn does anything; every duplicate silently no-ops. - Warm-model daemon — the Stop hook is a fresh process every turn; without
this it would reload Kokoro/Whisper from scratch each time (5+ seconds).
server.pykeeps a background daemon with both models warm so the hook stays fast. - Optional ElevenLabs backend for more realistic voices: set
ELEVENLABS_API_KEYandvoice_config set tts_backend elevenlabs. If the key is missing or a call fails for any reason, it silently falls back to local Kokoro — voice never breaks because of the cloud.
Requirements
- Apple Silicon Mac (M1 or later), macOS
- Python 3.11+, uv
- Working microphone and speakers
ffmpeg(optional, only needed for MP3/FLAC handling)
Setup on a new machine
Clone this repo somewhere permanent (its path gets baked into config below, so pick a final location, e.g.
~/tools/claude-voice-mcp):git clone [email protected]:Amaldeep98/Claude-realtime-voice-mcp.git cd Claude-realtime-voice-mcpRun setup:
./scripts/setup.shInstalls
uvif missing (via~/.local/bin), pins Python 3.12 (spaCy, one of Kokoro's text-processing dependencies, doesn't yet have wheels for newer Pythons), syncs the environment, and pre-downloads the default models (~2-3GB): Kokoro-82M (TTS) and Whisper large-v3-turbo (STT).The Stop hook is already configured — this repo ships its own
.claude/settings.jsonwith the hook wired up using Claude Code's${CLAUDE_PROJECT_DIR}placeholder, which resolves to wherever you cloned this repo, so nothing needs editing:{ "hooks": { "Stop": [ { "hooks": [ { "type": "command", "command": "uv --project ${CLAUDE_PROJECT_DIR} run python ${CLAUDE_PROJECT_DIR}/hooks/speak_on_stop.py", "timeout": 120 } ] } ] } }The 120s timeout matters: the hook speaks and then listens in hands-free mode, which can legitimately take over a minute — a shorter timeout silently kills it mid-listen with no error shown. If you start Claude Code fresh from a directory that already has this file (i.e. you
cd'd into the clone before launchingclaude), it's picked up automatically; if you were already mid-session when the clone appeared, run/hooksonce to reload.${CLAUDE_PROJECT_DIR}only resolves correctly for this project, though — if you register the hook globally instead (see Scope below), it needs a real absolute path, since a global hook fires from inside whatever project you're currently in, not from this one.Register the MCP server so Claude Code can see the
listen/speak/etc. tools — see Scope below for project-only vs. everywhere.
Scope: project-only vs. available everywhere
Three independent pieces, each defaulting to this project only, each with an "everywhere" option:
| Piece | Default (project-only) | Make it global |
|---|---|---|
| Voice settings (voice, speed, auto_speak, hands_free, ...) | Already global: ~/.claude-voice-mcp/config.json |
N/A — already global. Add a .voice-mcp.json in a specific project's directory if you want that project to override something. |
| MCP server (the tools themselves) | This repo's .mcp.json — only auto-discovered when Claude Code's cwd is this directory |
claude mcp add voice --scope user -- /absolute/path/to/uv --directory /absolute/path/to/claude-voice-mcp run server.py |
Slash commands (/talk, /voice, /talkback) |
This repo's .claude/commands/*.md — only available when working in this directory |
Copy the three .md files into ~/.claude/commands/ (create it if it doesn't exist) |
| Stop hook (auto-speak / hands-free) | This repo's .claude/settings.json |
Put the same hooks.Stop entry in ~/.claude/settings.json instead |
For everywhere-by-default, do the "global" option for all three of MCP
server, slash commands, and Stop hook. To opt in per-project instead, add
matching .mcp.json + .claude/commands/ + .claude/settings.json entries
to each project you want it in — e.g. for another project's .mcp.json:
{
"mcpServers": {
"voice": {
"type": "stdio",
"command": "uv",
"args": ["--directory", "/path/to/claude-voice-mcp", "run", "server.py"]
}
}
}
Configuration
All settings live in ~/.claude-voice-mcp/config.json and can be changed
live via the voice_config MCP tool (voice_config(action="set", key="...", value="...")) — no restart required.
| Key | Default | Meaning |
|---|---|---|
auto_speak |
true |
Speak replies automatically via the Stop hook |
auto_speak_verbosity |
"brief" |
off / brief (short summary) / full (whole reply) — see /talkback full/brief |
brief_max_chars |
320 |
Character cap for "brief" verbosity |
hands_free |
false |
Whether the Stop hook re-listens after speaking (armed by /talk) |
hands_free_idle_seconds |
90 |
How long hands-free waits for you to start talking before giving up |
vad_silence_ms |
5000 |
Trailing silence needed to end a recording once you've started talking |
tts_backend |
"kokoro" |
kokoro (local) or elevenlabs (cloud, needs ELEVENLABS_API_KEY, silently falls back to kokoro) |
voice |
"af_heart" |
Kokoro voice ID — see /voice for the full list |
elevenlabs_voice_id |
null |
Voice ID to use when tts_backend is elevenlabs |
speed |
1.0 |
Playback speed multiplier |
stt_backend |
"whisper" |
whisper (default) or voxtral (lower-latency streaming, heavier download) |
language |
"a" |
Language code (a=American English, b=British, e=Spanish, f=French, h=Hindi, i=Italian, j=Japanese, p=Brazilian Portuguese, z=Mandarin) |
stt_vocabulary |
[] |
Custom words/names Whisper is biased toward (see /vocab) — managed via vocabulary()//vocab, not usually set directly |
audio_cues |
true |
Chime when listening starts/stops |
notifications |
true |
macOS banner notifications for listening/speaking state |
Architecture
voice_mcp/
config.py # shared config, read by both the MCP server and the standalone hook
audio_io.py # mic capture + VAD, playback, cue tones, macOS notifications
sanitize.py # strip markdown/code/urls/paths before any TTS call
summarizer.py # turn a raw assistant turn into a short spoken summary
stt_guard.py # detect/trim Whisper hallucination (repeat loops) on noise/silence
vocab_correct.py # fuzzy-correct near-misses of taught vocabulary words (see /vocab)
stt/ # whisper_backend.py (default), voxtral_backend.py (opt-in)
tts/ # kokoro_backend.py (default), elevenlabs_backend.py (optional)
tools.py # tool implementations shared by server.py, the daemon, and the hook's fallback
daemon.py # background Unix-socket server (in server.py's process) keeping models warm
ipc_client.py # lightweight client the hook uses to reach the daemon, no heavy imports
server.py # FastMCP entrypoint: listen, speak, stop_speaking, list_voices, voice_config;
# also starts the daemon in a background thread
hooks/speak_on_stop.py # Stop hook: guaranteed auto-speak + hands-free, independent of the MCP server
# (talks to the daemon for speed, falls back to loading models directly)
Установка Claude Voice
У этого сервера нет опубликованного пакета — он собирается из исходников. Открой репозиторий и следуй инструкции в README.
▸ github.com/Amaldeep98/Claude-realtime-voice-mcpFAQ
Claude Voice MCP бесплатный?
Да, Claude Voice MCP бесплатный — установка в пару кликов через Unyly без оплаты.
Нужен ли API-ключ для Claude Voice?
Нет, Claude Voice работает без API-ключей и переменных окружения.
Claude Voice — hosted или self-hosted?
Self-hosted: сервер запускается локально на твоей машине командой из раздела установки.
Как установить Claude Voice в Claude Desktop, Claude Code или Cursor?
Открой Claude Voice на unyly.org, выбери вкладку своего клиента (Claude Desktop, Claude Code, Cursor) и нажми Install — конфиг сгенерируется автоматически, без правки JSON.
Похожие MCP
GitHub
PRs, issues, code search, CI status
автор: GitHubFilesystem
Secure file operations with configurable access controls.
Memory
Knowledge graph-based persistent memory system.
Template MCP Server
A CLI tool to create a new Model Context Protocol server project with TypeScript support, dual transport options, and an extensible structure
автор: mcpdotdirectCompare Claude Voice with
Не уверен что выбрать?
Найди свой стек за 60 секунд
Автор?
Embed-бейдж для README
Похожее
Все в категории development
