youtube-music-mcp

youtube-music-mcp

An MCP server that enables AI applications to search YouTube music videos and manage playlists via the YouTube Data API v3, with OAuth support for playlist and subscription management.

Category
访问服务器

README

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)

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

推荐服务器

Baidu Map

Baidu Map

百度地图核心API现已全面兼容MCP协议,是国内首家兼容MCP协议的地图服务商。

官方
精选
JavaScript
Playwright MCP Server

Playwright MCP Server

一个模型上下文协议服务器,它使大型语言模型能够通过结构化的可访问性快照与网页进行交互,而无需视觉模型或屏幕截图。

官方
精选
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

一个由人工智能驱动的工具,可以从自然语言描述生成现代化的用户界面组件,并与流行的集成开发环境(IDE)集成,从而简化用户界面开发流程。

官方
精选
本地
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

通过模型上下文协议启用与 Audiense Insights 账户的交互,从而促进营销洞察和受众数据的提取和分析,包括人口统计信息、行为和影响者互动。

官方
精选
本地
TypeScript
VeyraX

VeyraX

一个单一的 MCP 工具,连接你所有喜爱的工具:Gmail、日历以及其他 40 多个工具。

官方
精选
本地
graphlit-mcp-server

graphlit-mcp-server

模型上下文协议 (MCP) 服务器实现了 MCP 客户端与 Graphlit 服务之间的集成。 除了网络爬取之外,还可以将任何内容(从 Slack 到 Gmail 再到播客订阅源)导入到 Graphlit 项目中,然后从 MCP 客户端检索相关内容。

官方
精选
TypeScript
Kagi MCP Server

Kagi MCP Server

一个 MCP 服务器,集成了 Kagi 搜索功能和 Claude AI,使 Claude 能够在回答需要最新信息的问题时执行实时网络搜索。

官方
精选
Python
e2b-mcp-server

e2b-mcp-server

使用 MCP 通过 e2b 运行代码。

官方
精选
Neon MCP Server

Neon MCP Server

用于与 Neon 管理 API 和数据库交互的 MCP 服务器

官方
精选
Exa MCP Server

Exa MCP Server

模型上下文协议(MCP)服务器允许像 Claude 这样的 AI 助手使用 Exa AI 搜索 API 进行网络搜索。这种设置允许 AI 模型以安全和受控的方式获取实时的网络信息。

官方
精选