YouTube MCP Server
Server Configuration
Describes the environment variables required to run the server.
| Name | Required | Description | Default |
|---|---|---|---|
| MCP_HOST | No | HTTP listen address (HTTP mode only). | 0.0.0.0 |
| MCP_PORT | No | HTTP listen port (HTTP mode only). | 8088 |
| LOG_LEVEL | No | Level for the server and application loggers. | INFO |
| HTTP_PROXY | No | Generic proxy alternative for transcript fetching; unset by default. | |
| HTTPS_PROXY | No | Generic proxy alternative for transcript fetching; unset by default. | |
| DATABASE_PATH | No | SQLite cache file. Relative by default — set an absolute path in a container. | cache.db |
| MCP_TRANSPORT | No | Transport to use: stdio or http. Selects the transport in the youtube-mcp console script. | stdio |
| RESPONSE_LIMIT | No | Transcript truncation threshold, in characters. Cumulative-character policy, same for both variants. | 50000 |
| YOUTUBE_API_KEY | Yes | Required. Single YouTube Data API key. Parsed as a SecretStr, so it never appears in logs, reprs or tracebacks. | |
| CACHE_TTL_SECONDS | No | TTL for cached video statistics, in seconds. 0 means never expires. | 3600 |
| FASTMCP_STATELESS_HTTP | No | Leave it true for replicas. Set false only for a single-replica debugging session. | true |
| WEBSHARE_PROXY_PASSWORD | No | Webshare proxy password for transcript fetching; unset by default. Parsed as a SecretStr. | |
| WEBSHARE_PROXY_USERNAME | No | Webshare proxy username for transcript fetching; unset by default. | |
| YOUTUBE_TRANSCRIPT_LANG | No | Default transcript language when a tool call does not name one. | en |
Instructions
Guidance the server publishes about itself, which clients place ahead of the tool catalog so the model reads it before choosing anything.
This server publishes no instructions, or was last inspected before Glama recorded them.
Capabilities
Features and capabilities supported by this server
Protocol revision2025-11-25
| Capability | Details |
|---|---|
| tools | {
"listChanged": true
} |
| logging | {} |
| prompts | {
"listChanged": true
} |
| resources | {
"subscribe": false,
"listChanged": true
} |
Tools
Functions exposed to the LLM to take actions
| Name | Description |
|---|---|
| youtube_get_transcriptA | Fetch a YouTube video's transcript as plain text, optionally translated. Returns the caption text for one 11-character video ID, with the language that was
actually used, whether it is auto-generated, and Timestamps are OFF by default because each
Long transcripts are truncated at the server's Costs no YouTube Data API quota: transcripts are scraped from YouTube's internal
caption endpoint, not the Data API, and translation is a parameter on that same
endpoint. They are cached forever, so a repeat call is free — translated pages under
their own cache key, never mixed with the untranslated ones. Failures are expected and
specific — captions may be disabled, absent in the requested languages, or the IP may
be blocked (transient). A language the video cannot be translated into is an
|
| youtube_get_timestamped_transcriptA | Fetch a YouTube video's transcript as timed segments, optionally translated. Returns every caption segment as
Long transcripts are truncated at |
| youtube_list_transcript_languagesA | List the caption tracks available for a YouTube video. Returns one entry per track: Cached forever, costs no Data API quota. Fails cleanly when a video has captions disabled or no caption track at all — that is a normal outcome, not an outage. |
| youtube_search_in_transcriptA | Search inside a video's transcript and return only the matching segments. A case-insensitive substring search over the caption segments, done server-side, so a
narrow question ("does this video mention Kubernetes?") costs a few hundred tokens
instead of the whole transcript. Returns matching segments with their Limitations: matching is per caption segment, so a phrase spanning a segment boundary
will not match; the query is a literal substring, not a boolean/regex expression. Pass
|
| youtube_search_videosA | Search YouTube for videos by keyword. Uses a scarce quota bucket: 100 calls/day. Returns video IDs with titles, descriptions, channels and publish times, plus
Prefer other tools first. Each page — including one fetched with
|
| youtube_get_videoA | Get YouTube video metadata by ID — title, description, channel, duration, statistics. Accepts one ID or up to 50 (a single string is treated as one ID). Returns the videos
that exist plus Costs one unit from the shared 10,000-unit daily pool per 50 IDs. If you only need
view/like/comment counts, use
|
| youtube_get_video_statsA | Get view, like and comment counts for up to 50 videos in one call. Accepts one ID or a list. Uses A batch is not atomic: IDs that do not exist or are not publicly visible come back as
|
| youtube_get_commentsA | Get a video's top-level comments — text, author, likes, publish time, reply count. Ordered by Every item carries Comment text arrives with If the video's owner disabled comments, the call fails with a clear message saying so — that is normal and retrying will not change it. |
| youtube_get_comment_repliesA | Get the full set of replies to one top-level comment — text, author, likes, time.
This is the tool for reply text: the |
| youtube_get_channelA | Get a YouTube channel by ID or by handle — pass exactly one of the two. Returns the channel's title, description, custom URL, publish date and statistics
(subscriber count — rounded by YouTube — video count, total views) plus
|
| youtube_list_channel_videosA | List a channel's recent uploads, newest first — the cheap alternative to searching. Fetches the channel's uploads playlist (via Requires the channel's ID (start from |
| youtube_get_playlistA | Get one playlist's own metadata by ID — title, description, channel, size, visibility. Returns Costs one unit from the shared 10,000-unit pool. Requires the playlist ID — there
is no lookup by name. Discovery is deliberately not offered: the only endpoint that
could search for playlists is A playlist that does not exist and one that is private fail with different errors,
and neither is worth retrying: a private playlist stays private with an API key. An
|
| youtube_list_playlist_itemsA | List the videos in any playlist, in playlist order — one shared-pool unit per page. Unlike Costs one unit from the shared 10,000-unit pool per page, the large pool — never the
scarce 100/day
|
| youtube_list_categoriesA | List YouTube's video categories, optionally for one region. Returns each category's The whole list arrives in one response (no paging) and costs one unit from the
shared pool. Titles are returned in English. Some categories are not assignable to
new uploads ( |
| youtube_get_quota_statusA | Report how much of today's YouTube Data API quota is left, and when it resets. Returns one entry per bucket — Check this before a deliberate search. Costs no quota. This reads our own in-process counter, never the API, so it is free
to call and cannot itself fail with The numbers are approximate, and they are ours — not Google's. They count what this server process has spent, so they reset to zero whenever it restarts, and they can under-count calls made before a restart or by another process using the same API key. Google can still refuse a call this tool says is affordable, and a bucket shown as exhausted is exhausted for this process only. |
Prompts
Interactive templates invoked by user choice
| Name | Description |
|---|---|
No prompts | |
Resources
Contextual data attached and managed by the client
| Name | Description |
|---|---|
No resources | |
TDQS
Scored across 15 tools
Most tools target distinct resource/action pairs, and descriptions actively disambiguate the near-overlaps (get_transcript vs get_timestamped_transcript, get_comments vs get_comment_replies, list_channel_videos vs list_playlist_items). The main soft collision is youtube_get_video vs youtube_get_video_stats, since statistics can be requested via get_video's `parts` — the quota-bucket rationale explains the split but doesn't fully remove the choice ambiguity.
Every tool uses the same `youtube_` prefix followed by a clean verb_noun pattern (get_, list_, search_), all snake_case. Verb choice is predictable and maps to the operation type throughout.
15 tools is well within a healthy range and each one covers a distinct slice of the read surface (metadata, stats, transcripts, comments, playlists, search, quota). Nothing feels padded or redundant enough to warrant pruning.
The read-only domain is broadly covered: channels, videos, stats, comments/replies, playlists, transcripts, categories and quota. Notable gaps remain — there is no way to list a channel's non-uploads playlists or to search for channels directly, so playlist IDs and channel discovery depend on external input or a scarce search call.