YouTube niche & trend analytics MCP server -- a self-hosted alternative to NexLev / vidIQ / ViewStats, built on the free YouTube Data API v3 plus local history database
10 tools with mostly complete schemas and descriptions, but significant gaps in parameter documentation and error handling guidance. Tool names are action-oriented (collect_*, refresh_*, search_*, explain_*) and descriptions are detailed (avg ~250 chars), exceeding the 194-char baseline. However, many parameters lack type constraints (enums), and error recovery guidance is minimal. No tool annotations (readOnlyHint/destructiveHint) despite clear WRITE vs READ_ONLY risk markers. Output schemas are not documented. Composition is sound, tools chain well (e.g., collect_channel → refresh_stats → explain_outlier), but parameter relationships (e.g., period vs min_upload_date in collect_niche) are underdocumented.
Compute embeddings for already-collected videos that don't have one yet. 0 YouTube quota -- pure local compute over title/description already in Postgres, no API key needed. collect_channel/track_channel default to embed=False (cheap collection), so most of the corpus lacks embeddings until this runs. Needed before similar_channels or search_outliers(query=...) can see a given channel.
Pull a channel's recent uploads into the local DB, cheaply. Accepts a UC... id, an @handle, or any channel URL. Goes through the uploads playlist: 1 quota unit per 50 videos, no 500-result cap, and it does NOT touch the daily search budget. This is the right way to build a corpus.
Discover videos for a topic via YouTube search and store them locally. COSTS ONE OF YOUR 100 DAILY SEARCH CALLS PER PAGE -- this is the scarcest resource in the system, so prefer collect_channel for anything you can reach through a channel. query: search text, e.g. "гипотезы о мозге" or "faceless space explainer" label: niche bucket name (defaults to query); reuse it to keep growing one dataset period: convenience window for freshness -- "24h", "48h", "7d", "30d" (sets min_upload_date for you; this is how you feed the 24h sections) language: relevance hint, ISO 639-1, e.g. "ru" order: viewCount | date | relevance | rating video_duration: short (<4m) | medium | long (>20m) region / category_id: ISO country code and YouTube category id filters
Snapshot YouTube's own mostPopular chart into the local DB. 1 unit/page. HONEST LIMITATION: since 21 July 2025 this chart only covers Trending Music, Movies and Gaming -- YouTube retired the general Trending tab. For any other vertical it returns little or nothing, and general trends must come from collect_niche(period="24h") plus the local sections below.
No enum constraints on string parameters. 'order' accepts 'viewCount|date|relevance|rating' but is typed as bare string; 'scope' in refresh_stats accepts 'recent|tracked|niche|all' without enum. LLMs will hallucinate invalid values.
Output schemas not documented. Tools return complex objects (video snapshots with vph24h/acceleration, channel growth metrics, comment arrays) but LLMs have no schema to plan downstream calls or extract fields. Breaks tool chaining.
No tool annotations despite clear risk markers. collect_niche, collect_channel, refresh_* are WRITE operations; video_comments, explain_outlier are READ_ONLY. Missing destructiveHint/readOnlyHint annotations prevent agents from reasoning about idempotency and retry safety.
| Scored | Grade | Overall | Spec posture | Rubric |
|---|---|---|---|---|
| 2026-09-23 | D | 58 | 2026-07-28+ | v2 |
Pains/requests/video ideas mined from a video's comments via LLM. Cached for LLM_INSIGHTS_TTL_DAYS (7) -- a repeat call in that window is free. On a cache miss: 1 YouTube quota unit + an LLM call (costs real money once LLM_PROVIDER=openrouter is configured; a Null provider returns a clear hint instead of silently doing nothing).
Why this video beat its channel's own baseline: up to 3 hooks, a title pattern, a timing factor, a short replicable formula, and a confidence score -- grounded only in this video's real numbers, never invented. Cached for LLM_WHY_VIRAL_TTL_DAYS (14). Zero YouTube quota; costs an LLM call on a cache miss.
Fetch the live category id -> title map per region (1 unit per region). Optional: a built-in fallback map ships with the server.
Snapshot subscriber/view/video counts for channels, for growth tracking. 1 unit per 50 channels. Note the API rounds subscriberCount to 3 significant figures, so subscriber deltas are only meaningful below ~100k subs.
Re-read view/like/comment counts for stored videos and append a snapshot. This is what turns a snapshot API into a time series: vph24h, viewsGained24h, acceleration and title/thumbnail-change detection all come from these rows. Run it at least daily (the Docker worker does it for you). scope: recent | tracked | niche | all. Cost ~1 unit per 50 videos.
Top-level comments for one video, live from the API. 1 unit, not stored locally. A competitive signal nothing else here surfaces: what viewers actually praise, complain about or ask for. Returns raw author/text/likeCount -- judging tone or extracting themes is left to whichever Claude session calls this, same as everywhere else in this server. order: relevance | time. search_terms filters to comments containing a phrase.
Parameter relationships underdocumented. collect_niche accepts both 'period' (convenience window) and 'min_upload_date' (explicit filter), which takes precedence? refresh_stats 'scope' and 'niche' interact but dependency is not stated. Agents will guess wrong.
Error recovery guidance missing. No tool description explains what to do if quota is exhausted, API fails, or embeddings are unavailable. Agents have no fallback strategy.