Skip to main content
Glama
H4sh3

agent-notify

by H4sh3
README.md
<p align="center">
  <img src="assets/tts-mcp-logo.svg" alt="tts-mcp" width="160">
</p>

Local spoken notifications for Codex. Each message plays through your speakers
and appears in a small always-on-top overlay.

Notifications can include a short source tag. Callers may also request a
persistent overlay with an editable reply area. If `TTS_MCP_REPLY_COMMAND` is
configured, that local command receives `source` and `message` as JSON on stdin;
its stdout is used to prefill the reply area. Without a reply command, the area
starts empty for manual typing.

The popup header also shows the originating working folder (for example,
`TWITCH • ~/code/twitch-mcp`). Callers can override the automatically detected
directory with the `origin_folder` tool argument.

Urgent notifications blink, never auto-close, and must be dismissed with their
**ACCEPT** button.
In a reply editor, **Enter** submits the reply and **Shift+Enter** adds a newline.
Use **OPTIONS** to reset placement. Interactive notifications can be dragged by
their title bar or header, and their position is saved for future messages.
Opening Options pauses the overlay's close countdown until the panel is closed.
The panel also selects the local model used for subsequent messages and
lowers Spotify to a configurable percentage of its current volume while TTS is
playing. The model menu includes AuK Soft British female; Jenny; LJSpeech VITS,
Tacotron2, Glow-TTS,
SpeedySpeech, FastPitch, and Neural HMM variants; EK1 and SAM Tacotron models;
and VCTK VITS or FastPitch with speaker p225. Coqui models download on first use;
AuK uses the separate local runtime described below.
Use **PLAY SAMPLE** below the model menu to hear the selected voice before the
next notification. Each preview randomly chooses one of five short sentences.
Samples are generated in the background and cached for later previews.
Spotify's previous volume is restored when playback ends.

Spotify ducking defaults to 20% of its current volume. The Options choices are
Off, 10%, 20%, 30%, 40%, and 50%. On Linux, tts-mcp prefers a matching PipeWire
stream through `pw-dump` and `wpctl`, then falls back to Spotify's MPRIS volume
property through `busctl`. If neither control path is available, TTS playback
continues without changing Spotify.

The MCP server also exposes:

- `list_voices` — list local presets and optional OpenRouter Flux voices;
- `preview_voices` — play the same short phrase through up to six voices;
- `quit_spotify` — request a graceful Spotify quit, with an explicit forced
  fallback when requested.

Local voice previews include Jenny, LJSpeech VITS, and selected speakers from
the VCTK VITS model. Models are downloaded by Coqui on first use and cached.

## Install

