使用说明
yt-studio-mcp
MCP server for YouTube video management -- upload, thumbnails, captions, playlists, and livestream scheduling via the YouTube Data API.
Installation
Run directly with npx:
npx yt-studio-mcp
Or install globally:
npm install -g yt-studio-mcp
yt-studio-mcp
Configuration
Google Cloud OAuth Setup
This server uses OAuth 2.0 to access the YouTube Data API on your behalf.
- Go to Google Cloud Console
- Create a new project (or select an existing one)
- Enable the YouTube Data API v3:
- Navigate to APIs & Services > Library
- Search for "YouTube Data API v3"
- Click Enable
- Create OAuth 2.0 credentials:
- Navigate to APIs & Services > Credentials
- Click "Create Credentials" > "OAuth client ID"
- Application type: Desktop application
- Download or copy the Client ID and Client Secret
- Set environment variables:
export GOOGLE_CLIENT_ID="your-client-id"
export GOOGLE_CLIENT_SECRET="your-client-secret"
- Run the auth flow:
npx yt-studio-mcp auth
This opens a browser window for Google sign-in. After granting access, tokens are saved locally.
- Check auth status anytime:
npx yt-studio-mcp auth --status
Optional Configuration
| Variable | Description | Default |
|---|---|---|
YT_STUDIO_MCP_TOKEN_PATH |
Custom path for OAuth token storage | ~/.config/yt-studio-mcp/tokens.json |
YT_ALLOWED_UPLOAD_DIR |
Allowlist root for all local file uploads -- video, thumbnail, and captions. Absolute path to the directory holding those files. Unset means every file-path upload is refused, so despite the section heading this is not optional in practice. Example: /Users/you/podcast/video |
unset (all file uploads refused) |
export YT_ALLOWED_UPLOAD_DIR="/Users/you/podcast/video"
MCP Client Setup
Add to your Claude Desktop configuration (claude_desktop_config.json):
{
"mcpServers": {
"youtube": {
"command": "npx",
"args": ["-y", "yt-studio-mcp"],
"env": {
"GOOGLE_CLIENT_ID": "your-client-id",
"GOOGLE_CLIENT_SECRET": "your-client-secret",
"YT_ALLOWED_UPLOAD_DIR": "/Users/you/podcast/video"
}
}
}
}
Note: You must complete the OAuth auth flow (step 6 above) before the MCP server can make API calls. Tokens persist across sessions once authenticated.
Available Tools
| Tool | Description |
|---|---|
youtube_upload |
Upload a video to YouTube with metadata. Returns the video ID and URL. Set privacy to 'private' (default) for review before publishing. The file must resolve inside YT_ALLOWED_UPLOAD_DIR. |
youtube_set_thumbnail |
Set a custom thumbnail on a YouTube video. Image must be PNG or JPEG (by magic bytes), at least 1280x720, and under 2MB. The file must resolve inside YT_ALLOWED_UPLOAD_DIR. |
youtube_upload_captions |
Upload SubRip (SRT) or WebVTT caption tracks to a YouTube video. Format is detected from content. Supports multiple languages in a single call. At least one English track is required. Every file must resolve inside YT_ALLOWED_UPLOAD_DIR, and a refusal on any one entry means nothing is uploaded. |
youtube_list_playlists |
List all playlists on the authenticated YouTube channel. Returns playlist IDs, titles, descriptions, and video counts. |
youtube_add_to_playlist |
Add a YouTube video to one or more playlists. Handles partial failures: returns per-playlist success/error status. |
youtube_create_playlist |
Create a new playlist on the authenticated YouTube channel. Returns playlist ID and URL. |
youtube_schedule_livestream |
Schedule a YouTube livestream with all metadata and defaults. Binds to a reusable stream (OBS stream key stays the same). Sets Sports category, optional thumbnail/playlist, and attempts monetization with mid-rolls. |
Security
Local file uploads are confined to YT_ALLOWED_UPLOAD_DIR. Containment -- not
caller honesty -- decides which bytes leave your machine:
- Both the configured root and the candidate path are resolved with
realpathbefore a separator-aware containment check, so a symlink inside the root that points out of it is refused, and a sibling directory sharing a name prefix (/allowed-evilagainst root/allowed) is refused too. - Files are opened
O_RDONLY | O_NOFOLLOWwith a device/inode re-check against the pre-openlstat, closing the TOCTOU window between validation and read. Every uploaded byte comes off that one validated descriptor; the path is never reopened. - Content is validated by bytes, not by extension: video by container magic
(MP4/MOV, MKV/WebM, AVI), thumbnails by PNG/JPEG signature, captions by WebVTT
or SubRip structure. A text file named
.mp4or a binary blob named.srtis refused. - Refusals are deliberately uniform. Missing file, outside the root, not a regular file, and symlink escape all produce one identical message that never echoes a resolved path, so the server cannot be used as a filesystem existence oracle.
Breaking change from 0.2.0: file uploads require an allowlist root
Through 0.2.0, youtube_upload, youtube_set_thumbnail, and
youtube_upload_captions accepted any path and streamed those bytes to YouTube
under your OAuth credentials -- and youtube_upload accepts privacy: "public".
An AI agent steered by prompt injection could name ~/.ssh/id_rsa or a project
.env and publish it.
File uploads are now refused unless YT_ALLOWED_UPLOAD_DIR is set, and a path is
only accepted when it resolves inside that directory. If uploads started failing
after upgrading, set YT_ALLOWED_UPLOAD_DIR to your media directory.
Resources
The server exposes a Publish Workflow resource (resource:///publish-workflow) that provides a step-by-step guide for publishing a complete YouTube video via MCP tools -- covering upload, thumbnail, captions, and playlist attachment.
License
MIT