Youtube Player MCP

Un servidor MCP y servicio de reproducción multimedia que permite a un asistente de IA reproducir música en un altavoz real. Pídele a tu asistente que "ponga algo de lo-fi", "añada las siguientes tres pistas de ese álbum a la cola" o "agregue esta canción a mi lista de reproducción de entrenamiento", y el audio se reproducirá desde la máquina que ejecuta el servicio, incluso a través de Bluetooth.

Documentación

yt-player

Un servidor MCP y servicio de reproducción multimedia que permite a un asistente de IA reproducir música en un altavoz real. Pídele a tu asistente que "ponga algo de lo-fi", "añada las siguientes tres pistas de ese álbum a la cola" o "agregue esta canción a mi lista de reproducción de entrenamiento", y el audio se reproducirá desde la máquina que ejecuta el servicio, incluso a través de Bluetooth.

Tiene dos partes, ambas iniciadas por el comando yt-player:

  • Servicio de reproducción (yt-player serve): una aplicación FastAPI que transmite audio con yt-dlp y ffplay. Gestiona una cola, listas de reproducción e historial de reproducción, y los almacena en SQLite. También sirve una API REST y un flujo de eventos WebSocket, para que otros clientes también puedan usarlo.
  • Servidor MCP (yt-player mcp, o yt-player-mcp): una capa MCP ligera sobre la API HTTP del servicio. Admite transportes stdio, HTTP transmisible y SSE.

El servidor MCP es una instalación ligera que solo depende de mcp y httpx. Puede ejecutarse en una máquina diferente al servicio de reproducción.

YouTube es la primera fuente compatible. Los modelos de API son neutrales respecto a la fuente: los clientes trabajan con pistas, estado de reproducción, colas, listas de reproducción y comandos de reproducción.

Requisitos

El servicio de reproducción reproduce audio en la máquina donde se ejecuta.

PlataformaCómo ejecutar el servicio de reproducciónAudio
LinuxDocker Compose (recomendado) o yt-player servePulseAudio, PipeWire, ALSA o JACK
macOSyt-player serve (no Docker)CoreAudio
WindowsNo compatible; usa WSL o una máquina Linux

Para ejecutarlo sin Docker necesitas Python 3.10+, ffmpeg (que proporciona ffplay) y un runtime de JavaScript. Pueden estar en el PATH, o puedes indicar sus ubicaciones con FFPLAY_PATH y JS_RUNTIME. YouTube requiere que yt-dlp resuelva un desafío de JavaScript. Cualquiera de estos runtimes funciona:

RuntimeVersión mínimaJS_RUNTIME
Deno (predeterminado, recomendado)2.3.0deno
Node.js22.0.0node
Bun1.2.11bun
QuickJS2023-12-09quickjs

Herramientas opcionales:

  • pactl cambia el volumen de una pista en reproducción sin interrumpirla. Sin ella, un cambio de volumen reinicia la transmisión en la posición actual, lo que causa una breve pausa.
  • bluetoothctl (BlueZ, solo Linux) permite que el servicio conecte un altavoz Bluetooth por sí mismo.

Docker Desktop en macOS y Windows no puede pasar el audio del host a los contenedores, así que usa Docker solo en Linux. Cualquier máquina puede ejecutar el servidor MCP y controlar un servicio de reproducción que se ejecute en otro lugar (consulta Controlar un reproductor remoto).

El servidor MCP necesita Python 3.10+ y acceso de red al servicio de reproducción.

Inicio rápido

  1. Clona el repositorio y crea tu archivo de configuración:

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

    Crea data/ tú mismo para que sea tuyo. Si Docker lo crea, pertenece a root.

  2. Edita .env. Por defecto, el contenedor reproduce a través de tu socket de PulseAudio o PipeWire. PULSE_RUNTIME_DIR debe apuntar a /run/user/<UID>/pulse; ejecuta id -u para encontrar tu UID. Para elegir un altavoz o usar ALSA en su lugar, consulta Salida de audio.

  3. Opcional pero recomendado: agrega cookies de YouTube, lo que reduce la probabilidad de que YouTube bloquee las solicitudes como bot. Exporta tus cookies en formato Netscape (por ejemplo, con una extensión de navegador "cookies.txt") y guárdalas como data/cookies.txt. El servicio funciona sin ellas.

  4. Inicia el servicio de reproducción:

    docker compose up -d
    
  5. Verifica que responde:

    curl http://127.0.0.1:5454/player/state
    
  6. Conecta un cliente MCP usando una de las opciones a continuación.

