Skip to main content
Glama
nazaryanenko

youtube-music-mcp

by nazaryanenko

YouTube Playlist Generator MCP Server

A Model Context Protocol (MCP) server that enables AI applications to search for YouTube music videos and manage playlists using the official YouTube Data API v3.

Features

  • Search Music Videos: Find music videos on YouTube with customizable search parameters

  • Get Video Details: Retrieve comprehensive information about specific videos

  • Get Playlist Items: List videos from public playlists

  • Playlist Management: Complete playlist management including creating, editing, and managing playlists (requires OAuth setup)

Related MCP server: yt-fetch

Setup

Prerequisites

  • Node.js 18 or higher

  • A Google Cloud Project with YouTube Data API v3 enabled

  • YouTube Data API key

  • OAuth 2.0 Client credentials (optional, required for playlist management features)

Installation

  1. Clone this repository:

git clone https://github.com/nazaryanenko/youtube-music-mcp
  1. Install dependencies:

npm install
  1. Get a YouTube Data API key:

    • Go to the Google Cloud Console

    • Create a new project or select an existing one

    • Enable the YouTube Data API v3

    • Create credentials (API key)

    • Copy the API key

  2. Set up environment variables:

# Create a .env file
echo "YOUTUBE_API_KEY=your_api_key_here" > .env
echo "YOUTUBE_CLIENT_ID=your_client_id_here" >> .env
echo "YOUTUBE_CLIENT_SECRET=your_client_secret_here" >> .env
  1. Build the TypeScript code:

npm run build

Running the Server

For development with auto-rebuild:

npm run dev

For production:

npm start

Using with MCP Clients

This server uses the stdio transport, so it can be used with any MCP client that supports stdio.

Claude Desktop Configuration

Add to your Claude Desktop configuration file:

macOS: ~/Library/Application Support/Claude/claude_desktop_config.json Windows: %APPDATA%\Claude\claude_desktop_config.json

{
  "mcpServers": {
    "youtube-playlist-generator": {
      "command": "node",
      "args": ["/absolute/path/to/yt-playlist-generator/build/index.js"],
      "env": {
        "YOUTUBE_API_KEY": "your_youtube_api_key_here",
        "YOUTUBE_CLIENT_ID": "your_client_id_here",
        "YOUTUBE_CLIENT_SECRET": "your_client_secret_here"
      }
    }
  }
}

Visual Studio Code with GitHub Copilot Configuration

To use this MCP server with GitHub Copilot in Visual Studio Code, you need to configure it in your VS Code settings:

  1. Open VS Code Settings: Press Ctrl+, (Windows/Linux) or Cmd+, (macOS)

  2. Search for MCP: Type "mcp" in the search bar

  3. Add MCP Server Configuration: Add the following to your VS Code settings JSON:

{
  "github.copilot.chat.mcp.servers": {
    "youtube-playlist-generator": {
      "command": "node",
      "args": ["/absolute/path/to/yt-playlist-generator/build/index.js"],
      "env": {
        "YOUTUBE_API_KEY": "your_youtube_api_key_here",
        "YOUTUBE_CLIENT_ID": "your_client_id_here",
        "YOUTUBE_CLIENT_SECRET": "your_client_secret_here"
      }
    }
  }
}

Alternative: Use Settings UI

  1. Go to File > Preferences > Settings (or use Ctrl+,)

  2. Search for "GitHub Copilot MCP"

  3. Click "Edit in settings.json" next to "Github › Copilot › Chat: Mcp Servers"

  4. Add the server configuration as shown above

Environment Variables Setup

For security, you can also set environment variables system-wide instead of in the config:

Windows (PowerShell):

[Environment]::SetEnvironmentVariable("YOUTUBE_API_KEY", "your_api_key_here", "User")
[Environment]::SetEnvironmentVariable("YOUTUBE_CLIENT_ID", "your_client_id_here", "User")
[Environment]::SetEnvironmentVariable("YOUTUBE_CLIENT_SECRET", "your_client_secret_here", "User")

Windows (Command Prompt):

setx YOUTUBE_API_KEY "your_api_key_here"
setx YOUTUBE_CLIENT_ID "your_client_id_here"
setx YOUTUBE_CLIENT_SECRET "your_client_secret_here"

