Youtube Player MCP

Um servidor MCP e serviço de reprodução de mídia que permite que um assistente de IA toque música em um alto-falante real. Peça ao seu assistente para "tocar um pouco de lo-fi", "colocar na fila as próximas três faixas daquele álbum" ou "adicionar essa música à minha playlist de treino", e o áudio toca na máquina que executa o serviço, inclusive via Bluetooth.

Documentação

yt-player

Um servidor MCP e serviço de reprodução de mídia que permite a um assistente de IA tocar música em um alto-falante real. Peça ao seu assistente para "tocar um lo-fi", "colocar na fila as próximas três faixas daquele álbum" ou "adicionar esta música à minha playlist de treino", e o áudio toca na máquina que executa o serviço, inclusive via Bluetooth.

Ele tem duas partes, ambas iniciadas pelo comando yt-player:

  • Serviço de reprodução (yt-player serve): um aplicativo FastAPI que transmite áudio com yt-dlp e ffplay. Ele gerencia uma fila, playlists e histórico de reprodução, e os armazena em SQLite. Ele também serve uma API REST e um fluxo de eventos WebSocket, para que outros clientes também possam usá-lo.
  • Servidor MCP (yt-player mcp, ou yt-player-mcp): uma camada MCP leve sobre a API HTTP do serviço. Ele suporta transportes stdio, HTTP streamable e SSE.

O servidor MCP é uma instalação leve que depende apenas de mcp e httpx. Ele pode ser executado em uma máquina diferente do serviço de reprodução.

YouTube é a primeira fonte suportada. Os modelos de API são neutros em relação à fonte: os clientes trabalham com faixas, estado de reprodução, filas, playlists e comandos de reprodução.

Requisitos

O serviço de reprodução toca áudio na máquina em que é executado.

PlataformaComo executar o serviço de reproduçãoÁudio
LinuxDocker Compose (recomendado) ou yt-player servePulseAudio, PipeWire, ALSA ou JACK
macOSyt-player serve (não Docker)CoreAudio
WindowsNão suportado; use WSL ou uma máquina Linux

Para executá-lo sem Docker, você precisa de Python 3.10+, ffmpeg (que fornece ffplay) e um runtime JavaScript. Eles podem estar no PATH, ou você pode informar suas localizações com FFPLAY_PATH e JS_RUNTIME. O YouTube exige que o yt-dlp resolva um desafio JavaScript. Qualquer um desses runtimes funciona:

RuntimeVersão mínimaJS_RUNTIME
Deno (padrão, recomendado)2.3.0deno
Node.js22.0.0node
Bun1.2.11bun
QuickJS2023-12-09quickjs

Ferramentas opcionais:

  • pactl altera o volume de uma faixa em reprodução sem interrompê-la. Sem ele, uma alteração de volume reinicia o fluxo na posição atual, o que causa uma pequena pausa.
  • bluetoothctl (BlueZ, somente Linux) permite que o serviço conecte um alto-falante Bluetooth por conta própria.

O Docker Desktop no macOS e no Windows não pode passar o áudio do host para os contêineres, portanto use o Docker apenas no Linux. Qualquer máquina pode executar o servidor MCP e controlar um serviço de reprodução em execução em outro lugar (veja Controlando um player remoto).

O servidor MCP precisa de Python 3.10+ e acesso de rede ao serviço de reprodução.

Início rápido

  1. Clone o repositório e crie seu arquivo de configurações:

    git clone https://github.com/karunstha/yt-player.git
    cd yt-player
    cp .env.example .env
    mkdir -p data
    

    Crie o data/ você mesmo para que seja seu. Se o Docker o criar, ele pertencerá ao root.

  2. Edite o .env. Por padrão, o contêiner reproduz através do seu socket PulseAudio ou PipeWire. O PULSE_RUNTIME_DIR deve apontar para /run/user/<UID>/pulse; execute id -u para descobrir seu UID. Para escolher um alto-falante ou usar ALSA, veja Saída de áudio.

  3. Opcional, mas recomendado: adicione cookies do YouTube, o que torna menos provável que o YouTube bloqueie solicitações como bot. Exporte seus cookies no formato Netscape (por exemplo, com uma extensão de navegador "cookies.txt") e salve-os como data/cookies.txt. O serviço funciona sem eles.

  4. Inicie o serviço de reprodução:

    docker compose up -d
    
  5. Verifique se ele responde:

    curl http://127.0.0.1:5454/player/state
    
  6. Conecte um cliente MCP usando uma das opções abaixo.

Executando sem Docker

No Linux ou macOS, com as ferramentas do sistema listadas em Requisitos:

