Skip to main content

A text-to-speech MCP server powered by Kokoro — gives Claude Code a voice

Project description

Soliloquy

PyPI version Python License: MIT PyPI downloads

A text-to-speech MCP server powered by Kokoro — gives Claude Code a voice.

One command to install. No config, no API keys, no cloud.

Why Soliloquy?

Cloud TTS (ElevenLabs, OpenAI, etc.) Soliloquy
Privacy Text sent to third-party servers Runs entirely on your machine
Cost $0.18-15/1M chars Free forever
Offline No Yes
Usage Limits Quotas / rate limits Unlimited
Latency 200-500ms (network) ~50-100ms (local)
AI Integration Developer calls API from code AI agent decides when to speak
Setup API keys + billing One command, no config

What You Can Do

Once installed, just talk to Claude naturally:

  • "turn on auto speak" — automatically voice conversational responses
  • "read this file aloud" — listen to docs, chapters, or articles (text, markdown, PDF, EPUB)
  • "read this book" — EPUBs are treated as audiobooks: chapters announced, resume on next call
  • "pick up where we left off" — resume any book you've started
  • "jump to chapter 5" / "read the preface" / "next chapter" — navigate around
  • "speak aloud" — voice a specific response
  • "stop" — stop audio playback

Auto-Speak

The flagship feature. Auto-speak voices every Claude conversational response automatically using a background hook — no tool call needed, zero token overhead. It just works.

"Turn on auto speak"

From that point on, everything Claude says, you hear. Toggle it off just as easily:

"Turn off auto speak"

When you use read_aloud or speak explicitly, auto-speak steps aside and lets the explicit playback finish.

Speech Normalization

Soliloquy doesn't just read text literally — it understands what sounds natural:

  • Code blocks become "See the code below" instead of reading syntax aloud
  • Tables are summarized ("There's a table here with 5 rows")
  • Symbols are spoken naturally (arrows become "to", URLs are simplified)
  • Lists are enumerated ("First... Second... Third...")
  • Paragraph breaks produce natural pauses between sections

Technical content and markdown-heavy responses sound like a person reading them to you, not a robot parsing characters.

Audiobook Mode (Beta)

