Skip to main content
Glama
Achanandhi-M

youtube-watch-mcp

by Achanandhi-M
README.md
# youtube-watch-mcp

An [MCP](https://modelcontextprotocol.io) server that tracks your favorite YouTube creators and tells you when they've posted something new — right from Claude.

```
You:    Add MKBHD to my YouTube watchlist
Claude: Now watching "Marques Brownlee" (UCBJycsmduvYEL83R_U4JriQ).
        Latest upload so far: "The Best Car I've Ever Driven: McLaren W1"
        — future checks will only report videos newer than this.

You:    Did any of my channels post something new?
Claude: Checked 3 channel(s), found 1 new video total.
        Marques Brownlee: 1 new video
        - "..." (2026-07-05) https://www.youtube.com/watch?v=...
```

No YouTube API key, no Google Cloud project, no quotas — it reads each channel's public RSS feed.

## Features

- `add_channel` — start tracking a creator by channel ID, `@handle`, or channel URL
- `remove_channel` — stop tracking one
- `list_channels` — see everything you're watching
- `check_new_videos` — check one channel, or all of them, for uploads since the last check
- `get_latest_videos` — browse any channel's recent uploads without touching tracking state
- A `watchlist://channels` **resource** exposing your current watchlist as JSON

## How it works internally

```
┌─────────────┐   stdio (JSON-RPC)   ┌────────────────────┐   HTTPS   ┌──────────────────────┐
│ Claude       │ ───────────────────▶ │ youtube-watch-mcp  │ ────────▶ │ YouTube (public)     │
│ (MCP client) │ ◀─────────────────── │ (this server)      │ ◀──────── │ RSS + channel pages  │
└─────────────┘                      └────────────────────┘           └──────────────────────┘
                                              │
                                              ▼
                                     data/watchlist.json
                                     (local, persisted state)
```

**Transport.** The server communicates with its client (Claude Desktop, Claude Code, etc.) over **stdio** — the client spawns `node dist/index.js` as a subprocess and exchanges JSON-RPC messages over stdin/stdout. This is the standard local-server transport in MCP; nothing is exposed over the network.

**Tools vs. resources.** Each capability above (`add_channel`, `check_new_videos`, ...) is registered as an MCP **tool** — a function with a typed input schema (validated with [zod](https://zod.dev)) that the model can decide to call based on your request. `watchlist://channels` is registered as an MCP **resource** instead — a read-only piece of data a client can pull in as context without "calling" anything.

**Resolving a channel with no API key** (`src/youtube.ts`). When you pass a `@handle` or a channel URL, the server fetches that page's plain HTML and extracts the channel's real ID from it. This turned out to be less trivial than it sounds: a channel page's HTML contains dozens of `"channelId":"UC..."` strings for *unrelated* channels (recommended/related-channel shelves), so grabbing the first match resolves to the wrong creator. Instead, the server reads the page's `<link rel="canonical">` tag and `"externalId"` field — both of which specifically identify the page's own owner. If you pass a raw channel ID (`UC...`) directly, none of this scraping happens.

**Fetching uploads** (`src/youtube.ts`). Every YouTube channel exposes a public Atom feed at:

```
https://www.youtube.com/feeds/videos.xml?channel_id=UC...
```

No auth, no quota — but it only returns the ~15 most recent uploads. The server parses this XML with [fast-xml-parser](https://github.com/NaturalIntelligence/fast-xml-parser) into a simple `{ channelTitle, videos[] }` shape, where `videos` is ordered newest-first.

**Tracking "new since last time"** (`src/index.ts`, `src/storage.ts`). For each watched channel, the server persists the ID of the most recent video it has seen. `check_new_videos` walks the freshly-fetched feed from newest to oldest and collects every video until it hits that last-seen ID (or runs out of feed, if the channel posted more than ~15 videos since the last check). It then updates the stored ID to the current newest video. `add_channel` seeds this "last seen" value immediately with the channel's current latest upload, so adding a channel never immediately reports its entire back-catalog as "new."

**Storage** (`src/storage.ts`). The watchlist lives in `data/watchlist.json`, next to the compiled server — a flat JSON array of `{ channelId, nickname, addedAt, lastVideoId, lastCheckedAt }`. No database; it's just read, mutated, and rewritten on every change.

## Setup

### Prerequisites

- [Node.js](https://nodejs.org) 18+

### Install & build

```bash
git clone https://github.com/Achanandhi-M/youtube-watch-mcp.git
cd youtube-watch-mcp
npm install
npm run build
```

This compiles TypeScript from `src/` into `dist/`.

### Connect it to Claude Code

```bash
claude mcp add youtube-watch -- node /absolute/path/to/youtube-watch-mcp/dist/index.js
```

Restart/reconnect Claude Code and the tools listed above become available.

### Connect it to Claude Desktop

Add this to your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS) under `mcpServers`, then restart the app:

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

## Usage examples

- "Add MKBHD and Veritasium to my YouTube watchlist"
- "Check if any of my watched creators posted something new"
- "Show me the last 5 videos from @fireship without adding it to my watchlist"
- "Stop tracking Veritasium"

## Limitations

- YouTube's RSS feed only returns the ~15 most recent uploads per channel. If a channel you're tracking goes unchecked for long enough to publish more than that, older uploads in between won't be reported as "new" — check regularly rather than sporadically.
- Channel resolution from `@handle`/URL scrapes public HTML rather than using an official API, so it could break if YouTube changes its page markup. Using a direct channel ID (`UC...`) with `add_channel` avoids this entirely.
- No notifications/push — this is a pull-based tool. Nothing checks in the background; a check only happens when you (via Claude) ask for one.

## Project structure

```
src/
  index.ts     MCP server: tool + resource definitions
  youtube.ts   Channel resolution + RSS feed fetching/parsing
  storage.ts   JSON-file watchlist persistence
data/
  watchlist.json   Your tracked channels (gitignored, created on first run)
```

## License

MIT — see [LICENSE](LICENSE).

TDQS

A3.9/5.0

Scored across 5 tools

Disambiguation5/5

Each tool targets a distinct action: adding, checking, listing, and removing channels. No overlap in functionality.

Naming Consistency5/5

All tools follow snake_case verb_noun pattern (e.g., add_channel, check_new_videos), perfectly consistent.

Tool Count5/5

5 tools is appropriate for a YouTube watch tracking server, covering core operations without excess.

Completeness4/5

Covers CRUD for channels with add/list/remove, plus two video retrieval methods. Minor gap: no tool explicitly resets or updates watch status, but check_new_videos handles new video detection.

Maintenance

ActivityInactive
ResponsivenessNo issues