pip install "yt-player[server] @ git+https://github.com/karunstha/yt-player"
yt-player serve

Sem Docker, o serviço vincula-se a 127.0.0.1:5454, armazena dados em ~/.local/share/yt-player e reproduz através da saída de áudio padrão do sistema. Use --host/--port ou as variáveis de configuração para alterar isso, seja no ambiente ou em um arquivo .env no diretório a partir do qual você o inicia. No macOS, instale as ferramentas do sistema primeiro com brew install ffmpeg deno. Se você já tiver Node.js 22+, pode pular o Deno e definir JS_RUNTIME=node.

Na inicialização, o serviço imprime as configurações que está usando e avisa sobre qualquer coisa que impediria a reprodução, como um ffplay ou runtime JavaScript ausente. Serviços iniciados por launchd ou systemd recebem um PATH mínimo que geralmente exclui Homebrew e nvm. Nesse caso, defina FFPLAY_PATH e JS_RUNTIME com caminhos completos, por exemplo FFPLAY_PATH=/opt/homebrew/bin/ffplay.

Saída de áudio

Por padrão, o áudio vai para o dispositivo de saída padrão do sistema. Duas configurações alteram isso, em .env ou no ambiente:

  • AUDIO_DRIVER escolhe o sistema de som: pulseaudio, pipewire, alsa, jack ou coreaudio. Se não definido, o mais adequado é escolhido automaticamente. O Docker Compose define pulseaudio.
  • AUDIO_DEVICE escolhe o dispositivo de saída nesse sistema de som.
Sistema de somAUDIO_DRIVERAUDIO_DEVICEListe dispositivos com
PulseAudiopulseaudioNome do sink, ex.: alsa_output.usb-DAC-00.analog-stereopactl list short sinks
PipeWirepulseaudio (através de pipewire-pulse)Nome do sink, como acimapactl list short sinks
ALSAalsaDispositivo, ex.: plughw:1,0aplay -l
JACK, CoreAudio, PipeWire nativocomo nomeadoNão suportado; usa o padrão do sistema

Alto-falantes Bluetooth. O DEFAULT_BLUETOOTH_DEVICE_ID apenas conecta o alto-falante, antes da inicialização e antes de cada faixa. Para garantir que o áudio realmente vá para ele, defina também AUDIO_DEVICE para o sink dele, por exemplo bluez_output.AA_BB_CC_DD_EE_FF.1 no PipeWire ou bluez_sink.AA_BB_CC_DD_EE_FF.a2dp_sink no PulseAudio. O nome exato aparece em pactl list short sinks enquanto o alto-falante está conectado.

ALSA no Docker. Em hosts sem PulseAudio ou PipeWire, defina estes em .env:

AUDIO_DRIVER=alsa
AUDIO_DEVICE=plughw:1,0

O ALSA precisa de acesso exclusivo à placa de som, então a reprodução falha com "device busy" se um servidor de som no host já estiver usando essa placa. O Compose ainda monta PULSE_RUNTIME_DIR, então em um host sem PulseAudio, o Docker cria um diretório /run/user/<UID>/pulse vazio. É seguro ignorar.

Erros de áudio do ffplay aparecem no log do serviço (docker compose logs yt-player). Audio target '<name>' not available significa que o ffplay foi compilado sem esse sistema de som; escolha outro AUDIO_DRIVER ou deixe vazio.

Conectando um cliente MCP

Opção A: stdio (recomendado para clientes de desktop)

Seu cliente MCP inicia o servidor como um processo local. A maneira mais simples é uv, que o instala na primeira execução sem uma etapa de configuração separada.

Claude Code:

claude mcp add yt-player \
  -e MEDIA_SERVICE_URL=http://127.0.0.1:5454 \
  -- uvx --from git+https://github.com/karunstha/yt-player yt-player-mcp

Claude Desktop, Cursor e outros clientes configurados por JSON:

{
  "mcpServers": {
    "yt-player": {
      "command": "uvx",
      "args": ["--from", "git+https://github.com/karunstha/yt-player", "yt-player-mcp"],
      "env": {
        "MEDIA_SERVICE_URL": "http://127.0.0.1:5454"
      }
    }
  }
}

Sem uv, instale o pacote e aponte seu cliente para o comando yt-player-mcp:

pipx install git+https://github.com/karunstha/yt-player

Se seu cliente puder executar comandos Docker, você pode usar a imagem que já construiu:

docker run --rm -i --network host \
  -e MEDIA_SERVICE_URL=http://127.0.0.1:5454 \
  yt-player yt-player mcp

