Distill
A Claude Code MCP server that turns saved articles into AI-generated podcasts, tailored to your interests.
Save articles throughout the day. Generate a two-host conversational podcast on demand. MP3 lands in ~/Downloads, a sound plays, queue clears. Everything is local.
Install
git clone <this repo> ~/code/distill
cd ~/code/distill
npm install
Register with Claude Code
Add Distill as an MCP server. The simplest way — from the directory where you want to keep your queue (your "reading project"):
claude mcp add distill -- node /absolute/path/to/distill/index.js
Or manually add to your ~/.claude.json / project .mcp.json:
{
"mcpServers": {
"distill": {
"command": "node",
"args": ["/absolute/path/to/distill/index.js"]
}
}
}
Distill reads and writes files in the current working directory where Claude Code launched, so your queue and transcripts live alongside whatever project you're in. If you want a single global queue, set DISTILL_PROJECT_ROOT:
{
"mcpServers": {
"distill": {
"command": "node",
"args": ["/absolute/path/to/distill/index.js"],
"env": { "DISTILL_PROJECT_ROOT": "/Users/you/distill-home" }
}
}
}
Restart Claude Code. Confirm with /mcp — you should see distill connected.
Configure
Easy mode: run /distill:setup inside Claude Code. It will walk you through interests, show style, TTS provider, and API key one question at a time.
Manual mode: on first run Distill writes a default .distill file in the project root. Edit it directly:
INTERESTS=entrepreneurship, cybersecurity, AI, startups
SHOW_STYLE=The Briefing
TTS_PROVIDER=openai
OPENAI_API_KEY=sk-...
OUTPUT_DIR=~/Downloads
See .distill.example for a full annotated example.
Note on filesystem layout
The spec describes .distill as both a file and a directory. Since that's not possible, Distill uses:
.distill— config file (dotenv format).distill-data/— data directory (queue.json,transcripts/)
Slash commands
Claude Code exposes MCP prompts with the prefix /mcp__<server>__<prompt>, so all Distill commands start with /mcp__distill__:
| Command | What it does |
|---|---|
/mcp__distill__setup |
Interactive first-time configuration (interests, style, TTS, API key) |
/mcp__distill__save <url> |
Fetch and queue an article |
/mcp__distill__queue-list |
Show the current queue |
/mcp__distill__queue-clear |
Empty the queue (no podcast) |
/mcp__distill__podcast <url> |
Generate a podcast from a single article |
/mcp__distill__digest |
Generate a podcast from the queue, then clear |
/mcp__distill__interests-add <tag> |
Add an interest |
/mcp__distill__interests-remove <tag> |
Remove an interest |
/mcp__distill__interests-list |
Show current interests |
Tip: you can usually type the first few letters (e.g. /distill-pod) and let autocomplete fill the rest.
All commands also work as natural language:
"Save this to my podcast queue: https://…""Make a Deep Dive from my queue""Skip my usual interests, focus on the technical angle"
Show styles
| Style | Host dynamic | Vibe |
|---|---|---|
| The Briefing | Anchor + correspondent | Clean, newscast |
| The Debate | Skeptic vs optimist | Punchy, opinionated |
| The Deep Dive | Expert + curious learner | Educational |
| The Watercooler | Two colleagues chatting | Casual |
Override per session: "Make it a Debate style".
TTS providers
| Provider | Notes |
|---|---|
openai |
Default recommendation. Requires OPENAI_API_KEY. |
elevenlabs |
Most natural voices. Requires ELEVENLABS_API_KEY. |
inworld |
Character-focused. Requires INWORLD_API_KEY. |
system |
macOS say. Free, robotic. Requires ffmpeg (or sox) to produce MP3: brew install ffmpeg. |
How it works
- Article fetch:
@mozilla/readability+jsdom— strips nav/ads/footers and returns clean text. - Script generation: the MCP prompt tells Claude Code's own LLM to read the queue, load the show style, and write the dialogue script inline. No separate Claude API key needed.
- TTS: per-turn synthesis (one API call per dialogue turn, so each host gets its own voice), then concatenated into a single MP3.
- Transcript: plain-text copy saved to
.distill-data/transcripts/for debugging. - Notification: macOS
afplayplays a short system sound when the MP3 is ready.
Architecture & costs
Distill is a local, stdio-based MCP server. Everything runs on the user's machine. There is no hosted backend.
┌───────────────┐ stdio ┌───────────────┐ HTTPS ┌──────────┐
│ Claude Code │◀──────────▶│ distill MCP │────────────▶│ TTS API │
│ (your LLM) │ │ (node process)│ │ (yours) │
└───────────────┘ └───────────────┘ └──────────┘
│ │
│ ▼
│ .distill / .distill-data/
▼
writes script,
calls tools
Who pays for what:
| Cost | Paid by |
|---|---|
| LLM that writes the dialogue | The user — it runs inside their Claude Code session, on their Claude subscription / API billing |
| TTS audio generation | The user — they put their own OPENAI_API_KEY / ELEVENLABS_API_KEY / INWORLD_API_KEY in .distill |
| Hosting | Nobody — stdio MCP servers don't need a server |
This means anyone forking or installing Distill just clones the repo and points Claude Code at it. No account, no signup, nothing to deploy.
Scope
- Claude Code only — not a standalone CLI
- English only
- macOS assumed for system sound
- No JS-rendered or paywalled article support
- No scheduling — generate on demand
Troubleshooting
/mcpshows distill as failed — check the absolute path toindex.jsin your MCP config and thatnpm installran in the distill dir.- "Could not extract readable content" — the page is probably paywalled or JS-rendered. No retry in v1.
- TTS fails with "missing API key" — uncomment exactly one
TTS_PROVIDER=line in.distilland make sure its matching*_API_KEYis set. systemTTS fails with "requires ffmpeg or sox" —brew install ffmpeg.