macOS/Linux:

export YOUTUBE_API_KEY="your_api_key_here"
export YOUTUBE_CLIENT_ID="your_client_id_here"
export YOUTUBE_CLIENT_SECRET="your_client_secret_here"
# Add to ~/.bashrc, ~/.zshrc, or ~/.profile to persist
echo 'export YOUTUBE_API_KEY="your_api_key_here"' >> ~/.bashrc
echo 'export YOUTUBE_CLIENT_ID="your_client_id_here"' >> ~/.bashrc
echo 'export YOUTUBE_CLIENT_SECRET="your_client_secret_here"' >> ~/.bashrc

Then remove the env section from the VS Code configuration:

{
  "github.copilot.chat.mcp.servers": {
    "youtube-playlist-generator": {
      "command": "node",
      "args": ["/absolute/path/to/yt-playlist-generator/build/index.js"]
    }
  }
}

Usage in VS Code

Once configured, you can use the MCP server through GitHub Copilot Chat:

  1. Open Copilot Chat (Ctrl+Alt+I or click the chat icon)

  2. Use natural language to interact with YouTube:

    • "Search for music videos about jazz"

    • "Create a new playlist called 'My Favorites'"

    • "Add this video to my playlist"

    • "Show me details about this YouTube video"

Available Tools

Read-Only Tools (API Key Authentication)

search_music_videos

Search YouTube for music videos with specified criteria.

Parameters:

  • query (string): Search query for music videos

  • maxResults (number, 1-50, default: 10): Maximum number of results

  • order (enum): Sort order - "relevance", "date", "rating", "viewCount", "title"

get_video_details

Get detailed information about a specific YouTube video.

Parameters:

  • videoId (string): YouTube video ID

get_playlist_items

Retrieve videos from a public YouTube playlist.

Parameters:

  • playlistId (string): YouTube playlist ID

  • maxResults (number, 1-50, default: 25): Maximum number of items

OAuth-Required Tools (Playlist Management)

authenticate_youtube

Authenticate with YouTube OAuth to enable playlist management features.

Parameters:

  • getAuthUrl (boolean, default: false): Set to true to get the authorization URL

  • authCode (string, optional): Authorization code from OAuth flow

get_auth_status

Check current OAuth authentication status.

Parameters: None

create_playlist

Create a new YouTube playlist (requires OAuth authentication).

Parameters:

  • title (string): Playlist title

  • description (string, optional): Playlist description

  • privacy (enum): "private", "public", or "unlisted"

edit_playlist

Edit existing playlist information (requires OAuth authentication).

Parameters:

  • playlistId (string): ID of the playlist to edit

  • title (string, optional): New title for the playlist

  • description (string, optional): New description for the playlist

  • privacy (enum, optional): New privacy setting - "private", "public", or "unlisted"

Note: At least one field (title, description, or privacy) must be provided to update.

add_to_playlist

Add a video to an existing playlist (requires OAuth authentication).

Parameters:

  • playlistId (string): Target playlist ID

  • videoId (string): Video ID to add

list_playlists

List user's playlists (requires OAuth authentication).

Parameters:

  • maxResults (number, 1-50, default: 25): Maximum number of playlists

  • mine (boolean, default: true): List authenticated user's playlists

remove_from_playlist

Remove a video from a playlist (requires OAuth authentication).

Parameters:

  • playlistItemId (string): ID of the playlist item to remove (get from get_playlist_items)

Channel Subscription Tools (OAuth)

All subscription tools work under the existing https://www.googleapis.com/auth/youtube scope — no re-consent required. Every response includes a _quotaCost field; the running session total is also logged to stderr.

subscribe_to_channel

Subscribe the authenticated user to a channel. Idempotent: an already-subscribed channel returns { alreadySubscribed: true, subscriptionId, channelTitle } instead of erroring.

Parameters:

  • channel (string): channelId (UC...), @handle, channel URL, or channel name.

Quota: ~51 units on a new subscribe (50 insert + 1 duplicate lookup), 1–2 units when already subscribed.

unsubscribe_from_channel

Unsubscribe the authenticated user from a channel. If not subscribed, returns { success: false, reason: "not_subscribed" } rather than throwing.

