youtube-music-mcp
Provides tools for searching YouTube music videos, retrieving video details, managing playlists, and handling channel subscriptions using the YouTube Data API v3.
Click on "Install Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@youtube-music-mcpsearch for relaxing piano music"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
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
Clone this repository:
git clone https://github.com/nazaryanenko/youtube-music-mcpInstall dependencies:
npm installGet 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
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" >> .envBuild the TypeScript code:
npm run buildRunning the Server
For development with auto-rebuild:
npm run devFor production:
npm startUsing 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:
Open VS Code Settings: Press
Ctrl+,(Windows/Linux) orCmd+,(macOS)Search for MCP: Type "mcp" in the search bar
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
Go to File > Preferences > Settings (or use
Ctrl+,)Search for "GitHub Copilot MCP"
Click "Edit in settings.json" next to "Github › Copilot › Chat: Mcp Servers"
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"' >> ~/.bashrcThen 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:
Open Copilot Chat (
Ctrl+Alt+Ior click the chat icon)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 videosmaxResults(number, 1-50, default: 10): Maximum number of resultsorder(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 IDmaxResults(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 URLauthCode(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 titledescription(string, optional): Playlist descriptionprivacy(enum): "private", "public", or "unlisted"
edit_playlist
Edit existing playlist information (requires OAuth authentication).
Parameters:
playlistId(string): ID of the playlist to edittitle(string, optional): New title for the playlistdescription(string, optional): New description for the playlistprivacy(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 IDvideoId(string): Video ID to add
list_playlists
List user's playlists (requires OAuth authentication).
Parameters:
maxResults(number, 1-50, default: 25): Maximum number of playlistsmine(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
Go to takeout.google.com.
Deselect all, then select YouTube and YouTube Music.
Click All YouTube data included → uncheck everything except history.
Click Multiple formats → set history to JSON (default is HTML — the server refuses HTML with a clear error, since HTML parsing would be fragile).
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 towatch-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:
| Meaning |
| Path echoed back as |
| Takeout gave HTML instead of JSON — re-export with format = JSON. |
| Not |
| Broken JSON. Includes |
| SQLite reports |
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"): governshour_of_day/weekdaybucketing; 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≥ minStreakconsecutive videos within one sessionchannelCooccurrence: top 20 channel pairs that show up in the same session, withsessionsTogether ≥ 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) — withchannelId,keyType,channel,watches,likes: 0.likedButAbandoned: channels with likes but no watch activity in the last ~6 months (drifting-away signal) — withchannelId,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 |
| Notes |
Daily quota exhausted |
| Resets at midnight Pacific Time. |
Channel disabled subscriptions |
| |
Already subscribed (on insert) | Handled transparently as | |
OAuth token expired/invalid |
| Re-run |
Channel deleted / not found |
| |
Not subscribed (on delete) |
| 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
Go to Google Cloud Console
Create a new project or select existing one
Enable the YouTube Data API v3
Go to "Credentials" → "Create Credentials" → "OAuth 2.0 Client ID"
Choose "Desktop Application" as application type
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_here3. Authentication Flow
Use the
authenticate_youtubetool withgetAuthUrl: trueOpen the provided URL in your browser
Sign in to your Google account and grant permissions
Copy the authorization code
Use
authenticate_youtubetool again with theauthCodeparameter
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 buildstdio 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 devProject Structure
src/
└── index.ts # Main server implementation
build/ # Compiled JavaScript outputContributing
Fork the repository
Create a feature branch
Make your changes
Test thoroughly
Submit a pull request
License
MIT License - see LICENSE file for details
Troubleshooting
Common Issues
"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
"Quota exceeded" errors
You've hit the daily API quota limit
Wait for quota reset or request quota increase
"Playlist not found" errors
Ensure the playlist ID is correct
Verify the playlist is public (private playlists require OAuth)
Build errors
Make sure you have Node.js 18+ installed
Run
npm installto ensure all dependencies are installedCheck 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
This server cannot be installed
Maintenance
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
- Alicense-qualityDmaintenanceAn MCP server that allows AI models to control YouTube Music playback through Google Chrome by searching and playing songs using song and artist names.22MIT
- Flicense-qualityDmaintenanceAn MCP server that enables interaction with the YouTube Data API, allowing users to search videos, get video and channel details, analyze trends, and fetch video transcripts.
- FlicenseBqualityCmaintenanceMCP server for YouTube Data API v3 enabling video search, channel lookup, trending content, comments, and playlist management.11
- AlicenseAqualityDmaintenanceMCP server that provides YouTube video data to AI agents, supporting search, metadata, comments, and transcripts without an API key.512MIT
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
Latest Blog Posts
- Who's Calling? MCP Hosts Are an Identity Blind Spot (And the Spec Knows It)By Om-Shree-0709 on .mcpAgent IdentityOAuth 2.1
- Your AI Chatbot Just Exposed Your CEO's Salary to an InternBy Om-Shree-0709 on .Agent IdentityMCP SecurityOAuth Delegation
- Why MCP Servers Need Execution Sandboxing (And Why Your Current Stack Isn't Enough)By Om-Shree-0709 on .Agentic AiPrompt InjectionWebAssembly
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