Skip to main content
Glama
tuitamogamer-gpt

youtube-mcp-server

README.md
# youtube-mcp-server

A local **stdio MCP server** that gives Claude (or any MCP client) full programmatic control over a single YouTube channel. Built with the [MCP SDK](https://github.com/modelcontextprotocol/typescript-sdk), [googleapis](https://github.com/googleapis/google-api-nodejs-client), and strict TypeScript/ESM.

---

## What It Does

The server exposes ~46 tools grouped across eight domains:

### Channel (4 tools)

| Tool | Description |
|------|-------------|
| `youtube_get_my_channel` | Fetch full details (stats, branding, playlists) for the authenticated channel |
| `youtube_get_channel` | Fetch public details for any channel by ID, @handle, or username |
| `youtube_update_channel_branding` | Update description, keywords, country, language, or trailer video |
| `youtube_list_channel_sections` | List all sections on the channel home page |

### Videos (8 tools)

| Tool | Description |
|------|-------------|
| `youtube_get_video` | Fetch full metadata for any video by ID |
| `youtube_list_my_videos` | List videos uploaded to the authenticated channel (paginated) |
| `youtube_upload_video` | Upload a local video file to the channel |
| `youtube_update_video` | Update title, description, tags, category, or privacy for a video |
| `youtube_delete_video` | Permanently delete a video (requires `confirm: true`) |
| `youtube_set_video_thumbnail` | Upload a local image file as the custom thumbnail |
| `youtube_rate_video` | Like, dislike, or remove a rating on any video |
| `youtube_get_video_categories` | List available video category IDs for a given region |

### Playlists (6 tools)

| Tool | Description |
|------|-------------|
| `youtube_list_my_playlists` | List playlists owned by the authenticated channel |
| `youtube_get_playlist` | Fetch details for any playlist by ID |
| `youtube_create_playlist` | Create a new playlist with title, description, and privacy |
| `youtube_update_playlist` | Update title, description, or privacy of a playlist |
| `youtube_delete_playlist` | Permanently delete a playlist (requires `confirm: true`) |
| `youtube_list_playlist_items` | List items (videos) inside any playlist (paginated) |
| `youtube_add_playlist_item` | Add a video to a playlist |
| `youtube_remove_playlist_item` | Remove an item from a playlist (requires `confirm: true`) |

### Comments (6 tools)

| Tool | Description |
|------|-------------|
| `youtube_list_comment_threads` | List top-level comment threads on a video or channel |
| `youtube_get_comment_thread` | Fetch a single comment thread with replies |
| `youtube_add_comment` | Post a new top-level comment on a video |
| `youtube_reply_to_comment` | Post a reply to an existing comment thread |
| `youtube_update_comment` | Edit the text of a comment you own |
| `youtube_delete_comment` | Delete a comment (requires `confirm: true`) |
| `youtube_set_moderation_status` | Set a comment's moderation status (hold/publish/reject) |

### Captions (5 tools)

| Tool | Description |
|------|-------------|
| `youtube_list_captions` | List caption tracks for a video |
| `youtube_download_caption` | Download the raw caption track content |
| `youtube_upload_caption` | Upload a new caption file to a video |
| `youtube_update_caption` | Replace an existing caption track |
| `youtube_delete_caption` | Delete a caption track (requires `confirm: true`) |

### Search (2 tools)

| Tool | Description |
|------|-------------|
| `youtube_search` | Full YouTube search with filters (type, channel, order, date, region) |
| `youtube_search_my_videos` | Search within only the authenticated channel's videos |

### Subscriptions (3 tools)

| Tool | Description |
|------|-------------|
| `youtube_list_subscriptions` | List channels the authenticated account is subscribed to |
| `youtube_subscribe` | Subscribe to a channel by channel ID |
| `youtube_unsubscribe` | Unsubscribe from a channel (requires `confirm: true`) |

### Analytics (6 tools)

| Tool | Description |
|------|-------------|
| `youtube_get_channel_analytics` | Core channel metrics (views, watchTime, subscribers) over a date range |
| `youtube_get_video_analytics` | Per-video metrics over a date range |
| `youtube_get_traffic_sources` | Traffic source breakdown (search, suggested, external, etc.) |
| `youtube_get_audience_demographics` | Age/gender audience breakdown |
| `youtube_get_top_videos` | Top N videos by a chosen metric (views, watchTimeMins, etc.) |
| `youtube_get_revenue_analytics` | Revenue and ad metrics (requires monetized channel) |

---

## Prerequisites

- **Node.js >= 18** (ESM + `fetch` builtins required)
- A Google account with a YouTube channel
- A Google Cloud project (see setup below)

---

## Credentials & Configuration

This server authenticates to Google **as you** via OAuth 2.0. Nothing is hard-coded — you supply credentials at runtime, and **none of them are ever committed** (all are listed in `.gitignore`). You need **three secret values**, all obtained during the Google OAuth setup below:

| Value | What it is | Where it comes from |
|-------|-----------|---------------------|
| `client_id` | OAuth client identifier (ends in `…apps.googleusercontent.com`) | `credentials.json`, downloaded from Google Cloud Console |
| `client_secret` | OAuth client secret | `credentials.json` |
| `refresh_token` | Long-lived token authorizing access to *your* channel | produced by `npm run auth` (the browser consent flow) |

You provide these in **one** of two ways:

**Option A — file-based (recommended).** Drop the downloaded `credentials.json` into the project root and run `npm run auth`. That writes `token.json` (containing all three values) and the server picks it up automatically. Nothing else to configure.

**Option B — environment variables.** Instead of `token.json`, set the three values directly (handy for CI, Docker, or a secrets manager):

```bash
YT_CLIENT_ID=your-client-id.apps.googleusercontent.com
YT_CLIENT_SECRET=your-client-secret
YT_REFRESH_TOKEN=your-refresh-token
```

See [`.env.example`](.env.example) for a fully-commented template covering both options. **Never commit `credentials.json`, `token.json`, or a real `.env`** — anyone holding these can fully control your YouTube channel.

---

## Google OAuth Setup

### 1. Create or Select a Google Cloud Project

1. Go to [console.cloud.google.com](https://console.cloud.google.com).
2. Click the project selector (top-left) → **New Project**.
3. Name it (e.g. `my-youtube-app`), click **Create**.
4. Make sure the new project is selected in the dropdown before continuing.

### 2. Enable the Required APIs

1. In the left menu: **APIs & Services → Library**.
2. Search for and **Enable** each of the following:
   - **YouTube Data API v3** (required)
   - **YouTube Analytics API** (required)
   - **YouTube Reporting API** (optional — bulk reporting jobs)

### 3. Configure the OAuth Consent Screen

1. Go to **APIs & Services → OAuth consent screen**.
2. Select **External** as the User Type, click **Create**.
3. Fill in the required fields:
   - **App name** (e.g. `My YouTube Tool`)
   - **User support email** (your own Google account)
   - **Developer contact email** (your own Google account)
4. Click **Save and Continue** through the Scopes step (you will add scopes in code, not here — or add them here if prompted).
5. On the **Test users** step: click **+ Add Users** and add **your own Google account email**. Click **Save and Continue**.
6. Submit/finish the wizard.

> **Important — 7-day token expiry warning:**
> While the app's publishing status is **Testing**, Google issues refresh tokens that expire after **7 days**, regardless of any other setting. When your token expires, your app will get an `invalid_grant` error and you must re-authenticate.
>
> To avoid this disruption:
> - **Option A (recommended for personal use):** Keep the app in Testing but make sure your own account is listed as a Test User — re-auth when needed, or click **Publish App** to move to Production.
> - **Option B:** Click **Publish App** → your app moves to "In Production" and refresh tokens no longer have the 7-day limit (tokens only expire if unused for 6+ months or manually revoked).
>
> For a personal channel tool you own, publishing is safe and removes the friction.

### 4. Create OAuth Client Credentials (Desktop App)

1. Go to **APIs & Services → Credentials**.
2. Click **+ Create Credentials → OAuth client ID**.
3. Application type: **Desktop app**.
4. Name it (e.g. `youtube-desktop-client`), click **Create**.
5. Click **Download JSON** on the confirmation dialog (or find it in the credentials list and click the download icon).
6. Save the file as **`credentials.json`** in your project root.
7. **Keep this file private** — never commit it to version control.

### 5. Scopes Requested

The server requests all six of these scopes:

```
https://www.googleapis.com/auth/youtube
https://www.googleapis.com/auth/youtube.force-ssl
https://www.googleapis.com/auth/youtube.upload
https://www.googleapis.com/auth/youtubepartner
https://www.googleapis.com/auth/yt-analytics.readonly
https://www.googleapis.com/auth/yt-analytics-monetary.readonly
```

These cover full channel management, uploads, partner operations, and read access to both standard and monetary analytics.

### 6. Quota Limits

| Resource | Cost |
|---|---|
| Default daily quota | **10,000 units/day** |
| `search.list` | **100 units** per call |
| `videos.insert` (upload) | **~1,600 units** per call |
| Most read operations | 1–5 units |

Monitor usage at **APIs & Services → Quotas & System Limits**. You can request a quota increase via the console if needed.

### 7. Local Auth Flow with `@google-cloud/local-auth`

The `npm run auth` command uses `@google-cloud/local-auth` to open a browser consent flow and writes `token.json` to the project root.

```js
import { authenticate } from '@google-cloud/local-auth';
import path from 'path';

const SCOPES = [
  'https://www.googleapis.com/auth/youtube',
  'https://www.googleapis.com/auth/youtube.force-ssl',
  'https://www.googleapis.com/auth/youtube.upload',
  'https://www.googleapis.com/auth/youtubepartner',
  'https://www.googleapis.com/auth/yt-analytics.readonly',
  'https://www.googleapis.com/auth/yt-analytics-monetary.readonly',
];

const client = await authenticate({
  scopes: SCOPES,
  keyfilePath: path.join(process.cwd(), 'credentials.json'),
});
// client.credentials includes access_token AND refresh_token
```

**How the flow works:**

1. `authenticate()` starts a local loopback HTTP server on `localhost` (a random port).
2. It opens the Google consent URL in the user's default browser.
3. After the user grants consent, Google redirects to `http://localhost:<port>` with an auth code.
4. The library exchanges the code for tokens and returns an `OAuth2Client`.
5. `client.credentials.refresh_token` is present on the **first** authorization — persist it (e.g. `token.json`) and reuse it to avoid re-prompting on subsequent runs.

```js
import fs from 'fs';
const TOKEN_PATH = path.join(process.cwd(), 'token.json');

// After first auth, save credentials:
fs.writeFileSync(TOKEN_PATH, JSON.stringify(client.credentials));

// On subsequent runs, load and restore:
import { google } from 'googleapis';
const savedCreds = JSON.parse(fs.readFileSync(TOKEN_PATH, 'utf8'));
const oauth2Client = new google.auth.OAuth2();
oauth2Client.setCredentials(savedCreds);
// Pass oauth2Client to google.youtube({ version: 'v3', auth: oauth2Client })
```

**Sources:**
- [OAuth 2.0 for Mobile & Desktop Apps — YouTube Data API](https://developers.google.com/youtube/v3/guides/auth/installed-apps)
- [Obtaining Authorization Credentials — YouTube Data API](https://developers.google.com/youtube/registering_an_application)
- [Using OAuth 2.0 to Access Google APIs](https://developers.google.com/identity/protocols/oauth2)
- [OAuth 2.0 Policies (token expiry)](https://developers.google.com/identity/protocols/oauth2/policies)
- [OAuth 2.0 for Native Apps](https://developers.google.com/identity/protocols/oauth2/native-app)

---

## Install and Build

```bash
# Install dependencies
npm install

# Compile TypeScript to dist/
npm run build
```

---

## Authorize

Run the one-time OAuth flow to generate `token.json`:

```bash
npm run auth
```

This opens a browser window. Sign in with the Google account that owns your YouTube channel, grant all requested scopes, and the token is saved automatically.

> **Security note:** `credentials.json` and `token.json` are listed in `.gitignore` and must never be committed or shared. Anyone with these files can fully manage your YouTube channel.

---

## Connect to Claude Code

### Option A — `claude mcp add` command

```bash
claude mcp add youtube-mcp-server \
  --transport stdio \
  -- node "/ABSOLUTE/PATH/TO/youtube-mcp-server/dist/index.js"
```

If you want to pass the token path explicitly via an environment variable:

```bash
claude mcp add youtube-mcp-server \
  --transport stdio \
  --env YT_TOKEN_PATH="/ABSOLUTE/PATH/TO/youtube-mcp-server/token.json" \
  -- node "/ABSOLUTE/PATH/TO/youtube-mcp-server/dist/index.js"
```

### Option B — JSON config block

Add the following entry to your Claude Code MCP config file (`.claude/settings.json` or the global equivalent):

```json
{
  "mcpServers": {
    "youtube-mcp-server": {
      "type": "stdio",
      "command": "node",
      "args": ["/ABSOLUTE/PATH/TO/youtube-mcp-server/dist/index.js"],
      "env": {
        "YT_TOKEN_PATH": "/ABSOLUTE/PATH/TO/youtube-mcp-server/token.json"
      }
    }
  }
}
```

### Option C — Environment-variable auth (no token.json)

Instead of `token.json`, you can supply credentials directly via environment variables — useful for CI or shared environments:

```bash
export YT_CLIENT_ID="your-client-id"
export YT_CLIENT_SECRET="your-client-secret"
export YT_REFRESH_TOKEN="your-refresh-token"
node dist/index.js
```

Copy `.env.example` to `.env` and fill in the values if you use a `.env`-loading approach.

---

## Quota and Safety Notes

- **Daily quota:** 10,000 units by default. A single `youtube_search` call costs 100 units; a video upload costs ~1,600 units. Read operations cost 1–5 units each.
- **Destructive tools** (`youtube_delete_video`, `youtube_delete_playlist`, `youtube_delete_comment`, `youtube_delete_caption`, `youtube_remove_playlist_item`, `youtube_unsubscribe`) all require `confirm: true` in the call. This prevents accidental data loss when the model mis-fires.
- **Revenue analytics** (`youtube_get_revenue_analytics`) requires a monetized channel enrolled in the YouTube Partner Program.
- **Token expiry:** If you leave the OAuth app in "Testing" status, refresh tokens expire after 7 days. Either add yourself as a Test User and re-run `npm run auth` when needed, or publish the app to Production to remove the limit.

---

## Testing

### Interactive inspector (recommended for exploring tools)

```bash
npx @modelcontextprotocol/inspector node dist/index.js
```

Open the URL printed in the terminal to browse and call tools interactively.

### Smoke test (CI-friendly)

```bash
npm run build
node scripts/smoke.mjs
```

Expected output:

```
TOOLS:46
```

Exit code 0 means at least one tool is registered; exit code 1 means the server failed to respond or registered no tools.

TDQS

A4.3/5.0

Scored across 46 tools

Disambiguation4/5

Most tools have clear distinct purposes, but there is some overlap among analytics tools (e.g., youtube_channel_summary, youtube_top_videos, youtube_video_performance, etc.) that share similar metrics and could cause confusion. However, detailed descriptions help differentiate them.

Naming Consistency5/5

All tool names follow a consistent 'youtube_verb_noun' pattern (e.g., youtube_create_playlist, youtube_delete_video, youtube_list_playlists). No mixing of conventions or unclear verbs, making it easy for an agent to predict tool names.

Tool Count4/5

With 46 tools, the set is extensive but each tool serves a specific purpose within YouTube's API. While some tools could be consolidated (e.g., separate caption upload/download/update/list), the count is reasonable for a full-featured server covering videos, playlists, comments, analytics, and subscriptions.

Completeness4/5

The tool surface covers CRUD operations for videos, playlists, comments, captions, subscriptions, and channel branding, plus analytics. Minor gaps include no tool for live stream management or detailed subscriber list (though subscriptions.list is present). Overall, core workflows are well-supported.

Maintenance

ActivityInactive
ResponsivenessNo issues