Skip to main content
Glama
brentvatne

youtube-playlist-mcp

by brentvatne
README.md
# youtube-playlist-mcp

MCP server for analyzing and cleaning up your YouTube playlists — **including Watch Later**, which the official YouTube Data API hasn't been able to read since 2016. Built with [Bun](https://bun.sh) and TypeScript (Bun runs the TypeScript directly; there is no build step).

Instead of the official API (OAuth setup, Google Cloud project, 50-quota-units-per-write limits), this uses InnerTube — the internal API youtube.com itself uses — authenticated with your own browser cookies. No Google Cloud project, no OAuth client, no API quota. The only setup is exporting your cookies, which is automated.

## Tools

All tools operate on the authenticated user's own playlists. `"WL"` works as a `playlistId` everywhere, so Watch Later is a first-class citizen.

### `youtube_list_playlists`
Your playlists with title, video count, and URL.

### `youtube_get_playlist_items`
One page (~100 items) of a playlist via `continuationToken` paging. Each item has:
- `setVideoId` — the playlist-entry ID that removal expects (duplicate-safe)
- duration, availability status (deleted/private flagged)
- fuzzy view count and publish age ("50K views", "2 weeks ago") — enough for trend analysis with no extra calls. Since playlists are ordered and a save can't predate the video's publish date, a running max of publish dates from the bottom of the list up estimates a save-date timeline.

### `youtube_add_playlist_items`
Batch-add videos in a single request. Combined with remove, this moves videos between playlists (add to destination, verify, remove from source).

### `youtube_remove_playlist_items`
Batch-remove entries by `setVideoId`. Destructive; the assistant should confirm first.

### `youtube_create_playlist` / `youtube_update_playlist` / `youtube_delete_playlist`
Create (private by default, optionally seeded with videos), rename / edit description / change privacy, and delete playlists.

### `youtube_refresh_cookies` / `youtube_install_yt_dlp`
Self-healing auth: when cookies go stale, the assistant can re-export them from your browser directly (same as `bun run cookies`, no restart needed — the server picks up the new file on the next call), and install yt-dlp via Homebrew/pipx/pip if it's missing.

## Setup

Clone and register — that's it. No install step, no build, no auth flow:

```bash
git clone https://github.com/brentvatne/youtube-playlist-mcp.git
claude mcp add youtube -s user -- ./youtube-playlist-mcp/src/index.ts
```

Or any MCP client via stdio (use the absolute path):

```json
{
  "mcpServers": {
    "youtube": {
      "command": "/path/to/youtube-playlist-mcp/src/index.ts"
    }
  }
}
```

The only prerequisite is [Bun](https://bun.sh). The entry point is a self-executing script (`#!/usr/bin/env bun`), and Bun auto-installs the two dependencies from its global cache on first launch — no `node_modules` is created, no `bun install` needed. First launch may take a few extra seconds while Bun fetches packages.

**Auth happens on first use, from inside the session.** Ask the assistant to list your playlists: the `youtube_refresh_cookies` tool exports youtube.com cookies from your browser (and `youtube_install_yt_dlp` installs yt-dlp first if needed). You must be logged in to YouTube in that browser. Only youtube.com cookies are kept, saved to `~/.config/youtube-playlist-mcp/cookies.txt` (0600).

> **Expect a macOS password prompt.** Browsers encrypt their cookie stores with a key kept in the macOS Keychain, so the first export triggers a system dialog like *"security wants to use your confidential information stored in 'Chrome Safe Storage' in your keychain"*, asking for your Mac login password. That's the OS asking permission to release the decryption key to yt-dlp — the same mechanism Chrome itself uses — not this project collecting your password (the dialog is drawn by macOS; the password never reaches yt-dlp or this server). Enter your login password or click Allow. The assistant is instructed to warn you before triggering it.

Prefer to do it manually? `bun run cookies` from the repo does the same thing (`bun run cookies safari`, `firefox`, `brave`, `edge`, or `agent-browser` for other sources). Why not AppleScript? Scripting a browser only exposes `document.cookie`, which excludes the HttpOnly cookies (`SID`, `HSID`, `SSID`) that InnerTube auth requires.

## Development

```bash
bun install         # dev dependencies (typescript) — only needed for development
bun run smoke       # read-only test against the real API (requires cookies)
bun run typecheck   # tsc --noEmit
```

## Caveats

- **Cookies rotate.** Google rotates session cookies aggressively — sometimes within hours while the browser is open. Every stale-cookie symptom is detected and reported with a fix hint: 401/403s, private playlists 404ing as if nonexistent, and even 200 responses that YouTube served as logged-out (checked via the `logged_in` response flag, so stale cookies never silently return anonymous data). The assistant can recover on its own via `youtube_refresh_cookies`.
- **Your cookies are account-level credentials**: `cookies.txt` can do anything your YouTube session can. It stays local (0600) and is only ever sent to youtube.com, but treat the file accordingly.
- InnerTube is unofficial — if YouTube changes their response structure, tools return a diagnostic error pointing at where the JSON path broke (see `diagnoseJsonPath` in `src/util.ts`).
- Deleted/private videos hidden by YouTube ("N unavailable videos are hidden") aren't listed and expose no handle to remove.
- Watch history (`HL`) is not supported.
- Batched writes are validated live at 5,000-video scale (batches of 50, ~250 ms apart, zero failures). Keep request rates human-scale anyway.
- Config lives in `~/.config/youtube-playlist-mcp/` (override with `YOUTUBE_PLAYLIST_MCP_CONFIG_DIR`). Not affiliated with YouTube; mind their Terms of Service.