Skip to main content
Glama
teromknen

youtube-transcript-mcp

by teromknen
README.md
# youtube-transcript-mcp

*[Suomeksi / in Finnish: README.fi.md](README.fi.md)*

An MCP server that fetches a YouTube video's transcript, **writes it to disk**, and
returns only the file path, metadata, and the start of the text to the context. Claude
Desktop and Cowork read the actual content separately — in full or in chunks.

Fetching the transcript itself is done by
[@sinco-lab/mcp-youtube-transcript](https://github.com/sinco-lab/mcp-youtube-transcript)
(MIT), used **as a library**. This repo is only a thin tool layer
([src/index.js](src/index.js), ~200 lines) — no fork, no scraper to maintain.

## Why

The straightforward approach (the upstream package as-is) returns the entire transcript
into the conversation. Two consequences: a long video fills the context, and every
follow-up question about the same video fetches it again.

Measured on a 2-hour video:

| | Without this layer | With this layer |
|---|---|---|
| First fetch into context | ~32,000 tokens | **~260 tokens** |
| Same video again | ~32,000 tokens + network fetch | ~260 tokens, **3 ms**, no network |
| Tool definitions loaded every session | ~1,000 tokens (5 tools) | **~480 tokens** (3 tools) |

The transcript doesn't disappear — it's on disk in full and readable any time.

## Tools

| Tool | What it does |
|---|---|
| `fetch_transcript(url, lang?, timed?, refresh?)` | Fetches, stores, and returns the path + metadata + first ~400 chars. Uses the cache |
| `read_transcript(videoId, offset?, limit?)` | Reads the stored transcript, in chunks if needed |
| `get_video_info(url)` | Title and available transcript languages |

`read_transcript` returns content through MCP, not the filesystem — so it also works if
the client (e.g. Cowork) can't read the cache directory directly.

## Install

```
git clone https://github.com/teromknen/youtube-transcript-mcp.git
cd youtube-transcript-mcp
npm install
```

Configuration in `claude_desktop_config.json` (Cowork reads the same file):

```json
"youtube-transcript": {
  "command": "node",
  "args": ["/absolute/path/to/youtube-transcript-mcp/src/index.js"]
}
```

Config file location:
- Windows: `%APPDATA%\Claude\claude_desktop_config.json`
- macOS: `~/Library/Application Support/Claude/claude_desktop_config.json`
- Linux: `~/.config/Claude/claude_desktop_config.json`

## Where transcripts are stored

By default `%LOCALAPPDATA%\youtube-mcp\transcripts\` on Windows, or
`~/.cache/youtube-mcp/transcripts/` on macOS/Linux. Filename is
`<videoId>.<lang>.md` (the timestamped variant gets a `.timed` suffix). Override with
the `TRANSCRIPT_DIR` environment variable:

```json
"youtube-transcript": {
  "command": "node",
  "args": ["/absolute/path/to/youtube-transcript-mcp/src/index.js"],
  "env": { "TRANSCRIPT_DIR": "/path/you/want" }
}
```

The cache is never cleared automatically. Re-fetch a single video with `refresh: true`;
delete the whole directory by hand if it grows too large.

## Testing

```
npm test
```

The smoke test ([test/smoke.js](test/smoke.js)) runs the server over the stdio pipe and
checks the tool list, both fetch paths, the cache hit, chunked reading, UTF-8 surviving
the round trip through disk, and the error path. Pass a different video as an argument:

```
node test/smoke.js https://www.youtube.com/watch?v=<id>
```

## Updating

The engine package is pinned exactly (`--save-exact`), so nothing updates on its own.

```
npm install @sinco-lab/mcp-youtube-transcript@latest --save-exact
npm test
```

**Always run `npm test` after updating.** This layer imports directly from the
package's `dist/youtube.js`, which is not its public interface — upstream could
reorganize `dist` at any point. Pinning means this only breaks on your own update, and
the break is immediate and loud (an import error at startup), not silent — the smoke
test catches it before you restart Claude Desktop.

**When an update is actually needed.** The engine relies on YouTube's undocumented
internal API. When YouTube changes something, fetching breaks for **every** video at
once (`rate limit`, `could not find transcript data`, `fetch failed`). That's not a
config problem or a single-video issue — check the
[upstream releases](https://github.com/sinco-lab/mcp-youtube-transcript/releases).

Note that upstream maintenance is thin: one maintainer, and a **397-day gap** in its
release history during which the package was broken. If it stays broken for good, the
fallback is to swap the engine for `yt-dlp` — this tool layer only touches
`fetchAndStore()` in [src/index.js](src/index.js), so the swap is local.

## Limitations

- Only works on videos that **have** a transcript — it doesn't transcribe audio.
- Auto-generated transcripts have no punctuation and no speaker separation. Fine for a
  summary, not for exact quotes.
- The first fetch still costs the full price if you ask for a summary right away — the
  file only helps starting from the second question about the same video.
- The cache isn't cleaned up automatically. It grows without bound; delete it by hand
  if needed.
- YouTube's terms of service prohibit downloading content without permission; fetching
  transcripts for personal use sits in a gray area.

## License

[MIT](LICENSE)