OAuth-authenticated YouTube server built for creators who need write access. Goes beyond read-only Data API wrappers by letting you update video metadata, moderate comments, manage playlists, and pull channel analytics. The standout feature is the ComfyUI bridge: generate a thumbnail from a text prompt and push it to YouTube in a single tool call. Runs over streamable HTTP or stdio, handles token refresh automatically, and stays comfortably under the 10k daily quota for most workflows. Useful when you're running a channel from Claude and need more than search and fetch.
Public tool metadata for what this MCP can expose to an agent.
YOUTUBE_GET_CHANNEL_ACTIVITIESGets recent activities from a YouTube channel including video uploads, playlist additions, likes, and other channel events.6 paramsGets recent activities from a YouTube channel including video uploads, playlist additions, likes, and other channel events.
partstringchannelIdstringpageTokenstringmaxResultsintegerpublishedAfterstringpublishedBeforestringYOUTUBE_GET_CHANNEL_ID_BY_HANDLERetrieves the YouTube Channel ID for a specific YouTube channel handle.1 paramsRetrieves the YouTube Channel ID for a specific YouTube channel handle.
channel_handlestringYOUTUBE_GET_CHANNEL_STATISTICSGets detailed statistics for YouTube channels including subscriber counts, view counts, and video counts.2 paramsGets detailed statistics for YouTube channels including subscriber counts, view counts, and video counts.
idstringpartstringYOUTUBE_GET_VIDEO_DETAILS_BATCHRetrieves multiple YouTube video resource parts in a single batch call. Use when you need cohort-level metrics for many videos to reduce quota usage and latency.3 paramsRetrieves multiple YouTube video resource parts in a single batch call. Use when you need cohort-level metrics for many videos to reduce quota usage and latency.
hlstringidarraypartsarrayYOUTUBE_LIST_CAPTION_TRACKRetrieves a list of caption tracks for a YouTube video, returning an empty list if no captions exist or failing if the video ID is invalid or not found.2 paramsRetrieves a list of caption tracks for a YouTube video, returning an empty list if no captions exist or failing if the video ID is invalid or not found.
partstringvideoIdstringYOUTUBE_LIST_CHANNEL_VIDEOSLists videos from a specified YouTube channel, ensuring results are of `type: 'video'`.4 paramsLists videos from a specified YouTube channel, ensuring results are of `type: 'video'`.
partstringchannelIdstringpageTokenstringmaxResultsintegerYOUTUBE_LIST_PLAYLIST_ITEMSTool to list videos in a playlist, with pagination support. Use when walking through a channel's uploads playlist to enumerate all videos.7 paramsTool to list videos in a playlist, with pagination support. Use when walking through a channel's uploads playlist to enumerate all videos.
partstringfieldsstringvideoIdstringpageTokenstringmaxResultsintegerplaylistIdstringonBehalfOfContentOwnerstringYOUTUBE_LIST_USER_PLAYLISTSRetrieves playlists owned by the authenticated user, implicitly using mine=True.3 paramsRetrieves playlists owned by the authenticated user, implicitly using mine=True.
partstringpageTokenstringmaxResultsintegerYOUTUBE_LIST_USER_SUBSCRIPTIONSRetrieves the authenticated user's YouTube channel subscriptions, allowing specification of response parts and pagination.3 paramsRetrieves the authenticated user's YouTube channel subscriptions, allowing specification of response parts and pagination.
partstringpageTokenstringmaxResultsintegerYOUTUBE_LOAD_CAPTIONSDownloads a specific YouTube caption track, which must be owned by the authenticated user, and returns its content as text. Note: This action requires you to own the video (YouTube Data API v3 restriction). Non-owned videos will return 403 Forbidden errors, including many auto...2 paramsDownloads a specific YouTube caption track, which must be owned by the authenticated user, and returns its content as text. Note: This action requires you to own the video (YouTube Data API v3 restriction). Non-owned videos will return 403 Forbidden errors, including many auto...
idstringtfmtstringYOUTUBE_SEARCH_YOU_TUBESearches YouTube for videos, channels, or playlists using a query term, returning the raw API response.5 paramsSearches YouTube for videos, channels, or playlists using a query term, returning the raw API response.
qstringpartstringtypestringpageTokenstringmaxResultsintegerYOUTUBE_SUBSCRIBE_CHANNELSubscribes the authenticated user to a specified YouTube channel, identified by its unique `channelId` which must be valid and existing.1 paramsSubscribes the authenticated user to a specified YouTube channel, identified by its unique `channelId` which must be valid and existing.
channelIdstringYOUTUBE_UPDATE_THUMBNAILSets the custom thumbnail for a YouTube video using an image from thumbnailUrl; the authenticated user must have permission to edit the video.2 paramsSets the custom thumbnail for a YouTube video using an image from thumbnailUrl; the authenticated user must have permission to edit the video.
videoIdstringthumbnailUrlstringYOUTUBE_UPDATE_VIDEOUpdates metadata for a YouTube video identified by videoId, which must exist; an empty list for tags removes all existing tags.6 paramsUpdates metadata for a YouTube video identified by videoId, which must exist; an empty list for tags removes all existing tags.
tagsarraytitlestringvideoIdstringcategoryIdstringdescriptionstringprivacyStatusstringYOUTUBE_UPLOAD_VIDEOUploads a video from a local file path to a YouTube channel; the video file must be in a YouTube-supported format.6 paramsUploads a video from a local file path to a YouTube channel; the video file must be in a YouTube-supported format.
tagsarraytitlestringcategoryIdstringdescriptionstringprivacyStatusstringvideoFilePathstringYOUTUBE_VIDEO_DETAILSRetrieves specified information parts (e.g., snippet, contentDetails, statistics) for a YouTube video, identified by its ID.2 paramsRetrieves specified information parts (e.g., snippet, contentDetails, statistics) for a YouTube video, identified by its ID.
idstringpartstringOAuth-authenticated YouTube MCP for channel owners. Edit video metadata, reply to and moderate comments, manage playlists, query channel analytics, and generate or set AI thumbnails via a ComfyUI bridge. Goes beyond the read-only Data API v3 wrappers that dominate this space.
Most existing YouTube MCPs use an API key against Data API v3. Search videos, fetch public metadata, read-only. This one uses OAuth 2.0 (Authorization Code + PKCE) so it can actually write to your channel: update video titles, descriptions and tags, reply to comments, moderate spam, manage playlists. It also hits the separate YouTube Analytics API for channel stats, and generates a thumbnail via ComfyUI and pushes it to YouTube in a single MCP call.
Claude, use generate_and_set_thumbnail on video abc123:
prompt: "cyberpunk hacker at keyboard, neon blue and pink, high contrast"
ComfyUI renders 1280×720, youtube-mcp fetches the bytes, and POSTs to thumbnails.set. Done.
# npx, no install
npx @miller-joe/youtube-mcp --help
# Docker
docker run -p 9120:9120 \
-e YOUTUBE_CLIENT_ID=... \
-e YOUTUBE_CLIENT_SECRET=... \
-e YOUTUBE_TOKEN_FILE=/token/token.json \
-v $PWD/token:/token \
ghcr.io/miller-joe/youtube-mcp:latest
Google account plus YouTube channel. Use a personal account, not a workspace one you might lose.
Google Cloud project at https://console.cloud.google.com. Call it whatever you want (e.g. youtube-mcp).
Enable APIs:
OAuth consent screen: External, App name, support email. In Scopes, add:
youtube.uploadyoutube.force-sslyt-analytics.readonlyStay in Testing mode. Add yourself as a test user (required). As the project owner, your refresh token won't expire.
Create OAuth Client ID: Application type = Desktop app. Download the JSON.
Run the interactive auth flow:
npx @miller-joe/youtube-mcp --auth --client-secret-file ./client_secret.json
A browser opens, you log in to the Google account tied to your YouTube channel, and grant the requested scopes. On success, a refresh token is saved to ~/.config/youtube-mcp/token.json.
Start the server:
npx @miller-joe/youtube-mcp --client-secret-file ./client_secret.json
Or provide the client credentials via env: YOUTUBE_CLIENT_SECRET_FILE, or YOUTUBE_CLIENT_ID + YOUTUBE_CLIENT_SECRET.
claude mcp add --transport http youtube http://localhost:9120/mcp
Or point your MCP gateway at the Streamable HTTP endpoint.
| CLI flag | Env var | Default | Notes |
|---|---|---|---|
--client-secret-file | YOUTUBE_CLIENT_SECRET_FILE | (none) | Path to Google OAuth JSON |
--client-id | YOUTUBE_CLIENT_ID | (none) | Alternative to the secret file |
--client-secret | YOUTUBE_CLIENT_SECRET | (none) | Alternative to the secret file |
--token-file | YOUTUBE_TOKEN_FILE | ~/.config/youtube-mcp/token.json | Refresh token storage |
--host | MCP_HOST | 0.0.0.0 | Bind host (HTTP mode only) |
--port | MCP_PORT | 9120 | Bind port (HTTP mode only) |
--stdio | MCP_TRANSPORT=stdio | (unset) | Speak MCP over stdio instead of HTTP. Use when launched as a subprocess by a stdio-first MCP client (Claude Desktop, mcp-inspector). |
--comfyui-url | COMFYUI_URL | (unset, bridge disabled) | ComfyUI HTTP URL for bridge tools |
| (no flag) | COMFYUI_DEFAULT_CKPT | sd_xl_base_1.0.safetensors | Default checkpoint for bridge tool |
The server speaks streamable HTTP by default (great for Claude Code, MetaMCP, raw fetch). Pass --stdio (or set MCP_TRANSPORT=stdio) to switch into stdio mode, which is what stdio-first clients like Claude Desktop and the MCP Inspector expect:
// claude_desktop_config.json
{
"mcpServers": {
"youtube": {
"command": "npx",
"args": ["-y", "@miller-joe/youtube-mcp", "--stdio"],
"env": {
"YOUTUBE_CLIENT_SECRET_FILE": "/path/to/client_secret.json",
"YOUTUBE_TOKEN_FILE": "/path/to/token.json"
}
}
}
}
Stdio mode skips the OAuth-token preflight check — the server boots even without a stored token and surfaces auth errors at tool-call time. Run youtube-mcp --auth --client-secret-file <path> once in HTTP mode to seed the refresh token before pointing Claude Desktop at it.
list_my_videos: paginated list of the authenticated channel's uploads.get_video: full detail for one video.update_video_metadata: title, description, tags, category, privacy.delete_video: permanently delete a video. Requires confirm_video_title to match the current title exactly, as a guard against deleting the wrong video.list_captions: list caption tracks on a video (language, name, status, draft flag).upload_caption: upload an SRT or WebVTT caption track to a video.delete_caption: delete a caption track.list_my_shorts: find Shorts in recent uploads (filters by duration ≤60s).get_shorts_analytics: YouTube Analytics query restricted to Shorts (creatorContentType==SHORTS).create_playlist: create a playlist (default private).add_to_playlist: add a video to an existing playlist.list_comments: top-level comment threads on a video.reply_to_comment: reply to a top-level comment.moderate_comment: hold, approve, or reject a comment.query_channel_analytics: date-ranged metrics with optional dimensions and filters.COMFYUI_URL is configured)generate_and_set_thumbnail: generate a thumbnail via ComfyUI and set it on a video in one call.YouTube Data API free tier = 10,000 units/day. Key operation costs:
videos.list, commentThreads.list: 1 unit each.videos.update, comments.insert, thumbnails.set: 50 units each.videos.insert (upload): 1,600 units, so about 6 uploads per day on the free tier.Most creator-ops workflows stay well under the free cap.
┌────────────────┐ ┌──────────────────┐ ┌─────────────────┐
│ MCP client │────▶│ youtube-mcp │────▶│ YouTube APIs │
│ (Claude etc.) │◀────│ (this server) │◀────│ (Data/Analytics)│
└────────────────┘ └────────┬─────────┘ └─────────────────┘
│
│ (bridge tools only)
▼
┌──────────────────┐
│ ComfyUI │
│ (txt2img) │
└──────────────────┘
OAuth refresh tokens are cached locally and refreshed just-in-time before expiry. The bridge tool downloads image bytes from ComfyUI internally, so ComfyUI does not need to be publicly reachable.
git clone https://github.com/miller-joe/youtube-mcp
cd youtube-mcp
npm install
npm run dev
npm run build
npm test
Requires Node 20+.
Shipped:
list_my_shorts (duration filter) and get_shorts_analytics (creatorContentType==SHORTS).generate_and_set_thumbnail.Planned:
video_upload) with resumable-upload support.MIT © Joe Miller
If this saves you time, consider supporting development:
YOUTUBE_CLIENT_ID*Google OAuth 2.0 Client ID (from Google Cloud Console, Desktop app type). See README for setup.
YOUTUBE_CLIENT_SECRET*secretGoogle OAuth 2.0 Client Secret. Pair with YOUTUBE_CLIENT_ID.
YOUTUBE_CLIENT_SECRET_FILEAlternative to CLIENT_ID/CLIENT_SECRET: path to the JSON credentials file downloaded from Google Cloud.
YOUTUBE_TOKEN_FILERefresh token storage path. Default: ~/.config/youtube-mcp/token.json
COMFYUI_URLComfyUI HTTP URL for the generate_and_set_thumbnail bridge tool. When unset, that tool is disabled.
COMFYUI_DEFAULT_CKPTDefault ComfyUI checkpoint filename for bridge tool (default: sd_xl_base_1.0.safetensors)
io.github.socialapishub/social-media-api
io.github.xpaysh/social-media
com.thenextgennexus/youtube-media-mcp-server
io.github.ludmila-omlopes/youtube-video-analyzer
csoai-org/social-media-ai-mcp
com.ezbizservices/social-media