yt-consolidado
Provides tools for searching YouTube videos and channels, retrieving video and channel metadata, transcripts, RSS upload feeds, and discovering emerging channels using filters such as channel age, view counts, region, subscriber range, and average video duration, with quota tracking.
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@yt-consolidado find US channels under 30 days old in personal finance with a video over 100K views"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
consolidado — MCP server de YouTube
Um servidor MCP (Python 3.12) que junta três servidores open-source de YouTube em um só catálogo de 15 tools e acrescenta a camada que faltava: descobrir canais emergentes por filtros compostos — idade do canal, pico de views, duração média, país e faixa de inscritos — com cota medida e shortfall honesto.
Responde a esta pergunta, que nenhum dos três originais responde sozinho:
Quais canais publicaram pela primeira vez há menos de 30 dias, já têm um vídeo acima de 100 mil views, falam com público dos EUA e têm duração média entre 8 e 30 minutos?
Instalação em 3 comandos
git clone <este-projeto> C:\caminho\consolidado
cd C:\caminho\consolidado
uv sync --python 3.12Depois registre no cliente MCP (bloco pronto abaixo) e reinicie o app. Tempo real de setup em máquina limpa: ~2 min de download + 10 s de configuração.
Related MCP server: YouTube MCP Server Enhanced
1. Pré-requisitos
item | versão | obrigatório | por quê |
Python | 3.12+ | sim |
|
0.9+ | sim | resolve e tranca as dependências ( | |
Chave da YouTube Data API v3 | — | sim, para as tools de API | 100 unidades por |
ffmpeg | — | não | os tools de frame capture do tube-bridge não foram portados |
Node.js | — | não | o servidor é Python puro |
Conferir se ficou tudo no lugar:
uv run python -c "import mcp, googleapiclient, yt_dlp, feedparser, youtube_transcript_api; print('ok')"2. Variáveis de ambiente
Obrigatória para as tools de Data API:
$env:YOUTUBE_API_KEYS = "AIza...key1,AIza...key2,AIza...key3" # pool CSV, rotaciona em quotaExceededAceita também key única:
$env:YOUTUBE_API_KEY = "AIza...key1"Opcionais: nenhuma. O servidor não lê cache em disco, proxy ou timeout por variável de
ambiente — só as duas acima (auth.py:128-129). Sem key, as tools de API devolvem
{"success": false, "error": "No YouTube API key configured..."} e as de cota zero
(youtube_get_channel_videos, get_channel_rss_videos, youtube_get_transcript) continuam
funcionando.
3. Claude Desktop
Edite %APPDATA%\Claude\claude_desktop_config.json (feche o app antes; ele sobrescreve o
arquivo ao sair):
{
"mcpServers": {
"yt-consolidado": {
"command": "C:\\caminho\\consolidado\\.venv\\Scripts\\python.exe",
"args": ["-m", "consolidado.server"],
"env": {
"YOUTUBE_API_KEYS": "AIza...key1,AIza...key2"
}
}
}
}4. Cursor
Edite %USERPROFILE%\.cursor\mcp.json:
{
"mcpServers": {
"yt-consolidado": {
"command": "C:\\caminho\\consolidado\\.venv\\Scripts\\python.exe",
"args": ["-m", "consolidado.server"],
"env": {
"YOUTUBE_API_KEYS": "AIza...key1,AIza...key2"
}
}
}
}Transporte HTTP (para clientes remotos):
uv run python -m consolidado.server --transport streamable-http --host 127.0.0.1 --port 8000
uv run python -m consolidado.server --print-tools # catálogo das 15 tools em JSON5. Primeiro uso — prompt e resposta esperada
Exemplo A — busca simples (custo 103):
"Busque 3 vídeos sobre 'personal finance' publicados nos últimos 30 dias, ordene por data e devolva título, canal, views e duração."
O agente chama search_videos (100) e depois youtube_get_video_info por id (1 cada).
Resposta real obtida (aqui resumida):
título | canal | views | duração |
The rich never budget — New Personal Finance drops daily… | yodi montali | 2 | 1:09 |
Forgot a paycheck? Your state may have it. | Deep Frame | 0 | 0:31 |
I hate his smile — @Caleb Hammer | Seeking Finance clips | 122 | 1:14 |
Exemplo B — o produto, execução original (custo 193; anterior à correção da base de idade):
"Quero 10 canais que estejam bombando agora, primeira publicação há menos de 30 dias, algum vídeo com mais de 100 mil views, público norte-americano, duração média entre 8 e 30 minutos."
{
"success": true,
"query": "personal finance",
"n_requested": 10,
"n_returned": 0,
"channels": [],
"near_misses": [
{ "channel_title": "Ben Talks Finance", "max_views": 28126, "reason": "max_views below 100000" },
{ "channel_title": "Deep Frame", "max_views": 1615, "reason": "max_views below 100000" }
],
"rejected_counts": { "not_new": 10, "low_views": 15, "duration": 0, "verify_error": 0 },
"quota_total": 193,
"quota_breakdown": { "discovery": 100, "enrichment": 1, "verification": 92 },
"shortfalls": "Returned 0 of 10 requested. 36 unique channels found in discovery; 26 survived country/subscriber filters; 10 rejected: first publication older than 30 days; 15 rejected: no video above 100000 views; 0 rejected: average duration outside 8.0-30.0 min; 1 candidates skipped by max_candidates_checked=25. Filters were not relaxed automatically - widen them deliberately if you want more."
}Zero não é bug. O near_misses mostra o teto real da população naquele nicho (28 mil views,
não 100 mil). O produto devolve o que os filtros permitem e diz exatamente quem caiu em qual
corte — não relaxa nada sozinho.
Exemplo C — base de idade corrigida (execução ao vivo, custo 210):
max_channel_age_days passou a medir a data do primeiro vídeo publicado, nunca a data de
criação da conta. Cada candidato tem a playlist de uploads enumerada (até first_video_pages
páginas) para provar quando começou; quem não prova é rejeitado como age_unproven em vez de
estimado pela conta.
{
"success": true,
"query": "world war 2 documentary",
"n_requested": 15,
"n_returned": 0,
"near_misses": [
{ "channel_title": "Why Though?", "first_video_published_at": "2026-10-14T23:00:24Z",
"max_views": 240116, "avg_duration_minutes": 2.3, "reason": "avg duration outside 8.0-30.0 min" },
{ "channel_title": "WW2 HIDDEN CHAPTERS", "first_video_published_at": "2026-10-23T18:45:16Z",
"max_views": 4012, "avg_duration_minutes": null, "reason": "max_views below 100000" }
],
"rejected_counts": { "not_new": 6, "low_views": 14, "duration": 1, "verify_error": 0, "age_unproven": 4 },
"quota_total": 210,
"quota_breakdown": { "discovery": 100, "enrichment": 1, "verification": 109 },
"shortfalls": "Returned 0 of 15 requested. 48 unique channels found in discovery; 28 survived country/subscriber filters; 6 rejected: first publication older than 30 days; 14 rejected: no video above 100000 views; 1 rejected: average duration outside 8.0-30.0 min; 4 rejected: first video not proven within 3 upload pages; 3 candidates skipped by max_candidates_checked=25. Filters were not relaxed automatically - widen them deliberately if you want more."
}Artefato bruto: prova-de-tela/raw/live-age-basis.json.
6. Tools e custo de cota
Unidades oficiais da YouTube Data API v3 (auth.py::ENDPOINT_COST).
tool | origem | camada | cota por chamada | o que a torna cara |
| youtube-mcp | API → yt-dlp | 100 |
|
| tube-bridge | API → yt-dlp | 100 | idem; sem key cai para yt-dlp (0) |
| youtube-mcp | API | 101 |
|
| youtube-mcp | API | 1 | um único |
| youtube-mcp | API | 101 + 1–3/canal |
|
| youtube-mcp-server (TS) | API | 1 (ou 101) | 101 quando resolve o último upload via |
| youtube-mcp | RSS | 0 | Atom público, sem key |
| tube-bridge | API → yt-dlp | 1 |
|
| tube-bridge | yt-dlp → RSS | 0 | scraping, sem key |
| tube-bridge | timedtext público | 0 | sem key (pode ser bloqueado por IP — ver limitações) |
| nova | API | 1 + 1/50 |
|
| nova | API | 1 + páginas + lotes |
|
| nova | API | 2 | resolução do canal (1) + |
| nova (produto) | API | 100/página + 1/50 + 2–6/canal | 100 fixos na descoberta; 193 medidos antes da correção de idade, 210 depois (25 candidatos, |
| nova | — | 0 | lê o |
Regras práticas:
Duas buscas (
search_videos+youtube_search) custam 200 unidades.search.listé 100 fixo, mesmo commax_results=1.dry_run=trueno orquestrador custa 100 e mostra os candidatos antes de gastar verificação.get_quota_status()devolve o extrato da sessão: unidades por tool, por endpoint e keys restantes.
7. find_emerging_channels — a tool do produto
find_emerging_channels(
query, # nicho
n=10, # canais desejados (não é garantia de retorno)
max_channel_age_days=30, # primeira publicação há menos de N dias
min_video_views=100000, # algum vídeo acima de X views
region_code="US",
relevance_language="en",
min_avg_duration_min=8.0,
max_avg_duration_min=30.0,
subscriber_min=0,
subscriber_max=100000,
dry_run=False, # True para na descoberta (100 unidades)
discovery_pages=1, # 100 unidades por página de 50 vídeos
max_candidates_checked=25,
sample_size=10, # vídeos lidos por canal na verificação
small_channel_video_cap=100, # compatibilidade; a idade não usa mais este cap
first_video_pages=3, # páginas de uploads lidas por canal para provar o primeiro vídeo
)Cinco etapas: descoberta (search.list), enriquecimento (channels.list em lote de 50),
verificação dura (primeiro vídeo publicado, pico de views, duração média), ranking e saída.
Base da idade. O corte max_channel_age_days mede a data do primeiro vídeo publicado,
nunca a data de criação da conta. Um canal aberto há anos que começou a postar semana passada
passa — desde que o primeiro vídeo seja provado dentro de first_video_pages páginas de uploads.
Sem prova, o canal sai como age_unproven; a conta não é usada como estimativa.
A mesma base vale para find_breakout_creators: channel_age_months vem do primeiro vídeo. É uma
quebra deliberada de paridade com o find_breakout_growth_channels do youtube-mcp, que envelhecia
pelo cadastro; a saída mantém created_at (conta) e acrescenta first_video_published_at.
Três garantias:
Nada é relaxado automaticamente. Se voltarem menos que
n, o camposhortfallsexplica quem caiu em qual filtro e o resultado sai como está.near_missesestá sempre no output. Os canais que passaram a idade e falharam em views/duração vêm commax_viewseavg_duration_minutes, ordenados por pico — é o teto real da população antes de você afrouxar o corte.dry_run=Truepara na etapa 1. 100 unidades para inspecionar os candidatos antes de gastar verificação.
8. Limitações conhecidas
limitação | efeito | contorno |
Transcript bloqueado por IP |
| rodar de IP residencial; a paridade de dados dessa tool ficou não provada na FASE 6 por esse motivo |
Erro in-band, não exceção | falha de upstream em | cheque |
Shortfall é normal em nicho estreito |
| leia |
Contadores ao vivo |
| compare com o mesmo instante, não entre execuções |
Sem cache entre processos | cada chamada paga cota de novo (o tube-bridge original tinha cache SQLite) | use |
| 100 unidades por chamada, | prefira |
Chamadas serializadas | o cliente da API roda uma requisição por vez (lock process-wide); em paralelo elas enfileiram em vez de estourar SSL | comportamento intencional — contra |
Retry automático | erro de rede (SSL, timeout, conexão) é re-tentado 3× com espera de 2 s e 4 s antes de virar erro | não há retry para erro de cota: cota esgotada rotaciona de key, não repete |
Timeout de socket 30 s | definido em | acima disso a chamada já é lenta demais para um cliente MCP |
Verificação em lotes de 10 |
| se você quer o censo completo, chame com |
9. Testes
uv run pytest tests\ -v31 testes, nenhum toca a rede: parser ISO 8601, escolha de proxy, corte por data, média e
arredondamento, cota por chamada, staging do orquestrador, shortfall sem preenchimento
artificial, ranking e cap de candidatos. O FakeClient cobra as mesmas unidades oficiais.
10. Estrutura
src/consolidado/
auth.py pool de keys + QuotaTracker + ENDPOINT_COST
youtube_client.py wrapper da API v3, rotação em 403/quota
ytdlp_client.py camada 3 (0 cota)
rss_client.py camada 4 (0 cota)
server.py registro das 15 tools, stdio/sse/http
tools/
search.py search_videos, youtube_search
channels.py search_channels, get_channel_details, find_breakout_creators,
channels_getChannel, get_channel_rss_videos
videos.py youtube_get_video_info, youtube_get_channel_videos,
youtube_get_transcript
custom.py get_first_published_date, max_views_in_channel, avg_duration
orchestrator.py find_emerging_channels
tests/
conftest.py FakeClient com cota real
test_custom.py
test_orchestrator.py11. Origem e paridade
10 tools portadas de youtube-mcp (5), tube-bridge (4) e youtube-mcp-server (1), com
paridade semântica verificada caso a caso em 06-paridade.md (30 pares, 0 divergência de dado
essencial, 8 desvios documentados). 5 tools novas: get_first_published_date,
max_views_in_channel, avg_duration, find_emerging_channels, get_quota_status.
Decisões de projeto, desvios de spec e evidências brutas: ../../05-reconstruir.md,
../../06-paridade.md e ../../07-checklist-final.md.
Available Tools
15 toolsavg_durationB
Mean duration of the last N uploads. New tool.
Layer: Data API. Quota: 2 units for N ≤ 50 (1 playlist page + 1 videos batch).
Inputs: channel_id (required), sample_size. Returns: object
channels_getChannelB
Raw channel + normalizedMetadata — port of youtube-mcp-server (zubeid).
Layer: Data API. Quota: 1 unit; **+100 units** when ``include_latest_upload``
is true (the original resolves the newest upload with one search.list).
Set it to false when you only need country/joinedAt/emailsFound.
Inputs: channelId (required), include_latest_upload. Returns: object
find_breakout_creatorsC
New channels with subscriber velocity — port of youtube-mcp find_breakout_growth_channels.
Layer: Data API. Quota: 101 units.
Inputs: niche (required), max_results, region_code, max_subscribers, +2 more. Returns: object
find_emerging_channelsB
Emerging channels by composite filters — the product tool.
Layer: Data API. Quota: 100 per discovery page + 1 per 50 channels enriched
+ 3–5 per channel verified (~130–450 for a default run).
``small_channel_video_cap`` also enumerates channels with few uploads, so an
old channel that only started publishing last week is still caught.
Shortfalls are reported, never padded: filters are not relaxed automatically.
Inputs: query (required), n, dry_run, region_code, +11 more. Returns: object
get_channel_detailsB
Channel metadata by id/@handle/username — port of youtube-mcp.
Layer: Data API. Quota: 1 unit. Includes ``uploads_playlist_id``.
Inputs: raw, channel_id, for_handle, for_username. Returns: object
get_channel_rss_videosB
Latest uploads from the Atom feed — port of youtube-mcp get_channel_rss_videos.
Layer 4 (RSS). Quota: 0 units, no key. Feed exposes at most 15 entries.
Inputs: channel_id (required), max_results. Returns: object
get_first_published_dateC
Oldest publication date of a channel. Did not exist in any original.
Layer: Data API. Quota: 1 unit (channel creation proxy), plus 1 unit per 50
uploads when enumeration runs — when the channel is younger than
``enumerate_if_younger_than_days`` or has at most
``enumerate_if_video_count_under`` uploads (0 = never enumerate on size).
Inputs: channel_id (required), max_pages, enumerate_if_video_count_under, enumerate_if_younger_than_days. Returns: object
get_quota_statusA
Session quota ledger: units spent per tool and per endpoint, layers used.
No API call, 0 units. The Data API does not expose remaining quota, so this
is a session-local tally against the 10,000/day free tier per key.
Inputs: no named parameters. Returns: object
max_views_in_channelA
Highest view count among uploads since since_date. New tool.
Layer: Data API. Quota: 1 + 1 per 50 uploads scanned + 1 per 50 videos in
the window (``max_pages`` default caps at 500 uploads).
Inputs: channel_id (required), since_date (required), max_pages. Returns: object
search_channelsC
Search channels enriched with stats — port of youtube-mcp search_channels.
Layer: Data API. Quota: 101 units (100 search + 1 channels batch).
Inputs: query (required), order, max_results, region_code. Returns: object
search_videosB
Search videos/channels/playlists — port of youtube-mcp search_videos.
Layer: YouTube Data API (key required). Quota: 100 units.
Returns {success, query, total_results, next_page_token, results[]}.
Inputs: query (required), raw, order, max_results, +3 more. Returns: object
youtube_get_channel_videosB
Recent uploads via yt-dlp — port of tube-bridge youtube_get_channel_videos.
Layer 3 (0 units, no key). Falls back to Layer 4 (RSS) if yt-dlp is 403'd
and the argument is a ``UC…`` channel id.
Inputs: channel_url (required), limit. Returns: object
youtube_get_transcriptC
Video transcript — port of tube-bridge youtube_get_transcript.
Layer: public timedtext. Quota: 0 units, no key.
Inputs: url (required), lang, with_timestamps. Returns: object
youtube_get_video_infoC
Rich metadata for one video — port of tube-bridge youtube_get_video_info.
Layer 1/2 (1 unit) → Layer 3 (yt-dlp, 0 units). ``duration`` in seconds.
Inputs: url (required). Returns: object
youtube_searchC
Search with date/channel/duration filters — port of tube-bridge youtube_search.
Layer 1/2 (Data API, 100 units) → Layer 3 (yt-dlp, 0 units) on exhaustion.
Returns {query, source, total_results, next_page_token, videos[]}.
Inputs: query (required), limit, order, channel_id, +5 more. Returns: object
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
Loading tool schema changelog…
TDQS
Scored across 15 tools
Several tool pairs have near-identical purposes: search_videos vs youtube_search (both video search, one with filters/fallback), get_channel_details vs channels_getChannel (both channel metadata by id/handle), and get_channel_rss_videos vs youtube_get_channel_videos (both 'recent uploads of a channel'). Descriptions explain layer/quota differences, but an agent choosing by intent alone would frequently misselect, and find_breakout_creators vs find_emerging_channels vs search_channels further blur the discovery space.
Conventions are mixed: some tools carry a youtube_ namespace prefix (youtube_search, youtube_get_video_info), others are bare snake_case (search_videos, avg_duration), and one is camelCase with a resource prefix (channels_getChannel). Verb styles also vary (get_/find_/search_/avg_/max_), so the name gives little predictable signal about which layer or operation a tool uses.
15 tools is at the upper edge of the reasonable band and the count is inflated by near-duplicate ports (two search tools, two channel-detail tools, two recent-uploads tools) rather than by distinct capabilities. A consolidated surface would need roughly 10-11 tools for the same coverage.
Coverage is broad for a YouTube data-collection server: search, channel/video metadata, transcripts, RSS, channel analytics (avg duration, max views, first publish date), discovery, and quota accounting. Gaps exist around playlists (only searchable, no playlist fetch/list) and comments, but core workflows are reachable.
Maintenance
Related MCP Connectors
YouTube channels for AI agents, no API key: exact views, subscribers, videos, playlists, hashtags.
YouTube discovery, transcripts, library search, and monitors with API keys or OAuth.
YouTube search, whole channels, video stats, comments and full transcripts with timestamps.
YouTube research: channel search, viral videos, channel analytics, transcripts, thumbnails, TTS
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceEnables AI assistants to search YouTube for videos, channels, and playlists while retrieving detailed analytics and metrics through the YouTube Data API v3. Supports advanced filtering options and provides comprehensive statistics for content discovery and analysis.1MIT
- AlicenseNot gradedqualityDmaintenanceEnables comprehensive YouTube data extraction and analysis using yt-dlp, including video metadata, channel statistics, playlists, comments, transcripts, search, trending videos, and engagement analytics with intelligent caching and batch processing.6MIT
- AlicenseAqualityDmaintenanceProvides production-grade tools for YouTube channel resolution, video metadata extraction, transcripts, and playlist management. It features a quota-aware, AI-friendly design that supports structured searching and listing of public YouTube data.72MIT
- AlicenseAqualityDmaintenanceSearch and discover YouTube channels via natural language, track them, and summarize videos without transcripts using Gemini API.13MIT