🧪 Beta feature. Works well on cleanly-authored EPUBs (O'Reilly, Manning, Standard Ebooks, most professional publishers). EPUB tagging varies wildly in the wild — please open an issue if a specific EPUB misclassifies chapters or sounds wrong.

Pass an EPUB to read_aloud and Soliloquy treats it as an audiobook. Each chapter is announced by its title, frontmatter (cover, preface, "About the Author") is skipped by default, and playback resumes from where you left off on the next call.

pip install 'soliloquy-tts[epub]'      # EPUB only
pip install 'soliloquy-tts[documents]' # PDF + EPUB

Talk to Claude naturally:

You say… What happens
"Read this book" Starts a fresh book, or resumes if you've read it before
"What chapters are in this book?" Shows the table of contents
"Where am I?" Reports your current chapter and progress
"Jump to chapter 5" Skip to that chapter (chapter=5)
"Read 'The Forest'" Match a chapter by its title (chapter="The Forest")
"Next chapter" / "previous chapter" Move ±1 chapter
"Restart this chapter" Replay from the chapter's first sentence
"Start over" Wipe the bookmark and restart from chapter 1
"Show me the frontmatter too" Include cover, preface, etc. (include_frontmatter=True)

How resume works

Bookmarks live as JSON files under ~/.soliloquy/library/, keyed by the SHA256 content hash of the source file. A book moved to a new folder keeps its place; two identical copies share one bookmark. Atomic writes (tempfile + rename) mean a crash mid-write can never corrupt your position.

The bookmark layer uses a hybrid update strategy that tracks actual audio playback — not synthesis-ahead. Your position is saved every few audio chunks during playback and again on stop, so resume replays at most one short batch of audio. You'll never skip ahead and miss content.

Smart frontmatter detection

Four cascading signals identify cover, dedication, preface, appendix, index, and similar non-body content:

  1. EPUB3 nav doc entries marked epub:type="frontmatter|backmatter"
  2. Spine entries with linear="no"
  3. Spine idrefs starting with semantic names (cover, titlepage, preface, appendix, ...)
  4. Chapter titles matching frontmatter patterns ("Preface", "Index", "Appendix A: …")

Professional publishers tag spine entries cleanly — heuristics handle them well. Hobby or scraped EPUBs sometimes mislabel title pages or backmatter as body chapters. When that happens, pass include_frontmatter=True to play everything in spine order, or use chapter="<title>" to jump straight to the right content by name.

Known limitations

  • EPUB only. PDF and text files read as one-shot only — page-level book mode for PDFs is a future iteration.
  • Chapter numbers aren't spoken. The internal chapter index is the EPUB spine position (an implementation detail), not the "story chapter N" a reader would recognize. Only the title is announced. The index is still used for navigation (chapter=5, bookmark_status, etc.).
  • No multi-language detection. All chapters use the configured lang.

For non-book use cases, read_aloud still works as a one-shot reader for .txt, .md, and .pdf files (PDFs require the [pdf] extra).

Requirements

  • macOS, Windows, or Linux
  • Python 3.10+
  • PortAudio (audio output library)
Platform Install PortAudio
macOS brew install portaudio
Windows Bundled with sounddevice (no action needed)
Linux sudo apt install libportaudio2

Note: First install downloads ~2GB of dependencies (PyTorch, model weights). First run also downloads the Kokoro-82M model from HuggingFace.

Optional: PDF and EPUB support

To read PDFs and EPUBs (including audiobook-mode resume on EPUBs), install the document extras:

pip install 'soliloquy-tts[documents]'   # both PDF and EPUB
pip install 'soliloquy-tts[pdf]'         # PDF only
pip install 'soliloquy-tts[epub]'        # EPUB only

With uvx, replace soliloquy-tts in your setup command (e.g. uvx 'soliloquy-tts[documents]').

Optional: macOS Menu Bar Control

On macOS, you can install rumps for a menu bar icon that lets you stop playback instantly — no need to go through Claude Code:

pip install rumps

Without it, everything still works — you just use the stop command through Claude Code instead.

Quick Start

Make sure PortAudio is installed (see above), then:

uvx soliloquy-tts

That's it. This registers the MCP server, configures auto-speak, and sets everything up. Restart Claude Code afterward and you're good to go.

Requires uv. Install it with brew install uv (macOS), sudo apt install uv (Linux), or see the uv docs.

What happens when you run it

  1. Registers Soliloquy as an MCP server with Claude Code
  2. Writes a hook script for automatic voicing
  3. Configures the Claude Code Stop hook

You only need to do this once. After that, Claude Code starts Soliloquy automatically in the background whenever you open a session.

With pip

pip install soliloquy-tts
soliloquy

Same setup flow. Run soliloquy from your terminal and it handles the rest.

How It Works

Soliloquy uses a hybrid architecture to share a single model across multiple Claude Code sessions:

  • First session loads the Kokoro model and starts a local backend server
  • Additional sessions detect the running backend and connect as lightweight proxies (near-instant startup, no extra memory)
  • If the backend exits, the next session automatically takes over

This is completely transparent — no configuration needed.

Reference

Tools

speak — Synthesize and play text aloud.

Parameter Default Description
text (required) Text to speak
voice af_heart Voice ID
speed 1.0 Speed multiplier (0.5 - 2.0)
lang en-us Language code

read_aloud — Read a file aloud directly. Supports plain text, markdown, PDF (with [pdf]), and EPUB (with [epub]). EPUBs become audiobooks by default.

Parameter Default Description
path (required) Path to the file to read
voice af_heart Voice ID
speed 1.0 Speed multiplier (0.5 - 2.0)
lang en-us Language code
pages (none) PDF page range, e.g. "1-3" or "5"
chapter (none) EPUB navigation: integer, title substring, "next", "prev"
restart False EPUB: start at chapter 1, reset bookmark
restart_chapter False EPUB: restart current chapter from the beginning
as_book (auto) Force book mode on/off (default: on for EPUB, off otherwise)
include_frontmatter False EPUB: include cover, preface, etc. in playback

bookmark_status — Show your current position in a book.

list_chapters — Show the table of contents for an EPUB. Pass include_frontmatter=True to also list cover, preface, etc.

stop — Stop audio playback immediately.

auto_speak — Toggle automatic voicing on or off.

list_voices — List all available voices.

Voices

28 voices across American and British English. Default is af_heart.

View all voices
Voice Accent Gender
af_heartAmericanFemale
af_alloyAmericanFemale
af_aoedeAmericanFemale
af_bellaAmericanFemale
af_jessicaAmericanFemale
af_koreAmericanFemale
af_nicoleAmericanFemale
af_novaAmericanFemale
af_riverAmericanFemale
af_sarahAmericanFemale
af_skyAmericanFemale
am_adamAmericanMale
am_echoAmericanMale
am_ericAmericanMale
am_fenrirAmericanMale
am_liamAmericanMale
am_michaelAmericanMale
am_onyxAmericanMale
am_puckAmericanMale
am_santaAmericanMale
bf_aliceBritishFemale
bf_emmaBritishFemale
bf_isabellaBritishFemale
bf_lilyBritishFemale
bm_danielBritishMale
bm_fableBritishMale
bm_georgeBritishMale
bm_lewisBritishMale

Languages

en-us (default), en-gb, ja, zh, es, fr, hi, it, pt-br

Library Usage (StreamingSession)

Soliloquy can also be used as a Python library for streaming TTS — no MCP server needed. Push text as it arrives (from an LLM, WebSocket, etc.) and Soliloquy handles buffering, batching, and gapless playback.

from soliloquy.streaming import StreamingSession

session = StreamingSession(voice="af_heart", speed=1.0)

# Push text as it arrives
session.push("Here's the first sentence.")
session.push("And here's another one.")
session.push("The model is still generating...")

# Signal end of input — flushes remaining text and waits for audio
session.finish()

# Or cancel immediately
session.stop()

StreamingSession adapts batch size to audio buffer health — synthesizing immediately when the buffer is low, and batching efficiently when it's healthy. All the same speech normalization, adaptive chunking, and gapless playback from the MCP tools, with a simple push-based API.

Uninstall

soliloquy --uninstall

This removes the MCP server registration, auto-speak hook, and all config files. Works with uvx soliloquy-tts --uninstall too.

Development

git clone https://gitlab.com/bw-stovall/soliloquy.git
cd soliloquy
python3.11 -m venv .venv
source .venv/bin/activate
pip install -e .[dev,pdf,epub]
pytest tests/ -v

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

soliloquy_tts-0.9.0.tar.gz (81.9 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

soliloquy_tts-0.9.0-py3-none-any.whl (46.5 kB view details)

Uploaded Python 3

File details

Details for the file soliloquy_tts-0.9.0.tar.gz.

File metadata

  • Download URL: soliloquy_tts-0.9.0.tar.gz
  • Upload date:
  • Size: 81.9 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for soliloquy_tts-0.9.0.tar.gz
Algorithm Hash digest
SHA256 f0ac6502f5467b61617f39ae07d2d29c0e53690e05b2ed9e40929a76d5977532
MD5 17fbd65eed10496feb14bd4568b06cd7
BLAKE2b-256 17e315fe779e9a8501122c2d684ee2a9fe81e55d4aa50011914e260deadadcb1

See more details on using hashes here.

File details

Details for the file soliloquy_tts-0.9.0-py3-none-any.whl.

File metadata

  • Download URL: soliloquy_tts-0.9.0-py3-none-any.whl
  • Upload date:
  • Size: 46.5 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.11.15

File hashes

Hashes for soliloquy_tts-0.9.0-py3-none-any.whl
Algorithm Hash digest
SHA256 9119dda09eef79845be862252872467a39d638315394c3773c1c1d01777e8fab
MD5 0ea3ea8f2d802b8ca0854d6141add06b
BLAKE2b-256 2b056c2906b3a91f2ae1cc3eec854941c19ce76fd76ed3bb84905843f248a34a

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page