Parameters:

  • subscriptionId (string, optional): preferred — skips the lookup.

  • channelId (string, optional): used when subscriptionId is unknown.

At least one of the two must be supplied.

Quota: 50 units when subscriptionId is passed, ~51 units when only channelId is passed.

list_subscriptions

List the authenticated user's channel subscriptions. When called without pageToken, transparently auto-paginates up to 10 pages (500 subscriptions) and returns the merged list; pass an explicit pageToken to fetch a single page and drive pagination yourself.

Parameters:

  • maxResults (number, 1–50, default: 50): page size.

  • pageToken (string, optional): if supplied, only one page is returned.

  • order (enum): "alphabetical" (default) | "relevance" | "unread".

Quota: 1 unit per page.

get_channel_latest_videos

Get the latest uploads from a channel. Uses the uploads playlist instead of search.list — 1–2 units versus 100.

Parameters:

  • channel (string): channelId (UC...), @handle, or channel name.

  • maxResults (number, 1–50, default: 10).

  • publishedAfter (ISO 8601 string, optional): client-side filter on publish date.

  • excludeShorts (boolean, default: false): filter out videos shorter than 60 seconds. Adds 1 quota unit per 50 fetched.

Quota: ~2 units baseline (channels.list + playlistItems.list), +1 per additional 50 items when excludeShorts is on.

Watch History Tools (Local Takeout Import)

The YouTube Data API stopped exposing watch history in August 2016 — the relatedPlaylists.watchHistory field was removed and the legacy HL playlist ID returns no items. Instead of pretending otherwise, this server imports data from a Google Takeout export into a local SQLite database and answers queries against that.

Getting a Takeout export

  1. Go to takeout.google.com.

  2. Deselect all, then select YouTube and YouTube Music.

  3. Click All YouTube data included → uncheck everything except history.

  4. Click Multiple formats → set history to JSON (default is HTML — the server refuses HTML with a clear error, since HTML parsing would be fragile).

  5. Export. When ready, download the archive and locate Takeout/YouTube and YouTube Music/history/watch-history.json.

The DB file lives at ~/.yt-playlist-mcp-history.db by default (override with YOUTUBE_HISTORY_DB_PATH).

check_history_api_availability

Runtime probe to prove the Data API still lacks watch-history access. Runs three checks and returns their raw evidence — never hard-codes the verdict. If Google ever restores access, apiAvailable: true will bubble up automatically.

Parameters: None.

Returns { apiAvailable, checkedAt, evidence[], conclusion, alternative }. Evidence entries include step, request, statusCode, result.

import_watch_history

Streams the Takeout JSON file (safe for exports >100 MB) into SQLite. Deduplicates by (videoId, watchedAt), so re-importing is idempotent — running it again after a fresh Takeout adds new events without touching existing rows.

Parameters:

  • filePath (string): absolute or ~-expandable path to watch-history.json.

Skips ad-driven watches (details[].name starting with "From Google Ads") and marks deleted/removed videos separately. Returns:

{
  "success": true,
  "imported": 12345,
  "adsExcluded": 87,
  "deletedVideos": 42,
  "skippedDuplicates": 0,
  "dateRange": { "from": "…", "to": "…" },
  "dbPath": "…",
  "filePath": "…"
}

On a repeat import of the same or an overlapping file, skippedDuplicates reports how many events were already in the DB (deduplicated by (videoId, watchedAt)).

Errors — distinct codes so the caller can give the right advice:

error

Meaning

file_not_found

Path echoed back as filePath so you can see how ~ expanded.

html_not_json

Takeout gave HTML instead of JSON — re-export with format = JSON.

unexpected_extension

Not .html, not .json — probably wrong file.

parse_error

Broken JSON. Includes bytePosition when stream-json can locate the failure.

db_locked

SQLite reports SQLITE_BUSY/SQLITE_LOCKED — another process holds the DB.

query_watch_history

Aggregate queries over the imported history.

Parameters:

  • groupBy (enum): "channel" (default) | "month" | "hour_of_day" | "weekday".

  • channel (string, optional): exact-match channel filter.

  • from, to (ISO 8601 strings, optional): inclusive/exclusive date bounds.

  • limit (number, 1–1000, default 30).

  • timezone (string, IANA, default "Europe/Kyiv"): governs hour_of_day / weekday bucketing; date arithmetic honours DST.