Ejecutar sin Docker

En Linux o macOS, con las herramientas del sistema listadas en Requisitos:

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

Sin Docker, el servicio se vincula a 127.0.0.1:5454, almacena datos en ~/.local/share/yt-player y reproduce a través de la salida de audio predeterminada del sistema. Usa --host/--port o las variables de configuración para cambiar eso, ya sea en el entorno o en un archivo .env en el directorio desde el que lo inicias. En macOS, instala primero las herramientas del sistema con brew install ffmpeg deno. Si ya tienes Node.js 22+, puedes omitir Deno y configurar JS_RUNTIME=node.

Al iniciar, el servicio imprime la configuración que está usando y advierte sobre cualquier cosa que detendría la reproducción, como la falta de ffplay o un runtime de JavaScript. Los servicios iniciados por launchd o systemd obtienen un PATH mínimo que generalmente excluye Homebrew y nvm. En ese caso, configura FFPLAY_PATH y JS_RUNTIME con rutas completas, por ejemplo FFPLAY_PATH=/opt/homebrew/bin/ffplay.

Salida de audio

Por defecto, el audio va al dispositivo de salida predeterminado del sistema. Dos configuraciones cambian eso, en .env o en el entorno:

  • AUDIO_DRIVER elige el sistema de sonido: pulseaudio, pipewire, alsa, jack o coreaudio. Si no se configura, se elige automáticamente el más adecuado. Docker Compose configura pulseaudio.
  • AUDIO_DEVICE elige el dispositivo de salida en ese sistema de sonido.
Sistema de sonidoAUDIO_DRIVERAUDIO_DEVICEListar dispositivos con
PulseAudiopulseaudioNombre del sink, p. ej. alsa_output.usb-DAC-00.analog-stereopactl list short sinks
PipeWirepulseaudio (a través de pipewire-pulse)Nombre del sink, como arribapactl list short sinks
ALSAalsaDispositivo, p. ej. plughw:1,0aplay -l
JACK, CoreAudio, PipeWire nativocomo se nombreNo compatible; usa el predeterminado del sistema

Altavoces Bluetooth. DEFAULT_BLUETOOTH_DEVICE_ID solo conecta el altavoz, antes del inicio y antes de cada pista. Para asegurarte de que el audio realmente vaya a él, también configura AUDIO_DEVICE con su sink, por ejemplo bluez_output.AA_BB_CC_DD_EE_FF.1 en PipeWire o bluez_sink.AA_BB_CC_DD_EE_FF.a2dp_sink en PulseAudio. El nombre exacto aparece en pactl list short sinks mientras el altavoz está conectado.

ALSA en Docker. En hosts sin PulseAudio o PipeWire, configura esto en .env:

AUDIO_DRIVER=alsa
AUDIO_DEVICE=plughw:1,0

ALSA necesita acceso exclusivo a la tarjeta de sonido, por lo que la reproducción falla con "device busy" si un servidor de sonido en el host ya usa esa tarjeta. Compose aún monta PULSE_RUNTIME_DIR, así que en un host sin PulseAudio, Docker crea un directorio /run/user/<UID>/pulse vacío. Es seguro ignorarlo.

Los errores de audio de ffplay aparecen en el registro del servicio (docker compose logs yt-player). Audio target '<name>' not available significa que ffplay se compiló sin ese sistema de sonido; elige otro AUDIO_DRIVER o déjalo vacío.

Conectar un cliente MCP

Opción A: stdio (recomendado para clientes de escritorio)

Tu cliente MCP inicia el servidor como un proceso local. La forma más sencilla es uv, que lo instala en la primera ejecución sin un paso de configuración adicional.

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 y otros clientes configurados con 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"
      }
    }
  }
}

Sin uv, instala el paquete y apunta tu cliente al comando yt-player-mcp:

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

Si tu cliente puede ejecutar comandos de Docker, puedes usar la imagen que ya construiste:

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

Opción B: HTTP transmisible (de larga duración, Dockerizado)

