Skip to main content
Glama
deepujain

YouTube MCP Connector

by deepujain

YouTube MCP Connector

An MCP server (streamable HTTP) connecting any MCP-compatible client to the YouTube Data API v3: search videos, read subscriptions and playlists, build a "catch me up" digest of recent uploads, and manage playlists — with every write gated behind an explicit approval step.

Works with

This is a standard Model Context Protocol server — nothing in it is tied to any single assistant. Run it yourself (below) and point any MCP-compatible client at http://127.0.0.1:8000/mcp:

  • Meta Muse, Claude / Claude Code (Anthropic), ChatGPT (OpenAI)

  • Cursor, Windsurf, Cline, and other MCP-capable coding assistants

  • Any custom agent built on an MCP SDK (Python, TypeScript, …)

Related MCP server: mcp-apps

How it works

Any MCP client connects to this server's streamable-HTTP endpoint (/mcp). Credentials are never hard-coded: the server reads YOUTUBE_API_KEY / YOUTUBE_OAUTH_TOKEN from the environment (your local .env file).

Auth split (mirrors the YouTube API itself):

Tool kind

Credential

Why

Public reads (search_videos, get_video_details)

API key

Public data needs no user identity

Private reads (my_subscriptions, my_playlists, catch_me_up)

OAuth 2.0 user token

mine=true calls are per-user

All writes

OAuth 2.0 user token

Every mutation requires authorization

Approval gating: write tools (create_playlist, add_to_playlist, save_for_later, remove_from_playlist) never execute directly. They return a pending_confirmation payload with a single-use, expiring token (default 600 s). The agent surfaces the action description to the user; on approval it calls confirm_action with the token, which executes exactly once. cancel_action discards a pending action. This maps 1:1 onto the approval-card UX in MCP clients.

Known API limitation: YouTube's Data API cannot read or modify the native Watch Later playlist (support removed August 2016; the API returns watchLaterNotAccessible). save_for_later therefore uses a user-owned playlist named "Watch Later (via 1xAI)" as the supported replacement, creating it on first use.

Setup

You need a Google Cloud project. Do not create anything from this repo — these are manual steps in your own Google account:

  1. Go to Google Cloud Console and create (or pick) a project.

  2. APIs & Services → Library → search "YouTube Data API v3"Enable.

  3. APIs & Services → Credentials → Create Credentials → API key — for public reads. (Optional: restrict the key to the YouTube Data API v3.)

  4. OAuth consent screen → user type External → fill app name, support email, developer contact → add scopes:

    • https://www.googleapis.com/auth/youtube.readonly

    • https://www.googleapis.com/auth/youtube.force-ssl

    Tip: publish the consent screen to Production. Apps left in Testing issue refresh tokens that expire after 7 days.

  5. Create Credentials → OAuth client ID → type Desktop app (personal use) → run Google's OAuth flow once and capture an access token.

  6. Copy .env.example to .env and fill in the values:

    • YOUTUBE_API_KEY — public reads

    • YOUTUBE_OAUTH_TOKEN — private reads + writes (refresh when it expires)

    • YOUTUBE_HOST / YOUTUBE_PORT — bind address (default 127.0.0.1:8000)

    • YOUTUBE_APPROVAL_TTL_SECONDS — approval window (default 600)

    • YOUTUBE_HTTP_PROXY / YOUTUBE_HTTPS_PROXY — only if your host needs an explicit egress proxy (ambient proxy env vars are intentionally ignored)

    • YOUTUBE_CA_BUNDLE — path to a PEM CA bundle, only if your egress proxy re-signs TLS with a private CA (it extends the default trust store)

No billing account is needed: the API is free within the daily quota below.

Quota budget

Default project quota: 10,000 units/day, resetting at midnight Pacific. Costs are per call (each result page costs the full amount again).

Tool

API call(s)

Units

search_videos

search.list

100

my_subscriptions

subscriptions.list

1 / page

my_playlists

playlists.list

1 / page

get_video_details

videos.list (≤50 IDs batched)

1

catch_me_up

subscriptions.list + channels.list batch + playlistItems.list per channel

≈ 2 + #channels

create_playlist (on confirm)

playlists.insert

50

add_to_playlist (on confirm)

playlistItems.insert

50

remove_from_playlist (on confirm)

playlistItems.delete

50

save_for_later (on confirm)

playlistItems.insert (+ playlists.insert first time)

50 (100 first time)

