mcp-video-analyzer
Transforme qualquer vídeo — YouTube, Instagram, TikTok, Loom, URLs diretos ou arquivos locais — em transcrições, quadros-chave, texto OCR e metadados para agentes de IA.
Documentação
mcp-video-analyzer
Transforme qualquer vídeo — YouTube, Instagram, TikTok, Loom, X, Vimeo, links diretos, arquivos locais — em transcrições, quadros-chave, texto OCR e metadados para agentes de IA.
Nenhum MCP de vídeo existente combina transcrições + quadros visuais + metadados em uma única ferramenta. Este combina — em Loom, nas principais plataformas yt-dlp (YouTube/Vimeo/TikTok/Instagram/X/Twitch/Dailymotion/Facebook), URLs de vídeo diretos e arquivos locais.
Quer um pipeline completo, não apenas uma ferramenta? social-knowledge-base é construído sobre este servidor — ele baixa contas inteiras de criadores do Instagram (reels, stories, destaques), transcreve-as e transforma o resultado em uma base de conhecimento pesquisável e consultável via RAG, com notas geradas por IA. Use este MCP quando quiser análise por vídeo dentro de um agente; use social-knowledge-base quando quiser arquivar e consultar uma conta inteira.
Instalação
Pré-requisitos
- Node.js 22.12+ — necessário para executar o servidor via
npx - yt-dlp — obrigatório para URLs do YouTube/Vimeo/TikTok/Instagram/X/Twitch/Dailymotion/Facebook; opcional para todo o resto (melhora a qualidade do download do Loom). Instale com
pip install yt-dlp - Chrome/Chromium (opcional) — alternativa para extração de quadros se o yt-dlp não estiver disponível
Sem yt-dlp ou Chrome, URLs diretos e arquivos locais ainda geram quadros — o
ffmpeg-staticintegrado faz a extração, e o Loom usa o download do próprio CDN como alternativa. URLs de plataformas (YouTube etc.) degradam para um aviso claro de "instale o yt-dlp". Transcrições, metadados e comentários nunca exigem nenhum dos dois.
Há três formas de acesso: o plugin /video (Claude Code — comando de barra + servidor MCP configurado automaticamente), uma configuração MCP server simples (qualquer cliente MCP) ou a skill portátil + CLI (Codex, Cursor, Copilot e qualquer agente com shell — sem necessidade de MCP).
Claude Code — plugin /video (recomendado)
/plugin marketplace add guimatheus92/mcp-video-analyzer
/plugin install video@mcp-video-analyzer
Isso adiciona o comando de barra /video e registra automaticamente o servidor MCP — sem necessidade de claude mcp add:
/video https://youtu.be/jNQXAC9IVRw what happens at 0:10?
/video ~/Movies/screen-recording.mp4 when does the UI break?
Outros agentes — Codex, Cursor, Copilot, Gemini CLI, …
npx skills add guimatheus92/mcp-video-analyzer
Instala a skill video (formato Agent Skills) em todos os agentes detectados na sua máquina. Agentes sem o servidor MCP configurado usam automaticamente a CLI integrada como alternativa — zero configuração.
Claude Code (somente MCP)
claude mcp add video-analyzer -- npx mcp-video-analyzer@latest
Em seguida, reinicie o Claude Code ou inicie uma nova conversa.
VS Code / Cursor
Adicione ao seu arquivo de configurações MCP:
- VS Code:
File → Preferences → Settings → search "MCP"ou edite~/.vscode/mcp.json/%APPDATA%\Code\User\mcp.json(Windows) - Cursor:
Settings → MCP Servers → Add
{
"servers": {
"mcp-video-analyzer": {
"type": "stdio",
"command": "npx",
"args": ["mcp-video-analyzer@latest"]
}
}
}
Em seguida, recarregue a janela (Ctrl+Shift+P → "Developer: Reload Window").
Claude Desktop
Adicione ao seu arquivo de configuração do Claude Desktop:
- macOS:
~/Library/Application Support/Claude/claude_desktop_config.json - Windows:
%APPDATA%\Claude\claude_desktop_config.json
{
"mcpServers": {
"video-analyzer": {
"command": "npx",
"args": ["mcp-video-analyzer@latest"]
}
}
}
Em seguida, reinicie o Claude Desktop.
CLI (uso único, sem cliente MCP)
O mesmo mecanismo é exposto como um comando de uso único — é isso que a skill video usa em agentes sem MCP, e funciona de forma independente em qualquer terminal:
npx -y mcp-video-analyzer@latest analyze "https://youtu.be/jNQXAC9IVRw"
O stdout é um único documento JSON — metadata, transcript, ocrResults, timeline, warnings, frameCount e frames como entradas { time, filePath, mimeType } apontando para quadros-chave JPEG copiados para --out (padrão: o diretório de cache por usuário — %LOCALAPPDATA% no Windows, ~/Library/Caches no macOS, $XDG_CACHE_HOME ou ~/.cache no Linux — sob mcp-video-analyzer/<url-hash>/; defina MCP_CACHE_DIR para um caminho absoluto para realocá-lo). Diferente do diretório temporário onde costumava ficar, nada remove esse local, então os quadros persistem até você excluí-los — os diretórios são criados 0700. O progresso é transmitido no stderr, então stdout pode ser canalizado diretamente para um analisador JSON. Falhas parciais ficam em warnings com código de saída 0; apenas falhas graves saem com código 1.
| Flag | Descrição |
|---|---|
--detail <level> | brief (metadados + transcrição, sem quadros), standard (padrão), detailed |
--max-frames <n> | Máximo de quadros-chave, 1–60 (o padrão se adapta à duração) |
--max-width <px> | Limite de largura para quadros emitidos (padrão 800, ou MCP_FRAME_MAX_WIDTH); 0 mantém a resolução original — veja Tamanho do quadro |
--fields <list> | Filtro de saída — subconjunto separado por vírgulas: metadata,transcript,frames,comments,chapters,ocrResults,timeline,aiSummary. Filtra apenas o JSON emitido; use --detail brief para realmente pular download/extração de quadros |
--force-refresh | Ignorar o cache e reanalisar |
--ocr-language <codes> | Idiomas do Tesseract (padrão eng+por) |
--model <name> / --language <code> | Substituições do Whisper para a transcrição alternativa |
--out <dir> | Onde as imagens dos quadros são copiadas |
Execute sem argumentos (npx mcp-video-analyzer@latest) para iniciar o servidor MCP stdio — a CLI é puramente aditiva.
Verifique se funciona
Após a instalação, pergunte ao seu assistente de IA:
Analyze this video: https://www.youtube.com/watch?v=jNQXAC9IVRw
(também funciona com um link do Instagram/TikTok/Loom, uma URL .mp4 direta ou um caminho de arquivo local). Se o servidor estiver conectado, ele chamará automaticamente a ferramenta analyze_video.
Ferramentas
Oito ferramentas — a IA escolhe a mais barata para o trabalho e a chama automaticamente. Clique em qualquer ferramenta para expandir seus parâmetros e exemplos.
| Ferramenta | O que faz |
|---|---|
analyze_video | Análise completa: transcrição + quadros-chave + OCR + linha do tempo + metadados |
analyze_videos | Versão em lote, um resultado estruturado por fonte (retomável) |
get_transcript | Somente transcrição (legendas nativas ou alternativa Whisper) |
get_metadata | Metadados + comentários + capítulos, sem download |
get_frames | Somente quadros-chave (mudança de cena ou denso a 1 fps) |
analyze_moment | Análise aprofundada de um intervalo de tempo (rajada de quadros + transcrição + OCR) |
get_frame_at | Quadro único em um timestamp |
get_frame_burst | N quadros em uma janela estreita (movimento/animação) |
analyze_video — análise completa de vídeo
Extrai tudo de uma URL de vídeo em uma única chamada:
> Analyze this video: https://www.youtube.com/watch?v=abc123...
Retorna:
- Transcrição com timestamps e falantes
- Quadros-chave extraídos via detecção de mudança de cena (deduplicados automaticamente). Para clipes estáticos sem cortes de cena — por exemplo, Reels/Stories com cabeça falante onde apenas uma sobreposição de texto na tela muda — ele automaticamente usa amostragem temporal uniforme como alternativa, para que você ainda receba quadros (e OCR) em vez de um resultado vazio.
- Texto OCR extraído dos quadros (código, mensagens de erro, texto de interface, preços/datas/CTAs visíveis na tela)
- Linha do tempo anotada mesclando transcrição + quadros + OCR em uma visão unificada de "o que aconteceu quando"
- Metadados (título, duração, plataforma)
- Comentários dos espectadores
- Capítulos e resumo de IA (quando disponíveis)
A IA chamará automaticamente esta ferramenta ao ver uma URL de vídeo — sem necessidade de pedir.
Opções:
detail— profundidade da análise:"brief"(metadados + transcrição truncada, sem quadros),"standard"(padrão),"detailed"(amostragem densa, mais quadros)fields— array de campos específicos para retornar, ex.:["metadata", "transcript"]. Disponíveis:metadata,transcript,frames,comments,chapters,ocrResults,timeline,aiSummarymaxFrames(1-60) — limite de quadros extraídos. O padrão escala com a duração do vídeo no detalhestandard(~12 para ≤30s até 60 para >10min); fixo em 60 nodetailed, 0 nobrief. Um valor explícito sempre vencethreshold(0.0-1.0, padrão 0.1) — sensibilidade à mudança de cenaforceRefresh— ignorar cache e reanalisarskipFrames— pular extração de quadros para análise somente de transcriçãomodel/language/initialPrompt— substituições do Whisper por chamada para a transcrição alternativa (substituemWHISPER_MODEL/WHISPER_LANGUAGE/WHISPER_PROMPTapenas para esta chamada — escolha um modelo mais pesado ou um glossário de domínio para um clipe difícil sem reiniciar o servidor)
analyze_videos — análise em lote
> Analyze every .mp4 in this folder
Executa analyze_video sobre uma lista de sources com um limite de concurrency (padrão 2), retornando um resultado estruturado por fonte — contagens + avisos em caso de sucesso, ou um error por item em caso de falha (um arquivo ruim nunca aborta o lote). As imagens dos quadros não são embutidas, e transcrição/OCR/linha do tempo completos são retornados apenas quando fields está definido; caso contrário, você recebe contagens. Combine com MCP_WRITE_SIDECARS=1 (abaixo) para que o resultado de cada vídeo persista no disco e uma nova execução retome em vez de recalcular.
get_transcript — somente transcrição
> Get the transcript from this video
Extração rápida de transcrição. Usa transcrição Whisper como alternativa quando não há transcrição nativa disponível. Aceita as mesmas substituições model / language / initialPrompt por chamada que analyze_video.
get_metadata — somente metadados
> What's this video about?
Retorna metadados, comentários, capítulos e resumo de IA sem baixar o vídeo.
get_frames — somente quadros
> Extract frames from this video with dense sampling
Dois modos:
- Detecção de mudança de cena (padrão) — captura transições visuais
- Amostragem densa (
dense: true) — 1 quadro/seg para cobertura completa
analyze_moment — análise aprofundada de um intervalo de tempo
> Analyze what happens between 1:30 and 2:00 in this video
Combina extração de rajada de quadros + transcrição filtrada + OCR + linha do tempo anotada para um segmento focado. Use quando precisar entender exatamente o que acontece em um momento específico.
get_frame_at — quadro único em um timestamp
> Show me the frame at 1:23 in this video
A IA lê a transcrição, identifica um momento crítico e solicita o quadro exato para ver o que está na tela.
get_frame_burst — N quadros em um intervalo de tempo
> Show me 10 frames between 0:15 and 0:17 of this video
Para movimento, vibração, animações ou rolagem rápida — o modo rajada captura N quadros em uma janela estreita para que a IA possa ver mudanças quadro a quadro.
Níveis de Detalhe
| Nível | Quadros | Transcrição | OCR | Linha do tempo | Caso de uso |
|---|---|---|---|---|---|
brief | Nenhum | Primeiras 10 entradas | Não | Não | Verificação rápida — sobre o que é este vídeo? |
standard | Adaptativo à duração: ~12 (≤30s) até 60 (>10min), mudança de cena | Completa | Sim | Sim | Padrão — análise completa |
detailed | Até 60 (1fps denso) | Completa | Sim | Sim | Análise profunda — cada segundo capturado |
Cache
Os resultados são armazenados em cache na memória por 10 minutos. Chamadas subsequentes com a mesma URL e opções retornam instantaneamente. Use forceRefresh: true para ignorar o cache. skipFrames faz parte do cache e da chave auxiliar, então uma análise apenas de transcrição e uma com quadros da mesma URL nunca respondem uma pela outra.
Arquivos auxiliares persistentes (processamento em lote retomável)
O cache em memória é perdido ao reiniciar, o que torna o reprocessamento de um grande corpus local custoso. Defina MCP_WRITE_SIDECARS=1 para também persistir resultados ao lado de cada vídeo local para que o trabalho sobreviva a reinicializações e possa ser retomado:
<stem>.vtt— a transcrição, somente quando foi gerada pelo fallback Whisper (um<stem>.vttexistente do seu próprio pipeline nunca é sobrescrito). Uma chamada posterior o reutiliza via leitor auxiliar normal e ignora o Whisper completamente.<stem>.analysis.json+<stem>.frames/— o resultado completo (quadros + OCR + linha do tempo), chaveado pelomtime:sizedo vídeo e pelos parâmetros de análise. Em uma chamada posterior com carimbo + parâmetros correspondentes, o resultado é retornado diretamente do disco (sem extração, sem OCR).
Isso torna analyze_videos sobre milhares de arquivos retomável, e permite que um pipeline externo de transcrição por GPU e este MCP compartilhem resultados através do sistema de arquivos: o pipeline escreve <stem>.vtt, e o MCP o utiliza em vez de executar o Whisper.
Fontes Suportadas
| Fonte | Transcrição | Metadados | Comentários | Quadros | Autenticação |
|---|---|---|---|---|---|
| Loom | Sim | Sim | Sim | Sim (geralmente precisa de yt-dlp — veja nota) | Nenhuma |
| YouTube / Vimeo / TikTok / Instagram / X / Twitch / Dailymotion / Facebook | Legendas nativas (enviadas > geradas automaticamente) ou fallback Whisper | Sim (título, duração, uploader, visualizações, capítulos, data de upload) | Não | Sim (limitado a 1080p) | yt-dlp instalado; cookies para Instagram / restrito por idade (veja abaixo) |
| URL direta (.mp4, .mov, .mkv, .webm, …) | Não | Somente duração | Não | Sim | Nenhuma |
| URL direta + TwelveLabs | Sim (Pegasus, melhor esforço) | Piso de duração + título | Não | Sim | TWELVELABS_API_KEY |
Arquivo local (caminho absoluto ou URI file://) | Auxiliar .vtt/.srt ou fallback Whisper | Sondado via ffmpeg (duração, dimensões, codec, presença de áudio) | Não | Sim | Nenhuma |
Quadros do Loom: transcrição, metadados e comentários vêm diretamente da API do Loom sem ferramentas extras. A extração de quadros é diferente — o Loom serve a maioria dos vídeos como streams DASH separados de vídeo+áudio, que apenas yt-dlp (
pip install yt-dlp) busca e mescla. A mesclagem usa offmpeg-staticincluído, então nenhum ffmpeg do sistema é necessário. Sem yt-dlp, um fallback direto de CDN ainda cobre alguns vídeos; quando não consegue, você recebe transcrição + metadados + comentários além de um aviso explicando por que os quadros estão ausentes.Arquivos locais: passe um caminho absoluto (ex.:
/Users/you/clip.mp4) ou uma URIfile://como argumentourlpara qualquer ferramenta. Caminhos relativos são rejeitados — o diretório de trabalho do servidor é imprevisível a partir do cliente MCP. Observe que qualquer chamador do servidor MCP pode pedir que ele leia qualquer arquivo ao qual o processo do servidor tenha acesso. Caminhos UNC / compartilhamentos de rede (\\host\share\clip.mp4) são a exceção: eles alcançam a rede em vez do disco local, então seguem as regras de destino de rede e precisam deMCP_ALLOW_PRIVATE_URLS=1.Transcrições auxiliares: se um
clip.vtt,clip.srt,clip.en.vtt, etc. existir ao lado declip.mp4, ele é usado como transcrição automaticamente — sem ida ao Whisper. SRT é convertido para VTT em memória.Legendas incorporadas: se nenhum auxiliar for encontrado e o contêiner tiver um stream de legenda incorporado (comum em
.mkv/.mov/.mp4de gravadores de tela), ele é transmuxado para VTT via ffmpeg e usado como transcrição.Extensões reconhecidas (arquivos locais e URLs diretas):
.mp4.mov.mkv.webm.avi.m4v.wmv.flv.mpeg.mpg.m2ts.mts.3gp.ogv. A extensão apenas controla o roteamento — o ffmpeg faz a demuxagem real, então a maioria dos contêineres comuns funciona..tsé excluído para evitar colisão com arquivos-fonte TypeScript.
Destinos de rede
Apenas URLs http:// e https:// são buscadas, e somente para endereços públicos. Requisições para loopback (localhost, 127.0.0.1, ::1), faixas privadas/LAN, link-local, CGNAT, nomes mDNS .local e caminhos UNC do Windows são recusadas, assim como esquemas não-HTTP como ftp:// e data:.
Isso importa porque o argumento url é alcançável por atacantes no caso normal: um agente que dirige este servidor pode ser conduzido pelo conteúdo que lê, então uma URL que ele passa não é necessariamente uma que o usuário escolheu. Sem a restrição, o servidor é um proxy para qualquer rede em que esteja.
A verificação ocorre no endereço resolvido, não apenas no texto, então um hostname público que resolve para 10.0.0.5 também é recusado — e cada salto de uma cadeia de redirecionamento é reverificado, já que uma URL pública respondendo 302 Location: http://127.0.0.1/ passaria direto por uma verificação apenas no primeiro salto.
# Serving videos from a NAS or a local dev server? Opt back in:
MCP_ALLOW_PRIVATE_URLS=1
Endpoints de metadados de instância de nuvem (169.254.169.254, Azure 168.63.129.16 e similares) permanecem bloqueados mesmo com isso definido — não há vídeo legítimo lá, e são o que um SSRF geralmente procura.
Limitação conhecida: o endereço é verificado no momento da resolução, não no momento da conexão, então rebinding de DNS — um domínio respondendo um endereço público à verificação e um privado à conexão — não é coberto. Execute o servidor atrás de um proxy de saída se isso estiver no seu modelo de ameaça.
URLs de plataformas via yt-dlp (YouTube, Instagram, TikTok, …)
Páginas de vídeo único em grandes plataformas passam por yt-dlp (pip install yt-dlp — necessário para essas URLs). Playlists, canais e páginas de perfil são rejeitados por design; passe URLs de vídeos individuais (agrupe-os com analyze_videos).
- Transcrição: legendas nativas são preferidas e gratuitas — legendas enviadas primeiro, legendas geradas automaticamente como fallback (duplicação de janela deslizante é colapsada).
WHISPER_LANGUAGE(ex.:pt) também é usado para escolher o idioma das legendas. Vídeos sem legendas caem na cadeia normal do Whisper. - Metadados: título, duração, uploader/canal, contagem de visualizações, data de upload e capítulos — sem necessidade de download.
- Download: limitado a 1080p (quadros/OCR não precisam de mais), streams ao vivo são ignorados, e áudio+vídeo DASH é mesclado com o
ffmpeg-staticincluído (nenhum ffmpeg do sistema necessário). - Cookies — Instagram e vídeos restritos por idade geralmente exigem uma sessão logada:
| Variável de ambiente | O que faz | Exemplo |
|---|---|---|
YTDLP_COOKIES | Arquivo de cookies (formato Netscape), vence quando ambos estão definidos | C:/secrets/cookies.txt |
YTDLP_COOKIES_FROM_BROWSER | Extrai cookies de um navegador instalado | chrome, edge, firefox |
A extração de cookies do navegador exige que o navegador esteja fechado no Windows (o banco de dados de cookies fica bloqueado enquanto ele executa). Se isso for inconveniente, exporte um
cookies.txtuma vez (ex.: com uma extensão de navegador "Get cookies.txt") e aponteYTDLP_COOKIESpara ele. Vídeos privados/restritos por idade sem cookies válidos não travam a ferramenta — a linhaERROR:do yt-dlp aparece emwarnings[].
TwelveLabs Pegasus (opcional)
Defina a variável de ambiente TWELVELABS_API_KEY para analisar URLs de vídeo diretas com TwelveLabs Pegasus. O Pegasus analisa o vídeo no lado do servidor (visuais e seu próprio áudio) e retorna uma transcrição com carimbo de tempo gerada por IA além de um resumo de IA como texto — capacidades que o DirectAdapter não pode fornecer (uma URL .mp4 bruta não tem transcrição ou resumo por conta própria), e sem necessidade de chave Whisper.
A transcrição é saída de LLM de melhor esforço, não um despejo ASR determinístico: o Pegasus é instruído a emitir linhas [MM:SS] line, e linhas que não correspondem a esse formato são descartadas, então a redação e os carimbos de tempo exatos dependem da adesão do modelo às instruções. Falhas (chave inválida, timeout, erro de API) aparecem no warnings[] da ferramenta em vez de retornar silenciosamente uma transcrição vazia.
O maior ganho está nos caminhos somente texto: get_transcript e get_metadata retornam uma transcrição e resumo Pegasus para URLs diretas — alguns KB de texto, sem imagens de quadros, sem custo de token por quadro. analyze_video em detail: "standard"/"detailed" ainda extrai quadros adicionalmente (use detail: "brief" para permanecer somente texto).
Vídeos longos: o resumo e a transcrição completa compartilham uma única conclusão limitada (
max_tokens= 16384), então para vídeos muito longos a transcrição pode ser truncada. Para conteúdo de várias horas, dividir por janelas de tempo é a melhor abordagem.
É totalmente opt-in e não quebra nada: quando TWELVELABS_API_KEY está definido, o TwelveLabsAdapter lida com URLs de vídeo diretas (ele registra a URL pública com o TwelveLabs — sem upload); quando não está definido, o DirectAdapter lida com elas exatamente como antes. URLs do Loom não são afetadas. Obtenha uma chave em playground.twelvelabs.io.
Transcrição (fallback Whisper)
Quando uma fonte não tem transcrição nativa (sem auxiliar .vtt/.srt, sem legendas incorporadas, sem legendas de plataforma), a faixa de áudio é transcrita com Whisper via uma cadeia de fallback graciosa (em ordem de execução):
Faixas silenciosas: antes de qualquer execução do Whisper, o áudio é sondado com ffmpeg
volumedetect(primeiros 2 minutos). Uma faixa presente mas muda — comum em Reels/Stories silenciados — pula a transcrição completamente e emite um aviso de que a transcrição vazia é conteúdo esperado, não um erro, economizando uma execução inútil do Whisper.
- @huggingface/transformers (nativo JS, zero dependências externas) — somente opt-in: esta estratégia executa primeiro, mas apenas quando
WHISPER_HF_MODELestá explicitamente definido. Quando não está definido (o padrão), a estratégia é ignorada completamente, então a CLI abaixo vence e suas configuraçõesWHISPER_MODEL/WHISPER_LANGUAGEnunca são silenciosamente sobrescritas. - CLI
whisper— usada quando um executávelwhisperé encontrado (pip install -U openai-whisper). AponteWHISPER_BINpara o executável se ele não estiver emPATH. Modelo viaWHISPER_MODEL, idioma viaWHISPER_LANGUAGE. Offmpeg-staticincluído é colocado noPATHda CLI automaticamente, então nenhum ffmpeg do sistema é necessário. - API OpenAI Whisper — usada quando
OPENAI_API_KEYestá definido.
Nenhum backend configurado? Se nenhum dos três estiver disponível (sem
whisperemPATH/WHISPER_BIN, semOPENAI_API_KEY, semWHISPER_HF_MODEL), as ferramentas de transcrição retornam uma transcrição vazia com um aviso dizendo como habilitar uma — em vez de um "sem transcrição" silencioso. Instaleopenai-whisperou defina uma das chaves acima. (A CLI é iniciada comPYTHONUTF8=1para que transcrições não-inglesas/CJK não travem o processo Python no Windows.) | Variável de ambiente | Aplica-se a | Padrão | Exemplo | |---------|-----------|---------|---------| |WHISPER_MODEL|whisperCLI |tiny|small,medium| |WHISPER_LANGUAGE|whisperCLI / API OpenAI | detecção automática |pt,en,es| |WHISPER_PROMPT|whisperCLI / API OpenAI | — |Doha, Smiles, Livelo, Latam, milheiro| |WHISPER_BIN|whisperCLI |whisper(no PATH) |C:/.../Scripts/whisper.exe| |WHISPER_DEVICE|whisperCLI (enviado apenas se definido) | — |cuda,cpu| |WHISPER_COMPUTE| apenaswhisper-ctranslate2| — |float16,int8_float16,int8| |WHISPER_BEAM_SIZE|whisperCLI (enviado apenas se definido) | — |5| |WHISPER_WORD_TIMESTAMPS|whisperCLI (enviado apenas se definido) | desligado |1| |WHISPER_HF_MODEL| HF transformers (opt-in) | — (estratégia desligada) |Xenova/whisper-small| |OPENAI_API_KEY| API OpenAI | — |sk-…|
O modelo padrão
tinyé rápido, mas fraco para áudio não-inglês. Para fontes em português (ou outros idiomas não-inglês), instale o CLI e definaWHISPER_MODEL=small(oumedium) +WHISPER_LANGUAGE=ptpara obter precisão muito melhor. AdicioneWHISPER_PROMPTcom um glossário de domínio (nomes de marcas/lugares) para corrigir nomes próprios. Você também pode substituirmodel/language/initialPromptpor chamada emanalyze_video/get_transcript/analyze_videos— sem necessidade de reiniciar.GPU (faster-whisper):
whisper-ctranslate2(pip install -U whisper-ctranslate2) é um CLI substituto com as mesmas flags, além de--device cuda/--compute_type/--beam_size. AponteWHISPER_BINpara ele e definaWHISPER_DEVICE=cuda(+ opcionalmenteWHISPER_COMPUTE=float16). Essas flags de GPU são controladas por variável de ambiente — elas só são passadas quando definidas, então oopenai-whispersimples (que rejeita--compute_type) continua funcionando quando elas não estão definidas.Nota para Windows: o pip instala
whisper.exeno diretórioScripts/do Python, que muitas vezes não está noPATHque os MCP clients iniciados por GUI herdam. Se as transcrições voltarem vazias, definaWHISPER_BINpara o caminho completo dewhisper.exe.
Estratégias de Extração de Quadros
A extração de quadros usa uma cadeia de fallback de duas estratégias — nenhuma dependência única é necessária:
| Estratégia | Como funciona | Velocidade | Requisitos |
|---|---|---|---|
| yt-dlp + ffmpeg (principal) | Baixa o vídeo, extrai quadros via detecção de cenas | Rápida, precisa | yt-dlp (pip install yt-dlp) |
| Navegador (fallback) | Abre o vídeo no Chrome headless, busca nos timestamps, tira screenshots | Mais lenta, sem necessidade de download | Chrome ou Chromium instalado |
O fallback é automático — se o yt-dlp não estiver disponível, o servidor tenta a extração baseada em navegador via puppeteer-core. Se nenhum estiver disponível, a análise ainda retorna transcrição + metadados + comentários, apenas sem quadros.
Pipeline de Pós-Processamento
Após a extração de quadros, o pipeline aplica automaticamente:
| Etapa | O que faz | Por quê |
|---|---|---|
| Deduplicação de quadros | Remove quadros consecutivos quase idênticos usando hash perceptual (dHash + distância de Hamming) | Gravações de tela costumam ter momentos estáticos longos — a deduplicação remove quadros redundantes, economizando tokens |
| OCR | Extrai texto visível na tela de cada quadro (via tesseract.js). Cada quadro é primeiro pré-processado — escala de cinza + ampliação 2× + normalização de contraste + nitidez — o que melhora materialmente a precisão em sobreposições estilizadas (preços, datas, cupons, CTAs). | Captura código, mensagens de erro, saída de terminal, texto de UI que a transcrição não cobre |
| Linha do tempo anotada | Mescla timestamps da transcrição + timestamps dos quadros + texto do OCR em uma única visão cronológica | Dá à IA uma visão unificada de "o que foi dito, o que mudou visualmente e que texto apareceu" em cada momento |
A etapa de OCR requer tesseract.js (incluído como dependência). Se falhar ao carregar, a análise continua sem OCR — nenhum quadro ou transcrição é perdido. O pré-processamento de OCR está ativado por padrão; defina MCP_OCR_PREPROCESS=0 para fazer OCR nos quadros brutos.
O OCR sempre lê o quadro em resolução total, não a cópia emitida para o cliente. Os dois têm funções diferentes: o quadro emitido é limitado para custo de tokens, enquanto o reconhecimento precisa de cada pixel que puder obter.
Tamanho do quadro (capturas de UI densas)
Os quadros emitidos são limitados a 800 px de largura, o que atende ao caso comum — clipes de pessoa falando, Reels, reprodução de bugs — onde o assunto preenche o quadro.
É o tamanho errado para uma captura de UI densa: um terminal, dashboard, IDE ou gravação de planilha, onde o significado está em texto pequeno. Uma gravação de tela 1920×1080 sem escala chega a 800×450, e uma fonte de UI de 15 px cai abaixo do que um modelo de visão consegue resolver.
Passe maxWidth por chamada para manter mais (ou toda) a resolução da fonte — 0 desativa o limite:
get_frames(url, { maxFrames: 8, maxWidth: 0 }) // source resolution
get_frame_at(url, "2:14", { maxWidth: 1920 })
analyze_video(url, { detail: "standard", maxWidth: 1568 })
Suportado em analyze_video, analyze_videos, analyze_moment, get_frames, get_frame_at e get_frame_burst, e no CLI como --max-width <px>.
Quadros nativos custam várias vezes mais contexto que o padrão, então aumente o limite deliberadamente — get_frames retorna até 20 quadros e analyze_video em detailed até 60.
| Variável | Aplica-se a | Padrão | Notas |
|---|---|---|---|
MCP_FRAME_MAX_WIDTH | Largura do quadro emitido, em px | 800 | 0 (ou native/full/original) desativa o limite. Um maxWidth por chamada tem precedência sobre ele |
MCP_FRAME_JPEG_QUALITY | Qualidade JPEG do quadro emitido | 70 | Aumente quando glifos finos importarem; apenas por variável de ambiente, não há parâmetro de qualidade por chamada. Valores fora de 1–100 caem no padrão |
MCP_CACHE_DIR | Raiz para o cache tessdata e o --out padrão do CLI | diretório de cache por usuário | Apenas caminhos absolutos (um valor relativo é ignorado). Use quando $HOME for somente leitura ou ausente — um contêiner endurecido, ProtectHome=, um home com cota. A imagem Docker publicada define como /tmp/mcp-video-analyzer-cache para que --read-only --tmpfs /tmp funcione de imediato |
MCP_ALLOW_PRIVATE_URLS | Acesso a endereços de rede privados/loopback | desligado | 1 permite localhost, endereços LAN (192.168.x, 10.x, …), nomes .local e caminhos UNC. Desligado por padrão — veja Destinos de rede abaixo. Endpoints de metadados de nuvem permanecem bloqueados de qualquer forma |
Um valor que qualquer variável não possa usar — 1e3, 1920px, uma qualidade de 150 — é rejeitado com um aviso único no stderr e o padrão se aplica. Não é aceito silenciosamente: o objetivo da configuração é escapar de um downscale que de outra forma parece um resultado normal.
Prefira o parâmetro por chamada: o servidor inicia uma vez por sessão, então uma variável de ambiente não pode diferir entre uma visão geral de um clipe do YouTube e uma leitura detalhada de uma gravação de tela. A largura que uma chamada realmente usa faz parte da chave de cache e sidecar, então analisar o mesmo vídeo a 800 px e depois a maxWidth: 0 reexecuta o pipeline em vez de retornar o primeiro resultado duas vezes.
Ferramentas Complementares
Chrome DevTools MCP
Para depuração web ao vivo junto com análise de vídeo, combine este servidor com o Chrome DevTools MCP:
claude mcp add chrome-devtools npx @anthropic-ai/mcp-devtools@latest
Quando usar cada um:
| Cenário | Ferramenta |
|---|---|
| Relatório de bug gravado como vídeo Loom | mcp-video-analyzer — extraia transcrição, quadros e texto de erro da gravação |
| Depuração ao vivo de uma página web | Chrome DevTools MCP — inspecione DOM, console, rede, tire screenshots |
| Vídeo mostra problema de UI, precisa reproduzi-lo | Use ambos: analise o vídeo primeiro, depois abra a página no Chrome DevTools para reproduzir |
Os dois MCPs se complementam: o analisador de vídeo entende conteúdo gravado, o DevTools interage com páginas ao vivo.
Exemplo de Saída
A pasta examples/loom-demo/ contém saídas reais da análise de um vídeo Loom público (Boost In-App Demo Video, 2:55).
| Arquivo | O que mostra |
|---|---|
metadata.json | Título, duração, plataforma |
transcript.json | 42 entradas com timestamps e IDs de falantes |
timeline.json | Visão cronológica unificada (transcrição + quadros mesclados) |
moment-transcript-0m30s-0m45s.json | Transcrição filtrada para analyze_moment (0:30–0:45) |
full-analysis.json | Saída completa de analyze_video |
Imagens de quadros (19 no total em examples/loom-demo/frames/):
scene_*.jpg— detecção de mudança de cena (transições visuais principais)dense_*.jpg— amostragem densa de 1fps (cada 10º quadro salvo como amostra)burst_*.jpg— extração em rajada para análise de momento (0:30–0:45)
Regenerar após alterações:
npx tsx examples/generate.ts— requer yt-dlp + acesso à rede.
Desenvolvimento
# Install dependencies
npm install
# Run all checks (format, lint, typecheck, knip, tests)
npm run check
# Audit dependencies. `security` covers what the published package ships and
# is a blocking CI job; `security:all` adds devDependencies. Both also run on
# a weekly cron, because npm audit reads a live advisory database.
npm run security
# Build
npm run build
# Run E2E tests (requires network; add WHISPER_E2E=1 to include the
# transcription outcome test — needs a whisper CLI installed)
npm run test:e2e
# Just the video-format matrix: a real clip per container/codec
# (mp4/h264+hevc+av1, webm, mkv, mov, avi, m4v, mpeg, mpg, m2ts, mts,
# 3gp, ogv, flv, wmv) decoded end to end. ~15s on a warm cache; the
# first run fetches ~7MB of tesseract traineddata.
npm run test:formats
# Build + boot the real MCP server/CLI (seconds)
npm run test:smoke
# Everything: check → e2e → smoke → verify-package
npm run verify-all
# Open MCP Inspector for manual testing
npm run inspect
Arquitetura
src/
├── index.ts # Entry point (shebang + stdio)
├── server.ts # FastMCP server + tool registration
├── tools/ # MCP tool definitions (7 tools)
│ ├── analyze-video.ts # Full analysis with detail levels + caching
│ ├── analyze-moment.ts # Deep-dive on a time range
│ ├── get-transcript.ts # Transcript-only with Whisper fallback
│ ├── get-metadata.ts # Metadata + comments + chapters
│ ├── get-frames.ts # Frames-only (scene-change or dense)
│ ├── get-frame-at.ts # Single frame at timestamp
│ └── get-frame-burst.ts # N frames in a time range
├── adapters/ # Source-specific logic
│ ├── adapter.interface.ts # IVideoAdapter interface + registry
│ ├── loom.adapter.ts # Loom: authless GraphQL
│ ├── local-file.adapter.ts # Local files: absolute path or file:// URI
│ ├── twelvelabs.adapter.ts # TwelveLabs Pegasus: transcript + AI summary (opt-in)
│ └── direct.adapter.ts # Direct URL: any mp4/webm link
├── processors/ # Shared processing
│ ├── frame-extractor.ts # ffmpeg scene detection + dense + burst extraction
│ ├── browser-frame-extractor.ts # Headless Chrome fallback for frames
│ ├── audio-transcriber.ts # Whisper fallback (HF transformers → CLI → OpenAI)
│ ├── image-optimizer.ts # sharp resize/compress
│ ├── frame-dedup.ts # Perceptual dedup (dHash + Hamming distance)
│ ├── frame-ocr.ts # OCR text extraction (tesseract.js)
│ └── annotated-timeline.ts # Unified timeline (transcript + frames + OCR)
├── config/
│ └── detail-levels.ts # brief / standard / detailed config
├── utils/
│ ├── cache.ts # In-memory TTL cache with LRU eviction
│ ├── field-filter.ts # Selective field filtering for responses
│ ├── url-detector.ts # Platform detection from URL
│ ├── vtt-parser.ts # WebVTT → transcript entries
│ └── temp-files.ts # Temp directory management
└── types.ts # Shared TypeScript interfaces
Temporização de rajada
A extração em rajada aceita timestamps fracionários (por exemplo, 0:01.1). O
caminho FFmpeg seleciona até a quantidade solicitada de quadros de origem existentes dentro de
[from, to), sem duplicar quadros para satisfazer uma taxa de amostragem excessiva.
Os resultados do processador retêm o tempo de apresentação relativo ao vídeo de cada quadro selecionado,
incluindo segundos fracionários. O fallback do navegador preserva alvos de busca fracionários
e arquivos de screenshot separados; seus tempos são buscas solicitadas, não PTS de quadros de origem medidos.
A busca no navegador não é um substituto preciso de quadro para decodificação local.
get_frame_burst retorna um bloco de texto JSON contendo
{ frameCount, from, to, warnings, frames: [{ time, timingOrigin }] }, seguido
por imagens exatamente na mesma ordem que frames. timingOrigin: "source-pts"
significa tempo de apresentação FFmpeg medido relativo ao vídeo; "seek-target" significa
tempo de busca solicitado no navegador. Resultados vazios/degradados contêm frames: [].
Licença
MIT