Inicia el servidor MCP junto con el servicio de reproducción usando el perfil opcional de Compose:

docker compose --profile mcp up -d

El servidor entonces escucha en:

http://127.0.0.1:5455/mcp

Para Claude Code:

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

Fuera de Docker, el mismo servidor se ejecuta con yt-player mcp --transport streamable-http.

Controlar un reproductor remoto

El servidor MCP solo hace llamadas HTTP al servicio de reproducción, por lo que puede ejecutarse en una máquina diferente al altavoz. Por ejemplo, el servicio de reproducción puede ejecutarse en un mini PC Linux conectado a tus altavoces mientras tu asistente se ejecuta en una laptop. Apunta MEDIA_SERVICE_URL al reproductor:

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

Lee Seguridad antes de exponer cualquiera de los puertos en una red.

Herramientas MCP

ÁreaHerramientas
Estadohealth, get_playback_state, get_current_track
Búsquedasearch_media
Reproducciónplay_media, pause_playback, resume_playback, stop_playback, seek_playback, next_track, previous_track, set_volume, set_shuffle, set_repeat
Historialget_playback_history (paginado con next_before)
Colaget_queue, append_queue_track, insert_queue_track_next, queue_playlist, remove_queue_item, clear_queue, replace_queue_with_playlist
Listas de reproducciónlist_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
Guardar la colasave_queue_as_playlist, add_queue_to_playlist
Importar/exportarexport_playlists, import_playlists

Las herramientas que toman una pista (play_media, append_queue_track, insert_queue_track_next, add_track_to_playlist) aceptan ya sea una query de búsqueda o un url.

Las búsquedas y consultas toman un search_type opcional que define qué buscar:

search_typeBuscaEjemplo
songs (predeterminado)Canciones de YouTube Music"reproduce Bohemian Rhapsody"
music_videosVideos de YouTube Music"reproduce el video oficial de…"
podcastsEpisodios de podcasts de YouTube Music"reproduce el episodio de Lex Fridman con Sam Altman"
videosTodo YouTubetutoriales, charlas, transmisiones en vivo

Cambia el valor predeterminado con DEFAULT_SEARCH_TYPE. add_tracks_to_playlist toma una lista donde cada entrada es una URL o una consulta de búsqueda.

Una lista de reproducción guarda cada pista una sola vez. Agregar una pista que ya está allí falla con un mensaje que lo indica. Las adiciones por lotes (add_tracks_to_playlist, save_queue_as_playlist, add_queue_to_playlist) omiten duplicados y los listan en el resultado como skipped. Las pistas que no se pudieron encontrar se listan como failed. Las listas de reproducción se pueden referenciar por ID, nombre o un slug del nombre.

Solución de problemas

  • El servicio se inicia con advertencias. Corrige lo que nombran: instala la herramienta faltante o apunta FFPLAY_PATH o JS_RUNTIME hacia ella.
  • El estado de reproducción muestra error, o no se reproduce nada. Revisa el registro del servicio (docker compose logs yt-player, o la terminal que ejecuta yt-player serve). Los errores de yt-dlp y ffplay aparecen allí. Para problemas de audio, consulta Salida de audio.
  • YouTube dice "Sign in to confirm you're not a bot". Agrega cookies como cookies.txt en el directorio de datos (consulta Inicio rápido).
  • El asistente informa "Could not reach the media service". El servicio de reproducción no está en ejecución, o MEDIA_SERVICE_URL en la configuración de tu cliente MCP apunta al host o puerto incorrecto.

Seguridad

Ni el servicio de reproducción ni el servidor MCP tienen autenticación.

  • Bajo Docker Compose, el servicio de reproducción se vincula a 0.0.0.0:5454 con red de host, así que cualquiera en tu red puede controlar la reproducción. Configura SERVICE_HOST=127.0.0.1 en .env si solo esta máquina necesita acceso. De lo contrario, ejecútalo solo en una red confiable, o colócalo detrás de un firewall o un proxy inverso con autenticación. Fuera de Docker, yt-player serve se vincula a 127.0.0.1 por defecto.
  • El transporte HTTP de MCP se vincula a 127.0.0.1 por defecto. Mantén MCP_HOST=127.0.0.1 a menos que quieras intencionalmente que otras máquinas controlen la reproducción. Si lo vinculas a 0.0.0.0, protege el puerto de la misma manera.
  • El contenedor de reproducción se ejecuta con privileged: true y monta el estado de D-Bus y Bluetooth del host para poder gestionar dispositivos Bluetooth. Elimina esas configuraciones si no necesitas Bluetooth.
  • cookies.txt contiene tu sesión de YouTube. El directorio data/ y .env están listados en .gitignore; nunca los confirmes en el repositorio.

Configuración

Ambas partes leen variables de entorno. yt-player serve también carga un archivo .env desde el directorio en el que se inicia. El servidor MCP no lee .env; configura sus variables en la configuración de tu cliente MCP, como en los ejemplos anteriores.

Los valores vacíos (como SERVICE_PORT=) usan el predeterminado. Los valores inválidos detienen el inicio con un mensaje que nombra la configuración.

Docker Compose (configurado en .env, consulta .env.example):

  • PULSE_RUNTIME_DIR: directorio PulseAudio del host montado en el contenedor. Por defecto es /run/user/1000/pulse.
  • DATA_PATH: directorio del host montado en /data. Por defecto es ./data.
  • SERVICE_HOST, SERVICE_PORT, AUDIO_DRIVER, AUDIO_DEVICE, DEFAULT_BLUETOOTH_DEVICE_ID, DEFAULT_VOLUME, DEFAULT_SEARCH_TYPE, MCP_HOST, MCP_PORT: se pasan a los contenedores (ver más abajo). Con Compose, SERVICE_HOST por defecto es 0.0.0.0 y AUDIO_DRIVER es pulseaudio.

Servicio de reproducción:

  • SERVICE_HOST: host de enlace. Por defecto es 127.0.0.1.
  • SERVICE_PORT: puerto de enlace. Por defecto es 5454.
  • DATA_DIR: directorio para datos persistentes del servicio. Por defecto es $XDG_DATA_HOME/yt-player (normalmente ~/.local/share/yt-player); la imagen Docker usa /data.
  • DATABASE_PATH: ruta de la base de datos SQLite. Por defecto es $DATA_DIR/media_service.sqlite3.
  • COOKIES_FILE: archivo de cookies en formato Netscape pasado a yt-dlp, usado solo si existe. Por defecto es $DATA_DIR/cookies.txt.
  • AUDIO_DRIVER: sistema de sonido usado para la reproducción. Vacío elige uno automáticamente. Ver Salida de audio.
  • AUDIO_DEVICE: dispositivo de salida en ese sistema de sonido. Vacío usa el predeterminado del sistema.
  • DEFAULT_BLUETOOTH_DEVICE_ID: dirección MAC opcional de un dispositivo Bluetooth para conectar al inicio y antes de la reproducción.
  • DEFAULT_VOLUME: volumen inicial de 0.0 a 1.0. Por defecto es 0.8.
  • FFPLAY_PATH: el ejecutable ffplay. Por defecto es ffplay en el PATH.
  • DEFAULT_SEARCH_TYPE: qué buscan las búsquedas cuando una solicitud no lo especifica: songs, music_videos, podcasts o videos. Por defecto es songs.
  • JS_RUNTIME: runtime de JavaScript que usa yt-dlp para el desafío de YouTube: deno, node, bun o quickjs. Añade una ruta como node:/usr/local/bin/node si no está en el PATH. Proporciona varios, separados por comas, para recurrir en el orden de prioridad de yt-dlp (deno, node, quickjs, bun). Por defecto es deno. La imagen Docker incluye solo Deno.
  • YTDLP_REMOTE_COMPONENTS: qué puede descargar yt-dlp si el solucionador de desafíos incluido (yt-dlp-ejs) falta o no coincide con la versión instalada de yt-dlp: ejs:github, ejs:npm o none. Por defecto es ejs:github. Normalmente no se descarga nada, porque el extra server instala el solucionador correspondiente.
  • PLAYBACK_COMPLETION_GRACE_SECONDS: las salidas fallidas del backend dentro de estos segundos tras el final de la pista se tratan como completadas. Por defecto es 5.
  • PLAYBACK_RECOVERY_RETRIES: reinicios automáticos de stream por pista después de una salida no limpia del backend. Por defecto es 2.