Example daily budget: 5 searches (500) + 3 digests over 40 subscriptions (~126) + 20 detail lookups (20) + 10 playlist writes (500) ≈ 1,150 units — about 11% of the free quota. Design rule used throughout: never call search.list when videos.list/playlistItems.list can answer the question.

On quotaExceeded (HTTP 403) every tool returns a structured quota_exceeded error telling the user when the quota resets.

Run the server

Local Python:

python3 -m venv .venv && .venv/bin/pip install -e ".[dev]"
cp .env.example .env   # then fill in your keys
.venv/bin/python -m youtube_mcp.server
# Point your MCP client at http://127.0.0.1:8000/mcp

Or Docker (the image bakes in a /healthz liveness probe):

cp .env.example .env   # then fill in your keys
docker build -t youtube-mcp .
docker run --env-file .env -p 8000:8000 youtube-mcp

Connect a client

Point any MCP-compatible client at http://127.0.0.1:8000/mcp (streamable HTTP). Claude Code, Cursor, Windsurf, Cline, and ChatGPT's developer mode all accept a remote MCP server URL in their MCP/integration settings — paste the URL there. See the MCP documentation for your client's exact config shape.

Run the tests

.venv/bin/python -m pytest -q   # unit tests (mocked HTTP): 71 tests

# Integration tests hit the real API (reads only, ~110 units).
# Skipped automatically when credentials are absent.
YOUTUBE_API_KEY=... YOUTUBE_OAUTH_TOKEN=... .venv/bin/python -m pytest -q -m integration

Integration tests are skipped automatically when credentials are absent. Write-path tests run against mocks only — real writes are exercised manually through the propose → approve → confirm_action flow.

Troubleshooting

Learned the hard way while building this:

  • channelNotFound on my_subscriptions / my_playlists / catch_me_up: the Google account that granted OAuth has no YouTube channel. Create one at youtube.com (any name works), then re-run the OAuth flow.

  • quotaExceeded (HTTP 403) immediately: run OAuth against your own Cloud project. Shared/demo projects (e.g. Google's OAuth 2.0 Playground project) can already have their quota exhausted, and there is nothing you can do about it. Your own project gets a fresh 10,000 units/day.

  • Auth errors right after pasting a token: make sure you pasted the full access token — a truncated paste fails every call. You can sanity-check a token's scopes at https://oauth2.googleapis.com/tokeninfo?access_token=TOKEN.

  • Refresh token stops working after ~7 days: your OAuth consent screen is still in Testing mode. Publish it to Production (step 4 of Setup).

  • Behind a corporate egress proxy: the server ignores ambient HTTP_PROXY / HTTPS_PROXY on purpose (they break inside containers). Set YOUTUBE_HTTP_PROXY / YOUTUBE_HTTPS_PROXY explicitly; if the proxy re-signs TLS with a private CA, point YOUTUBE_CA_BUNDLE at your PEM bundle.

Project layout

src/youtube_mcp/
  config.py     # env-var settings (no secrets in code)
  errors.py     # typed errors: auth, quota, not-found, API
  client.py     # httpx client: key/OAuth injection, error mapping, pagination
  quota.py      # quota cost table + daily budget
  approvals.py  # single-use expiring tokens for write approvals
  tools.py      # the 11 tool implementations (pure, fully tested)
  server.py     # FastMCP wiring → streamable HTTP
tests/
  test_client.py       # auth headers, error mapping, pagination
  test_approvals.py    # TTL, single-use, cancel
  test_tools.py        # every tool: happy path, errors, auth/quota, empty, paging
  test_integration.py  # real API, reads only, skipped without credentials

Example prompts

  1. "Catch me up on my subscriptions from this week — what did I miss?"

  2. "Find me a video under 20 minutes that explains how sourdough starter works."

  3. "Save this video for later: https://www.youtube.com/watch?v=…"

  4. "What are the three most-viewed uploads from Marques Brownlee this month, and how long is each?"

  5. "Make a private playlist called 'Weekend cooking' and add the pasta video you found yesterday."

License

MIT — see LICENSE.

Related MCP Connectors

Related MCP Servers

  • A
    license
    A
    quality
    D
    maintenance
    Enables YouTube content browsing, video searching, and metadata retrieval via the YouTube Data API v3. It also facilitates fetching video transcripts for summarization and analysis within MCP-compatible AI clients.
    7
    19 npm
    1
    MIT
  • A
    license
    Not graded
    quality
    D
    maintenance
    Enables interacting with YouTube Data API v3 through MCP tools (get-video, get-channel, get-latest-video) and bundled UI apps for video and channel profiles.
    3 npm
    4
    MIT