Skip to main content
Glama
README.md
# yt-transcript-mcp

**🇩🇪 Deutsch** · [🇬🇧 English](#english)

Ein MCP-Server, über den Claude (Claude Code, Claude Desktop oder claude.ai) die
Transkripte von YouTube-Videos holen kann. Du gibst Claude einen Link, er holt das
Transkript und fasst das Video für dich zusammen.

- **Untertitel zuerst:** Gibt es Untertitel (manuell oder automatisch), werden sie
  mit [yt-dlp](https://github.com/yt-dlp/yt-dlp) geladen und in Fließtext umgewandelt.
- **Sonst Whisper:** Fehlen Untertitel, wird nur die Tonspur geladen und mit der
  OpenAI-Transkriptions-API (Standard: `gpt-4o-transcribe`) in Text umgewandelt.
  Lange Videos werden automatisch in Teile unter 25 MB zerlegt.
- **Speichern:** Jedes Transkript kann als Markdown mit Frontmatter in einem
  Ordner landen, z. B. in einem Obsidian-Vault.

## Aufbau

Alle Zugänge nutzen denselben Kern (`core/transcript.mjs`):

| Teil | Datei | Zweck |
|---|---|---|
| MCP-Server (stdio) | `mcp/server.mjs` | Lokal für Claude Code / Claude Desktop |
| HTTP-Endpoint | `http/app.mjs` | `POST /transcript`, z. B. für einen iPhone-Kurzbefehl über einen Tunnel |
| CLI | `cli.mjs` | Zum Testen im Terminal |
| Cloud-MCP-Server (optional) | `cloud/` | Remote-Connector für claude.ai mit Google-Login (Fly.io + Cloudflare R2) |
| Mac-Worker (optional) | `worker/poll.mjs` | Arbeitet die Aufträge aus der Cloud lokal ab |

**Warum lokal?** YouTube blockiert Anfragen von Cloud-/Rechenzentrums-IPs oft.
Deshalb läuft das eigentliche Laden immer auf deinem eigenen Rechner (Heim-IP).
Der optionale Cloud-Server liest nur fertige Transkripte aus R2 und legt für
fehlende Videos einen Auftrag in eine Warteschlange, die der Mac-Worker abarbeitet.

## Voraussetzungen

- Node.js 20 oder neuer (getestet mit 24)
- Python 3 mit `yt-dlp` und `curl_cffi` (für Browser-Impersonation):
  ```bash
  python3 -m pip install -U yt-dlp curl_cffi
  ```
- `ffmpeg` und `ffprobe` (z. B. `brew install ffmpeg`)
- Optional: ein OpenAI-API-Key für den Whisper-Fallback

## Installation

```bash
git clone https://github.com/gmn9t7pf7b-bit/yt-transcript-mcp.git
cd yt-transcript-mcp
npm install
cp .env.example .env   # dann ausfüllen
```

Wichtige Variablen in `.env`:

| Variable | Bedeutung |
|---|---|
| `OPENAI_API_KEY` | Nur für Videos ohne Untertitel nötig |
| `VAULT_DIR` | Zielordner für die `.md`-Dateien (leer = nicht speichern) |
| `TRANSCRIPT_SUBDIR` | Unterordner, Standard `Transkripte/YouTube` |
| `SUB_LANGS` | Sprachpriorität, Standard `de,en` (Originalsprache kommt immer zuerst) |
| `MCP_AUTH_TOKEN` | Bearer-Token für die HTTP-Endpoints. Ohne Token wird jede Anfrage abgelehnt |

## Nutzung

### Claude Code

```bash
claude mcp add yt-transcript --scope user -- node "$(pwd)/mcp/server.mjs"
```

Danach einfach schreiben: *„Fass mir dieses Video zusammen: https://youtu.be/…“*

### Claude Desktop

```bash
./install-desktop.sh
```

Das Skript beendet Claude Desktop, legt ein Backup der Konfiguration an, trägt den
Server ein und startet die App neu. Alternativ in
`~/Library/Application Support/Claude/claude_desktop_config.json` selbst eintragen:

```json
{ "mcpServers": { "yt-transcript": { "command": "node", "args": ["/pfad/zu/yt-transcript-mcp/mcp/server.mjs"] } } }
```

### CLI

```bash
node cli.mjs "https://www.youtube.com/watch?v=VIDEO_ID"            # speichert in VAULT_DIR
node cli.mjs "https://www.youtube.com/watch?v=VIDEO_ID" --no-save
```

### HTTP-Endpoint (z. B. für Kurzbefehle)

```bash
npm run http     # lauscht nur auf 127.0.0.1:3010
npm run tunnel   # optional: Cloudflare Quick Tunnel davor
```

```
POST /transcript
Authorization: Bearer <MCP_AUTH_TOKEN>
Content-Type: application/json

{ "url": "https://youtu.be/VIDEO_ID", "langs": ["de", "en"], "save": true }
```

## MCP-Tools

**`get_youtube_transcript`** (lokal)
- `url`: YouTube-Link oder 11-stellige Video-ID
- `langs`: Sprachpriorität, z. B. `["de","en"]`
- `save`: im Zielordner speichern (Standard `true`)

**Cloud-Server** zusätzlich:
- `get_youtube_transcript` mit `offset` / `maxChars` zum seitenweisen Lesen langer Transkripte
- `search_youtube_transcripts(query?, limit?)`: gespeicherte Transkripte nach Titel/Kanal durchsuchen

## Optional: Cloud-Connector für claude.ai

Damit funktioniert das auch vom Handy aus, solange der Mac irgendwann online ist.

1. **Cloudflare R2:** Bucket anlegen und einen API-Token nur mit Lese-/Schreibrecht
   auf diesen Bucket erzeugen.
2. **Google-OAuth-Client** (Typ „Webanwendung“) mit der Redirect-URI
   `https://<deine-domain>/oauth/google/callback`.
3. `.env.cloud.example` nach `.env.cloud` kopieren, ausfüllen und als Fly-Secrets setzen:
   ```bash
   fly launch --no-deploy
   fly volumes create yt_transcript_data --size 1
   fly secrets import < .env.cloud
   fly deploy
   ```
4. **Cloudflare davor schalten** (Proxy aktiv). Der Server lehnt alle Anfragen ab,
   die nicht über Cloudflare-IPs kommen (`cloud/cloudflareGuard.mjs`); nur `/health`
   ist ausgenommen.
5. **Mac-Worker starten:** `npm run worker`, am besten als launchd-Agent.
6. **In Claude verbinden:** *Einstellungen → Connectors → Eigener Connector* mit
   `https://<deine-domain>/mcp`. Claude registriert sich per Dynamic Client
   Registration und leitet zum Google-Login weiter. Nur `ALLOWED_EMAIL` kommt durch.

## Sicherheit

- Echte Zugangsdaten gehören nur in `.env` / `.env.cloud` (beide in `.gitignore`).
- Video-IDs werden strikt geprüft (`^[\w-]{11}$`), bevor sie in Dateinamen,
  R2-Schlüssel oder yt-dlp-Aufrufe gelangen. yt-dlp und ffmpeg werden ohne Shell gestartet.
- Lokale HTTP-Endpoints lauschen nur auf `127.0.0.1`, verlangen immer ein Bearer-Token
  und vergleichen es in konstanter Zeit.
- Cloud-Server: Login nur für eine Google-Adresse, kurzlebige JWTs (1 h, HS256),
  rotierende Refresh-Tokens (90 Tage Leerlauf-Ablauf), Client-Registrierung nur mit
  Redirect-URIs auf `claude.ai` / `claude.com`.
- Bekannte Einschränkung: Refresh-Tokens liegen im Klartext auf dem Fly-Volume.

## Wartung

YouTube ändert sich oft. Bei Fehlern zuerst yt-dlp aktualisieren:

```bash
python3 -m pip install -U yt-dlp curl_cffi
```

## Hinweis

Bitte beachte die Nutzungsbedingungen von YouTube und die Urheberrechte der
Inhalte.

## Rechte

© 2026 gmn9t7pf7b-bit. **Alle Rechte vorbehalten.** Das Repository ist öffentlich,
um das Projekt zu zeigen. Es ist keine Open-Source-Software: Kopieren, Verändern,
Weitergeben oder Einsetzen nur mit vorheriger Erlaubnis (siehe [LICENSE](LICENSE)).

---

<a id="english"></a>

# yt-transcript-mcp (English)

[🇩🇪 Deutsch](#yt-transcript-mcp) · **🇬🇧 English**

An MCP server that lets Claude (Claude Code, Claude Desktop or claude.ai) fetch
YouTube transcripts. Give Claude a link, it fetches the transcript and summarizes
the video for you.

- **Subtitles first:** If subtitles exist (manual or auto-generated), they are
  downloaded with [yt-dlp](https://github.com/yt-dlp/yt-dlp) and turned into plain prose.
- **Whisper fallback:** Without subtitles, only the audio track is downloaded and
  transcribed with the OpenAI transcription API (default: `gpt-4o-transcribe`).
  Long videos are split into chunks below 25 MB automatically.
- **Saving:** Every transcript can be saved as Markdown with frontmatter, e.g.
  into an Obsidian vault.

## Architecture

All entry points share one core (`core/transcript.mjs`):

| Part | File | Purpose |
|---|---|---|
| MCP server (stdio) | `mcp/server.mjs` | Local use with Claude Code / Claude Desktop |
| HTTP endpoint | `http/app.mjs` | `POST /transcript`, e.g. for an iOS Shortcut via a tunnel |
| CLI | `cli.mjs` | Testing from the terminal |
| Cloud MCP server (optional) | `cloud/` | Remote connector for claude.ai with Google login (Fly.io + Cloudflare R2) |
| Mac worker (optional) | `worker/poll.mjs` | Processes cloud requests locally |

**Why local?** YouTube often blocks requests from cloud/datacenter IPs, so the
actual fetching always runs on your own machine (residential IP). The optional
cloud server only reads finished transcripts from R2 and queues jobs for missing
videos, which the Mac worker picks up.

## Requirements

- Node.js 20+ (tested with 24)
- Python 3 with `yt-dlp` and `curl_cffi`: `python3 -m pip install -U yt-dlp curl_cffi`
- `ffmpeg` and `ffprobe`
- Optional: an OpenAI API key for the Whisper fallback

## Setup

```bash
git clone https://github.com/gmn9t7pf7b-bit/yt-transcript-mcp.git
cd yt-transcript-mcp
npm install
cp .env.example .env   # then fill it in
```

Key variables: `OPENAI_API_KEY` (only needed for videos without subtitles),
`VAULT_DIR` (target folder, empty = don't save), `TRANSCRIPT_SUBDIR`,
`SUB_LANGS` (default `de,en`), `MCP_AUTH_TOKEN` (required for the HTTP endpoints;
without it every request is rejected).

## Usage

- **Claude Code:** `claude mcp add yt-transcript --scope user -- node "$(pwd)/mcp/server.mjs"`,
  then ask *"Summarize this video: https://youtu.be/…"*
- **Claude Desktop:** run `./install-desktop.sh`, or add the server to
  `claude_desktop_config.json` manually (see the German section above).
- **CLI:** `node cli.mjs "<youtube-url>" [--no-save]`
- **HTTP:** `npm run http` (binds to `127.0.0.1:3010`), then
  `POST /transcript` with `Authorization: Bearer <MCP_AUTH_TOKEN>` and
  `{ "url": "…", "langs": ["en"], "save": true }`.

## MCP tools

- `get_youtube_transcript(url, langs?, save?)` (local)
- Cloud server: `get_youtube_transcript(url, offset?, maxChars?)` for paged reading,
  and `search_youtube_transcripts(query?, limit?)`.

## Optional: cloud connector for claude.ai

1. Create a Cloudflare R2 bucket and an API token scoped to that bucket.
2. Create a Google OAuth client (web application) with redirect URI
   `https://<your-domain>/oauth/google/callback`.
3. Copy `.env.cloud.example` to `.env.cloud`, fill it in, then
   `fly launch --no-deploy`, `fly volumes create yt_transcript_data --size 1`,
   `fly secrets import < .env.cloud`, `fly deploy`.
4. Put Cloudflare in front (proxied). The server rejects requests that don't come
   from Cloudflare IPs, except `/health`.
5. Run the Mac worker: `npm run worker` (ideally as a launchd agent).
6. In Claude: *Settings → Connectors → Custom connector* with `https://<your-domain>/mcp`.
   Claude registers itself via Dynamic Client Registration; only `ALLOWED_EMAIL`
   can log in with Google.

## Security

- Real credentials belong only in `.env` / `.env.cloud` (both git-ignored).
- Video IDs are strictly validated before reaching file names, R2 keys or yt-dlp;
  child processes are spawned without a shell.
- Local HTTP endpoints bind to `127.0.0.1`, always require a bearer token and
  compare it in constant time.
- Cloud server: single-account Google login, short-lived JWTs (1 h, HS256),
  rotating refresh tokens (90-day idle expiry), client registration limited to
  `claude.ai` / `claude.com` redirect URIs.
- Known limitation: refresh tokens are stored in plain text on the Fly volume.

## Disclaimer

Please respect YouTube's Terms of Service and the copyright of the content.


## Rights

© 2026 gmn9t7pf7b-bit. **All rights reserved.** This repository is public to
showcase the project. It is not open source: copying, modifying, distributing or
using it requires prior permission (see [LICENSE](LICENSE)).