Servidor MCP:

  • MEDIA_SERVICE_URL: URL base del servicio de reproducción. Por defecto es http://127.0.0.1:5454.
  • MEDIA_SERVICE_TIMEOUT_SECONDS: tiempo de espera HTTP para llamadas al servicio de reproducción. Por defecto es 30.
  • MCP_TRANSPORT: stdio, streamable-http o sse. Por defecto es stdio.
  • MCP_HOST: host de enlace para transportes HTTP. Por defecto es 127.0.0.1.
  • MCP_PORT: puerto de enlace para transportes HTTP. Por defecto es 5455.
  • MCP_STREAMABLE_HTTP_PATH: ruta HTTP transmisible. Por defecto es /mcp.
  • MCP_SSE_PATH: ruta de eventos SSE. Por defecto es /sse.
  • MCP_MESSAGE_PATH: ruta de mensajes SSE. Por defecto es /messages/.

Docker Compose monta DATA_PATH (por defecto ./data) en /data, para que las listas de reproducción, los metadatos de pistas guardados, el historial de reproducción y las cookies sobrevivan al reemplazo del contenedor.

API HTTP

El servidor MCP es un cliente de esta API. También puedes llamarla directamente o construir tu propia interfaz sobre ella.

Reproducción:

  • POST /player/play con uno de url, query o 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

Búsqueda:

  • GET /search?q=...&count=10&type=songs: type es songs, music_videos, podcasts o videos
  • Las solicitudes que toman un query (elementos /player/play, /queue, /queue/tracks, /playlists/{id}/tracks y bulk) también aceptan search_type

Cola:

  • GET /queue
  • POST /queue
  • DELETE /queue
  • POST /queue/tracks
  • POST /queue/tracks/next
  • POST /queue/playlists/{id} con play_next y shuffle opcionales: añade las pistas de una lista de reproducción sin reemplazar la cola
  • DELETE /queue/items/{item_id}

Listas de reproducción:

  • POST /playlists
  • GET /playlists
  • GET /playlists/{id}
  • PATCH /playlists/{id}
  • DELETE /playlists/{id}
  • POST /playlists/{id}/tracks
  • POST /playlists/{id}/tracks/bulk con items (hasta 50 pistas): devuelve added, skipped y failed
  • POST /playlists/{id}/tracks/queue: añade la cola actual, devuelve la misma forma que bulk
  • POST /playlists/{id}/tracks/current
  • DELETE /playlists/{id}/tracks/{track_id}
  • POST /playlists/{id}/tracks/{track_id}/move con un position basado en 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 en tiempo real:

  • WS /events

El WebSocket envía playback.sync al conectar y luego transmite eventos significativos como track.changed, playback.state_changed, playback.seeked, queue.changed, playlist.changed y track.completed. Durante la reproducción, emite actualizaciones de playback.sync de baja frecuencia aproximadamente cada 3 segundos.

API heredada

Los endpoints originales siguen disponibles:

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

Estructura del proyecto

  • yt_player/cli.py: el comando yt-player y sus modos serve y mcp.
  • yt_player/factory.py: creación de la app FastAPI, manejo del ciclo de vida, conexión de servicios y manejadores de errores.
  • yt_player/env.py: análisis de variables de entorno compartido entre el servicio y el servidor MCP.
  • yt_player/core/: configuración del servicio, opciones de yt-dlp (ytdlp.py), errores estructurados del servicio y utilidades compartidas.
  • yt_player/player/: rutas de reproducción, rutas de cola, rutas de reproducción heredadas, controlador de reproducción, servicio de cola, backend ffplay/yt-dlp y modelos de reproductor/cola.
  • yt_player/playlists/: rutas de listas de reproducción, servicio persistente de listas, repositorio SQLite y modelos de listas.
  • yt_player/sources/: integraciones de fuentes de medios. YouTube está implementado actualmente en yt_player/sources/youtube.py.
  • yt_player/events/: ruta WebSocket, transmisor de eventos y modelos de eventos.
  • yt_player/mcp/: servidor MCP y envoltorio del cliente HTTP para la API del servicio de medios.
  • main.py: punto de entrada ASGI para ejecutar la app directamente con Uvicorn (uvicorn main:app).

Desarrollo

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

Licencia

MIT