yt-transcript-mcp
YouTube transcripts as token-efficient AI context. One fetch, cached forever.
Agent-first: returns structured JSON by default. Zero dependencies on yt-dlp, ffmpeg, or API keys.
Works with Claude Desktop, ChatGPT Desktop, Cursor, Windsurf, and any MCP client.
Why?
When AI browses YouTube for a transcript, it processes the entire page: navigation, ads, recommendations, scripts. That's 75,000-150,000 tokens of noise to extract maybe 6,000 tokens of actual content.
This tool fetches only the transcript.
| Tokens | Speed | Repeat queries | |
|---|---|---|---|
| AI browses YouTube | 75-150k | 20-90s | Same cost every time |
| ytfetch-mcp | 6-12k | 1-3s | Instant (cached) |
~50 KB per video in cache. A year of daily use stays under 120 MB.
Demo
You say:
Fetch the transcript from https://www.youtube.com/watch?v=dQw4w9WgXcQ
Default response (compact JSON, segments only):
{
"is_error": false,
"video_id": "dQw4w9WgXcQ",
"title": "Never Gonna Give You Up",
"channel": "Rick Astley",
"published": "2009-10-25",
"language": "en",
"caption_type": "manual",
"segment_count": 56,
"transcript_duration_seconds": 213.5,
"content_hash": "a1b2c3...",
"cache_hit": false,
"warnings": [],
"segments": [
{"text": "We're no strangers to love", "start": 18.0, "end": 21.4},
{"text": "You know the rules and so do I", "start": 21.4, "end": 24.8}
]
}
Structured, machine-readable, one transcript representation. No HTML, no noise, no wasted tokens.
Install
One line. No git clone needed.
Claude Desktop
Add to ~/Library/Application Support/Claude/claude_desktop_config.json:
{
"mcpServers": {
"yt-transcript": {
"command": "uvx",
"args": ["ytfetch-mcp"]
}
}
}
Restart Claude Desktop. Done.
ChatGPT Desktop
Same config in your Codex MCP settings, or add manually:
| Field | Value |
|---|---|
| Command | uvx (or full path: ~/.local/bin/uvx) |
| Arguments | ytfetch-mcp |
Tip: Find your uvx path with
which uvx. Restart the app after config changes.
Cursor / Windsurf / VS Code
Paste the same JSON block into your MCP server config.
Requires
uv (includes uvx): curl -LsSf https://astral.sh/uv/install.sh | sh
Parameters
| Parameter | Description | Default |
|---|---|---|
url |
YouTube URL (required, any format) | |
languages |
Language codes in priority order | sv,en |
output |
segments, text, or both |
segments |
format |
json or markdown |
json |
include_timestamps |
true for [HH:MM:SS] per line in text output |
false |
title |
Override title | |
channel |
Override channel | |
published |
Override date (YYYY-MM-DD) | |
bypass_cache |
true to force fresh fetch |
false |
Output modes
output |
What you get |
|---|---|
segments (default) |
Array of {text, start, end} for structured consumption |
text |
Single readable string (clean or timestamped) |
both |
Both representations |
Markdown format (format=markdown) always renders readable text regardless of output mode.
Error handling
Every error returns a structured response with a machine-readable code and a retryable flag so agents can branch automatically:
{
"is_error": true,
"error_code": "VIDEO_UNAVAILABLE",
"error_message": "Video is unavailable, private, or removed.",
"retryable": false,
"retry_count": 0
}
| Error code | Meaning | Retryable |
|---|---|---|
INVALID_URL |
Not a YouTube URL or malformed video ID | No |
TRANSCRIPT_NOT_AVAILABLE |
Transcripts disabled for this video | No |
LANGUAGE_NOT_AVAILABLE |
No transcript in requested languages | No |
VIDEO_UNAVAILABLE |
Video unavailable, private, age-restricted, or unplayable | No |
YOUTUBE_IP_BLOCKED |
YouTube is blocking your IP | No |
PO_TOKEN_REQUIRED |
Video requires Proof-of-Origin token | No |
RATE_LIMITED |
YouTube rate limit (429) | Yes |
Provenance
Every response includes provenance so you know exactly where the data comes from:
caption_type:manual,auto-generated, orunknownmetadata_sources: per-field tracking ({"title": "oembed", "published": "pytubefix"})content_hash: SHA256 of the segments array for reproducibilitywarnings:AUTO_GENERATED(speech recognition, may contain errors),LANGUAGE_FALLBACK(got a different language than requested),METADATA_FETCH_FAILED(some metadata unavailable)
Cache
Transcripts cached locally in ~/.cache/yt-transcript/. Keyed by video ID + language preference. Second fetch: instant, zero network.
- Cache entries validated on load (version, types, segments, metadata)
- Legacy or corrupted entries silently skipped
- Cache write failures never block transcript delivery
CLI
Also works standalone, no MCP client needed:
uvx ytfetch-mcp # starts the MCP server
uv run yt_transcript.py https://youtu.be/ABC123 # CLI mode, saves .md file
CLI flags: --date, --title, --channel, --lang, --out, --no-clean, --no-cache.
Roadmap
- Summary mode -- condensed output for lower token cost
- Token budget (
max_tokens) -- fit any context window - Batch URLs -- multiple videos in one call
- Chapter/topic filtering -- return only relevant sections
- Remote HTTP transport -- expose as streamable HTTP MCP server
- Schema.org metadata -- replace pytubefix for publish date
- MCP outputSchema / structured content
Support
If this saves you time or tokens:
MIT \u00a9 Bj\u00f6rn Walther
Release files for ytfetch-mcp 1.2.0
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | |
|---|---|---|---|
| ytfetch_mcp-1.2.0.tar.gz | 21.2 kB | Details |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Reset |
|---|---|---|---|---|
| ytfetch_mcp-1.2.0-py3-none-any.whl | Python 3 | none | any | Details |
Total release size: 35.8 kB
Release files / ytfetch_mcp-1.2.0.tar.gz
| Download URL | ytfetch_mcp-1.2.0.tar.gz |
|---|---|
| Size | 21.2 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
659d703c9db68a59a1e98103c65d4d1356dbefae084ddde7d88956bd7d195331
|
|
BLAKE2b-256 checksum How to use checksums |
49b28b581bf86156419526e8791ec687572115b77acbbb9fcd59817f324993c5
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency logRelease files / ytfetch_mcp-1.2.0-py3-none-any.whl
| Download URL | ytfetch_mcp-1.2.0-py3-none-any.whl |
|---|---|
| Size | 14.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
5cdbacd7c7c22be91431df5298c766a0047f9ed7bedaed9fdfdb78b6cdd0964b
|
|
BLAKE2b-256 checksum How to use checksums |
93176931f08c2933a08aaab6313c3090ce82bc84198e50ac97364c0b18911292
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is trusted publishing? |
Yes |
| Uploaded via |
twine/7.0.0 CPython/3.13.14
|
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
PyPI Publish Attestation
PyPI verified that this artifact, at this checksum, originated from the publisher listed below.
Signed by GitHub Actions, verified by PyPI on Sep 5, 2026.
Transparency log