mcp-voice
Provides text-to-speech via the ElevenLabs TTS API (output to PC speakers, Telegram voice messages, or MP3 files) and cloud speech-to-text via ElevenLabs Scribe v2. Includes credit balance tracking, per-day character limits, and automatic deletion of TTS-generated history entries.
Sends voice messages to Telegram via sendVoice, delivers incoming Telegram messages and voice messages into a running session within a time window, and allows replying to Telegram messages with text, a voice message, or playback on the PC.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@mcp-voiceRead this aloud on my PC: The deployment is complete."
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
mcp-voice
Lokální MCP server (Python, stdio) pro hlasový výstup a přepis hlasovek v Claude Code:
rekni– text → ElevenLabs TTS → reproduktory PC (paplay), hlasovka do Telegramu (sendVoice), nebo MP3 soubor. Cílautovybere podle toho, odkud přišla poslední zpráva (terminál, nebo Remote Control) a jestli je uživatel u PC (zámek, nečinnost).prepis– hlasovka → text. Výchozí lokálně (faster-whisperlarge-v3-turbo, GPU nebo CPU), ElevenLabs Scribe v2 jen výslovně (engine="cloud").stav– co by udělalrekni(cil="auto")právě teď a proč (bez sítě).zastav– utne přehrávání na PC a vyprázdní frontu.odpovez– odpověď na zprávu z Telegramu (text, hlasovka, nebo přehrání na PC), jen v tahu, který z Telegramu přišel.Příjem z Telegramu – volitelně: zprávy a hlasovky z Telegramu se v časovém okně doručí do běžící session (sekce „Příjem z Telegramu“).
Mluvený text se v odpovědi nástroje nevrací. Všechny odpovědi jsou kompaktní JSON {"ok": true, …} nebo {"ok": false, "chyba": {"kod", "zprava"}}.
Požadavky
Linux x86_64 a Python 3.12. Lockfily jsou vygenerované pro
--python-version 3.12 --python-platform x86_64-unknown-linux-gnu. Na jiné verzi Pythonu nebo jiné architektuře (ARM) je potřeba vygenerovat vlastní lock (konec sekce Instalace); taková kombinace není ověřená. Na Debianu a Ubuntu je potřeba i balíčekpython3.12-venv, jinakpython3.12 -m venvskončí chybou o chybějícímensurepip.Claude Code: ověřeno na verzi 2.1.314. Strážce, hooky a příjem z Telegramu stojí na formátech, které Claude Code nedokumentuje (vstup hooků, transkript,
params._meta), starší verze nejsou ověřené. Okno příjmu z Telegramu server otevře jen na 2.1.314 a novějších 2.1.x, jinak jen stelegram.prijem.povolit_neoverenou_verzi: true. Jiný klient MCP vizstrazce.vyzadovat.Systémové programy:
ffmpegaffprobe(slibopusalibmp3lame),paplaynebopw-cat,gdbus,loginctl, volitelněxpropaprlimit.Keyring: služba Secret Service v přihlášené relaci (GNOME Keyring, KeePassXC s integrací Secret Service apod.), odemčená. Bez ní server hlásí
KEYRING_UNAVAILABLE. Ověření je v sekci „Tajemství“.Detekce zámku a nečinnosti: Cinnamon nebo GNOME přes DBus (jen čtení), logind
LockedHint.Lokální přepis (volitelný): NVIDIA GPU s ovladačem pro CUDA 12 (knihovny CUDA jsou pip wheely, systémová CUDA není potřeba), jinak CPU.
Příjem z Telegramu (volitelný):
systémový Python ≥ 3.12 jako
/usr/bin/python3: hook rozsahu běží mimo venv (/usr/bin/python3 -I, jen standardní knihovna). Leží-li systémový Python jinde, uprav cestu ve vzoru hooků (sekce „Hooky: vzor a test před nasazením“);bwrap(balíčekbubblewrap) a neprivilegované user namespaces. Ověření:bwrap --unshare-all --ro-bind / / true; echo $?vypíše0. Pokud distribuce user namespaces omezuje (například Ubuntu 24.04 a novější s AppArmorem, kdesysctl kernel.apparmor_restrict_unprivileged_usernsvrací1), může tento test skončit chybou oprávnění. Pak je potřeba profil AppArmoru, kterýbwrapuser namespaces povolí, podle dokumentace distribuce. Vypnutí omezení přes sysctl oslabí ochranu celého systému. Bez funkčníhobwrappříjem funguje dál, jen opatrněji: Bash z Telegramu se vždy ptá a běží bez obalu, hlasovky se dekódují bez izolace (vstup.dekodovani_izolace: auto), nebo se odmítnou (bwrap).
Related MCP server: simple-notify-mcp
Instalace
Krok 0, stažení:
git clone https://github.com/Naagloshi/mcp-voice.git
cd mcp-voiceKrok 1, venv a nástroj uv (přesná verze s ověřením hashe, stejná jako v lockfilech):
python3.12 -m venv .venv
.venv/bin/pip install --require-hashes -r requirements-uv.lockKrok 2, vyber jednu variantu.
Varianta A, základ: TTS (rekni), Telegram a cloudový přepis. Stáhne zhruba 100 MB.
.venv/bin/uv pip install --python .venv/bin/python --require-hashes -r requirements.lock
.venv/bin/uv pip install --python .venv/bin/python --offline --no-build-isolation --no-deps -e .První příkaz nainstaluje závislosti z locku a ověří hash každého balíčku. Druhý nainstaluje samotný projekt (editovatelně, ze zdrojů v repozitáři). Build backend hatchling přitom nestahuje: vezme ho z venv, kam ho první příkaz nainstaloval z locku (--no-build-isolation), a --offline zajistí, že se nic mimo lock nestáhne. Bez --no-build-isolation by si uv stáhl hatchling a jeho závislosti z PyPI bez ověření hashe.
Varianta B, navíc lokální přepis (faster-whisper): stáhne zhruba 3 GB knihoven CUDA 12 (pip wheely kvůli GPU; stáhnou se i na stroji bez NVIDIA GPU, přepis pak běží na CPU) a potom model asi 1,6 GB.
.venv/bin/uv pip install --python .venv/bin/python --require-hashes -r requirements-lokalni.lock
.venv/bin/uv pip install --python .venv/bin/python --offline --no-build-isolation --no-deps -e .Druhý příkaz je stejný jako ve variantě A (build backend z locku, nic se nestahuje).
Model pro lokální přepis se stahuje jednou, z pevné revize a s ověřením hashe každého souboru (repozitář modelu změnil vlastníka, proto pin na commit). mcp-voice-model stahni funguje jen ve variantě B, v základu skončí hláškou, že lokální přepis není nainstalovaný:
.venv/bin/mcp-voice-model stahni # do stt.model_cesta
.venv/bin/mcp-voice-model overZa běhu se model načítá jen z lokální cesty (HF_HUB_OFFLINE=1).
Lockfily obsahují hashe všech balíčků, včetně build backendu (hatchling a editables jsou v extra dev). Po změně pyproject.toml:
uv pip compile pyproject.toml --extra dev [--extra lokalni] --generate-hashes --python-version 3.12 --python-platform x86_64-unknown-linux-gnu -o <lock> a pip-audit -r <lock> --require-hashes. Když se v locku změní verze uv, zkopíruj jeho řádky i do requirements-uv.lock. Test tests/test_locky.py hlídá shodu uv, hashe u všech balíčků a to, že build backend v locku odpovídá [build-system].
Rychlý start
Minimum pro rekni na reproduktory PC. Podrobnosti jsou v odkazovaných sekcích.
Instalace, varianta A (výše).
Ověř keyring (sekce „Tajemství“): výpis
.venv/bin/python -c 'import keyring; print(keyring.get_keyring())'má začínat nakeyring.backends.SecretService(za ním bývá(priority: …)). V pořádku je ikeyring.backends.chainer.ChainerBackend(častý na KDE).V účtu ElevenLabs vypni „Data use“ (Terms and Privacy) a vytvoř API klíč s oprávněním Text to Speech. Ulož ho do keyringu (hodnotu vložíš na výzvu, do historie shellu se nedostane):
.venv/bin/keyring set mcp-voice elevenlabs:api_keyKonfigurace ze vzoru:
mkdir -p ~/.config/mcp-voice test -e ~/.config/mcp-voice/config.yaml || install -m 600 config.yaml.example ~/.config/mcp-voice/config.yamlPříkazy jsou pro bash (fungují i v
sh). Existující konfiguraci druhý příkaz nepřepíše, takže rychlý start jde zopakovat. V souboru odkomentujvychozi:podelevenlabs.hlasya zástupnou hodnotu nahraď voice_id (ID hlasu z webu ElevenLabs, sekce Voices). Zástupná hodnota validací neprojde. Dál nastavelevenlabs.data_use_vypnuto: true. Denní limit je výchozí 500 znaků (limity.znaku_za_den).Registruj server v projektu, kde ho chceš používat (příkaz spusť v adresáři toho projektu, cestu k venv uveď absolutně):
claude mcp add --scope local hlas -- /cesta/k/mcp-voice/.venv/bin/mcp-voiceHooky: z
examples/settings-hooks.jsonzkopíruj do.claude/settings.jsonprojektu položky promcp-voice-hookamcp-voice-guarda zaVENVdosaď absolutní cestu k.venv(sekce „Hooky: vzor a test před nasazením“). Bez strážce server nic neodešle (GUARD_MISSING).Spusť Claude Code v projektu znovu a požádej: „Řekni nahlas: zkouška.“ Nástroj
stav(bez sítě) ukáže, co byrekniudělal a proč.
Dál podle potřeby: Telegram („Telegram: první nastavení“), lokální přepis (varianta B), status bar („Status bar: mcp-voice-status“), příjem zpráv z Telegramu („Příjem z Telegramu“).
Konfigurace
~/.config/mcp-voice/config.yaml (jiná cesta: env MCP_VOICE_CONFIG, stejná pro server i hooky), vzor s komentáři v config.yaml.example. Chybějící nebo prázdný soubor = výchozí hodnoty. Tajemství do konfigurace nepatří (viz „Tajemství“ níže).
Důležité body:
elevenlabs.data_use_vypnuto: falseje kontrolní bod: dokud ho nenastavíš natrue(po vypnutí „Data use“ v účtu ElevenLabs, sekce Terms and Privacy), server do ElevenLabs nic nepošle. Opt-out platí jen pro data poslaná po vypnutí.stt.engineneexistuje záměrně: výchozí přepis je vždy lokální a cloud se dá jen zakázat (stt.cloud_povolen: false). Uvedenístt.engineje chyba.limity.znaku_za_den– denní rozpočet znaků TTS (počítá se před odesláním),volani_za_minutuaduplicita_sbrzdí smyčky.elevenlabs.kredity(výchozítrue) – po každém úspěšnémreknijeden dotaz na zůstatek kreditů do<cesty.stav>/status.json(viz „Status bar“).false= žádný dotaz navíc.elevenlabs.mazat_historii(výchozítrue) – po každém TTS smaže položku, kterou TTS vytvořil v historii účtu ElevenLabs (viz „Historie ElevenLabs“).false= položky v historii zůstanou.strazce.prepis_cloudastrazce.citlive_kontextynečte server, ale hookmcp-voice-guard, a to při každém volání (viz „Strážcemcp-voice-guarda citlivé kontexty“).
Kdo konfiguraci čte
Komponenta | Kdy | Co používá | Při neplatné konfiguraci |
server | jen při startu (změna = restart serveru) | vše | nenastartuje (stderr |
| při každém volání nástroje hlasu | validuje celý soubor, používá hlavně |
|
| při každém promptu |
| nic nezapíše, na stderr jen typ chyby ( |
| – | konfiguraci nečte: politiku bere ze snímku, který zapíše server, adresář stavu z | – (neexistující adresář stavu jen ohlásí varováním na stderr) |
| při spuštění |
| kód 2 |
| při spuštění |
| prázdný výstup, kód 0 |
| při spuštění |
| stderr |
Hook čte konfiguraci hned, server až po restartu. Po změně proto server restartuj, jinak se rozejdou (třeba změněný telegram.prijem.povel: hook žádost převezme, server ji odmítne jako povel_nesouhlasi).
Validace
YAML: bezpečný loader (
SafeLoader). Duplicitní klíč a merge klíč<<jsou chyba s číslem řádku: obojí by tiše vzalo poslední hodnotu. Kotvy a aliasy (&,*) projdou. Kořen musí být mapa.Neznámý klíč je chyba (zpráva ho jmenuje) v každé mapě: v kořeni, v sekci, v každé podsekci (
telegram.prijem.okno,rezimy.plny.bash_domov…), v citlivém kontextu, vsignaly, v položcevstupyibash_koreny. Překlep by jinak tiše vrátil výchozí hodnotu, ustrazce.citlive_kontextyby to znamenalo žádnou detekci.Typy: přepínače jen
true/false(ne"ano"ani1), čísla nesmí býttrue/falsea musí být v rozsahu (u celočíselných klíčů se desetinná část usekne), výčtové hodnoty přesně podle tabulek. Seznam musí být seznam, jen seznamy v citlivém kontextu (signály,pole,hodnoty,predpony) smí být i jeden text. Text smí být i číslo (chat_id: 123…). Pokud tabulka neříká jinak, má text nejvýš 500 znaků.Rozsah chyby: chyba kdekoli v
telegram.prijem(i v jeho podsekcích) nastaví celý příjem na výchozí hodnoty spovoleno: false. Důvod ukáže nástrojstav(chyba_konfigurace) a odpověď na povel okna, ostatní funkce běží dál. Chyba jinde, včetnětelegram.odpovedi,telegram.retenceastrazce.citlive_kontexty, zneplatní celou konfiguraci (tabulka výše).Co validace nehlídá: že alias
elevenlabs.hlasje velevenlabs.hlasy(jinakreknivrátíVOICE_UNKNOWN), že adresáře a model existují, a prázdný text u klíčů s předepsaným tvarem (registrace,povel,hlas,jazyk), který projde, ale nedává smysl. Cesty vcestyasttpiš absolutně nebo~/…: relativní cesta projde a vztáhne se k pracovnímu adresáři procesu (server a hooky ho mají každý jiný),nullprojde a selže až za běhu (ucesty.stavhned při startu serveru). Relativní položkustt.povolene_adresareserver tiše vynechá.
Přehled klíčů
Rozsah „a–b“ je včetně krajů. Pro sekci telegram.prijem je podrobný význam v sekci „Příjem z Telegramu“, pro strazce v sekci „Strážce mcp-voice-guard a citlivé kontexty“.
elevenlabs
Klíč | Výchozí | Hodnoty | Význam |
|
|
| model TTS, když ho |
|
| seznam jmen modelů | parametr |
|
| seznam jmen modelů | modelům v seznamu se posílá |
|
| alias | výchozí hlas, klíč z |
|
| mapa alias → voice_id ( | hlasy pro |
|
|
|
|
|
| 2–3 malá písmena |
|
|
| 1–300 | timeout požadavku TTS (s) |
|
| mapa jméno → číslo | posílá se do API beze změny ( |
|
|
| kontrolní bod (výše); |
|
|
| dotaz na zůstatek kreditů po |
|
|
| mazání položky historie po TTS („Historie ElevenLabs“) |
telegram
Klíč | Výchozí | Hodnoty | Význam |
| prázdné | celé číslo (i záporné), nejvýš 20 číslic | soukromý chat s botem; prázdné = Telegram vypnutý ( |
|
| 1–120 | timeout volání Telegram API (s) |
|
|
| zprávy a hlasovky od bota nejde přeposlat ani uložit |
|
| 1–5 | text |
|
| 100–4096 | znaků na zprávu (4096 = limit Telegramu) |
|
| 0–47 | mazání zpráv v chatu po tolika hodinách, |
| – | mapa | příjem z Telegramu, tabulka níže |
telegram.prijem (cesty klíčů relativně k telegram.prijem)
Klíč | Výchozí | Hodnoty | Význam |
|
|
| hlavní vypínač; |
|
| 0–5 celých čísel > 0 | kdo smí psát; po každé změně (i |
|
|
| jméno registrace serveru v Claude Code ( |
|
|
| slash příkaz okna |
|
| 10–50 | long poll |
|
| 5–240, nejvýš | délka okna, když povel neuvede čas |
|
| 5–240 | strop délky okna v povelu. Pevný strop v kódu je 240 min, konfigurace ho jen snižuje; při snížení pod 60 sniž i |
|
| 5–240, nejvýš | zavřít okno po tolika minutách bez zprávy z Telegramu |
|
|
| režim, když ho povel neuvede |
|
| nejvýš 100 přesných jmen ( | nástroje navíc, které se berou jako čtení: bez dotazu ve všech režimech |
|
| nejvýš 100 cest | omezí |
|
|
| dialog oprávnění v tahu z Telegramu ve všech režimech: |
|
| nejvýš 100 map | adresáře navíc k pracovnímu adresáři viditelné v obalu |
|
|
| obal Bash z Telegramu v |
|
|
| Bash v obalu se sítí ( |
|
|
| Bash v obalu v |
| výchozí seznam |
| složky nástrojů z domova, v obalu jen pro čtení ( |
|
| nejvýš 100 absolutních cest, ne | další místa skrytá v obalu ( |
|
| jako u | totéž pro režim |
| výchozí seznam |
| čtení i zápis zamítnut ve všech režimech; výchozí |
| výchozí seznam | jako | jen zápis; výchozí |
|
| nejvýš 100 jmen nebo vzorů jmen bez | další jména konfigurace agenta (tvrdý zákaz zápisu, např. |
|
| nejvýš 100 přesných jmen nástrojů | tvrdé zákazy navíc k pevným |
|
|
| tokeny zprávy nahrazené značkou |
|
|
|
|
|
|
| pojistka Bash v každém tahu (konec sekce „Režimy a politika nástrojů“); |
|
|
| okno se otevře i na verzi Claude Code, na které formát socketu a transkriptu nebyl ověřen (háček 6) |
|
| 1–4096 | delší zpráva z Telegramu se odmítne |
|
| 1–30 | limit zpráv; druhé překročení okno zavře |
|
| 1–1000 | limit zpráv za okno |
|
| 1 až menší z | delší hlasovka se odmítne |
|
| 1–20 | větší hlasovka se odmítne |
|
|
| dekódování hlasovky v |
|
| 1–50 | zprávy čekající na volnou session; další se odmítne |
|
| 10–3600 | po tolika sekundách čekání oznámí server frontu do Telegramu |
|
| 15–600 | session bez změny transkriptu tak dlouho je volná pro další zprávu |
|
| 1–120 | pak se zpráva doručí i do zaneprázdněné session |
|
| 2–60 | doručení, které se do této doby neobjeví v transkriptu, je „nepotvrzeno“ |
| viz | 0–50 frází na seznam, 2–40 znaků; po normalizaci (malá písmena, bez diakritiky) se nesmí opakovat napříč seznamy | fráze ve zprávě, které určí formu odpovědi |
|
|
| šablona „odpověď jen v terminálu“ uvede do Telegramu název citlivého kontextu (nejvýš 40 znaků) |
|
|
| jazyk šablon do Telegramu a textů hooku |
Kořen bash_koreny (cesta absolutní nebo ~/…, bez ..) nesmí být /, domov ani jeho předek, chráněná cesta ani její předek (včetně cesty.stav), konfigurace agenta, /dev, /proc, /sys, /run, /var/run, /boot, nic pod nimi ani jejich předek (/var), ani samotné /tmp (podsložka smí). Kontroluje se i cíl symlinku. Kořen, který tato pravidla poruší až za běhu, server do politiky nezařadí. Podrobnosti obalu jsou v „Režimy a politika nástrojů“.
cil (pravidla výběru v „Výběr cíle“)
Klíč | Výchozí | Hodnoty | Význam |
|
| 5–86400 | Remote Control a neznámý zdroj: nečinnost od tohoto prahu = uživatel není u PC |
|
| 5–86400 | zpráva z terminálu: odemčené PC, ale nečinnost od tohoto prahu = Telegram |
|
| 0–120 | snímek stavu PC z hooku |
|
| seznam | aktivní okno klienta při promptu z Remote Control = uživatel je u PC |
|
|
| výslovné |
|
|
| zdroj zámku a nečinnosti |
| žádné |
|
|
prehravani, limity, cesty, kořen
Klíč | Výchozí | Hodnoty | Význam |
|
|
| přehrávač na PC |
|
| 1–5000 | nejdelší text jednoho |
|
| 0–10 000 000 | denní rozpočet znaků TTS, |
|
| 1–600 | víc volání za minutu = |
|
| 0–86400 | stejný text znovu do tolika sekund = |
|
| absolutní nebo | stavový adresář (razítka, souhlasy, okno příjmu, |
|
| absolutní nebo | kam |
| prázdné | text do 2000 znaků | připojí se k instrukcím serveru pro model |
stt
Klíč | Výchozí | Hodnoty | Význam |
|
|
|
|
|
| cesta | adresář modelu ( |
|
|
|
|
|
|
| přesnost výpočtu modelu |
|
| 2–3 malá písmena, | jazyk přepisu hlasovek z Telegramu; nástroj |
| prázdné | do 300 znaků | slova, která má přepis upřednostnit (vlastní jména) |
| prázdné | do 500 znaků | úvodní kontext pro přepis |
|
| seznam cest (absolutní nebo | jen odsud smí |
|
| 0.01–200 | větší soubor = |
|
| 1–7200 | delší audio = |
|
| 0–86400 | uvolnit model z paměti po tolika sekundách bez přepisu, |
|
| 5–3600 | timeout jednoho přepisu, lokálního i cloudového (s) |
strazce
Klíč | Výchozí | Hodnoty | Význam |
|
|
|
|
|
| 5–3600 | nejvyšší stáří razítka (s); starší se uklízí |
|
|
| cloudový přepis: dotaz při každém volání, nebo posoudit jako |
|
| nejvýš 20 kontextů | citlivé kontexty (čte hook), klíče kontextu níže |
Klíč kontextu | Výchozí | Hodnoty |
| povinný | 1–40 znaků |
| povinný | jednořádkový text do 300 znaků, ukáže se v dialogu |
|
|
|
|
|
|
|
|
|
|
|
|
| povinný | mapa s aspoň jedním neprázdným signálem; každý signál je seznam (nebo jeden text) nejvýš 100 položek bez řídicích znaků |
| – | glob |
| – | absolutní nebo |
| – | 3–200 znaků |
| – | položky |
Pevné limity v kódu
Konfigurace tyto meze nepřekročí, nanejvýš je zúží:
Okno příjmu 5 až 240 minut (
okno.max_minstrop jen snižuje). Povel pod 5 minut nebo nad strop se odmítne.Telegram:
long_poll_snejvýš 50, zpráva nejvýš 4096 znaků (vstup.max_znaku,odpovedi.max_znaku_casti), odpověď nejvýš 5 zpráv, retence nejvýš 47 h, nejvýš 5 povolených uživatelů.Hlasovka z Telegramu nejvýš 3600 s a
stt.max_s, nejvýš 20 MB.Seznamy v
telegram.prijemnejvýš 100 položek (forma50 frází na seznam), nejvýš 20 citlivých kontextů po 100 položkách na signál.Délky textů:
instrukce_navic2000,initial_prompt500,hotwords300,upozorneni300,nazevkontextu 40 znaků.Nejde odebrat ani vypnout: tvrdé zákazy nástrojů v tahu z Telegramu (
ZAKAZANE_V_TAHUvpolitika.py,nastroje_navicje nesmí obsahovat), zákaz zápisu do konfigurace agenta (.claude/,CLAUDE.md,CLAUDE.local.md,.mcp.json), chráněný zápis do balíčku, venvu a repozitářemcp-voice, ochrana konfiguračního souboru zMCP_VOICE_CONFIG(je-li nastavená), skrytý domov a místa připojení v obalu (/home,/mnt,/media,/srv,/net,/nfs,/afs), systémové složky jako kořen Bash, cloud jako výchozí engine přepisu.Limity skenu stromu a obalu (500 000 položek, hloubka 32, 3 s, 100 000 znaků, 2 000 připojení) jsou v „Režimy a politika nástrojů“, hlídací časovač strážce (8 s) v „Strážce
mcp-voice-guarda citlivé kontexty“.
Tajemství (keyring, služba mcp-voice)
Klíč | Obsah |
| API klíč ElevenLabs (doporučeno omezit scope na Text to Speech + Speech to Text a nastavit kvótu klíče). Pro zůstatek kreditů navíc oprávnění „User: Access“ (jen čtení účtu a předplatného), pro mazání historie „History: Write“ (smí číst i mazat celou historii účtu). |
| token bota pro všechno, co jde přes Telegram: |
.venv/bin/keyring set mcp-voice elevenlabs:api_key.venv/bin/keyring set mcp-voice telegram:bot_tokenkeyring set se na hodnotu zeptá a nevypisuje ji, do historie shellu se tak nedostane.
Ověření backendu. Server čte keyring v procesu, který spustí Claude Code, takže potřebuje Secret Service v téže relaci (proměnná DBUS_SESSION_BUS_ADDRESS). Kontrola ve stejném prostředí, odkud spouštíš Claude Code:
.venv/bin/python -c 'import keyring; print(keyring.get_keyring())'začíná na
keyring.backends.SecretService(napříkladkeyring.backends.SecretService.Keyring (priority: 5)) = v pořádku;keyring.backends.chainer.ChainerBackend= v pořádku, keyring zkouší víc backendů za sebou (častý na KDE, kde vedle Secret Service běží KWallet);keyring.backends.fail.Keyring= backend chybí (Secret Service neběží, relace bez DBus, třeba přes SSH). Server pak vracíKEYRING_UNAVAILABLE, neSECRET_MISSING.Přehled všech dostupných backendů:
.venv/bin/keyring --list-backends.
SECRET_MISSING znamená, že backend funguje, ale položka ve službě mcp-voice chybí (překlep v klíči, jiný uživatel).
Keyring chrání před únikem přes proměnné prostředí a soubory, není to hranice: čte ho každý proces uživatele.
Rotace tokenu bota: /revoke v BotFatheru → nový keyring set. Bot nikdy nic nežádá ani se neptá; zpráva od něj s výzvou je podvrh.
Napojení na Claude Code
Registrace serveru (příklad, ideálně jen v projektu, kde se má používat; spusť v adresáři projektu):
claude mcp add --scope local hlas -- /cesta/k/mcp-voice/.venv/bin/mcp-voiceJméno registrace. Jméno hlas určuje jména nástrojů v Claude Code (mcp__hlas__rekni …). Vzor hooků i výchozí telegram.prijem.registrace s ním počítají. Při jiném jménu (třeba voice) přepiš ve vzoru hooků každé mcp__hlas__ na mcp__voice__ a nastav telegram.prijem.registrace: voice.
Hooky: vzor a test před nasazením
Jediný vzor je examples/settings-hooks.json. Je to platný celek pro plnou instalaci včetně příjmu z Telegramu. Obsah klíče hooks vlož do .claude/settings.json projektu (nebo do .claude/settings.local.json, který se necommituje) a zástupné hodnoty nahraď absolutními cestami:
VENV= adresář.venvmcp-voice;PKG= adresář balíčku, zjistíš ho příkazem/cesta/k/mcp-voice/.venv/bin/python -c "import mcp_voice, os; print(os.path.dirname(mcp_voice.__file__))"(při instalaci podle README je tosrc/mcp_voicev repozitáři);STAV= stejná cesta jakocesty.stavv konfiguraci (výchozí~/.local/state/mcp-voice). Hook rozsahu konfiguraci nečte. Jiná cesta projde tiše a okno příjmu se pak nikdy neotevře. Neexistující adresář hook ohlásí varováním na stderr.
Kolik toho registrovat:
Použití | Položky ze vzoru |
TTS a přepis, bez příjmu z Telegramu |
|
navíc příjem z Telegramu | celý vzor, tedy navíc všechny položky |
Položky jen vyber, jinak je neupravuj (kromě cest a jména registrace). Hook rozsahu se spouští mimo venv systémovým Pythonem s absolutní cestou, /usr/bin/python3 -I. Absolutní cesta znamená, že se interpret nehledá v PATH session (podstrčený python3 v adresáři na začátku PATH se nespustí). Izolovaný režim -I ignoruje proměnné PYTHON* a uživatelské site-packages a nepřidá do sys.path adresář skriptu ani pracovní adresář. Interpret a moduly hooku s onFailure: block tak nezmění PATH, proměnné PYTHON* ani soubor v projektu (třeba json.py). Proměnné dynamického linkeru (LD_PRELOAD, LD_LIBRARY_PATH) tím ošetřené nejsou; kdo je umí nastavit procesu Claude Code, ovládá i Claude Code samotný. Pokud systémový Python ≥ 3.12 neleží v /usr/bin/python3, uprav cestu ve všech položkách hooku rozsahu. Nedosazuj Python z venv ani shim (pyenv, asdf), který vybírá interpret podle prostředí. Hook potřebuje jen standardní knihovnu a sourozenecké moduly načítá podle vlastní cesty. Vstupní bod .venv/bin/mcp-voice-rozsah dělá totéž, ale běží z venv.
Test před vložením. Hook s "onFailure": "block" při chybě (neexistující cesta, kód 2, timeout) zablokuje volání. U hooku rozsahu s matcherem .* to znamená všechny nástroje v projektu. Každý příkaz proto nejdřív spusť ručně se stejnými cestami, jaké vložíš do settings.json:
printf '{}' | /usr/bin/python3 -I PKG/rozsah_hook.py --udalost pre --stav STAV; echo "kód $?"Očekávání: jediný řádek kód 0. Hláška can't open file a kód 2 = špatné PKG, takový hook nevkládej. Hláška o neexistujícím souboru a kód 127 = Python neleží v /usr/bin/python3, oprav cestu (v testu i ve všech položkách hooku rozsahu). Varování adresář stavu … neexistuje = STAV neodpovídá cesty.stav, nebo server ještě neběžel (po prvním startu serveru test zopakuj).
printf '{}' | VENV/bin/mcp-voice-guard; echo "kód $?"Očekávání: jediný řádek kód 0. Hláška shellu o neexistujícím souboru (No such file or directory, podle jazyka systému) a kód 127 = špatné VENV.
Obnova, když hook zablokuje všechny nástroje. Uvnitř Claude Code to opravit nejde, blokované jsou i nástroje na úpravu souborů.
Ukonči Claude Code.
V textovém editoru mimo Claude Code oprav cesty, nebo dočasně odstraň položky s
"onFailure": "block"v.claude/settings.json(případně.claude/settings.local.json) projektu.Zopakuj test výše a Claude Code spusť znovu. Změnu souboru za běhu nemusí Claude Code převzít hned, proto restart.
Hook UserPromptSubmit: odkud přišla zpráva a stav PC
mcp-voice-hook při každém promptu zapíše do <stav>/session-<pid claude>.json vazbu na aktuální session a transkript a snímek stavu PC (zámek, nečinnost, třída aktivního okna WM_CLASS, nikdy titulek). Text promptu neukládá. Vždy skončí s kódem 0; výstup má jen u povelu /telegram a u zprávy z Telegramu (sekce „Příjem z Telegramu“). Bez hooku server čte session z prostředí (po /clear zastará) a zdroj je častěji neznamy. Registrace: položka UserPromptSubmit ve vzoru hooků.
Zdroj zprávy se čte z transkriptu (origin.kind == "human", promptSource, queueSkipSlashCommands). Pole jsou nedokumentovaná; když chybí nebo mají neznámou hodnotu, zdroj je neznamy a auto režim pošle do Telegramu jen tehdy, když stav PC říká, že u něj uživatel není.
Externí strážce (PreToolUse) a razítko
S strazce.vyzadovat: true (výchozí) server u rekni a prepis nic neodešle, dokud volání neprověří hook PreToolUse. Server tak zůstane zavřený i tam, kde hook neběží (jiný klient, jiný projekt, pád hooku). Protokol:
Claude Code posílá v
params._metapole"claudecode/toolUseId", hook dostane stejnou hodnotu jakotool_use_id.Hook po rozhodnutí „pustit“ nebo „zeptat se“ zapíše
<stav>/razitka/<tool_use_id>.json(adresář0700, soubor0600,O_CREAT|O_EXCL):{"v": 1, "nastroj": "rekni", "sha256": "<sha256 kanonického JSON tool_input>", "cas": <epoch s>}, kde kanonický JSON =json.dumps(tool_input, sort_keys=True, separators=(",", ":"), ensure_ascii=False)v UTF-8.Při zamítnutí razítko nevzniká. Server razítko najde, hned smaže, ověří nástroj, hash a stáří (
strazce.max_stari_s). Jinak vrátíGUARD_MISSINGneboGUARD_MISMATCH.
Testovací vektor pro vlastní strážce: {"text": "Dobrý den, čtu zprávu.", "cil": "auto", "ulozit": false, "hlas": null} → 99a52086c7831e14a7693de02946c837369f17f72362e3ee1edc0f3652609d16.
Razítko umí zapsat i vlastní hook. Hotový strážce je mcp-voice-guard (další sekce). Hook vždy registruj s "onFailure": "block" a explicitním timeout, aby pád nebo timeout hooku volání zablokoval.
Strážce mcp-voice-guard a citlivé kontexty
mcp-voice-guard je hook pro PreToolUse i PostToolUse. Rozhoduje takto:
Volání | Rozhodnutí |
ze subagenta ( |
|
| bez zásahu (nic neodchází) |
| razítko, bez dotazu |
| dotaz při každém volání ( |
| dotaz ( |
ostatní | razítko, bez dotazu |
K čemu citlivé kontexty jsou. Uživatel pracuje v Claude Code i s daty, která nemají opustit firmu (pracovní repozitář, firemní databáze, pošta). Text pro TTS odchází do ElevenLabs. Citlivý kontext je upozornění: jakmile session sáhla na něco citlivého, hlas se před odesláním zeptá. Není to DLP filtr. Text pro TTS se neprohledává, posuzuje se celá session. Model, který text sestavuje, může citlivý obsah převzít i ze zdroje, který žádný signál nemá.
Kontexty se definují v config.yaml (vzor v config.yaml.example). Hook čte konfiguraci při každém volání, změna platí hned. Neplatná konfigurace znamená deny pro rekni a prepis. Neznámý klíč (v kořeni i v každé sekci), duplicitní klíč a merge klíč YAML << jsou taky chyba: překlep citlive_kontexy, druhý řádek citlive_kontexty: [] nebo druhý << by detekci tiše vypnul. Totéž platí pro server, který s neplatnou konfigurací nenastartuje.
strazce:
prepis_cloud: dotaz
citlive_kontexty:
- nazev: ACME
upozorneni: "Session pracuje s daty ACME. Poslat text do ElevenLabs?"
platnost: den
signaly:
mcp: ["acme-db", "acme-*"]
agenti: ["mail-assistant"]
skilly: ["acme"]
cesty: ["~/work/acme"]
texty: [".agents/skills/acme/"]
vstupy:
- {nastroj: "mcp__notes__*", pole: area, hodnoty: [acme]}
- {nastroj: "mcp__notes__*", pole: [path, rel_path], predpony: ["acme/"]}Signály se hledají v transkriptu session (hlavním i u všech subagentů, <session>/subagents/*.jsonl). Stačí jeden, aby se kontext pro session označil:
Signál | Co se porovnává | Příklad |
| jméno MCP serveru u volání |
|
|
|
|
| skill spuštěný nástrojem |
|
| pracovní adresář (hook i řádky transkriptu), pole |
|
| podřetězec v příkazu |
|
| pole vstupu nástroje, jehož jméno odpovídá globu |
|
Platnost souhlasu (platnost): den = do konce dne pro danou session (výchozí), session = do konce session, volani = ptá se při každém volání. Souhlas platí pro kontext, ne pro celou session. Když se v téže session později objeví další kontext, hook se zeptá znovu, ale jen na ten nový. Když se trefí víc kontextů najednou, dotaz jmenuje všechny i s jejich upozorněním a souhlas platí pro každý podle jeho platnosti. Změna platnosti v konfiguraci dřívější souhlas zruší.
Jak vzniká souhlas. PreToolUse vydá ask a uloží čekající záznam pro tool_use_id. Když nástroj opravdu proběhne, PostToolUse se stejným tool_use_id a session zapíše souhlas. Model souhlas nepodvrhne parametrem nástroje, protože tool_use_id přiděluje API. Odmítnutý dotaz souhlas nezapíše. PostToolUseFailure neregistruj, mohl by přijít i po zamítnutí. Souhlas s cloudovým přepisem v session bez kontextu žádný souhlas nezakládá.
Režimy oprávnění. Dotaz (ask) hook vydá jen v režimech default, acceptEdits, plan a auto, kde ho klient ukáže uživateli. V bypassPermissions, dontAsk a v neznámém nebo chybějícím režimu se nejde zeptat, takže každé volání, které by vedlo na dotaz, hook zamítne (deny s důvodem) a razítko nevznikne. Volání bez dotazu projdou normálně.
Subagenti. Hlas smí používat jen hlavní session. Subagent dostane deny a razítko nevznikne. Signály ze subagentů se ale počítají do kontextu session: když subagent sáhne na acme-db, hlavní session se před dalším rekni zeptá.
Selhání. Vlastní chyba hooku (třeba nečitelný transkript nebo hlídací časovač 8 s) vede na dotaz s důvodem (fail-to-ask), v režimu bez dotazu na deny. Nečitelný vstup hooku skončí kódem 2 (blokace). Pád nebo timeout procesu pokryje onFailure: block a razítko.
Stav je v <cesty.stav>/strazce/ (adresář 0700, soubory 0600): sken-<session>.json (nalezené kontexty a offsety čtení transkriptu; čte se jen přírůstek a nález je pro session trvalý; změna signálů v konfiguraci vynutí nový průchod), souhlasy-v2.json, cekajici-<tool_use_id>.json a statistika.json (jen denní počty dotazů a souhlasů).
Registrace: položky mcp-voice-guard v PreToolUse (s onFailure: block) a PostToolUse ze vzoru hooků (sekce „Hooky: vzor a test před nasazením“).
Omezení.
Je to pojistka proti omylu, ne bezpečnostní hranice. Každý proces pod stejným uživatelem (i příkaz
Bash, který spustí model) může stavové soubory přepsat a souhlas nebo „čistý“ sken podvrhnout, stejně jako konfiguraci. Pomůže pravidlodenypro editační nástroje nad~/.config/mcp-voice/**a<cesty.stav>/**, Bash tím ale pokrytý není.Detekce vidí jen to, co je v transkriptu: jména nástrojů, cesty, příkazy a zadání. Nevidí obsah výsledků nástrojů, relativní cesty v příkazech (
cd ../acme),Glob/Grepbez cesty ani vyhledávání bez filtru.Klienti jiní než Claude Code hook nespustí. Server pak bez razítka nic neodešle (
GUARD_MISSING), pokudstrazce.vyzadovatnevypneš.Hook a server musí číst stejnou konfiguraci, aby razítka ležela ve stejném
cesty.stav. Proto pro oba nastav stejnéMCP_VOICE_CONFIG, nebo ho nenastavuj ani jednomu.
Status bar: mcp-voice-status
Server po každém úspěšném rekni zapíše do <cesty.stav>/status.json (0600, atomicky) denní počet znaků a limit a na pozadí, aby odpověď ani přehrání nečekaly, jednou zavolá GET /v1/user/subscription (timeout 5 s, bez opakování). Z odpovědi uloží jen zůstatek kreditů, limit a čas obnovy, nikdy ID účtu, e-mail ani část klíče. Klíč k tomu potřebuje oprávnění „User: Access“ (jen čtení účtu a předplatného). Bez něj (401/403) jsou kredity null a kredity_chyba: "opravneni", rekni tím nikdy neselže. Při výpadku sítě zůstane poslední známá hodnota. Zápis čeká i na mazání historie (viz níže), takže výsledek staršího dotazu se může zapsat až po novějším. Aby novější zůstatek nepřepsal, rozhoduje čas odpovědi (kredity_cas). Nástroj stav vrací poslední známé kredity (kredity) bez sítě a obnoví denní část souboru. Zápis čeká na zámek souboru nejvýš asi 50 ms, pak se vynechá. Při čtení se berou jen známá pole: čísla celá od 0 do 10¹², časy a den jen jako ASCII ([0-9A-Za-z:+.-], nejvýš 40 znaků), takže podvržený soubor nedostane do výstupu řídicí ani bidi znaky.
mcp-voice-status soubor jen čte (nikdy síť) a vypíše jeden segment, třeba 🔊 193/500 · 24,6k kr (znaky dnes / denní limit · zbývající kredity). Nový den začíná od nuly. Bez souboru nebo při chybě vypíše prázdný řádek a skončí s kódem 0. --json vypíše celý stav, --stav DIR přeskočí čtení konfigurace.
Napojení do statusline Claude Code (~/.claude/settings.json):
{"statusLine": {"type": "command", "command": "/cesta/k/statusline.sh"}}#!/bin/sh
# statusline.sh: vlastní řádek, za něj segment hlasu (prázdný, když není co ukázat)
cat > /dev/null # JSON od Claude Code na stdin tu není potřeba
printf '%s\n' "$(/cesta/k/mcp-voice/.venv/bin/mcp-voice-status)"Kdo má statusline ve vlastním skriptu a nechce spouštět další proces, může status.json číst přímo: pole den, znaku_dnes, limit_den, kredity_zbyva (popis formátu je v src/mcp_voice/status.py).
Historie ElevenLabs
ElevenLabs ukládá text i audio každého TTS přes API do historie účtu (History v UI); vypnout to jde jen v režimu Zero Retention, který je pro tarif Enterprise. Server proto po každém TTS vezme ID položky z hlavičky odpovědi history-item-id a zavolá DELETE /v1/history/{id} (timeout 5 s, bez přesměrování, jen host api.elevenlabs.io). Položka nemusí být hned po konci streamu uložená, proto se DELETE při 404 nebo 422 jednou zopakuje po 3 s; jiné kódy se neopakují. Maže se i tehdy, když po hlavičce selže zbytek: čtení zvuku (síť, timeout, příliš dlouhá odpověď, prázdný zvuk, zrušení volání) nebo doručení (Telegram, zápis souboru), protože položka už mohla vzniknout. Na jiné položky historie server nesahá a historii nevypisuje. ID se před vložením do URL ověří ([A-Za-z0-9_-], nejvýš 64 znaků), jinak se nemaže nic.
smazano znamená smazáno z History účtu. Co si ElevenLabs drží v interních logech a podle své retence, tím smazat nejde.
Klíč k tomu potřebuje oprávnění „History: Write“. Tím ale smí celou historii účtu číst i mazat, ne jen položky serveru: uniklý klíč zpřístupní všechny neodmazané položky (starší TTS i ty, které skončily nesmazano:*). Kdo to nechce, nechá oprávnění vypnuté a nastaví elevenlabs.mazat_historii: false (položky pak zůstanou a smaže je ručně v UI). Kdo oprávnění zapne, má starší položky smazat ručně, server se k nim nevrací.
Mazání běží na pozadí spolu s dotazem na kredity, odpověď rekni ani přehrání na něj nečekají a jeho selhání rekni nikdy neshodí. Odpověď rekni proto nese stav v okamžiku odpovědi, konečný výsledek je v status.json (historie, historie_cas) a v nástroji stav (historie: {stav, cas}, mazat_historii):
Hodnota | Význam |
| v odpovědi |
| položka smazána z History účtu ( |
| klíč nemá „History: Write“ ( |
| jiný HTTP kód, u |
| výpadek sítě nebo timeout |
| hlavička |
| neočekávaný host odpovědi nebo neznámá chyba |
|
|
| odpověď TTS ID položky nenesla, není co mazat |
Podrobnosti jdou na stderr jako WARNING mcp_voice.historie: … (HTTP kód, typ síťové chyby, u neplatného ID jen délka a třídy znaků); ID položky ani klíč nikdy. Claude Code ale stderr serveru po navázání spojení do mcp-logs-<server>/ nemusí ukládat, spolehlivý zdroj je stav a status.json.
status.json drží výsledek posledního TTS, který doběhl, a to až do dalšího rekni. Při dvou rychle po sobě jdoucích voláních zůstane výsledek toho, jehož mazání skončilo později; smazano tak může zakrýt neúspěch druhého volání. Neodmazané položky (nesmazano:*) zůstanou v historii účtu a server se k nim nevrací.
Telegram: první nastavení
Telegram potřebuje rekni s cílem Telegram (i cil="auto", když nejsi u PC), odpovez a příjem z Telegramu.
V Telegramu otevři
@BotFather, pošli/newbota zadej jméno a uživatelské jméno bota (musí končit nabot). BotFather odpoví tokenem.Token ulož do keyringu (vložíš ho na výzvu):
.venv/bin/keyring set mcp-voice telegram:bot_tokenDoporučeno: v BotFatheru
/setjoingroups→ Disable, aby bot nešel přidat do skupin.Otevři chat se svým botem a pošli mu libovolnou zprávu (třeba
/start). Bot nemůže psát uživateli, který s ním chat nezačal.Zjisti
chat_ida svéuser_id. Příkaz vezme token z keyringu a jednou zavolágetUpdates. Token nevypíše ani při chybě: když v keyringu chybí nebo nemá tvarčíslo:znaky, skončí hláškou bez volání Telegramu, a u jakékoli jiné chyby vypíše jen její typ:.venv/bin/python -I -c 'import sys; sys.excepthook = lambda typ, e, tb: print("Chyba:", typ.__name__, "(text chyby se nevypisuje, mohl by obsahovat token)", file=sys.stderr); import json, keyring, re, urllib.request; t = (keyring.get_password("mcp-voice", "telegram:bot_token") or "").strip(); t or sys.exit("Token v keyringu chybí (služba mcp-voice, klíč telegram:bot_token), ulož ho podle kroku 2."); re.fullmatch(r"[0-9]+:[A-Za-z0-9_-]+", t) or sys.exit("Token v keyringu nemá tvar číslo:znaky (jen A-Z, a-z, 0-9, _ a -, bez mezer), ulož ho znovu podle kroku 2."); r = json.load(urllib.request.urlopen("https://api.telegram.org/bot%s/getUpdates" % t, timeout=20)); [print("chat_id:", u["message"]["chat"]["id"], "user_id:", u["message"]["from"]["id"], "typ:", u["message"]["chat"]["type"]) for u in r["result"] if "message" in u]'Chyba
HTTPErrorznamená, že Telegram volání odmítl: neplatný token, nebo právě běží okno příjmu z Telegramu (viz níž).URLErrorneboTimeoutErrorje problém se sítí. U soukromého chatu (typ: private) jsouchat_idauser_idstejné. Prázdný výstup: pošli botovi zprávu znovu a příkaz zopakuj (Telegram drží nevyzvednuté zprávy nejvýš 24 h). Nespouštěj ho, když je otevřené okno příjmu z Telegramu, jinak by si zprávy brali navzájem.Do konfigurace zapiš
telegram.chat_id: "<chat_id>", pro příjem z Telegramu itelegram.prijem.povolene_user_id: [<user_id>].Restartuj server (nová session Claude Code) a vyzkoušej: „Pošli mi do Telegramu hlasovku: zkouška.“
Příjem z Telegramu
Volitelná funkce (výchozí vypnutá): po povelu v terminálu nebo v Remote Control server na omezenou dobu přijímá zprávy a hlasovky od jednoho uživatele z jeho soukromého chatu s botem a vkládá je do běžící session přes inbox socket Claude Code. Odpověď jde zpět do Telegramu nástrojem odpovez.
Háčky napřed:
Bezpečnost stojí na hooku
PreToolUse(rozsah_hook.py). Auto režim Claude Code tahy ze socketu sám nezastaví, klasifikátor taky ne. Hook proto musí být registrovaný s"onFailure": "block"a server okno neotevře, dokud nemá důkaz, že hook v session běží.Inbox socket není bezpečnostní hranice: píše do něj každý proces uživatele. Zprávu z Telegramu hook pozná podle
origin.verifiedPeerPidaverifiedPeerProcStart, které Claude Code doplní podle procesu odesílatele (server), ne podle textu.Model v tahu ze socketu umí sám psát do jiných session (
SendMessage). V tahu z Telegramu jsou meziseancové a plánovací nástroje zakázané ve všech režimech. V projektech bez příjmu nastav v.claude/settings.local.json"crossSessionInbound": "hold".Chráněné cesty u Bash spolehlivě chrání jen izolace
bwrap. Mimobwrap(strom, který nejde projít, po potvrzení) hlídá Bash jen textová kontrola, kterou obfuskace obejde.Obal
bwrapdostane Bash přesupdatedInputhooku. Claude Code výstupy více hookůPreToolUseneslučuje:updatedInputplatí jen z hooku, jehož rozhodnutí vyhrálo, a ze stejně silných rozhodnutí z toho, který doběhl poslední. Jiný hook (vlastní, z pluginu nebo frontmatteru skillu), který pro Bash vrátíallowneboaskbezupdatedInput, tak obal může zahodit. Proto se Bash z Telegramu i vplnyve výchozím stavu ptá (asks obalem; proti cizímuallowjeaskodolný) a kontrola vPostToolUseokno zavře, když Bash běžel s jiným příkazem, než jaký hook obalil.Formát řádku socketu a pole transkriptu nejsou dokumentované (ověřeno na Claude Code 2.1.314). Server kontroluje verzi a tvar záznamů a při odchylce okno neotevře nebo zavře.
Dialog oprávnění (
ask) v tahu ze socketu jde potvrdit v terminálu nebo v Remote Control, i při zamčeném PC (ověřeno na 2.1.314). Proto je výchozírezimy.prace.pri_dotazu: cekat: dialog čeká, až ho potvrdíš v RC nebo v terminálu.odmitnoutho rovnou zamítne. Session bez Remote Control má vždyodmitnout(i když se RC odpojí během okna). Dialog se nikdy nezmění na povolení a z Telegramu nejde potvrdit nic.Server musí být přímý potomek procesu
claude(kontrola socketupid == getppid()): registruj přímo.venv/bin/mcp-voice, nespouštěj ho přesuv runani shellový obal (sh -c, skript). Jinak je každé doručení „nepotvrzeno“.
Instalace
Bot, token a ID podle „Telegram: první nastavení“. Konfigurace:
telegram.chat_id,telegram.prijem.povoleno: true,povolene_user_id(tvoje user_id,chat_idmusí být mezi nimi).Allowlist potvrď v samostatném terminálu mimo Claude Code (bez TTY, z procesu
claudenebo s jeho proměnnými prostředí se odmítne, ani přes!); po každé změněpovolene_user_idnebochat_idznovu:/cesta/k/mcp-voice/.venv/bin/mcp-voice-telegram potvrdDalší podpříkazy, bez sítě:
mcp-voice-telegram stavvypíše, jestli je příjem zapnutý a allowlist potvrzený, a stav oken.mcp-voice-telegram zavrizapíše žádost o zavření aktivního okna (funguje i bez TTY, třeba ze skriptu).Celý vzor hooků včetně testu před vložením (sekce „Hooky: vzor a test před nasazením“). Hook rozsahu běží
/usr/bin/python3 -Ijen ze standardní knihovny a adresář stavu dostane parametrem--stav. Ten musí být shodný scesty.stav, jinak se okno nikdy neotevře (bez--stavplatí proměnnáMCP_VOICE_STATE_DIR, jinak~/.local/state/mcp-voice).PostToolUsestrážce má matcher.*(signály citlivých kontextů ve výsledcích všech nástrojů, i Bash, Agent, WebFetch a subagentů); bez otevřeného okna a bez kontextů skončí rychle (~50 ms na volání). Hook rozsahu vPostToolUseaPostToolUseFailures matcheremBash(--udalost post, timeout 10 s: zámek až 3 s a sken až 3 s) hlídá, že Bash z Telegramu běžel s obalem a nezměnil konfiguraci agenta ani Gitu. Vypršení tohoto hooku Claude Code nebere jako zamítnutí, kontrola by pak neproběhla.Slash příkaz:
examples/claude-command-telegram.mdzkopíruj do.claude/commands/telegram.mdprojektu (disable-model-invocation: true, model si ho sám nespustí).Status bar:
mcp-voice-statusukáže i segment okna (TG do 16:30 · práce,TG tah #4821,TG čeká potvrzení,TG fronta 2,TG ověřuji).
Povely
/telegram start [ČAS] [REŽIM] ČAS = 30m | 90min | 2h | 30 minut | 2 hodiny (5 min až 4 h, výchozí 1 h), REŽIM = cteni | prace | plny
/telegram stop
/telegram stavJen jako celý prompt, napsaný v terminálu nebo v Remote Control (ověří se v transkriptu: origin.kind == human, turnOrigin == human, promptSource typed, queued nebo u slash příkazu chybí). Z Telegramu povel nikdy neplatí. start ověřuje server, ne hook: Claude Code zapisuje záznam promptu do transkriptu až po hookách UserPromptSubmit (ověřeno na 2.1.314 a 2.1.316). Hook předá žádost s prompt_id a otiskem povelu, server počká nejvýš 60 s na začátek tahu se stejným promptId a ověří ho (záznam nejvýš 30 s před převzetím žádosti; starší záznam se stejným textem neplatí). Neověřený povel okno neotevře: server ho zaloguje a pošle šablonu „Okno se neotevřelo“. Okno pak otevře odpovez v tahu povelu (důkaz běhu hooku rozsahu), nebo server sám po 120 s s platným heartbeatem; odpovez před ověřením povel ověří, bez ověřeného povelu okno neotevře. Server délku a režim znovu přečte z povelu v transkriptu, žádost hooku je jen nese (nesouhlas = povel_nesouhlasi). start v otevřeném okně změní konec a režim. Okno se zavře po čase, po 60 min bez zprávy z Telegramu (klid session bez nástrojů okno nezavírá), povelem stop, zprávou /stop z Telegramu, při /clear, konci session, přesunu okna do jiné session, chybě Telegramu (401, 403, 409), opakovaném překročení limitu, když Bash z Telegramu běžel bez obalu bwrap (bash_bez_obalu), nebo když po něm v zapisovatelném kořeni vznikla, zmizela či se nahradila konfigurace agenta nebo Gitu, případně to nešlo ověřit (konfigurace_agenta). Dokud server žádost o zavření nezpracuje, hook tahu z Telegramu zamítá všechna volání. Mimo okno server getUpdates nevolá; při otevření zahodí backlog, při zavření frontu.
Z Telegramu jde jen /stop a /stav. Ostatní tokeny zprávy, které odpovídají maskám zakazane_prikazy (výchozí /[!/]*), se nahradí značkou [zakázaný příkaz] a obálka uvede jejich počet. V maskách * a ? nepřekročí /, takže absolutní cesta („přečti /etc/hosts“) příkaz není, /loop 5m a /compact ano. Přijímá se jen text a hlasovka (voice, Ogg/Opus); přeposlané a speciální zprávy, fotky a soubory se odmítnou. Text se čistí (řídicí, bidi a neviditelné znaky).
Režimy a politika nástrojů
Režim | Bez dotazu | Dotaz (dialog) | Zamítnuto |
| čtení ( | – | vše ostatní, |
| čtení | vše ostatní (Bash v | tvrdé zákazy, chráněné cesty a chráněný zápis |
| vestavěné nástroje; Bash v | Bash v | tvrdé zákazy, chráněné cesty a chráněný zápis |
Tvrdé zákazy (všechny režimy): SendMessage, ListAgents, Cron*, ScheduleWakeup, RemoteTrigger, Monitor, TaskStop, běh na pozadí, plánovací režim a worktree, AskUserQuestion, cloudový přepis a zápis do konfigurace agenta: .claude/ v libovolné hloubce (i ~/.claude/), CLAUDE.md, CLAUDE.local.md a .mcp.json kdekoli. Zákaz platí pro Write, Edit, MultiEdit, NotebookEdit, MCP nástroje s cestou i Bash (textová kontrola a v obalu jen pro čtení), důvod v dialogu zní „zápis do konfigurace agenta … je z Telegramu zakázaný“. konfigurace_agenta.pridat přidá další jména nebo vzory (soubory instrukcí jiných agentů), odebrat výchozí nejde. Čtení těchto souborů se neomezuje (kromě ~/.claude, který je chráněný celý). Chráněné cesty (čtení i zápis, všechny režimy): ~/.ssh, ~/.gnupg, ~/.claude, konfigurace a stav mcp-voice, keyring, správci hesel, přihlášení CLI agentů a nástrojů (~/.codex, ~/.gemini, gh, rclone, cloudy …), poštovní a komunikační klienti (Thunderbird, Evolution, Signal, Telegram Desktop, Slack, Discord …), profily prohlížečů, historie shellu a dalších programů (~/.bash_history, .*_history, ~/.lesshst, ~/.viminfo, fish), historie schránky, stav nvim (odkládací soubory), /proc, sockety session a vzory .env, .env.*, *.env, .envrc, .netrc, .npmrc, *.pem, *.key, *.kdbx, id_rsa*, *credentials*, *secret* … (vzory jmen bez ohledu na velikost písmen; úplný seznam CHRANENE_VYCHOZI v politika.py). Chráněný zápis navíc: AGENTS.md, GEMINI.md, .gemini/, .codex/, .cursor/, .vscode/, .husky/, .pre-commit-config.yaml, git hooky a .git/config libovolného repa, .envrc, ~/.gitconfig, ~/.config/git, shell rc soubory (~/.bashrc, ~/.bash_aliases, ~/.profile, ~/.zshrc, fish …), ~/.config/systemd, ~/.config/autostart, ~/.config/environment.d, ~/.local/bin, ~/.local/share/applications, crontab (i příkazy crontab, at, systemd-run v Bash). Vždy a bez možnosti odebrat: adresář balíčku mcp-voice (zdroj hooku), jeho venv a repozitář editovatelné instalace, odvozené za běhu. MCP nástroje se kontrolují po řetězcových parametrech: absolutní i relativní cesty, jména souborů a file:// URL. Konfigurace seznamy rozšíří (pridat), výchozí položku jen vědomě odebere (odebrat_vychozi, přesná shoda). rezimy.cteni.koreny čtení volitelně omezí na zadané adresáře; prázdné = bez omezení.
Grep v tahu z Telegramu vrací jen jména souborů (obsah pak jde přes Read, který se kontroluje po souborech); obsahový výstup se zamítá, dokud není pokusem ověřené, že Claude Code vynucený output_mode použije. I jména souborů prozradí po bitech obsah (^KEY=a), proto se Grep nad stromem s chráněným souborem (.env, klíče) nebo nad stromem, který nejde projít celý (limity skenu níže), zamítne (cteni) nebo ptá. Glob s absolutním vzorem do chráněného adresáře (~/.ssh/*) se zamítne.
Bash v tahu z Telegramu se zabalí do bwrap jako sandbox: --unshare-all (bez sítě, IPC a PID), --new-session, --die-with-parent, --clearenv (jen PATH, HOME, USER, LANG, TERM …), celý souborový systém jen pro čtení (--ro-bind / /), vlastní /dev, /proc, /tmp a /run (žádný D-Bus a klíčenka, systemd --user, ssh-agent, gpg-agent, X11, docker, tmux ani sockety session).
Domov a místa připojení jsou skrytá (
--tmpfs): celý domov,/home,/mnt,/media,/srv,/net,/nfs,/afsarezimy.plny.bash_skryt. Uvnitř je vidět jen pracovní adresář, kořenyrezimy.<prace|plny>.bash_korenya složky nástrojů z domova jen pro čtení (rezimy.plny.bash_domov, výchozí~/.local/bin,~/.local/lib,~/.cargo/bin,~/.rustup,~/.nvm,~/.pyenv,~/.local/share/uv…; přidávej jen složky bez tajemství, chráněná jména se v nich neprohledávají)..env, databáze hesel ani data jiných projektů a NAS tak z obalu vidět nejsou, ani přes obfuskaci. Pracovní adresář, který je domovem, místem připojení nebo jejich předkem, obal nesestaví (Bash se ptá bez obalu).bash_koreny: seznam{cesta, pristup: rw|ro}pro další adresáře (jiný projekt). Výchozí prázdný = jen pracovní adresář. Kořen, který je chráněnou cestou, konfigurací agenta, nebo obsahuje chráněnou cestu či domov (~,~/.config,/), validace odmítne, stejně jako systémové složky/dev,/proc,/sys,/run,/var/run,/boot, vše pod nimi a jejich předky (/var) a samotné/tmp(podsložka/tmpsmí).~/binje chráněný zápis (je vPATH), kořen srwse tedy připojí jen pro čtení. Chráněná jména a chráněný zápis platí i uvnitř kořenů.Uvnitř viditelných kořenů: chráněné adresáře
--tmpfs, chráněné soubory a soubory s chráněným jménem--ro-bind /dev/null. Zapisovat jde jen do pracovního adresáře, kořenůrwa soukromého/tmp; v nich zůstanou jen pro čtení v libovolné hloubce konfigurace agenta, chráněný zápis (AGENTS.md,.vscode/…, u symlinku jeho cíl), soubor.git(worktree, submodul), závislosti (node_modules,.venv,vendor) a v každém gitdiru (.giti.git/modules/**submodulů)hooks/,config,config.worktree,worktrees/*/config.worktree, soubory z[include]a cílcore.hooksPath(i relativní z~/.gitconfig). Chybějícíhooks/dostane prázdný--tmpfsjen pro čtení (na disku po něm zůstane prázdná složka, stejná, jakou vytvořígit init). Ostatní obsah.git(objekty, reference, index) zůstává zapisovatelný, aby git fungoval.Přejmenování rodiče: každá složka mezi zapisovatelným kořenem a položkou jen pro čtení se připojí sama na sebe (
--bind d d), takžemv,rm -ranirmdirrodiče (sub/nadsub/.claude/,.gitnad.git/hooks) neprojdou (EBUSY).mvpak zkusí kopii a selže; kopie pod jiným jménem může zůstat, novou konfiguraci agenta ale pozná kontrola integrity. Připojení jdou od mělkých k hlubokým (jinak by pozdější rodič překryl dřívějšího potomka).Kontrola integrity po běhu (Bash z Telegramu s obalem): hook
preuloží ktool_use_idsnímek zapisovatelných kořenů (typ a inode, u symlinku i cíl) všech položek se jménem konfigurace agenta nebo chráněného zápisu, souborů a složek.gita jejich háčků a konfigurace (integrita-<session>-<tool_use_id>.json). Hookpostkořeny znovu prohledá. Nová, smazaná nebo nahrazená položka (.claude/settings.local.jsonv novém.claude/,nove/CLAUDE.md,CLAUDE.local.md,.mcp.json,CLAUDE.mdsymlink nahrazený souborem, nový.git) zavře okno (konfigurace_agenta), do Telegramu odejde šablona bez cest a v terminálu se ukáže upozornění (systemMessage) se seznamem cest. Do serverového logu jdou cesty taky. Chybějící snímek, chyba nebo sken nad limit = zavření (fail-closed). mtime se nesleduje: existující položky jsou v obalu jen pro čtení a Claude Code sám přepisuje.claude/settings.local.json(volba „neptat se znovu“). Snímek nevzniká u Bash bez obalu (dotaz s holým příkazem).Prevence neexistujících jmen se nedělá: předvytvořit
CLAUDE.md,.mcp.jsonnebo.claude/jako místo připojení (--ro-bindprázdného souboru) bybwrapzaložil na disku v každém kořeni (prázdný.mcp.jsonje pro Claude Code chybná konfigurace, šum vgit status) a nové podsložky (nove/CLAUDE.md) by to stejně nepokrylo. Pokrývá je kontrola integrity.Sken stromu: jeden průchod bez symlinků, vzory jmen jako jeden regulární výraz. Neprochází
.git(submoduly v.git/modulesjen po gitdirech, bez objektů),node_modules,.venv,__pycache__;vendorprochází (bývají v něm klíče balíčků). Limity 500 000 položek, hloubka 32 a 3 s pro všechny kořeny jednoho volání dohromady (timeout hooku je 5 s; kontrola integrity po běhu má vlastní 3 s); obal nejvýš 100 000 znaků (jeden argumentbash -c) a 2 000 připojení. Co limit překročí, obal nesestaví a Bash se ptá bez obalu (fail-closed). Pomalý kořen (NAS, studená cache) tak vede k dotazu bez obalu, ne k vypršení hooku. Změřeno (teplá cache):~/projects280 tisíc položek 0,44 s, obal 77 tisíc znaků (110 složek připojených na sebe, 618 jen pro čtení), kontrola po běhu 0,42 s; repozitář se 169 tisíci položkami 0,28 s. Cache podle mtime adresářů se nevyplatí: jen kontrola mtime všech adresářů stojí zhruba polovinu skenu.rezimy.plny.bash_sit: truevrátí Bash síť (--share-net) – vědomé riziko: abstraktní unixové sockety (X11, některé D-Bus) jsou pak dosažitelné.Bash v
plnyse ve výchozím stavu ptá (ask, v dialogu je obalený příkaz). Bez dotazu běží jen srezimy.plny.bash_bez_dotazu: truea v okně s ověřenou verzí Claude Code. Zapni to jen tehdy, když žádný jiný hookPreToolUse(ani z pluginu nebo skillu) nevrací pro Bashallowneboask: takový hook obal zahodí (háček 5) a sallowběží holý příkaz bez dotazu. Kontrola vPostToolUseto pozná podle otisku obaleného příkazu a okno zavře (bash_bez_obalu, šablona do Telegramu); první takový příkaz ale už proběhl. Bezbwrapse Bash vplnyptá.Pojistka v každém tahu (i bez okna): Bash nesmí zmínit
cc-socks,CLAUDE_CODE_MESSAGING,mcp-voice-telegramani konfiguraci a stavmcp-voice.
Zbytková rizika: textová kontrola Bash je jen pojistka a zamítá i čtení konfigurace agenta (cat .claude/settings.json, git diff CLAUDE.md; Read funguje) a /proc/cpuinfo. MCP nástroje (kromě odpovez, rekni, stav) se zamítnou, když jednořádkový řetězec obsahuje jméno konfigurace agenta jako celou složku cesty, i ve větě („uprav moje slozka/.claude/settings.json“). Neexistující konfiguraci agenta nebo chráněný soubor (sub/.claude/, CLAUDE.md) a náhradu symlinku CLAUDE.md souborem jde v bwrap v zapisovatelném kořeni udělat obfuskovaným příkazem; obal tomu nezabrání, kontrola integrity to po běhu pozná a okno zavře (příkaz ale už proběhl, soubor na disku zůstane do ruční kontroly). Totéž nový .git v podsložce (git init), a háčky submodulu, jehož gitdir v .git/modules vznikne až během příkazu. Úpravy ostatních souborů v kořeni (Makefile, package.json skripty, .gitmodules) obal nehlídá; spustí je až člověk. Stromy nad limity skenu (velké monorepo, celý ~/projects s mnoha chráněnými soubory) mají Bash vždy s dotazem a bez obalu. Chráněná jména uvnitř node_modules a .venv se nemaskují (kód třetích stran). Potvrzení allowlistu z procesu, který se odpojí od claude a smaže jeho proměnné, kontrola nepozná.
Původ tahu: telegram (zpráva od serveru), clovek, uloha (předání subagenta), cizi (jiná session nebo proces), naplanovany (/loop, cron), nezname. V otevřeném okně mají cizi, naplanovany (omezit_naplanovane: true) a nezname jen čtení. Zpráva doručená uprostřed tahu omezí zbytek tahu. Subagent dědí třídu tahu, ve kterém vznikl, až do SubagentStop. Nic bezpečnostního nezávisí na Stop (Esc ho nespustí). Hook čte transkript od konce nejvýš 1 MB; dlouhý tah člověka (velké výsledky, komprimace) pozná podle značky prompt_id, kterou hook rozsahu uloží při volání nástroje, když přečtený začátek tahu s tímto promptId napsal člověk (čas značky = začátek tahu). Značka neplatí, jakmile server po ní vloží zprávu z Telegramu. Shrnutí komprimace tah nezačíná.
Živost hooku rozsahu: server okno otevře jen s čerstvým heartbeatem hooku (tah povelu). Pak ho zavře jen tehdy, když transkript obsahuje volání nástroje hlavního vlákna novější, než by hook heartbeat obnovil (nástroje běží a hook ne). Klidná session bez nástrojů okno nezavírá; heartbeat obnovují i události Stop, Notification a SubagentStart/Stop.
Odpovědi, citlivé kontexty, retence
Forma odpovědi: hlasovka → hlas, text → text; fráze ve zprávě (
telegram.prijem.forma) mají přednost, vyhrává poslední výskyt.odpovez(forma="auto")ji převezme. Text se dělí na nejvýš 3 zprávy, bezparse_mode, jako odpověď na zprávu. Tah bezodpovezskončí šablonou „Hotovo, odpověď je v terminálu“. Do Telegramu jinak posílá server jen pevné šablony.Citlivé kontexty:
odpovezareknido Telegramu mají razítko strážce. Klíče kontextu:telegram: souhlas | nikdy,telegram_platnost: kontext | okno,telegram_dotaz_v_tahu: rc | odmitnout. Dialog jmenuje příjemce („Text … → Telegram (cloud, bez koncového šifrování)“). Uforma="auto"dialog uvádí, že odpověď může jít i hlasem přes ElevenLabs. V tahu z Telegramu strážce hledá textové signály kontextů ve výsledcích všech nástrojů (i Bash, Agent, WebFetch), u subagenta podle třídy tahu, ve kterém vznikl. Textupozornenipiš obecně („Data odejdou mimo (ElevenLabs, Telegram)“).Retence:
retence.smazat_po_h(výchozí 24, max 47, 0 = nemazat) – server maže zprávy obou stran v soukromém chatu (deleteMessages), dokud běží, a po startu dožene dluh do 48 h. S retencí se mažou i hlasovky běžnéhorekni(cil="telegram")mimo okno. Zprávy, které jsi poslal mimo okno, se nemažou; ze zahozeného backlogu při otevření okna se smaže jen poslední zpráva (vícgetUpdatessoffset=-1nevrátí).Transkript: u povelu a doručení server i hook čtou metadata záznamů, text jen hashují a hledají značku
[Telegram #<id>. Nic se nevypisuje ani neloguje.
Akceptační scénáře (ruční, kromě A1–A16 ze specifikace)
# | Scénář | Očekávání | Automatický test |
A17 | Okno otevřené, 20 min bez nástrojů, pak zpráva z Telegramu | zpráva se doručí, okno zůstane |
|
A18 | Dlouhý tah u PC s velkými soubory v otevřeném okně (> 1 MB transkriptu) | Bash, Write a Edit se chovají jako mimo okno, ne „Omezený tah (nezname)“ |
|
A19 | Session s citlivým kontextem, tah z Telegramu, |
|
|
Provozní doporučení
Dvoufázové ověření v Telegramu (cloudové heslo) i na účtu Claude, kontrola aktivních relací Telegramu, v BotFather /setjoingroups vypnout, při ztrátě zařízení token bota rotovat (/revoke) a znovu uložit do keyringu. Okno otevírej jen na nutnou dobu a sleduj status bar.
Bezpečnost a soukromí
Logy: jen stderr, jen metadata (nástroj, cíl, důvod, počet znaků, délka, ms, kód chyby). Nikdy text, přepis,
chat_id,voice_id, token ani klíč. Loggeryhttpx/httpcorejsou natvrdo naWARNING(URL Telegramu nese token), filtr maskuje známá tajemství. Chyby nástrojů nikdy neobsahují text výjimky.Claude Code ukládá stderr MCP serverů trvale do
~/.cache/claude-cli-nodejs/<projekt>/mcp-logs-<server>/. I proto do logu nejde nic citlivého.ElevenLabs: text pro TTS (a u
engine="cloud"celé audio) odchází do ElevenLabs. Podle zásad z 9. 10. 2026 je smí používat k tréninku, dokud v účtu nevypneš „Data use“. TTS přes API se ukládá do historie účtu (text i audio); server vytvořenou položku hned maže (viz „Historie ElevenLabs“).Telegram: cloudový chat bez koncového šifrování. Klient zná jen metody
sendVoice,sendMessage,getUpdates,getFile,deleteMessagesa hostapi.telegram.org, bez přesměrování a bezcaption; po odeslání ověří, že zpráva skončila v soukromém chatu z konfigurace.getUpdatesjen v otevřeném okně příjmu.Přepis: soubor jen z
stt.povolene_adresare(realpath+commonpath, otevřeníO_NOFOLLOW, kontrola skutečné cesty deskriptoru, jen běžný soubor). Audio se načte jednou do paměti;ffprobeaffmpegčtou tytéž bajty zmemfdpřes protokolfd(playlisty aconcatnesáhnou jinam), s limity CPU a paměti. Povolené formáty: ogg, mp3, wav, flac, m4a/mp4, webm. Dekóduje systémovýffmpeg, ne PyAV. Lokální selhání nikdy automaticky nepřejde do cloudu. Přepis je nedůvěryhodný vstup ("zdroj": "prepis_hlasovky").DBus: jen
GetActiveaGetIdletimes pevným argv a timeoutem; nikdyLock,SetActive,SimulateUserActivity.Soubory:
umask 077, stavové adresáře0700, soubory0600, zápis atomicky; výstupní MP3 přesO_CREAT|O_EXCL|O_NOFOLLOW.
Výběr cíle (cil="auto")
Zdroj | Pravidlo |
terminál | PC zamčené → Telegram; nečinnost ≥ |
Remote Control | zamčené teď nebo při promptu → Telegram; okno klienta a aktivita při promptu → PC; aktivita po promptu → PC; nečinnost při promptu (bez snímku nečinnost teď) ≥ |
tah z Telegramu | vždy Telegram (okno příjmu otevřené, původ tahu podle transkriptu) |
neznámý | zamčené nebo nečinnost ≥ |
tiche_hodiny přesměrují auto z PC do Telegramu. Výslovné cil="pc" při zamčeném nebo neznámém stavu PC vrátí PC_LOCKED / PC_STATE_UNKNOWN (přepínač pc_pri_zamceni: hrat). Přehrávání na PC se při zamčení utne a zbytek fronty zahodí.
Kódy chyb
TEXT_EMPTY, TEXT_TOO_LONG, ARGUMENT_INVALID, VOICE_UNKNOWN, MODEL_NOT_ALLOWED, DAILY_LIMIT, RATE_LIMIT, DUPLICATE, DATA_USE_NOT_CONFIRMED, SECRET_MISSING, KEYRING_UNAVAILABLE, ELEVENLABS_AUTH, ELEVENLABS_QUOTA, ELEVENLABS_RATE, ELEVENLABS_VOICE, ELEVENLABS_ERROR, TARGET_UNRESOLVED, PC_LOCKED, PC_STATE_UNKNOWN, PLAYER_UNAVAILABLE, TELEGRAM_NOT_CONFIGURED, TELEGRAM_ERROR, ENCODER_ERROR, OUTPUT_PATH_INVALID, FILE_NOT_ALLOWED, FILE_NOT_FOUND, FILE_TOO_LARGE, AUDIO_TOO_LONG, AUDIO_UNREADABLE, CLOUD_DISABLED, STT_LOCAL_UNAVAILABLE, STT_LOCAL_FAILED, GUARD_MISSING, GUARD_MISMATCH, WINDOW_CLOSED, NOT_TELEGRAM_TURN, SECRET_LIKE, INTERNAL_ERROR.
Testy
.venv/bin/pytest -q # bez sítě: HTTP přes httpx.MockTransport, falešná tajemství
.venv/bin/pytest -q --cov=mcp_voiceNa základní instalaci (varianta A) se testy, které potřebují lokální přepis, přeskočí. Test se skutečným modelem běží jen tam, kde je model stažený. tests/test_repo.py hlídá, že do repozitáře nepronikne osobní cesta ani token.
Test soukromých slov (jména, domény) je volitelný a seznam slov v repozitáři záměrně není. Zapneš ho proměnnou MCP_VOICE_SOUKROMA_SLOVA s cestou k vlastnímu souboru (jedno slovo na řádek, # = komentář), bez ní se přeskočí:
MCP_VOICE_SOUKROMA_SLOVA=/cesta/k/slova.txt .venv/bin/pytest -q tests/test_repo.pyLicence
MIT, viz LICENSE. Vzory převzaté jako myšlenky (bez kódu): sekvenční fronta přehrávání a nevracení mluveného textu (blacktop/mcp-tts, MIT), parametry Ogg/Opus pro sendVoice (Agent2Telegram, MIT), hranice vstupních a výstupních souborů (elevenlabs-mcp, MIT).
This server cannot be deployed
Maintenance
Related MCP Connectors
Text to speech in 149 languages: MP3 links from any assistant. Free without an account.
Text to speech and transcription for any AI model: MP3 voiceovers, audio and YouTube to text.
Video summaries, voice message transcription and translation in the same voice, your own voice TTS
1Search, read and reply to your Telegram Business chats, transcribed voice included.
Related MCP Servers
- FlicenseAqualityAmaintenanceBridges AI assistants to a Telegram bot to enable two-way messaging, interactive confirmations, and live status updates. It supports automatic voice transcription via local Whisper models and provides secure, single-user communication for MCP-compatible hosts.46-
- AlicenseAqualityDmaintenanceProvides text-to-speech and Telegram notification capabilities via the Model Context Protocol. Supports multiple TTS providers and Telegram messaging with optional setup web interface.241 npm3MIT
- AlicenseNot gradedqualityDmaintenanceTranscribe voice messages to text and generate spoken audio from text, served as cached Ogg/Opus files for instant playback.3MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI clients to convert text into spoken MP3 audio by calling a local voice gateway that renders it with the Eleven v4 model and delivers the result to either a local web playback list or a fixed Telegram chat. It also returns short clips as inline MCP audio content blocks for clients that support embedded playback.MIT