Opção B: HTTP streamable (de longa duração, Dockerizado)

Inicie o servidor MCP junto com o serviço de reprodução usando o perfil opcional do Compose:

docker compose --profile mcp up -d

O servidor então escuta em:

http://127.0.0.1:5455/mcp

Para Claude Code:

claude mcp add --transport http yt-player http://127.0.0.1:5455/mcp

Fora do Docker, o mesmo servidor é executado com yt-player mcp --transport streamable-http.

Controlando um player remoto

O servidor MCP apenas faz chamadas HTTP ao serviço de reprodução, então ele pode ser executado em uma máquina diferente do alto-falante. Por exemplo, o serviço de reprodução pode ser executado em um mini PC Linux conectado aos seus alto-falantes enquanto seu assistente roda em um laptop. Aponte MEDIA_SERVICE_URL para o player:

MEDIA_SERVICE_URL=http://media-box.local:5454

Leia Segurança antes de expor qualquer uma das portas em uma rede.

Ferramentas MCP

ÁreaFerramentas
Statushealth, get_playback_state, get_current_track
Pesquisasearch_media
Reproduçãoplay_media, pause_playback, resume_playback, stop_playback, seek_playback, next_track, previous_track, set_volume, set_shuffle, set_repeat
Históricoget_playback_history (paginado com next_before)
Filaget_queue, append_queue_track, insert_queue_track_next, queue_playlist, remove_queue_item, clear_queue, replace_queue_with_playlist
Playlistslist_playlists, get_playlist, create_playlist, rename_playlist, delete_playlist, add_track_to_playlist, add_tracks_to_playlist, add_current_track_to_playlist, move_playlist_track, remove_track_from_playlist, clear_playlist, play_playlist
Salvando a filasave_queue_as_playlist, add_queue_to_playlist
Importar/exportarexport_playlists, import_playlists

Ferramentas que recebem uma faixa (play_media, append_queue_track, insert_queue_track_next, add_track_to_playlist) aceitam uma query de pesquisa ou um url.

Pesquisas e consultas aceitam um search_type opcional que define o que procurar:

search_typePesquisasExemplo
songs (padrão)Músicas do YouTube Music"tocar Bohemian Rhapsody"
music_videosVídeos do YouTube Music"tocar o vídeo oficial de…"
podcastsEpisódios de podcast do YouTube Music"tocar o episódio do Lex Fridman com Sam Altman"
videosTodo o YouTubetutoriais, palestras, transmissões ao vivo

Altere o padrão com DEFAULT_SEARCH_TYPE. add_tracks_to_playlist aceita uma lista onde cada entrada é uma URL ou uma consulta de pesquisa.

Uma playlist guarda cada faixa uma única vez. Adicionar uma faixa que já está lá falha com uma mensagem informando isso. Adições em lote (add_tracks_to_playlist, save_queue_as_playlist, add_queue_to_playlist) pulam duplicatas e as listam no resultado como skipped. Faixas que não puderam ser encontradas são listadas como failed. As playlists podem ser referenciadas por ID, nome ou um slug do nome.

Solução de problemas

  • O serviço inicia com avisos. Corrija o que eles indicam: instale a ferramenta ausente ou aponte FFPLAY_PATH ou JS_RUNTIME para ela.
  • O estado de reprodução mostra error, ou nada toca. Verifique o log do serviço (docker compose logs yt-player, ou o terminal executando yt-player serve). Erros do yt-dlp e do ffplay aparecem lá. Para problemas de áudio, veja Saída de áudio.
  • O YouTube diz "Sign in to confirm you're not a bot". Adicione cookies como cookies.txt no diretório de dados (veja Início rápido).
  • O assistente relata "Could not reach the media service". O serviço de reprodução não está em execução, ou MEDIA_SERVICE_URL na configuração do seu cliente MCP aponta para o host ou porta errados.

Segurança

Nem o serviço de reprodução nem o servidor MCP têm autenticação.

  • Sob Docker Compose, o serviço de reprodução vincula-se a 0.0.0.0:5454 com rede do host, então qualquer pessoa na sua rede pode controlar a reprodução. Defina SERVICE_HOST=127.0.0.1 em .env se apenas esta máquina precisar de acesso. Caso contrário, execute-o apenas em uma rede confiável, ou coloque-o atrás de um firewall ou proxy reverso com autenticação. Fora do Docker, yt-player serve vincula-se a 127.0.0.1 por padrão.
  • O transporte HTTP do MCP vincula-se a 127.0.0.1 por padrão. Mantenha MCP_HOST=127.0.0.1 a menos que você queira intencionalmente que outras máquinas controlem a reprodução. Se você o vincular a 0.0.0.0, proteja a porta da mesma forma.
  • O contêiner de reprodução é executado com privileged: true e monta o estado D-Bus e Bluetooth do host para poder gerenciar dispositivos Bluetooth. Remova essas configurações se não precisar de Bluetooth.
  • cookies.txt contém sua sessão do YouTube. O diretório data/ e .env estão listados em .gitignore; nunca os envie para o controle de versão.