Rows marked deleted are excluded from aggregates. Returns { groupBy, timezone, totalEventsMatched, filters, groups[] }.

watch_history_status

Fast probe: does the DB exist, how many events, source split (youtube vs youtube_music), date range, per-file import log. Use this before deciding whether to re-run import_watch_history.

Parameters: None.

Watch-history analytics (pre-aggregated for LLM context)

All five analytics tools return a compact envelope — ≤ 50 KB per response — with heavy trimming when needed. Raw events are only returned by sample_watch_events. Every tool accepts the shared filter set:

  • from / to (ISO 8601): date-range bounds.

  • source: "youtube" | "youtube_music" | "all" (default).

  • timezone (IANA, default "Europe/Kyiv"): governs hour-of-day, weekday, month buckets.

Every response is wrapped as:

{
  "meta": {
    "dbEvents": 12345,
    "dateRange": { "from": "2020-01-...", "to": "2026-07-..." },
    "filtered": 8901,
    "timezone": "Europe/Kyiv",
    "truncated": false
  },
  "data": { /* tool-specific */ }
}

get_watch_overview

One-call snapshot. Returns totals, source split, deleted / ad-excluded counts, month-by-month volume, hour-of-day distribution split into weekday/weekend, and the peak day. Start any exploration here.

get_channel_stats

Per-channel breakdown, grouped by stable channelId — a channel that renamed still lands in one row.

Fields per row: channelId (UC…, null for old Takeouts without a /channel/ URL), keyType ("id" when grouped by UC, "name" fallback for URL-less rows), channel (freshest display name — pulled from the row with MAX(watchedAt)), aliases[] (older display names for the same channelId, if any), channelUrl, watches, uniqueVideos, firstWatch, lastWatch, last90d, share.

sortBy: "watches" (default) surfaces long-term favorites; sortBy: "recent" surfaces channels with the newest activity (fresh obsessions). The client derives "dead interests" (watches ≥ 20 with old lastWatch) locally rather than asking for a separate endpoint.

Migration note: the underlying watch_events.channelId column is added and backfilled from channelUrl the first time the server opens an older DB (log line [history migration] backfilled channelId for N/M rows). Rows whose Takeout URL was an @handle (no UC) stay NULL and fall back to name-based grouping.

get_sessions_analysis

Sessions are contiguous runs of watches separated by gaps under gapMinutes (default 30). Returns:

  • session medians (length in minutes, videos per session)

  • rabbitHoles: top same-channel streaks of ≥ minStreak consecutive videos within one session

  • channelCooccurrence: top 20 channel pairs that show up in the same session, with sessionsTogether ≥ 3 — raw material for topic clustering, which the client performs itself

All aggregation runs inside SQLite via LAG() window functions; the events table is never materialized in JS memory.

get_taste_divergence

Compares watching against a live pull of videos.list?myRating=like (up to 500 videos, cached in-process for 1 hour). Matching is by channelId for both directions — a rename doesn't break the join. Rows without a channelId (old Takeouts) fall back to name matching and are flagged with keyType: "name-fallback" (weaker match — draw softer conclusions from those).

Returns:

  • quietDominants: heavily watched channels you have never liked (habit-consumption signal) — with channelId, keyType, channel, watches, likes: 0.

  • likedButAbandoned: channels with likes but no watch activity in the last ~6 months (drifting-away signal) — with channelId, keyType, channel, likes, lastWatch.

  • likesCachedAt: ISO timestamp of the current in-process cache — subtract from wall time for age.

Requires OAuth. Costs 1 quota unit per 50 liked videos fetched, once per hour.

sample_watch_events

The only tool that returns raw event rows (title, channel, watchedAt, videoId), newest-first. Optional exact-match channel filter. Hard limit: 100 rows — asking for more returns error: "limit_too_high" rather than a silent truncation.

Quota Tracking

Every subscription tool returns _quotaCost — the estimated cost of that single call — and the server logs a running session total to stderr after each API call, e.g.:

[quota] +50 for subscriptions.insert (session total: 61 units)

The default YouTube Data API daily quota is 10 000 units. Structured errors are returned for the following conditions:

Condition

error value

Notes

Daily quota exhausted

quota_exceeded

