Features
- Full YouTube Data API v3 coverage — 52 tools spanning playlists, videos, channels & sections, subscriptions, comments, captions, thumbnails, watermarks, playlist cover images, and read-only reference data, all under the
youtube.force-sslscope - Quota-aware by design — every tool documents its cost up front, from 1-unit reads to the 1600-unit
upload-video - Guarded destructive actions — every delete, unsubscribe, moderation action, or other irreversible/outward tool refuses to run without
confirm: true - Full-replace done safely — YouTube's
updatemethods overwrite an entire resource part; tools likeupdate-video,update-playlist,update-channel, andupdate-channel-sectionread the current resource first so an update that only changes one field never silently blanks the rest - Safe bulk cleanup —
clean-playlistscans for[Deleted video]/[Private video]tombstones, duplicates, and videos from named channels, then defaults to a dry run so you see the plan before anything is removed - Local-file media uploads — videos, captions, thumbnails, watermarks, and playlist cover images all upload from a local file path via resumable upload where the API requires it
- One-time OAuth setup — a desktop-flow
npm run setuphandles Google authorisation and prints a refresh token; you supply it (and the client id/secret) to the server via environment variables, from any store you like - Typed end to end — Zod-validated tool schemas and a Vitest suite covering every handler
Install
The server works with any MCP-compatible client — Claude Desktop, Claude Code, Cursor, Cline, Zed, Continue, and others.
Install the package:
npm install -g @kud/mcp-youtubeIn Claude Code or Claude Desktop you can instead install it as a plugin, which wires up the registration for you:
/plugin install youtube@kudEither way, run the one-time OAuth setup before first use:
npm run setupThis walks you through creating a Google Cloud OAuth client (Desktop app type), opens a browser to authorise the youtube.force-ssl scope, and prints the resulting credentials — it writes nothing to disk. Stash them wherever you keep secrets (keychain, secrets manager, your MCP client's env block) and expose them to the server as environment variables:
| Variable | Holds |
|---|---|
MCP_YOUTUBE_CLIENT_ID | OAuth client ID |
MCP_YOUTUBE_CLIENT_SECRET | OAuth client secret |
MCP_YOUTUBE_REFRESH_TOKEN | long-lived refresh token |
The server reads these three from the environment only — it's store-agnostic, so how they get there is up to you. A keychain-backed export works well:
export MCP_YOUTUBE_REFRESH_TOKEN=$(security find-generic-password -s mcp-youtube-refresh-token -w)Tip: set the OAuth app's publishing status to In production in Google Cloud.
youtube.force-sslis a sensitive scope, and while the app sits in Testing the refresh token expires after 7 days.
Configuration
Register the server with your MCP client. The exact config file and its location depend on the client — Claude Desktop, for example, uses claude_desktop_config.json (Settings → Developer → Edit Config); Cursor, Cline, Zed, and Continue each have their own. The registration follows the standard mcpServers shape:
{
"mcpServers": {
"youtube": {
"command": "npx",
"args": ["-y", "@kud/mcp-youtube"],
"env": {
"MCP_YOUTUBE_CLIENT_ID": "your-client-id",
"MCP_YOUTUBE_CLIENT_SECRET": "your-client-secret",
"MCP_YOUTUBE_REFRESH_TOKEN": "your-refresh-token"
}
}
}
}If you installed via /plugin install youtube@kud, the plugin handles registration — you only need to supply the three environment variables above.
Usage
YouTube's Data API v3 gives every project a fixed 10,000 quota units/day. A single careless search, bulk delete, or video upload can burn through a meaningful chunk of that, so every tool documents its cost up front — and every destructive or outward-irreversible tool requires an explicit confirm: true.
52 tools cover the full read/write surface reachable under the youtube.force-ssl scope:
| Category | Tools | Docs |
|---|---|---|
| Searching | search, list-playlists, get-playlist | Searching |
| Playlists | create-playlist, update-playlist, add-to-playlist, update-playlist-item | Creating & Adding |
| Cleaning | clean-playlist | Cleaning Playlists |
| Deleting | remove-from-playlist, delete-playlist | Deleting & Pruning |
| Videos | list-videos, update-video, rate-video, get-video-rating, delete-video, upload-video, report-video-abuse | Videos |
| Channels & sections | update-channel, list-channels, list-channel-sections, create-channel-section, update-channel-section, delete-channel-section | Channels & Sections |
| Community | list-subscriptions, subscribe, unsubscribe, list-comment-threads, create-comment-thread, list-comments, reply-to-comment, update-comment, delete-comment, set-comment-moderation-status | Community |
| Captions & media | list-captions, upload-caption, update-caption, download-caption, delete-caption, set-thumbnail, set-watermark, unset-watermark, list-playlist-images, upload-playlist-image, update-playlist-image, delete-playlist-image | Captions & Media |
| Reference data | list-activities, list-video-categories, list-i18n-languages, list-i18n-regions, list-video-abuse-report-reasons, list-members, list-membership-levels | Reference Data |
Once installed, just ask your MCP client:
> Clean up the tombstones in my "Focus" playlist
Scanned 214 items — found 6 tombstones ([Deleted video]/[Private video]).
This is a dry run, no changes made. Estimated cost to delete: 300 units.
Want me to go ahead?
> Yes, and dedupe it too
Deleted 6 tombstones and 3 duplicates (9 items, 450 units).clean-playlist always returns its plan first — pass dryRun: false (or just confirm in conversation) to actually delete.
Development
git clone https://github.com/kud/mcp-youtube.git
cd mcp-youtube
npm install
npm run dev| Script | Purpose |
|---|---|
npm run dev | Run the server directly from source (tsx) |
npm run build | Compile to dist/ |
npm test | Run the Vitest suite |
npm run typecheck | Type-check without emitting |
npm run inspect:dev | Launch the MCP Inspector against source |