Configuração

Ambas as partes leem variáveis de ambiente. yt-player serve também carrega um arquivo .env do diretório em que é iniciado. O servidor MCP não lê .env; defina suas variáveis na configuração do seu cliente MCP, como nos exemplos acima.

Valores vazios (como SERVICE_PORT=) usam o padrão. Valores inválidos interrompem a inicialização com uma mensagem nomeando a configuração.

Docker Compose (definido em .env, veja .env.example):

  • PULSE_RUNTIME_DIR: diretório PulseAudio do host montado no contêiner. O padrão é /run/user/1000/pulse.
  • DATA_PATH: diretório do host montado em /data. O padrão é ./data.
  • SERVICE_HOST, SERVICE_PORT, AUDIO_DRIVER, AUDIO_DEVICE, DEFAULT_BLUETOOTH_DEVICE_ID, DEFAULT_VOLUME, DEFAULT_SEARCH_TYPE, MCP_HOST, MCP_PORT: repassados para os contêineres (veja abaixo). No Compose, SERVICE_HOST tem como padrão 0.0.0.0 e AUDIO_DRIVER tem como padrão pulseaudio.

Serviço de reprodução:

  • SERVICE_HOST: host de bind. O padrão é 127.0.0.1.
  • SERVICE_PORT: porta de bind. O padrão é 5454.
  • DATA_DIR: diretório para dados persistentes do serviço. O padrão é $XDG_DATA_HOME/yt-player (geralmente ~/.local/share/yt-player); a imagem Docker usa /data.
  • DATABASE_PATH: caminho do banco de dados SQLite. O padrão é $DATA_DIR/media_service.sqlite3.
  • COOKIES_FILE: arquivo de cookies no formato Netscape passado ao yt-dlp, usado apenas se existir. O padrão é $DATA_DIR/cookies.txt.
  • AUDIO_DRIVER: sistema de som usado para reprodução. Vazio escolhe um automaticamente. Veja Saída de áudio.
  • AUDIO_DEVICE: dispositivo de saída nesse sistema de som. Vazio usa o padrão do sistema.
  • DEFAULT_BLUETOOTH_DEVICE_ID: endereço MAC opcional de dispositivo Bluetooth para conectar na inicialização e antes da reprodução.
  • DEFAULT_VOLUME: volume inicial de 0.0 a 1.0. O padrão é 0.8.
  • FFPLAY_PATH: o executável ffplay. O padrão é ffplay no PATH.
  • DEFAULT_SEARCH_TYPE: o que as buscas procuram quando uma solicitação não especifica: songs, music_videos, podcasts ou videos. O padrão é songs.
  • JS_RUNTIME: runtime JavaScript que o yt-dlp usa para o desafio do YouTube: deno, node, bun ou quickjs. Adicione um caminho como node:/usr/local/bin/node se não estiver no PATH. Forneça vários, separados por vírgula, para fallback na ordem de prioridade do yt-dlp (deno, node, quickjs, bun). O padrão é deno. A imagem Docker inclui apenas Deno.
  • YTDLP_REMOTE_COMPONENTS: o que o yt-dlp pode baixar se o solucionador de desafio incluído (yt-dlp-ejs) estiver ausente ou não corresponder à versão instalada do yt-dlp: ejs:github, ejs:npm ou none. O padrão é ejs:github. Normalmente nada é baixado, porque o extra server instala o solucionador correspondente.
  • PLAYBACK_COMPLETION_GRACE_SECONDS: saídas de backend com falha dentro deste número de segundos após o fim da faixa são tratadas como concluídas. O padrão é 5.
  • PLAYBACK_RECOVERY_RETRIES: reinícios automáticos de stream por faixa após uma saída não limpa do backend. O padrão é 2.

