YouTube downloader for AI assistants (MCP) and humans (TUI). Subtitles + search + persistent history. Minimal CLI core; TUI and MCP are optional extras.
Project description
yoink
MCP server that lets AI assistants download YouTube videos.
Give Claude, Cursor, or any MCP-compatible AI the ability to download YouTube videos, playlists, and audio through natural language. Also ships with a terminal UI for humans.
pip install yoink-yt · MCP Setup · Terminal UI · API Reference
What is yoink?
yoink is an MCP (Model Context Protocol) server that gives AI assistants like Claude Desktop, Cursor, Windsurf, and other MCP-compatible tools the ability to download YouTube videos on your behalf.
It also ships with a standalone terminal UI (TUI) for when you want to download videos yourself.
The problem
Every YouTube downloader makes you do the work — find the URL, open a tool, pick a format, wait for it, rename the file, repeat.
The yoink way
Just tell your AI assistant what you want:
"Download the latest 3Blue1Brown video in 720p"
"Grab that playlist as audio-only MP3s"
"Download this lecture with subtitles to my Desktop"
Your AI handles the entire workflow — resolves the URL, picks the format, starts the download, tracks progress. No copy-pasting. No menus. Just ask.
⚡ Quick Start
Pick your install — yoink ships a tiny CLI core, with TUI and MCP as optional extras:
pip install yoink-yt # plain CLI only (just yt-dlp)
pip install 'yoink-yt[tui]' # + Terminal UI (textual)
pip install 'yoink-yt[mcp]' # + MCP server for AI assistants
pip install 'yoink-yt[all]' # everything
[!IMPORTANT] Upgrading from 0.1.x? As of 0.3.0 the TUI and MCP server are optional extras and download history is persisted to
~/.yoink/state.db. Ifyoinkoryoink-mcperrors after upgrading, reinstall with the extra you need:pip install --upgrade 'yoink-yt[all]'.
|
For AI assistants (MCP server) pip install 'yoink-yt[mcp]'
yoink-mcp
Add to Claude Desktop, Cursor, or any MCP client — your AI gets 7 YouTube tools instantly. |
For humans (terminal UI) pip install 'yoink-yt[tui]'
yoink
A full terminal UI with progress bars, quality picker, playlist support, and keyboard shortcuts. |
Just want to grab a video from your shell? No extras needed:
pip install yoink-yt
yoink-cli "https://youtu.be/..." -q 720
[!TIP] Install ffmpeg for best results — needed to merge video+audio streams and convert to MP3.
brew install ffmpeg # macOS sudo apt install ffmpeg # Ubuntu/Debian
🤖 MCP Setup for AI Assistants
yoink exposes 7 tools via the Model Context Protocol over STDIO, giving any MCP-compatible AI assistant full YouTube download capabilities.
Claude Desktop
Add to your claude_desktop_config.json:
{
"mcpServers": {
"yoink": {
"command": "yoink-mcp"
}
}
}
Restart Claude Desktop. Done — Claude can now download YouTube videos.
Claude Code
Add to your .mcp.json:
{
"mcpServers": {
"yoink": {
"command": "yoink-mcp",
"type": "stdio"
}
}
}
Cursor / Windsurf / Other MCP Clients
Any MCP client that supports STDIO transport works. Point it at yoink-mcp as the command.
Using uv (from source instead of pip)
{
"mcpServers": {
"yoink": {
"command": "uv",
"args": ["--directory", "/path/to/yoink", "run", "yoink-mcp"]
}
}
}
Example prompts for your AI
| What you say | What yoink does |
|---|---|
| "Find me the latest 3Blue1Brown video and summarize it" | search_youtube → get_subtitles → AI summarizes from transcript |
| "Summarize this video: [url]" | get_subtitles → AI works from the transcript text directly |
| "Download this video: [url]" | Downloads in best quality to ~/Downloads |
| "Get the 720p version of [url]" | Fetches formats, picks 720p, downloads |
| "Download this playlist as audio" | Gets playlist info, downloads each as audio |
| "What formats are available for [url]?" | Lists all quality options with file sizes |
| "Show me what I downloaded last week" | list_downloads reads persisted history |
| "Cancel the download" | Stops an in-progress download |
| "What's the progress?" | Shows status of all active downloads |
| "Download [url] and let me know when it's done" | download_and_wait streams progress until finished |
| "Convert [url] to MP3" | Downloads audio and converts to MP3 |
🔌 MCP Tools API Reference
These are the 10 tools your AI assistant gets access to when yoink is configured as an MCP server:
| Tool | Description | Key Parameters |
|---|---|---|
search_youtube |
NEW in 0.3.0. Search YouTube and return matching videos. Lets the AI find URLs without leaving the conversation. | query, limit |
get_subtitles |
NEW in 0.3.0. Fetch a video's transcript as plain text (manual subs preferred, auto-captions fallback). The single biggest enabler for "summarize this video" / Q&A workflows. | url, lang |
get_video_info |
Fetch video metadata (title, duration, uploader, available formats) | url |
get_playlist_info |
List all videos in a YouTube playlist | url |
get_formats |
List available download qualities with file sizes | url |
start_download |
Start downloading a video, returns a tracking ID. Now supports idempotency_key for safe retry. |
url, format_string, output_dir, idempotency_key |
download_and_wait |
NEW in 0.3.0. Download a video and stream progress notifications until it finishes. Best when the AI needs the file before the next step. | url, format_string, output_dir |
list_downloads |
List recent downloads (active + persisted history). Survives MCP server restarts. | limit |
get_download_progress |
Check status of a specific download by ID | download_id |
cancel_download |
Cancel an active download | download_id |
[!NOTE] Idempotency & dedup. Pass
idempotency_keytostart_downloadto make retries safe across restarts — a second call with the same key returns the existingdownload_id. Active-URL dedup is also built in: a duplicate URL while one is in flight returnserror_code: duplicate_active.
[!NOTE] Structured errors. Every error response includes an
error_codefield (bot_check,rate_limited,age_restricted,ffmpeg_missing, ...) so the AI can branch on the failure mode instead of regex-matching messages.
[!NOTE] Persistent history. Download state is mirrored to
~/.yoink/state.db(SQLite) solist_downloadsreturns history across MCP server restarts. Delete the file to reset.
🎬 Terminal UI for Humans
For when you want to download videos yourself. A full-featured TUI powered by Textual.
yoink # default: 3 concurrent downloads
yoink -j 5 # up to 10
|
Keyboard shortcuts
|
Features
|
🛠 Architecture
src/yoink/
├── __main__.py # python -m yoink → CLI
├── cli.py # Plain argparse CLI (no optional deps)
├── core/ # Shared engine (used by CLI, MCP, TUI)
│ ├── models.py # Stdlib dataclasses — no pydantic
│ ├── errors.py # ErrorCode enum + classify_error pattern matcher
│ ├── extractor.py # YouTube metadata, search, subtitle extraction
│ ├── engine.py # Single download executor with progress hooks
│ ├── state.py # SQLite-backed download history (~/.yoink/state.db)
│ └── manager.py # Concurrent orchestration + dedup + idempotency
├── mcp_server/ # [mcp] extra — MCP interface for AI assistants
│ └── server.py # FastMCP server with 10 tools over STDIO
└── tui/ # [tui] extra — Terminal UI for humans
├── app.py # Lazy-loading entry point
├── _impl.py # Textual application (only imported when [tui] installed)
├── screens/ # Main screen, format picker modal
├── widgets/ # URL bar, video panel, playlist panel, download queue
└── styles/ # Textual CSS styling
The CLI, MCP server, and TUI are thin wrappers around the same core engine. The core handles all yt-dlp interaction, threading, progress tracking, and error handling — and depends only on yt-dlp plus the Python stdlib.
Design decisions
- Threading model: yt-dlp is synchronous, so each download runs in its own thread via
ThreadPoolExecutor. A FIFO dispatcher thread ensures downloads start in submission order. - Progress reporting: Hooks are rate-limited to 100ms intervals to avoid callback floods in both MCP and TUI contexts.
- Playlist optimization: Uses
extract_flatto avoid fetching full metadata for large playlists upfront. - Error handling: Raw yt-dlp errors are pattern-matched against 15 common cases and translated to user-friendly messages.
- Duplicate detection: The download manager tracks active URLs and rejects duplicates at the engine level, with a
forcebypass for retries. - Cancellation: Uses
threading.Eventchecked in every progress hook callback for responsive cancellation. - Minimal core: Data contracts are stdlib
@dataclass— no pydantic, no third-party validation library. The CLI install pulls onlyyt-dlp. - Lazy extras: Importing
yoink.tui.apporyoink.mcp_server.serversucceeds even without the[tui]/[mcp]extras; missing extras surface a friendly install hint when the entry point runs, not at import time. - Durable state:
~/.yoink/state.dbis a single SQLite file with WAL mode; reads concurrent, writes serialized via lock. Multiple yoink processes share the same history. - Subtitle parsing: Pure-stdlib WebVTT parser strips cue headers and YouTube's inline word-level
<00:00:01.000>timing tags, deduplicates consecutive identical lines (auto-captions overlap heavily). - Search:
yt-dlpytsearchN:queryextractor withextract_flat=True— no per-result fetch.
🔧 Development
git clone https://github.com/JayshKhan/yoink.git
cd yoink
pip install -e ".[dev]" # all extras + pytest
pytest # ~130 tests
yoink-cli --help # plain CLI
yoink # TUI
yoink-mcp # MCP server
Frequently Asked Questions
What AI assistants work with yoink?
Any AI assistant that supports the Model Context Protocol (MCP) over STDIO transport. This includes Claude Desktop, Claude Code, Cursor, Windsurf, and any custom MCP client.
Does it only work with YouTube?
yoink uses yt-dlp under the hood, which supports thousands of sites. However, yoink is optimized and tested for YouTube. Other sites may work but are not officially supported.
Can I use the MCP server and TUI at the same time?
They are separate processes with separate download managers, so yes. They won't share state or conflict with each other.
Where do downloads go?
By default, ~/Downloads. The MCP start_download tool accepts an output_dir parameter, and the TUI has an editable path below the URL bar.
What if ffmpeg is not installed?
You can still download videos, but some quality options that require merging separate video and audio streams won't work. MP3 conversion also requires ffmpeg.
📄 License
MIT — do whatever you want with it. Yoink responsibly.
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file yoink_yt-0.3.0.tar.gz.
File metadata
- Download URL: yoink_yt-0.3.0.tar.gz
- Upload date:
- Size: 471.9 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
2417601e41e8151083a921ddfe619bb50c383b83395a682f886cee8b38d795e6
|
|
| MD5 |
2360a2ba974f79f4fa12d39b5ca441a5
|
|
| BLAKE2b-256 |
75ba1e8b91c189f75f4ebe5e628dbc72d29b0bcff7183b11e5e22c6da68548aa
|
Provenance
The following attestation bundles were made for yoink_yt-0.3.0.tar.gz:
Publisher:
publish.yml on JayshKhan/yoink
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yoink_yt-0.3.0.tar.gz -
Subject digest:
2417601e41e8151083a921ddfe619bb50c383b83395a682f886cee8b38d795e6 - Sigstore transparency entry: 1399955655
- Sigstore integration time:
-
Permalink:
JayshKhan/yoink@af035a4ed72055ffdd65f8cf4e0023322f3db65f -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/JayshKhan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@af035a4ed72055ffdd65f8cf4e0023322f3db65f -
Trigger Event:
push
-
Statement type:
File details
Details for the file yoink_yt-0.3.0-py3-none-any.whl.
File metadata
- Download URL: yoink_yt-0.3.0-py3-none-any.whl
- Upload date:
- Size: 416.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
29502a6c15bf89521093f437a909907c91fd7fd89829d9fd0b2f020cc995be19
|
|
| MD5 |
cfc44b21d64f00588a3d5d771ea92c60
|
|
| BLAKE2b-256 |
0cbcd8911579b8932a8916ae31504b8744c933dd3d6f4ef8dcead890b7699200
|
Provenance
The following attestation bundles were made for yoink_yt-0.3.0-py3-none-any.whl:
Publisher:
publish.yml on JayshKhan/yoink
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
yoink_yt-0.3.0-py3-none-any.whl -
Subject digest:
29502a6c15bf89521093f437a909907c91fd7fd89829d9fd0b2f020cc995be19 - Sigstore transparency entry: 1399955721
- Sigstore integration time:
-
Permalink:
JayshKhan/yoink@af035a4ed72055ffdd65f8cf4e0023322f3db65f -
Branch / Tag:
refs/tags/v0.3.0 - Owner: https://github.com/JayshKhan
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@af035a4ed72055ffdd65f8cf4e0023322f3db65f -
Trigger Event:
push
-
Statement type: