Skip to main content
Glama
CreatorGeetansh

YouTube MCP Server

YouTube MCP Server

An open-source Model Context Protocol (MCP) server for using YouTube from MCP clients such as Claude Desktop, Claude Code, and Codex.

The primary workflow is:

  1. Give an MCP client a list of songs.

  2. Review ranked YouTube matches before anything is changed.

  3. Create a private playlist from the selected videos.

The server will also provide quota-conscious tools for searching YouTube and reading videos, channels, playlists, and comments.

IMPORTANT

The TypeScript package, stdio server, public and authenticated reads, PKCE OAuth, music preparation, confirmed new-playlist creation, and full previewed playlist mutations are implemented and tested. A prepared music draft can be committed to a new playlist or appended to an existing owned playlist.

Design goals

  • Safe playlist writes with preview-before-commit semantics.

  • Official YouTube Data API v3 endpoints only.

  • Bring-your-own Google OAuth client; the project never ships shared Google credentials.

  • Secrets stored in the operating-system keychain whenever possible.

  • Predictable quota use, pagination, caching, retries, and normalized errors.

  • Local stdio transport for simple installation and a small attack surface.

  • Structured, bounded tool outputs that treat YouTube content as untrusted data.

  • Cross-platform TypeScript support on Node.js 20.17 or newer.

Related MCP server: yt-curator-mcp

Planned v1 scope

Read tools

  • Search videos, channels, and playlists.

  • Read video, channel, playlist, and comment data.

  • Read the authenticated user's channel, uploads, and playlists.

  • Return provider page tokens for explicit, stateless pagination.

Music playlist workflow

  • Accept up to 50 structured tracks per preparation request.

  • Search and rank likely YouTube music-video matches.

  • Show ambiguity and alternatives instead of silently choosing weak matches.

  • Commit explicitly selected matches to a new playlist or an existing owned playlist.

  • Default new playlists to private.

Comment management

  • Post a top-level comment on a video, or reply to an existing comment.

  • Delete a comment, or set its moderation status, behind a preview and a one-time handle.

  • Uses the youtube.force-ssl scope already requested for playlist writes, so no additional consent is needed.

Playlist management

  • Create playlists and add videos.

  • Update playlist metadata or privacy.

  • Reorder or remove playlist items.

  • Delete playlists after a short-lived, one-time confirmation handle is issued.

Playlist updates, item removal/reordering, and deletion use two tools: youtube_prepare_playlist_mutation returns the exact diff and a 10-minute handle without writing; youtube_apply_playlist_mutation rechecks ownership and the playlist snapshot before consuming that handle once.

Video upload and lifecycle

  • Upload a local video file through a resumable upload, defaulting to private.

  • Update title, description, tags, or privacy on a video you own. Unspecified fields are carried forward rather than cleared.

  • Set a custom thumbnail from a local JPEG or PNG.

  • Preview and then permanently delete a video you own.

Video writes require the additional youtube.upload scope and refuse outright when a profile was authorized without it. youtube_plan_video_upload validates the file and reports the cost without contacting YouTube, because videos.insert costs 1600 units — about 16% of a default daily budget per upload. An upload is never retried: if the outcome is unknown, that is reported so you can check for a partial upload instead of publishing a duplicate.

Playlist, comment, and video management are the implemented write surfaces. Analytics reporting (0.8) is on the roadmap and is not callable yet. Ratings, subscriptions, and channel-settings writes remain permanently out of scope.

Setup

The npm package has not been published yet, so the server is built and run from a clone. Work through the steps in order.

Step 1 — check for Node.js and npm

node -v
npm -v

If node -v prints v20.17 or newer and npm -v prints a version, skip to Step 3. If either command reports "command not found", continue with Step 2.

Step 2 — install Node.js and npm (only if Step 1 failed)

npm ships with Node.js; installing Node installs both. Pick one row for your platform, then re-run Step 1 to confirm.

Platform

Command

macOS (Homebrew)

brew install node@22

macOS / Windows / Linux (no package manager)

Download the LTS installer from nodejs.org/en/download and run it

Windows (winget)

winget install OpenJS.NodeJS.LTS

Debian / Ubuntu

curl -fsSL https://deb.nodesource.com/setup_22.x | sudo -E bash - && sudo apt-get install -y nodejs

Fedora / RHEL

sudo dnf install nodejs npm

If you would rather not install Node system-wide, or you need several Node versions side by side, use a version manager:

# macOS and Linux
curl -fsSL https://raw.githubusercontent.com/nvm-sh/nvm/v0.40.1/install.sh | bash
nvm install 22
nvm use 22

On Windows the equivalent is nvm-windows: nvm install 22 then nvm use 22.

Close and reopen the terminal after installing, then re-run node -v and npm -v.

Step 3 — install dependencies and build

git clone <repository-url>
cd "Youtube MCP"
npm ci
npm run build

npm ci installs the exact versions in package-lock.json; use npm install only when you intend to change dependencies. The build writes the executable to dist/cli/index.js, which every command below invokes.

Verify the build and the local data directory:

node dist/cli/index.js doctor

Step 4 — create the Google credentials

Everything below comes from your own Google Cloud project. This project never ships shared Google credentials.

  1. Create or select a project in the Google Cloud console.

  2. Enable YouTube Data API v3 for that project.

  3. Create an API key (Credentials → Create credentials → API key). This covers public reads.

  4. Configure the OAuth consent screen. While the project is in Testing status, add your own Google account under Test users, or login will be refused.

  5. Create an OAuth client of type Desktop app, then copy both its client ID and its client secret.

Google requires client_secret in the authorization-code exchange even for installed applications, so PKCE supplements the secret here rather than replacing it.

Step 5 — the credentials the server needs

Four credentials exist in total. You supply the first three; the fourth is obtained for you by login.

Credential

Needed for

Where it comes from

How you supply it

Where it is kept

YOUTUBE_API_KEY

Public reads (search, videos, channels, public playlists, comments)

Step 4.3

Process environment only

Not persisted. It is read from the environment on every start, so an MCP client must pass it in each launch.

YOUTUBE_OAUTH_CLIENT_ID

Any account action: reading your own playlists, creating playlists

Step 4.5

YOUTUBE_OAUTH_CLIENT_ID env var, or the interactive setup prompt

Profile JSON in the data directory. It is not a secret.

YOUTUBE_OAUTH_CLIENT_SECRET

The authorization-code exchange during login

Step 4.5

YOUTUBE_OAUTH_CLIENT_SECRET env var, or the interactive setup prompt

Operating-system keychain, per profile. Never written to the profile JSON.

OAuth refresh token

Staying signed in across restarts

Produced by login

Operating-system keychain, per profile. Access tokens stay in memory only.

Optional environment variables: YOUTUBE_MCP_PROFILE (default default), YOUTUBE_MCP_DATA_DIR, and YOUTUBE_MCP_LOG_LEVEL (error, warn, info, debug). See .env.example.

Never paste any of these into a chat message, a shared MCP configuration file, or a command that will be committed. Prefer the interactive prompts or your client's environment/secret-injection field.

Step 6 — run setup, then log in

Run these in order. setup rewrites the profile's stored scopes and channel identity, so running it after login discards that state and requires logging in again.

macOS and Linux:

YOUTUBE_OAUTH_CLIENT_ID="YOUR_DESKTOP_CLIENT_ID" \
  YOUTUBE_OAUTH_CLIENT_SECRET="YOUR_DESKTOP_CLIENT_SECRET" \
  node dist/cli/index.js setup
node dist/cli/index.js login
node dist/cli/index.js status

Windows PowerShell:

$env:YOUTUBE_OAUTH_CLIENT_ID  = "YOUR_DESKTOP_CLIENT_ID"
$env:YOUTUBE_OAUTH_CLIENT_SECRET = "YOUR_DESKTOP_CLIENT_SECRET"
node dist\cli\index.js setup
node dist\cli\index.js login
node dist\cli\index.js status
Remove-Item Env:\YOUTUBE_OAUTH_CLIENT_SECRET

To avoid putting the secret in shell history or the process table at all, omit both variables and let setup prompt for them:

node dist/cli/index.js setup

setup prompts for each missing value when the terminal is interactive.

login opens Google's authorization page and returns through a random loopback port on 127.0.0.1, using PKCE S256 and a random state value. It fails immediately, before opening a browser, when no client secret is stored for the profile.

To revoke and remove the stored credential:

node dist/cli/index.js logout

Step 7 — start the server

YOUTUBE_API_KEY="your-api-key" node dist/cli/index.js serve

The server speaks MCP over stdio, so it is normally launched by a client rather than by hand. Available commands are serve, doctor, status, setup, login, and logout.

Local data location

Profiles, the quota ledger, drafts, and operation journals live in a 0700 directory:

Platform

Default path

macOS

~/Library/Application Support/youtube-mcp

Linux

$XDG_DATA_HOME/youtube-mcp, otherwise ~/.local/share/youtube-mcp

Windows

%LOCALAPPDATA%\youtube-mcp

Override with YOUTUBE_MCP_DATA_DIR. To remove all local state, run logout and then delete that directory. Keychain entries are removed by logout.

Connecting a client to the local build

Until the package is published, point clients at the absolute path of your built dist/cli/index.js.

Claude Code

claude mcp add youtube --scope user \
  --env YOUTUBE_MCP_PROFILE=default \
  --env YOUTUBE_API_KEY=your-api-key -- \
  node /absolute/path/to/Youtube\ MCP/dist/cli/index.js serve

Claude Desktop

{
  "mcpServers": {
    "youtube": {
      "command": "node",
      "args": ["/absolute/path/to/Youtube MCP/dist/cli/index.js", "serve"],
      "env": {
        "YOUTUBE_MCP_PROFILE": "default",
        "YOUTUBE_API_KEY": "your-api-key"
      }
    }
  }
}

Codex

[mcp_servers.youtube]
command = "node"
args = ["/absolute/path/to/Youtube MCP/dist/cli/index.js", "serve"]

[mcp_servers.youtube.env]
YOUTUBE_MCP_PROFILE = "default"
YOUTUBE_API_KEY = "your-api-key"

Re-run npm run build after pulling changes; clients execute the compiled dist output, not src.

Google authorization for this local server is performed by its own setup and login commands. Client-level MCP login commands do not replace the downstream Google OAuth flow.

Post-publication client configuration

Once the package is released, pin a released version instead of using latest so an MCP client cannot change behavior unexpectedly.

Claude Desktop

{
  "mcpServers": {
    "youtube": {
      "command": "npx",
      "args": ["-y", "@youtube-mcp/server@0.7.0", "serve"],
      "env": {
        "YOUTUBE_MCP_PROFILE": "default"
      }
    }
  }
}

On native Windows, use "command": "cmd" and prefix the arguments with "/c", "npx".

Claude Code

claude mcp add youtube --scope user \
  --env YOUTUBE_MCP_PROFILE=default -- \
  npx -y @youtube-mcp/server@0.7.0 serve

Codex

codex mcp add youtube \
  --env YOUTUBE_MCP_PROFILE=default -- \
  npx -y @youtube-mcp/server@0.7.0 serve

Equivalent Codex configuration:

[mcp_servers.youtube]
command = "npx"
args = ["-y", "@youtube-mcp/server@0.7.0", "serve"]

[mcp_servers.youtube.env]
YOUTUBE_MCP_PROFILE = "default"

How much can be added in one go

Hard schema limits per tool call:

Operation

Maximum per call

Tracks per youtube_prepare_music_playlist

50

Selections per youtube_commit_music_playlist

50

Video IDs per youtube_get_videos

50

Item removals per playlist mutation

50

Reorder moves per playlist mutation

50

Items per read page

50

So 50 songs is the ceiling for a single commit. A longer list is handled by committing in batches of 50: create the playlist with the first batch, then pass its target_playlist_id on each subsequent commit to append. Duplicates already present are skipped and reported rather than inserted twice.

The daily quota is the tighter constraint in practice. Against Google's default 10,000 units per project per day, one 50-song run costs roughly:

Step

Calls

Published unit cost

Subtotal

search.list, one per track

50

100

5,000

videos.list hydration, batched by 50

1–5

1

1–5

playlists.insert

1

50

50

playlistItems.insert

50

50

2,500

Total

≈ 7,550

That means roughly one 50-song playlist per project per day. A second full run the same day will exhaust the quota and fail partway through insertion. Preparing the same list twice is especially expensive: the searches are charged again even though the answers are unchanged.

Quota resets at midnight US Pacific time, which is the day boundary the local ledger uses.

Quota expectations

youtube_quota_status reports locally observed usage, not an authoritative Google balance. General units and search.list calls are tracked separately because Google applies a separate default daily search-call limit.

search.list is billed at its published 100 units, so general_units reflects real consumption. Releases before 0.5.0 recorded it as 1 unit and therefore under-reported usage by 99 units per search; a write could be rejected for quota while the reported figure still looked low. Upgrade if you are on an earlier build.

Quota values can change. Implementation and release work must verify the current official cost table instead of treating values in this README as permanent constants.

Troubleshooting

A commit reports status: "partial" with empty completed and everything in pending. The playlist was created but the first insert was rejected — most often the daily quota. Nothing is retried blindly, so no duplicate items are written. Check youtube_quota_status, delete the empty playlist, and re-run after the Pacific-time reset. Because a draft is single-use, re-running requires a fresh youtube_prepare_music_playlist.

login fails before a browser opens. No client secret is stored for the profile. Run setup first, and confirm you are on the intended YOUTUBE_MCP_PROFILE.

Authorization succeeds, then stops working about a week later. Google OAuth projects left in Testing status issue refresh tokens that expire after seven days. Publish the consent screen or re-run login.

403 on a public read. YOUTUBE_API_KEY is missing from the server's environment. It is never persisted, so it must be present in every launch — including the env block of the MCP client configuration.

Authentication model

  • Public reads require YOUTUBE_API_KEY in the process environment.

  • Account reads require OAuth with the youtube.readonly scope.

  • Playlist creation and comment writes require youtube.force-ssl because Google does not provide a playlist-only scope.

  • Video upload and lifecycle require youtube.upload. It is requested separately, and video tools refuse when a profile was authorized without it. Add it to your consent screen and re-run login before uploading.

  • The server counteracts that broad Google scope with a strict endpoint allowlist: only playlist and playlist-item write endpoints are callable.

  • Installed applications use Authorization Code + PKCE, a random state, and a loopback redirect on 127.0.0.1 with a random port.

  • Service accounts are not supported for ordinary YouTube accounts.

Never commit API keys, OAuth client data, access tokens, refresh tokens, local databases, debug logs, or .env files.

Captions and analytics

General public transcript retrieval is not part of v1. The official captions download endpoint is permission-gated and expensive, so unofficial scraping will not be used. Owner-authorized caption management may be considered later.

YouTube Analytics and Reporting APIs are also deferred. They require separate OAuth, data models, and operational behavior and should not complicate the initial playlist-focused server.

Development

The implemented stack is TypeScript, Node.js 20.17+, ESM, the official MCP TypeScript SDK, Zod validation, direct typed REST calls to approved Google endpoints, SQLite for local quota/draft/journal state, and an OS-keychain adapter for OAuth refresh tokens.

Current checks:

npm run format:check
npm run lint
npm run typecheck
npm test
npm run build

Implementation should follow the phases and acceptance gates in PLAN.md. Agent-specific constraints and definitions of done are in AGENTS.md. Claude Code should begin with CLAUDE.md.

Project status

  • Product and security architecture

  • Repository development instructions

  • TypeScript package scaffold

  • Public read tools

  • OAuth and profiles

  • Music matching and preview

  • Confirmed new-playlist creation

  • Previewed playlist update, reorder, removal, and deletion

  • Existing-playlist target for music draft commits (0.5)

  • Correct search.list general-unit accounting in the quota ledger (0.5)

  • Comment insert, reply, delete, and moderation (0.6)

  • Video upload, metadata update, delete, and thumbnails (0.7)

  • YouTube Analytics reporting (0.8)

  • Cross-client integration tests

  • First npm release

License

Licensed under the Apache License 2.0. The complete license text is in LICENSE.

References

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    B
    maintenance
    Enables YouTube playlist curation including inventory, deduplication, merging, and deletion via MCP tools.
    12
    MIT
  • F
    license
    Not graded
    quality
    C
    maintenance
    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.
    3
    -