Resets at midnight Pacific Time.

Channel disabled subscriptions

subscription_forbidden

Already subscribed (on insert)

Handled transparently as alreadySubscribed: true.

OAuth token expired/invalid

auth_expired

Re-run authenticate_youtube.

Channel deleted / not found

not_found

Not subscribed (on delete)

success: false, reason: "not_subscribed"

Not an MCP error.

OAuth Authentication Setup

For full playlist management capabilities, you need to set up OAuth 2.0:

1. Google Cloud Console Setup

  1. Go to Google Cloud Console

  2. Create a new project or select existing one

  3. Enable the YouTube Data API v3

  4. Go to "Credentials" → "Create Credentials" → "OAuth 2.0 Client ID"

  5. Choose "Desktop Application" as application type

  6. Download the credentials or copy the Client ID and Client Secret

2. Environment Configuration

Add your OAuth credentials to the .env file:

YOUTUBE_API_KEY=your_api_key_here
YOUTUBE_CLIENT_ID=your_client_id_here
YOUTUBE_CLIENT_SECRET=your_client_secret_here

3. Authentication Flow

  1. Use the authenticate_youtube tool with getAuthUrl: true

  2. Open the provided URL in your browser

  3. Sign in to your Google account and grant permissions

  4. Copy the authorization code

  5. Use authenticate_youtube tool again with the authCode parameter

4. Using Authenticated Features

Once authenticated, you can:

  • Create new playlists

  • Edit existing playlist information (title, description, privacy)

  • Add videos to your playlists

  • List your private playlists

  • Remove videos from playlists

API Limitations

  • Quota Limits: YouTube API has daily quota limits (10,000 units/day by default)

  • Rate Limiting: API calls are subject to rate limiting

  • OAuth Tokens: Access tokens expire and need refresh (handled automatically)

  • Permissions: OAuth scope determines available operations

Development

Building

npm run build

stdio transport: never write to stdout

The server uses the MCP stdio transport — stdout is reserved for JSON-RPC frames. A single console.log, process.stdout.write, or a logger that defaults to stdout (pino/winston/etc. without explicit stderr config) will corrupt the message framing and the client will silently drop responses, making every tool appear to hang.

When adding new tools:

  • Log to stderr only: console.error(...).

  • If you must use a logger, pin its destination stream to process.stderr.

  • Verify with: node build/index.js 2>/dev/null | head -c 200 — at idle this MUST print nothing.

Development Mode

npm run dev

Project Structure

src/
└── index.ts          # Main server implementation

build/                # Compiled JavaScript output

Contributing

  1. Fork the repository

  2. Create a feature branch

  3. Make your changes

  4. Test thoroughly

  5. Submit a pull request

License

MIT License - see LICENSE file for details

Troubleshooting

Common Issues

  1. "YOUTUBE_API_KEY environment variable is required"

    • Make sure you've set the YOUTUBE_API_KEY environment variable

    • Verify the API key is valid and has YouTube Data API access

  2. "Quota exceeded" errors

    • You've hit the daily API quota limit

    • Wait for quota reset or request quota increase

  3. "Playlist not found" errors

    • Ensure the playlist ID is correct

    • Verify the playlist is public (private playlists require OAuth)

  4. Build errors

    • Make sure you have Node.js 18+ installed

    • Run npm install to ensure all dependencies are installed

    • Check TypeScript compilation with npm run build

Security Considerations

  • Never commit API keys to version control

  • Use environment variables for sensitive configuration

  • Validate all user inputs

  • Implement proper error handling and logging


This project was created using GitHub Copilot

F
license - not found
-
quality - not tested
C
maintenance

Maintenance

Maintainers
Response time
Release cycle
Releases (12mo)
Commit activity

Resources

Unclaimed servers have limited discoverability.

Looking for Admin?

If you are the server author, to access and configure the admin panel.

Related MCP Servers

View all related MCP servers

Related MCP Connectors

  • MCP server for Suno AI music generation, lyrics, and covers

  • YouTube MCP — wraps the YouTube Data API v3 (BYO API key)

  • MCP server for Google Veo AI video generation

View all MCP Connectors

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/nazaryanenko/youtube-music-mcp'

If you have feedback or need assistance with the MCP directory API, please join our Discord server