Supports Python 3.11 through 3.14 and requires
[`uv`](https://docs.astral.sh/uv/).

```bash
uv sync
uv run --no-sync agent-notify-setup
codex mcp add agent-notify -- "$(pwd)/.venv/bin/tts-mcp"
```

Then add the notifier executable to the top level of `~/.codex/config.toml`:

```toml
notify = ["/absolute/path/to/tts-mcp/.venv/bin/tts-notify"]
```

Restart Codex, then run `/mcp` to check that `agent-notify` is connected. The
`notify` hook runs after Codex has produced its final response, so routine TTS
does not appear as a duplicate MCP tool call. The setup command downloads and
loads the voice model, then opens a real test notification. Its generated audio
is cached for later runs.

Upgrading from the earlier `tts.speak` tool:

```bash
codex mcp remove tts
codex mcp add agent-notify -- "$(pwd)/.venv/bin/tts-mcp"
```

## Turn TTS on or off

Use the **TTS ON / TTS OFF** button in any notification window. Your choice is
saved and applies to future messages; muted messages remain visible so you can
turn speech back on. Turning TTS off also stops the message currently playing,
and the muted window remains open for 15 seconds. A small bar at the bottom
shows the remaining display time. Use the pause button beside the bar to keep a
notification open, then resume the remaining countdown or close it manually.

You can also use the terminal:

```bash
uv run tts-config off
uv run tts-config on
uv run tts-config status
```

## Try it

```bash
uv run tts-notify \
  '{"type":"agent-turn-complete","last-assistant-message":"TTS is ready."}'
```

## Test

```bash
uv run pytest
```

## Optional settings

Set these environment variables when registering the server with
`codex mcp add ... --env NAME=VALUE`:

- `TTS_MCP_MODEL` — Coqui model name; overrides the model saved from Options
- `TTS_MCP_SPEAKER` — speaker ID for a multi-speaker model
- `TTS_MCP_OUTPUT_DIR` — directory for cached WAV files
- `TTS_MCP_OVERLAY=off` — play audio without the overlay
- `TTS_MCP_REPLY_COMMAND` — optional local LLM command used to draft replies
- `TTS_MCP_REPLY_TIMEOUT` — local reply command timeout in seconds (default 120)
- `TTS_MCP_REDUCE_MOTION=on` — replace urgent blinking with a solid alert
- `TTS_MCP_PROVIDER` — `local` (default) or `openrouter`
- `OPENROUTER_API_KEY` — required only for OpenRouter synthesis
- `TTS_MCP_OPENROUTER_MODEL` — defaults to `deepgram/flux-tts:free`
- `TTS_MCP_OPENROUTER_VOICE` — defaults to `flux-bree-en`
- `TTS_MCP_OPENROUTER_FALLBACK=0` — fail instead of using local Coqui

The **AuK — Soft British female** option uses the synthetic voice selected in
the listening comparison. It is also available through `list_voices` as
`auk-soft-british-female`. Selecting it in Options applies to both MCP speech
and the completion notifier; the existing speech on/off setting still applies.
New speech uses a local reference recording to preserve the chosen voice.
The recording is not distributed with this repository. To use this preset,
provide a recording you have permission to use at
`src/tts_mcp/assets/soft-british-female.wav`, matching `REFERENCE_TEXT` in
`src/tts_mcp/auk_tts.py` (or update that text to match your recording).

AuK runs locally in a separate CUDA Python environment, leaving Coqui's
dependencies in the MCP environment. Its runtime is configured in
`~/.local/share/tts-mcp/auk-runtime.json` (or `TTS_MCP_AUK_CONFIG`) with paths
for `python`, `source_dir` (AuK's `src` directory), `transformers_source`
(Transformers 4.57's `src` directory), `model_dir` (AuK Base and VAE weights),
and `encoder_dir` (Qwen2.5-Omni-3B Thinker weights). The isolated environment
needs AuK's inference dependencies and CUDA-enabled PyTorch. Uncached messages
load the model in a worker process; cached WAVs play immediately. GPU synthesis
is serialized across MCP and notifier processes. Generation errors remain
visible rather than silently changing voices.

The default model is `tts_models/en/jenny/jenny`. Audio continues to work when
no graphical display is available.

## Licensing

This project's original code is available under the [MIT license](LICENSE).
That license does not relicense dependencies, downloaded model weights,
datasets, or externally supplied audio. Model weights and reference recordings
are not included in this repository.

AuK's code and weights use the
[MIT license](https://huggingface.co/tencent/AuK/blob/main/LICENSE), but the
AuK integration also requires Qwen2.5-Omni-3B, whose
[Qwen Research License](https://huggingface.co/Qwen/Qwen2.5-Omni-3B/blob/main/LICENSE)
permits research/evaluation use only and requires a separate license for
commercial use. AuK's MIT license does not override that encoder restriction.
Local execution does not remove model license conditions.

Coqui's software license and each downloaded voice model's license are separate.
Check the exact model's terms before use or redistribution. In particular,
the default Jenny model has
[custom attribution and naming requirements](https://github.com/dioco-group/jenny-tts-dataset#important).
Provider services have their own terms as well. Supply reference audio only
when you have the necessary rights; generated recordings are not automatically
covered by this project's MIT license.

## Optional OpenRouter TTS

Local Coqui remains the default. To use Deepgram Flux TTS through OpenRouter,
set these environment variables on the MCP server:

```bash
TTS_MCP_PROVIDER=openrouter
OPENROUTER_API_KEY=...
TTS_MCP_OPENROUTER_MODEL=deepgram/flux-tts:free
TTS_MCP_OPENROUTER_VOICE=flux-bree-en
```

The remote provider uses OpenRouter's speech endpoint and keeps the generated
WAV in the existing audio cache. Set `TTS_MCP_OPENROUTER_FALLBACK=0` to disable
the default local-Coqui fallback when OpenRouter is unavailable.