A small MCP server giving agents YouTube video context: transcripts, deep links, and metadata
Strong tool naming (all verb-noun pattern) and comprehensive descriptions (avg 280 chars, well above 194 baseline). All 7 tools have detailed, LLM-optimized descriptions explaining WHAT, WHEN, and HOW. Input schemas present with types and descriptions for all parameters. Output schemas documented via TypedDict (list_transcripts, get_video_metadata, get_most_replayed) or Image type (get_video_frame, get_video_preview). Key gaps: no enum constraints on string parameters (languages, translate_to); no explicit error handling guidance in descriptions; no pagination support despite tools returning potentially large results (transcripts, metadata); no per-tool security/permission declarations.
Build a YouTube link that opens a video at a specific moment. Returns a watch URL like https://www.youtube.com/watch?v=<id>&t=<seconds> so a user can click straight to the moment something is discussed. Pair it with get_transcript(include_timestamps= True) -- read off the [mm:ss] of the relevant block and pass it as start -- to turn "where is X mentioned?" into a clickable link.
Get a YouTube video's "most replayed" moments -- the peaks of its viewer-interest heatmap (the curve shown above the timeline marking where people rewatch most). Use this for "what are the best / most-rewatched parts?", "jump me to the good part", or to weight a summary toward what viewers actually care about. Each peak is a high-interest *region* (region_start_seconds..region_end_seconds) with the hottest instant at peak_start_seconds, a ready-to-share url that opens the video at the start of the stretch, and the chapter it falls in. relative_intensity is 0..1 *within this video* (1.0 = its single most-rewatched moment) -- it is NOT a view count and is not comparable across videos. A peak with is_opening=True sits at the very start (t~=0): that spot is almost always inflated by playback starting there, not a genuine rewatch, so discount it as a "best part". It is returned in addition to (not counted against) top_n, so the opening can't crowd out content. To say what is actually happening at a peak, read its peak_label (mm:ss) and look it up with get_transcript(include_timestamps=True); profile is a coarse 0..1 curve for the overall shape (front-loaded vs steady vs spikes near the end). has_data may be False -- then peaks is empty and note explains why (many newer, low-traffic, or Shorts videos have no heatmap).
Fetch a YouTube video's existing captions as text so you can answer questions about it. Returns existing captions/subtitles only; it does not transcribe audio. Videos without captions have nothing to return.
No enum constraints on string parameters. 'languages' accepts arbitrary strings; should enumerate ISO 639-1 codes. 'translate_to' similarly unconstrained. Invites hallucinated language codes.
No error handling guidance in tool descriptions. Tools may fail (video not found, no captions, ffmpeg missing) but descriptions don't explain recovery steps or error categories. LLM has no guidance on retry vs user-fixable vs fatal.
No pagination support. get_transcript and list_transcripts may return large results (full video transcripts, all available languages) without limit/offset/page parameters. Can exhaust context window.
Output schema for get_video_frame and get_video_preview not fully documented. Both return Image type but no explicit schema showing image format, size, encoding. Downstream tools cannot plan based on output structure.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | C | 63 | 2026-07-28+ | v2 |
Capture a single still frame (screenshot) from a YouTube video at a moment and return it as an image, so a multimodal model can answer "what's on screen here?". Use this to see the video at a specific time -- e.g. read a slide, a caption burned into the video, or a UI being demoed. Pair it with get_most_replayed or get_transcript(include_timestamps =True) to pick an interesting moment, then grab the frame there. Requires ffmpeg on the server. The captured frame is the nearest keyframe at or just before the requested moment (it can be off by a second or two) and is downscaled to max_width to keep the response small.
Get a YouTube video's metadata: title, channel, upload date, duration, view/like counts, chapters and tags. Use this to answer questions about a video (its name, who made it, how long it is, when it came out) without fetching its transcript.
Get a visual overview of a YouTube video as ONE tiled contact-sheet image: `tiles` frames sampled evenly across the video (or across a start..end window of it), plus a text legend mapping each tile to its mm:ss timestamp. Use this to see what's going on across a video (talking head vs slides vs demo footage, scene changes, "is there a chart anywhere?") and to pick moments worth a closer look. To inspect one part in more detail, call it again with start/end around that part -- but pick the window from the transcript, chapters, or get_most_replayed first and zoom once; don't binary-search the video with repeated sheets, since every returned image stays in context. Read each tile's timestamp from the legend -- do not count grid cells yourself. Tiles are small and not readable: to read a slide, caption, or UI, follow up with get_video_frame(video, at=<that tile's timestamp>). Windows under ~1 minute may return near-duplicate tiles (frames land on keyframes, a few seconds apart). Requires ffmpeg on the server (the system binary, or the one bundled by the [media] extra).
List the transcripts available for a YouTube video. Use this when get_transcript can't find your requested language. It reports each available transcript (language, code, whether it's auto-generated, whether it's translatable) plus the set of languages you can pass to get_transcript's translate_to.
No tool-level permission or scope declarations. Tools are read-only (safe) but this is not formally declared via toolAnnotations (readOnlyHint). Agents cannot infer safety without reading descriptions.