youtube-transcript-mcp
Provides tools to fetch YouTube video transcripts from any YouTube URL, list available caption languages, retrieve video metadata, and parse YouTube URLs, with support for formats like text, markdown, SRT, and VTT.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@youtube-transcript-mcpget the transcript of https://youtu.be/dQw4w9WgXcQ"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
youtube-transcript-mcp
An MCP server that returns the transcript of a YouTube video from any YouTube URL — no API key, no browser, no OAuth. It knows that a meaningful share of videos have no transcript at all, and says so precisely instead of failing with a vague error or returning empty text.
Any reference shape. watch URLs,
youtu.be, Shorts, live, embeds, music, nocookie, mobile, legacy/v/, attribution links, oEmbed, URL-encoded links, Markdown links, prose around a link, or a bare 11-character id.Honest failure.
NO_TRANSCRIPT,TRANSCRIPT_GENERATING,PRIVATE_VIDEO,AGE_RESTRICTED,MEMBERS_ONLY,LIVE_ENDED,BOT_CHECK,RATE_LIMITED,LANGUAGE_UNAVAILABLE… each with a hint and, where useful, the list of languages that do exist.Resilient fetching. Several InnerTube clients, the watch page, the transcript panel endpoint and an optional local
yt-dlpfallback, tried in order, because a single endpoint gets you empty bodies or 429s depending on the IP you come from.Language aware. Preference lists, manual-vs-auto-generated awareness, and optional machine translation into a language the video does not offer.
Install
git clone https://github.com/0xCaFeBEef/free-youtube-transcript-mcp.git youtube-transcript-mcp
cd youtube-transcript-mcp
pnpm install # or: npm install
pnpm buildRequires Node.js 20+ (uses global fetch).
Wire it into an MCP client
Claude Desktop, DSH, Cursor, or anything else that speaks MCP over stdio:
{
"mcpServers": {
"youtube-transcript": {
"command": "node",
"args": ["/absolute/path/to/youtube-transcript-mcp/dist/index.js"],
"env": { "YTA_YTDLP": "auto" }
}
}
}Or keep the source checked out and let the client run it directly with tsx:
{
"mcpServers": {
"youtube-transcript": {
"command": "npx",
"args": ["tsx", "/absolute/path/to/youtube-transcript-mcp/src/index.ts"]
}
}
}Try it from a terminal first
node dist/index.js --probe "https://youtu.be/dQw4w9WgXcQ"
node dist/index.js --probe "<url>" --lang hi --format markdown --timestamps
node dist/index.js --list-languages "<url>"
node dist/index.js --info "<url>"
node dist/index.js --helpRelated MCP server: YouTube Transcript MCP
Tools
Tool | What it does |
| The transcript. |
| Every caption track: language, display name, whether it is auto-generated, plus YouTube's translation targets. |
| Title, author, duration, and whether captions exist at all. Cheap pre-flight for batch work. |
| Offline parse: video id, playlist id, start offset, how it was recognised. Costs no request. |
output is one of text (default), markdown (timestamped bullets with deep links),
segments (JSON with millisecond timings), srt or vtt.
URLs that work
All of these resolve to the same video:
https://www.youtube.com/watch?v=VIDEO_ID
https://www.youtube.com/watch?v=VIDEO_ID&list=RDVIDEO_ID&index=3&t=42s&si=abc123
https://www.youtube.com/watch?feature=share&v=VIDEO_ID
https://www.youtube.com/watch/VIDEO_ID
https://www.youtube.com/v/VIDEO_ID /vi/VIDEO_ID /e/VIDEO_ID
https://www.youtube.com/embed/VIDEO_ID (also youtube-nocookie.com)
https://www.youtube.com/shorts/VIDEO_ID
https://www.youtube.com/live/VIDEO_ID
https://youtu.be/VIDEO_ID?t=1m30s
https://m.youtube.com/watch?v=VIDEO_ID
https://music.youtube.com/watch?v=VIDEO_ID
https://www.youtube.co.uk/watch?v=VIDEO_ID
https://www.youtube.com/attribution_link?a=xyz&v=VIDEO_ID
https://www.youtube.com/oembed?url=https%3A%2F%2Fwww.youtube.com%2Fwatch%3Fv%3DVIDEO_ID
https://www.youtube.com/watch%3Fv%3DVIDEO_ID (double-encoded)
VIDEO_ID (bare id)
<https://youtu.be/VIDEO_ID> [title](https://youtu.be/VIDEO_ID) here: https://youtu.be/VIDEO_ID!Timestamps in t, start, start_s or #t= (accepting 42, 42s, 1m30s,
1h2m3s, 01:30, 1:02:03) are parsed and reported, but a transcript is always for
the whole video — a start offset does not trim it.
Playlists, channels, @handles, search pages, feeds, community posts and /clip/
links are not one video, so they are rejected with an explanation rather than a
guess. A list= parameter travelling with a video URL is noted and ignored.
When there is no transcript
This is a normal outcome, not an error condition to retry.
Code | Meaning | Retry? |
| No captions exist: none uploaded, none generated. Or only auto-captions exist and you asked not to include them. | no |
| YouTube has queued auto-captions but not produced them yet (common hours after upload). | later |
| Bad id, deleted, or never public. | no |
| Requires the owner's or a member's session. | with cookies |
| Blocked for this session or region. | with cookies / proxy |
| Recording gone, or a scheduled premiere. 24/7 streams carry no captions. | no |
| Captions exist, but not in the language you asked for. | different language |
| YouTube is throttling this IP (HTTP 403/429). Back off; cookies help. | yes, later |
| Every fetch path was refused. | yes |
Failures come back as isError: true with a JSON body:
{
"ok": false,
"error": "None of the requested languages (xx) is available for this video.",
"code": "LANGUAGE_UNAVAILABLE",
"retryable": false,
"hint": "Call youtube_list_languages to see what exists, or request one of those.",
"details": {
"requested": ["xx"],
"available": [
{ "language": "en", "name": "English", "generated": false },
{ "language": "en", "name": "English (auto-generated)", "generated": true }
]
}
}Languages
languages is a priority list: ["hi", "en"] prefers Hindi and falls back to English,
noting the substitution in notes. Matching is exact tag first, then base language
(en matches en-US), and human-written tracks always beat auto-generated ones at
the same distance. includeAutoCaptions: false refuses speech-recognised tracks
outright — useful when wording accuracy matters.
translateTo asks YouTube to machine-translate. It works even when the video has no
track in the target language: the tool picks the original-language track and
translates that, and says so in notes.
Configuration
All optional, all environment variables.
Variable | Default | Purpose |
|
| Interface language and region sent to YouTube; affects track display names. |
| – | Raw |
| – | Netscape cookie file (the format browser extensions and yt-dlp emit); parsed for HTTP and passed to yt-dlp. |
|
| Timeout per HTTP request. |
|
| Retries for transport-level failures, with backoff. |
|
| Transcript cap per call; truncation is reported, not hidden. |
|
| In-memory transcript cache lifetime. |
|
|
|
|
| Path to the binary. |
|
| Override the provider order. |
| desktop Chrome UA | Sent on every request. |
How it fetches, and why it sometimes cannot
InnerTube
playerAPI (Android, then iOS, MWEB, TV-embedded, web). The mobile clients still return ordinary caption URLs.The watch page, whose
ytInitialPlayerResponsealso lists tracks — but those URLs carry aexp=xpo,xpeproof-of-origin experiment flag that makes/api/timedtextanswer with an empty body. The flag is stripped before use.The
get_transcriptpanel endpoint, using the pre-encoded params the watch page embeds.Local
yt-dlp, if installed, as the most refusal-resistant path.
The first provider that lists tracks wins; if the download then fails, the providers
that were skipped are consulted for another copy of the same track. Payloads are
requested as json3, then srv3, then vtt, and parsed by sniffing when the answer
arrives in a different format than asked.
Practical consequences worth knowing:
Datacenter and shared IPs get throttled. Empty 200-body caption responses and HTTP 429 are both anti-abuse responses, not bugs in the video. The tool backs off instead of retrying harder, and reports
RATE_LIMITED/BOT_CHECK.Cookies unlock access-limited videos (
YTA_COOKIES/YTA_COOKIES_FILE).Auto-generated captions roll: each cue can repeat the previous line. Repeats are folded before you see them.
Music videos often carry placeholder captions (
[♪♪♪]); those are real cues, kept.
Development
pnpm test # 40+ unit tests: URL parsing, wire formats, selection, rendering
pnpm test:live # opt-in tests that hit YouTube (RUN_LIVE=1)
pnpm typecheckNotes
Unofficial API. Scraping transcripts can conflict with YouTube's Terms of Service; use it considerately, cache responses, and do not hammer the endpoints. Captions are the uploader's content — respect whatever licence applies to the video.
This server cannot be deployed
Maintenance
Related MCP Connectors
Fetch the full transcript of any YouTube video as clean text. No API key, no signup.
Fetch transcripts, subtitles, chapters, metadata and frames from YouTube and 10+ video platforms
Free YouTube transcripts, no API key: videos, channel lists, latest uploads, bulk download links.
YouTube transcripts, search, channel/playlist listings and upload tracking for AI agents.
Related MCP Servers
- AlicenseAqualityDmaintenanceRetrieves transcripts from YouTube videos with support for multiple languages, timestamp control, and language detection. Enables video content analysis, summarization, and quote extraction without manually downloading or watching videos.264 npm15MIT
- AlicenseNot gradedqualityCmaintenanceEnables AI models to extract transcripts from YouTube videos in multiple languages with zero local setup. It supports all YouTube URL formats and features smart caching via Cloudflare Workers for fast responses.47 npm1MIT
- AlicenseAqualityDmaintenanceEnables fetching, searching, and summarizing YouTube video transcripts with multi-language support.4MIT
- FlicenseAqualityCmaintenanceEnables fetching YouTube video transcripts with metadata, including timed captions in multiple formats (JSON, SRT, VTT, CSV, TXT) and preprocessing options.41-