Servidor MCP:

  • MEDIA_SERVICE_URL: URL base do serviço de reprodução. O padrão é http://127.0.0.1:5454.
  • MEDIA_SERVICE_TIMEOUT_SECONDS: timeout HTTP para chamadas ao serviço de reprodução. O padrão é 30.
  • MCP_TRANSPORT: stdio, streamable-http ou sse. O padrão é stdio.
  • MCP_HOST: host de bind para transportes HTTP. O padrão é 127.0.0.1.
  • MCP_PORT: porta de bind para transportes HTTP. O padrão é 5455.
  • MCP_STREAMABLE_HTTP_PATH: caminho HTTP streamable. O padrão é /mcp.
  • MCP_SSE_PATH: caminho de eventos SSE. O padrão é /sse.
  • MCP_MESSAGE_PATH: caminho de mensagens SSE. O padrão é /messages/.

O Docker Compose monta DATA_PATH (padrão ./data) em /data, para que playlists, metadados de faixas salvos, histórico de reprodução e cookies sobrevivam à substituição do contêiner.

API HTTP

O servidor MCP é um cliente desta API. Você também pode chamá-la diretamente ou construir sua própria interface sobre ela.

Reprodução:

  • POST /player/play com um de url, query ou track
  • GET /player/state
  • GET /player/current
  • POST /player/pause
  • POST /player/resume
  • POST /player/stop
  • POST /player/seek
  • POST /player/next
  • POST /player/previous
  • POST /player/volume
  • POST /player/shuffle
  • POST /player/repeat
  • GET /player/history?days=7&before=<iso-date-time>&limit=100

Busca:

  • GET /search?q=...&count=10&type=songs: type é songs, music_videos, podcasts ou videos
  • Solicitações que aceitam um query (itens /player/play, /queue, /queue/tracks, /playlists/{id}/tracks e bulk) também aceitam search_type

Fila:

  • GET /queue
  • POST /queue
  • DELETE /queue
  • POST /queue/tracks
  • POST /queue/tracks/next
  • POST /queue/playlists/{id} com play_next e shuffle opcionais: adiciona as faixas de uma playlist sem substituir a fila
  • DELETE /queue/items/{item_id}

Playlists:

  • POST /playlists
  • GET /playlists
  • GET /playlists/{id}
  • PATCH /playlists/{id}
  • DELETE /playlists/{id}
  • POST /playlists/{id}/tracks
  • POST /playlists/{id}/tracks/bulk com items (até 50 faixas): retorna added, skipped e failed
  • POST /playlists/{id}/tracks/queue: anexa a fila atual, retorna o mesmo formato que bulk
  • POST /playlists/{id}/tracks/current
  • DELETE /playlists/{id}/tracks/{track_id}
  • POST /playlists/{id}/tracks/{track_id}/move com um position baseado em 0
  • POST /playlists/{id}/tracks/reorder
  • DELETE /playlists/{id}/tracks
  • POST /playlists/{id}/play
  • POST /playlists/{id}/shuffle
  • GET /playlists/export
  • GET /playlists/{id}/export
  • POST /playlists/import

Eventos em tempo real:

  • WS /events

O WebSocket envia playback.sync na conexão e depois transmite eventos significativos como track.changed, playback.state_changed, playback.seeked, queue.changed, playlist.changed e track.completed. Durante a reprodução, emite atualizações de playback.sync em baixa frequência aproximadamente a cada 3 segundos.

API legada

Os endpoints originais ainda estão disponíveis:

  • POST /play
  • POST /stop
  • GET /status
  • POST /search
  • POST /play-search
  • POST /shuffle

Estrutura do projeto

  • yt_player/cli.py: o comando yt-player e seus modos serve e mcp.
  • yt_player/factory.py: criação do app FastAPI, gerenciamento de ciclo de vida, conexão de serviços e manipuladores de erro.
  • yt_player/env.py: análise de variáveis de ambiente compartilhada entre o serviço e o servidor MCP.
  • yt_player/core/: configuração do serviço, opções do yt-dlp (ytdlp.py), erros estruturados do serviço e utilitários compartilhados.
  • yt_player/player/: rotas de reprodução, rotas de fila, rotas legadas de reprodução, controlador de reprodução, serviço de fila, backend ffplay/yt-dlp e modelos de player/fila.
  • yt_player/playlists/: rotas de playlist, serviço persistente de playlist, repositório SQLite e modelos de playlist.
  • yt_player/sources/: integrações de fontes de mídia. O YouTube está implementado atualmente em yt_player/sources/youtube.py.
  • yt_player/events/: rota WebSocket, transmissor de eventos e modelos de eventos.
  • yt_player/mcp/: servidor MCP e wrapper de cliente HTTP para a API do serviço de mídia.
  • main.py: ponto de entrada ASGI para executar o app diretamente com Uvicorn (uvicorn main:app).

Desenvolvimento

python3 -m venv .venv
.venv/bin/pip install -e ".[dev]"
.venv/bin/python -m pytest

Licença

MIT