Engine DJ MCP (engine-dj-mcp)
Pregunta a un asistente de IA sobre tu biblioteca de Engine DJ (Denon): búsqueda por BPM y clave Camelot, auditorías de duplicados y archivos faltantes, cues y beatgrids, y ediciones de playlists o etiquetas bajo petición (opt-in, con respaldo primero). Local, macOS, código abierto. No oficial.
Documentación
engine-dj-mcp
Pregúntale a Claude, o a cualquier asistente de IA, sobre tu biblioteca de Engine DJ: encuentra pistas por BPM, tonalidad, género o tus propios comentarios, revisa la colección en busca de duplicados, archivos faltantes y pistas sin cues, lee los cues y beatgrids que Engine almacenó, y haz que se creen playlists o se corrijan etiquetas de pistas cuando lo pidas. Funciona a través de MCP, el Model Context Protocol, la forma estándar en que aplicaciones de IA como Claude Desktop, Claude Code, Cursor y VS Code se conectan a herramientas en tu propia computadora: la aplicación inicia este servidor, y el asistente lo llama cuando una pregunta necesita tu biblioteca.
Lee la biblioteca en tu computadora y las de tus unidades USB. Las
abre solo lectura a nivel del sistema operativo y no cambia nada
a menos que lo inicies con --allow-writes — entonces puede crear y editar
playlists y editar género, comentario, etiqueta, año y calificación, y copia la
base de datos completa a una copia de seguridad antes del primer cambio. Consulta Seguridad.
El servidor en sí no envía nada a ningún lugar; lo que tu aplicación de IA hace con sus
respuestas se cubre en PRIVACY.md.
Estado: Activo · Pre-1.0. En uso regular y mantenido; antes de 1.0 una versión MENOR puede cambiar el comportamiento, una PATCH nunca lo hace. Los cambios se registran en CHANGELOG.md.
No afiliado, respaldado ni patrocinado por inMusic Brands, Denon DJ, o el producto Engine DJ. "Engine DJ" se usa aquí solo para nombrar el software cuya biblioteca esta herramienta lee y escribe. No se utilizan logotipos ni imágenes de marca de inMusic o Denon DJ en este proyecto.
Lo que puedes preguntar
Una vez conectado, estas son preguntas ordinarias en el chat:
- "Algo oscuro alrededor de 124 en tono menor que no haya tocado en seis meses."
- "Encuéntrame algo armónicamente compatible con 8A entre 138 y 142."
- "¿Qué está roto en mi colección — archivos faltantes, duplicados, pistas sin cues?"
- "¿Dónde están los puntos de cue en esta pista, y qué tempo analizó Engine?"
- "Créame una playlist de todo en 5A desde 140 BPM en adelante." (necesita
--allow-writes) - "Pon el género de estas cinco pistas en Minimal y califícalas con cuatro estrellas." (necesita
--allow-writes)
Instalación
Necesitas Node.js 22.16 o más reciente. No hay nada más
que instalar: cada aplicación a continuación inicia el servidor con npx, que lo descarga
de npm la primera vez.
npx engine-dj-mcp
Ese comando es lo que ejecutan las aplicaciones; no necesitas ejecutarlo tú mismo.
Claude Desktop
Configuración → Desarrollador → Editar Config, y agrega a claude_desktop_config.json:
{
"mcpServers": {
"engine-dj": { "command": "npx", "args": ["-y", "engine-dj-mcp"] }
}
}
Reinicia Claude Desktop.
Claude Code
claude mcp add --scope user engine-dj -- npx -y engine-dj-mcp
Cursor
Agregar a Cursor
— o abre este enlace profundo directamente, o agrega la misma entrada mcpServers que para
Claude Desktop a ~/.cursor/mcp.json:
cursor://anysphere.cursor-deeplink/mcp/install?name=engine-dj&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImVuZ2luZS1kai1tY3AiXX0%3D
VS Code
Agregar a VS Code — o ejecuta:
code --add-mcp '{"name":"engine-dj","command":"npx","args":["-y","engine-dj-mcp"]}'
Permitir que escriba
Para permitir que el asistente cree y edite playlists y edite etiquetas de pistas, agrega
--allow-writes a args — una bandera en lugar de una variable de entorno
precisamente para que sea visible en la configuración que estás leyendo:
{
"mcpServers": {
"engine-dj": { "command": "npx", "args": ["-y", "engine-dj-mcp@0.17.2", "--allow-writes"] }
}
}
Fija la versión en esta. Sin fijar, npx obtiene lo más reciente en
cada inicio, y esta configuración le da a ese código acceso de escritura a tu
biblioteca. Fijada, una nueva versión llega solo cuando cambias el número.
Cierra Engine DJ antes de pedir un cambio; consulta Escritura.
Compatibilidad
- macOS: sí. Cada verificación contra una biblioteca real se ha ejecutado en macOS,
y encuentra bibliotecas donde Engine DJ las guarda allí:
~/Musicy la parte superior de cada unidad bajo/Volumes. - Windows y Linux: aún no compatibles. La suite de pruebas pasa en Ubuntu,
pero no se ha leído ninguna biblioteca real en ninguno de los dos sistemas. Fuera de macOS, el servidor
busca solo en
~/Music/Engine Library, por lo que una biblioteca en una unidad USB no se encuentra, y no hay opción para apuntarlo a otro lugar. - Node.js 22.16 o más reciente, sin dependencias nativas.
node:sqlitedejó de necesitar una bandera en 22.13, pero la copia de seguridad previa a la escritura usa subackup(), agregado en 22.16. CI ejecuta la suite completa en Node 22.16 y 24, en macOS y Ubuntu. - Bibliotecas Engine DJ en esquema 3.0.0 hasta 3.0.2 — Engine DJ 4.5 y 5.x. Todo aquí se ha ejercitado contra una biblioteca real de esquema 3.0.2; 3.0.0 y 3.0.1 son aceptados por la verificación de versión y cubiertos por fixtures generados, pero no se ha leído ninguna biblioteca real en esas versiones. Cualquier cosa fuera del rango se lista con su versión y se rechaza, nunca se lee a ciegas.
Herramientas
Nueve herramientas de solo lectura, y cinco que escriben — create_playlist,
add_tracks_to_playlist, remove_tracks_from_playlist, reorder_playlist
y update_track_metadata — que aparecen solo cuando inicias el servidor con
--allow-writes. Cada herramienta que lee datos de la biblioteca también acepta un
argumento opcional library — consulta Elegir una biblioteca.
search_tracks
La principal. Búsqueda de texto completo con diacríticos plegados, más filtros para tempo, tonalidad, calificación, cuándo se agregó una pista y cuándo se reprodujo por última vez.
| Argumento | Qué hace |
|---|---|
q | Texto completo sobre título, artista, álbum, género, comentario y etiqueta. Los diacríticos se pliegan, por lo que bjork coincide con Björk — la búsqueda propia de Engine no lo hace. |
bpm | { min, max } o { around, tolerance_pct }. Tempo resuelto, por lo que un BPM analizado gana sobre la etiqueta. |
key | { camelot: [...] } para claves exactas, { compatible_with: "8A" } para vecinos armónicos, { mode: "minor" } para un lado completo de la rueda. |
rating | { min, max }, en estrellas, 0–5. Engine almacena 0, 20, 40, 60, 80, 100; el filtro convierte, por lo que { min: 4 } significa cuatro estrellas y más. El campo rating devuelve el número almacenado, y rating_stars lo mismo en estrellas. |
played | { never: true }, o { before, after } tomando una fecha ISO o una forma relativa como -6 months. |
added | { before, after }, mismas formas de fecha. |
flags | analyzed, available, has_cues, has_beatgrid. has_cues significa que un hot cue está realmente configurado — consulta Limitaciones. |
playlist | { id } o { name } — buscar dentro de una playlist. Los resultados aún vuelven por relevancia o id; get_playlist_tracks es lo que preserva el orden de la playlist. |
fields | Qué columnas devolver. Por defecto id, artist, title, bpm, camelot, rating. |
limit, cursor | Tamaño de página (por defecto 25, máximo 200) y un cursor opaco para la siguiente página. |
include_total | Desactivado por defecto porque contar cuesta mucho más que la página. Limitado a 1000 — un resultado limitado lleva total_capped: true y significa "al menos 1000". |
get_tracks
Metadatos completos para ids de pistas específicos, devueltos en el orden que pediste. Los ids desconocidos se omiten en lugar de fallar la llamada.
ids (requerido), fields, redact_paths.
get_playlists
Tu árbol de playlists, en el orden que Engine DJ lo muestra — carpetas incluidas.
La lista es plana y en el orden en que leerías la barra lateral con cada
carpeta expandida: depth y path llevan el anidamiento, parent_id nombra la
carpeta en la que se encuentra una lista.
| Campo | Significado |
|---|---|
name, id, path | path es el Folder/Sub/Name completo, y es único — un name simple no necesita serlo. |
depth, parent_id | El anidamiento. parent_id es null en el nivel superior. |
is_folder | La lista tiene listas hijas. Engine no tiene una bandera de carpeta — una carpeta es una playlist bajo la que se encuentran otras playlists — por lo que una carpeta vaciada se lee como una playlist vacía. |
is_persisted | La bandera propia de Engine para una lista guardada en el dispositivo. Ambos valores aparecen en listas que Engine muestra, por lo que no se filtra nada en ella. |
track_count | Entradas en esa lista sola, nunca acumuladas de sus hijas — el número que Engine muestra junto a ella. |
missing_count | Cuántas de esas entradas nombran una pista que esta biblioteca no tiene. |
limit (por defecto 200, máximo 1000). warnings aparece si la cadena de enlaces de una playlist
está dañada; nada se elimina nunca de la lista por una de ellas.
get_playlist_tracks
Las pistas de una playlist, en orden de playlist.
Nómbrala con playlist_id o con playlist_name — exactamente uno de los dos.
Los nombres de playlist son únicos solo dentro de una carpeta, por lo que un nombre que coincida con más de
una se rechaza con el id y la ruta completa de cada candidato en lugar de adivinarse;
pasa el path de get_playlists para indicar cuál querías.
Cada fila lleva position, su lugar basado en 1 en la playlist. Mismas
convenciones fields, limit y cursor que search_tracks.
Una entrada cuya pista no está en esta biblioteca conserva su lugar y vuelve como
{ position, entry_id, track_id, missing: true }, con missing_count
junto a entry_count. Eso es ordinario en lugar de corrupción — las entradas
de playlist sobreviven a sus pistas y viajan entre unidades — y se mantienen en
su lugar para que el número de filas aún coincida con la longitud propia de la playlist. En
una biblioteca de referencia, una playlist de 43 entradas contiene exactamente una pista que esa biblioteca
puede reproducir realmente.
get_track_performance
Decodifica el PerformanceData binario que Engine almacena por pista: hot cues, el
cue principal, loops guardados, el beatgrid y un perfil de forma de onda grueso.
Cada campo lleva su propio estado de decodificación y su propio marcador layout.
layout: "verified" significa que el diseño de bytes se confirmó contra una biblioteca
real, por lo que status: "ok" es una afirmación sobre los valores. layout: "unverified"
significaría solo que los bytes se analizaron; ningún campo lo devuelve hoy.
Las posiciones son desplazamientos de muestra; los elementos de cue y loop también llevan segundos.
items: [] con slots: 8 significa una pista analizada sin cues configurados.
id (requerido).
audit_library
Once verificaciones de salud de la colección. Devuelve un conteo y una pequeña muestra de ids por verificación, nunca el conjunto de resultados completo — una biblioteca con miles de pistas sin analizar no debería llenar el contexto de un asistente.
| Verificación | Encuentra |
|---|---|
missing_files | Pistas cuyo archivo ha desaparecido del disco |
unavailable | Pistas que Engine ha marcado como no disponibles |
unanalyzed | Pistas que Engine no ha analizado |
no_cues | Pistas sin hot cue configurado |
no_beatgrid | Pistas sin datos de beatgrid |
missing_key | Pistas sin tonalidad detectada |
suspicious_bpm | Tempo analizado y etiquetado en desacuerdo, o tempo fuera de 60–200 |
duplicates | Mismo artista y título, comparados sin importar mayúsculas en cualquier escritura |
empty_metadata | Sin artista o sin título |
orphan_entries | Entradas de playlist que apuntan a pistas que no están en esta biblioteca — get_playlist_tracks muestra dónde se encuentra cada una |
path_form_mismatch | El archivo está en el disco, pero su nombre allí — o el de una carpeta en el camino — está en una forma Unicode diferente de la ruta que Engine almacenó. macOS lo encuentra de todos modos; Linux no (medido en el controlador exFAT del kernel), y Engine OS en un reproductor es Linux, por lo que estos pueden fallar al cargar en hardware. Las diferencias solo de mayúsculas no se cuentan: exFAT y Windows ignoran mayúsculas |
checks — omítelo para ejecutar las once.
run_sql
Una vía de escape para preguntas que las herramientas anteriores no cubren. La solo lectura está aplicada por el kernel, no por esta herramienta. Los resultados están limitados sea cual sea la consulta.
Prefiere side.track_derived.camelot y side.track_derived.tempo en cláusulas WHERE
sobre las funciones SQL camelot() y tempo() — las funciones se ejecutan
por fila y anulan los índices.
sql (requerido), params, limit.
list_libraries
Toda biblioteca encontrada, incluidas aquellas cuyo esquema no es compatible, se lista con su versión, para que puedas distinguir un servidor roto de una biblioteca faltante. Se vuelve a escanear en cada llamada, por lo que una unidad conectada después de que el servidor se iniciara aparece sin necesidad de reiniciar. Una biblioteca temporalmente ilegible —por ejemplo, mientras Engine DJ escribe en ella— permanece listada con status: "unreadable" en lugar de desaparecer.
Sin argumentos.
refresh_index
Reconstruye el índice de búsqueda si la biblioteca cambió. Normalmente no es necesario; el servidor verifica por sí mismo si está desactualizado antes de responder.
create_playlist
La primera de las cinco herramientas que escriben, ninguna de las cuales se registra en absoluto a menos que el servidor se haya iniciado con --allow-writes.
Crea una nueva lista de reproducción de nivel superior a partir de ids de pistas — track_ids establece tanto su contenido como su orden, por lo que una lista construida por search_tracks llega a Engine DJ en el orden que el asistente eligió. Nada más cambia: ninguna lista se renombra, reordena, vacía o elimina, y ninguna pista, cue o beatgrid se modifica. La única fila existente que se mueve es el enlace de la lista anterior, y el propio trigger de inserción de Engine es quien la mueve.
| Argumento | Qué hace |
|---|---|
title | Nombre de la nueva lista. No debe estar ya en uso en el nivel superior — Engine permite un nombre por carpeta. |
track_ids | Ids de search_tracks o get_tracks, en orden de lista. Puede estar vacío, para una lista vacía. Una pista puede aparecer como máximo una vez, que es la regla propia de Engine. |
Cada entrada almacena la identidad de origen de la pista — (originDatabaseUuid, originTrackId), el par que Engine compara — no el id de fila local, por lo que una lista construida aquí se lee de la misma manera que la propia de Engine.
El resultado incluye playlist_id, tracks_added, library y backup_path. Para deshacerlo, elimina la lista en Engine DJ; backup_path es una instantánea de toda la biblioteca para el caso de que algo saliera mal a un nivel inferior, no un deshacer — consulta Restaurar una instantánea.
Sus propios rechazos: playlist_exists para un título ya ocupado, unknown_track para un id que esta biblioteca no tiene, duplicate_track para el mismo id dos veces — además de los que comparten todas las herramientas de escritura.
add_tracks_to_playlist
Añade una o más pistas a una lista de reproducción existente — esto edita el contenido de esa lista, no crea una nueva (eso lo hace create_playlist). Si la cadena de entradas de la lista ya está dañada, la escritura se rechaza directamente en lugar de repararse, y no se añade nada.
Una lista que es una carpeta (is_folder: true — tiene listas hijas) se edita como cualquier otra: Engine no tiene un tipo de carpeta separado, una carpeta puede tener sus propias entradas, y las tres herramientas de edición añaden, eliminan y reordenan esas entradas sin queja. Las listas dentro de ella no se tocan en ningún caso.
| Argumento | Qué hace |
|---|---|
playlist_id / playlist_name | Exactamente uno de los dos, resuelto de la misma manera que get_playlist_tracks: un nombre que coincida con más de una lista se rechaza listando el id y la ruta completa de cada candidato, sin adivinarlo. |
track_ids | Ids de search_tracks o get_tracks, en el orden en que deben aparecer. Una pista ya presente en la lista se rechaza como duplicate_track — Engine permite una pista en una lista solo una vez. |
at | Dónde aterrizan las nuevas pistas, según las posiciones actuales basadas en 1 de la lista (la misma numeración que reporta get_playlist_tracks): "start", "end" (el predeterminado), o { after_position: n }. |
El resultado incluye playlist_id, tracks_added, positions — dónde aterrizaron las nuevas pistas — undo, undo_complete (siempre true aquí), library y backup_path. undo es la llamada exacta a remove_tracks_from_playlist que revierte esta edición: las posiciones donde aterrizaron las pistas, más expect_track_ids nombrando las pistas que aterrizaron allí, de modo que una lista que algo más cambió mientras tanto se rechaza en lugar de eliminar las filas equivocadas. Llámala para deshacer en lugar de restaurar backup_path — consulta Restaurar una instantánea, y Un deshacer cubre una biblioteca para lo que no alcanza. Sus propios rechazos: playlist_not_found, playlist_chain_damaged, invalid_position, y unknown_track / duplicate_track como para create_playlist — además de los que comparten todas las herramientas de escritura.
remove_tracks_from_playlist
Elimina una o más pistas de una lista de reproducción existente por posición — esto edita el contenido de esa lista; nunca toca ninguna otra lista. Si la cadena de entradas ya está dañada, la escritura se rechaza directamente en lugar de repararse.
| Argumento | Qué hace |
|---|---|
playlist_id / playlist_name | Exactamente uno de los dos, resuelto de la misma manera que get_playlist_tracks. |
positions | Posiciones basadas en 1 que get_playlist_tracks reporta para esta lista en este momento. Incluye entradas cuya pista falta en la biblioteca (missing: true) — eliminar una es una forma legítima de limpiar un hueco, y la única eliminación que undo no puede revertir (ver abajo). |
expect_track_ids | Opcional, una entrada por posición: verifica que cada posición nombrada aún tenga la pista esperada antes de eliminar nada, rechazando toda la llamada en caso contrario. null significa "esta posición debería tener una entrada cuya pista falta", no "sin expectativa". |
El resultado incluye playlist_id, tracks_removed, removed — el track_id de cada posición, null para una que falta — undo, undo_complete, library y backup_path. undo es una secuencia de llamadas a add_tracks_to_playlist, una por cada pista eliminada que se pueda restaurar. Ejecútalas en el orden dado, nunca en paralelo ni en orden inverso — la posición objetivo de cada paso se calcula contra la lista tal como queda después de que el paso anterior ya se haya ejecutado, así que dispararlas fuera de orden coloca las pistas en lugares equivocados. Preferible a restaurar backup_path por la misma razón que arriba.
undo_complete es false cuando la eliminación incluyó una entrada cuya pista falta en la biblioteca: esa entrada nombraba una pista que esta biblioteca no tiene, por lo que ninguna llamada a add_tracks_to_playlist puede devolverla, y un undo_note nombra esas posiciones. Los pasos que se devuelven aún se ejecutan y restauran todo lo demás; las entradas faltantes solo se pueden recuperar desde backup_path, que revierte toda la biblioteca.
Sus propios rechazos: playlist_not_found, playlist_chain_damaged, y invalid_position — para una posición repetida o fuera de rango, o una que no contiene lo que expect_track_ids esperaba — además de los que comparten todas las herramientas de escritura.
playlist_chain_damaged siempre significa lo mismo para las tres herramientas de edición: la cadena de entradas de la lista ya estaba rota antes de la edición, por lo que la edición se negó a tocarla. Si en cambio la verificación que cada edición ejecuta sobre su propio trabajo no coincide — la cadena no se leyó de vuelta como se escribió — la transacción se revierte y eso regresa como library_unreadable, con detail: "not_committed". Ambos dejan la biblioteca exactamente como estaba; solo el segundo significa que este servidor dice que no entiende lo que la biblioteca acaba de hacer.
reorder_playlist
Reordena las pistas de una lista de reproducción existente — esto cambia el orden de las entradas existentes de esa lista; no añade ni elimina nada. Si la cadena de entradas ya está dañada, la escritura se rechaza directamente en lugar de repararse.
| Argumento | Qué hace |
|---|---|
playlist_id / playlist_name | Exactamente uno de los dos, resuelto de la misma manera que get_playlist_tracks. |
order | Una permutación completa de 1..n, siendo n el recuento actual de entradas de la lista. order[i] nombra la posición actual basada en 1 (de get_playlist_tracks) de la pista que debería terminar en la posición i + 1. No se acepta una instrucción parcial de "mover x a y" — nombra cada posición, incluidas las que no se mueven. |
El resultado incluye playlist_id, undo, undo_complete (siempre true aquí), library y backup_path. undo es la permutación inversa exacta, como una sola llamada a reorder_playlist. Sus propios rechazos: playlist_not_found, playlist_chain_damaged, y invalid_position si order no es una permutación completa de las posiciones actuales de la lista — además de los que comparten todas las herramientas de escritura.
Reordenar al orden que una lista ya tiene se acepta y no reescribe ninguna entrada: aún así sella el lastEditTime de la lista, y aún cuesta la instantánea de esta sesión si nada se había escrito todavía.
update_track_metadata
Cambia género, comentario, etiqueta, año o calificación en pistas — los valores que Engine DJ muestra en sus columnas. Escribe en la base de datos de Engine, no en las etiquetas de los archivos de audio. Engine mismo escribe un comentario en el archivo cuando lo editas allí, pero no un género ni una calificación, por lo que otro software que lea las etiquetas no verá estas ediciones de ninguna manera.
| Argumento | |
|---|---|
updates | Hasta 200 entradas, cada una { id, genre?, comment?, label?, year?, rating_stars? }. Solo cambian los campos nombrados. "" limpia un campo de texto; year: 0 significa desconocido, como lo almacena Engine; rating_stars es 0–5. |
library | Requerido cuando hay más de una biblioteca conectada — consulta Elegir una biblioteca. |
Una pista que ya contiene los valores solicitados no se escribe, por lo que repetir una llamada no cambia nada; se cuenta en unchanged. El resultado incluye updated, unchanged, changed — qué campos cambiaron en qué pistas — undo, undo_complete (siempre true), library, y backup_path siempre que la transacción de escritura se ejecutó — lo que puede incluir updated: 0, si las pistas ya habían cambiado a los valores solicitados para cuando se tomó el bloqueo de escritura.
Deshacer. undo es una sola llamada a update_track_metadata que restaura los valores anteriores de exactamente los campos que cambiaron, y nombra la biblioteca. Restaura valores, no lastEditTime: el propio trigger de Engine sella cada edición, incluido el deshacer. Cada entrada lleva expect establecido a lo que esta llamada escribió, por lo que un deshacer reproducido después de que alguien editara la pista de nuevo se rechaza como stale_value en lugar de sobrescribir esa edición. rating_raw y expect existen para esto; una edición ordinaria no necesita ninguno.
Conserva el deshacer de la primera respuesta. Repetir una llamada que ya se completó no encuentra nada que cambiar y devuelve un deshacer vacío. Para trabajo repartido en varias llamadas, reproduce los deshaceres en orden inverso.
Sus propios rechazos: unknown_track; track_not_editable — una pista cuyo origen está vacío (el trigger de Engine reescribe un origen vacío en cualquier actualización, lo que la desvincularía de las entradas de listas en otras unidades), o un campo que contiene un valor que esta herramienta no podría devolver, como una calificación fuera de 0–255; stale_value — la pista cambió después de que se leyeron los valores en expect (hasta 20 discrepancias regresan en un campo estructurado mismatches, con el recuento total en el mensaje en prosa); y invalid_argument. En stale_value, dile al usuario qué pistas y campos cambiaron — no reconstruyas expect desde una lectura nueva para forzar la escritura sin el consentimiento del usuario, o sobrescribirás silenciosamente la edición que el DJ hizo desde entonces. Además de los que comparten todas las herramientas de escritura, excepto index_stale y los errores de consulta: esta herramienta aborda pistas por id y nunca toca el índice de búsqueda.
Buscar justo después de una edición. Género, comentario y etiqueta están en el índice de búsqueda, que se reconstruye en la siguiente lectura. Mientras Engine DJ mantenga la biblioteca abierta, no se puede reconstruir, por lo que una búsqueda puede seguir mostrando los valores antiguos, y refresh_index no puede ayudar hasta que Engine la suelte. La edición en sí está en la base de datos.
Listas de reproducción inteligentes. Una lista de reproducción inteligente cuyas reglas coinciden con el género cambia su contenido cuando se renombra un género, aunque ninguna de sus propias filas haya sido modificada.
Dos bibliotecas conectadas. No asumas que una edición de etiqueta se propaga de la misma manera que una edición de lista de reproducción (ver Un deshacer cubre una biblioteca): medido una vez, una edición de etiqueta realizada en la biblioteca USB no se copió a la biblioteca de la computadora en un inicio fresco de Engine DJ. La otra dirección no se ha medido para etiquetas. Las pistas editadas se marcan para sincronización (isMetadataOfPackedTrackChanged) de la misma manera que Engine DJ marca sus propias ediciones de etiqueta — medido 2026-09-15. Que una sincronización explícita a una unidad luego lleve la edición es para lo que parece estar la marca, pero no se ha medido.
Negativas que comparten todas las herramientas de escritura
Estas provienen de lo que ocurre antes de la escritura misma — elegir la biblioteca, actualizar su índice, resolver la lista de reproducción — y de las propias comprobaciones de la escritura.
| Código | Significado | ¿Nada escrito? |
|---|---|---|
invalid_argument | Los argumentos no tienen sentido — tanto playlist_id como playlist_name, una lista vacía donde se requiere una, o un playlist_name que coincide con varias listas de reproducción (se enumeran todos los candidatos). | sí |
library_not_found | library no nombra nada conectado — la negativa enumera qué hay — o no se pudo leer el encabezado de la biblioteca. | sí |
ambiguous_library | No se proporcionó library, y hay más de una biblioteca compatible conectada — o el uuid dado es compartido por copias en diferentes unidades. Las enumera — ver Elegir una biblioteca. | sí |
unsupported_schema | La versión de la biblioteca está fuera de lo que este servidor soporta. | sí |
library_needs_recovery | Engine DJ dejó un diario no recuperado. Inicia Engine una vez. | sí |
library_busy | Algo mantiene un bloqueo conflictivo en este momento. Reintenta. | sí |
index_stale | El índice aún no se pudo construir, típicamente porque Engine mantiene un bloqueo en la primera ejecución. Lleva retry_after_ms. | sí |
query_timeout, query_process_crashed | La búsqueda que resuelve una lista de reproducción falló. Solo herramientas de edición. | sí |
library_unreadable | La biblioteca no se pudo leer; la instantánea tomada antes de la primera escritura no se pudo hacer (un disco lleno, o un Node anterior a 22.16); o la lectura posterior de una escritura no coincidió con lo que escribió, y se revirtió. | ver detail |
detail en estos errores. Una vez que la escritura misma ha comenzado, detail es exactamente una de dos cadenas, y un cliente puede leerla para decidir si la biblioteca cambió: not_committed — la biblioteca es lo que era — o committed_unverified — el caso raro: la escritura puede haber aterrizado pero no se pudo confirmar, y solo este caso devuelve un backup_path. Las negativas planteadas antes de ese punto — cada fila arriba marcada como "sí" — nunca abrieron la biblioteca para escritura, sea lo que sea que diga su detail: puede estar ausente, ser not_committed, o texto explicativo como los candidatos que enumera un playlist_name ambiguo.
Recursos
engine://schema— la semántica de campos que un asistente necesita antes de escribir SQL: cómo Engine codifica la clave musical, por qué el tempo esCOALESCE(bpmAnalyzed, bpm), queTrack.pathes relativo, dónde vive realmente el orden de la lista de reproducción, y qué columnas auxiliares están indexadas.engine://libraries— lo que se descubrió al inicio y si el esquema de cada biblioteca es compatible. Una instantánea;list_librarieses la vista en vivo.
Elegir una biblioteca
Engine DJ mantiene una biblioteca en tu computadora y otra en cada unidad a la que exportas, por lo que normalmente hay más de una conectada. list_libraries reporta cada una con un uuid y un path, y toda herramienta que lee datos de biblioteca toma un argumento opcional library. Pasa cualquiera de las dos formas exactamente como se imprime — la ruta ~/… se acepta junto con la absoluta. Un valor que no coincide con ninguna vuelve como library_not_found, enumerando entre qué puedes elegir.
Déjalo fuera y el servidor usa la biblioteca compatible con más pistas. Eso importa: la biblioteca local que Engine DJ crea al instalar se escanea primero y a menudo está vacía, por lo que "la primera encontrada" ocultaría la unidad desde la que realmente trabajas.
Esa regla es suficiente para una lectura, que no cambia nada: con dos bibliotecas conectadas, una lectura elige una. Pasa library cuando importa cuál.
Una escritura se niega en su lugar, tan pronto como hay más de una biblioteca compatible conectada — sin importar sus conteos de pistas. ambiguous_library enumera cada candidato con su conteo de pistas, y detail: "not_committed".
El conteo nunca fue la pregunta correcta. Una versión anterior se negaba solo ante un empate exacto, razonando que una unidad USB y su copia empatan precisamente porque una es copia de la otra — medido 2026-09-01, ambas bibliotecas reales en 257. Pero importa una pista en un lado y el empate desaparece, y el predeterminado toma silenciosamente la más grande. Una lista de reproducción escrita en la unidad equivocada al menos es visible allí; el género de una pista no lo es, y te quedas creyendo que la edición no funcionó.
Con una sola biblioteca nada cambia: nunca tienes que nombrarla.
Las copias comparten un uuid. Copia una carpeta Engine Library a otra unidad — un stick de repuesto para un concierto — y la copia conserva el uuid del original, por lo que con ambas conectadas un uuid nombra dos bibliotecas. Una escritura que nombre ese uuid se niega de la misma manera, con ambiguous_library enumerando ambas rutas, en lugar de aterrizar en la unidad que se escaneó primero. Pasa la ruta en su lugar — distingue las copias — y vuelve a leer de esa ruta cualquier cosa de la que dependa la escritura, ya que una lectura que nombre el uuid puede haber venido de la otra copia. Las lecturas que nombran un uuid compartido no se niegan: responden desde una de las copias. Antes de cada escritura, las unidades se escanean de nuevo, por lo que una copia conectada después de que el servidor inició se cuenta — siempre que su biblioteca pueda leerse.
La negativa le dice al asistente que te pregunte en lugar de elegir. De lo contrario, "pasa library, aquí están las dos" es una invitación a tomar la primera, lo que pone la escritura de nuevo en un disco arbitrario y hace que la negativa sea inútil.
Cada biblioteca obtiene su propio índice y su propia conexión, abierta la primera vez que le preguntas algo a esa biblioteca. Comparar dos bibliotecas entre sí — "¿qué hay en esta unidad pero no en esa?" — no es algo que este servidor haga.
Seguridad
Las garantías que este servidor hace sobre tu biblioteca se enumeran en PRINCIPLES.md; esta sección es cómo funcionan.
Tu biblioteca se abre solo lectura a nivel del sistema operativo, no por convención y no por un PRAGMA que una consulta podría volver a apagar. Las escrituras son rechazadas por SQLite mismo, y sin --allow-writes ningún archivo se crea jamás dentro de tu carpeta Engine Library. El índice de búsqueda vive en ~/.engine-dj-mcp/.
Escritura
Sin --allow-writes el servidor no tiene ninguna herramienta que pueda escribir, y el párrafo anterior se mantiene exactamente como está escrito: SQLite mismo se niega.
Con la marca, aparecen cinco herramientas. create_playlist agrega una nueva lista de reproducción y nada más. add_tracks_to_playlist, remove_tracks_from_playlist y reorder_playlist van más allá: con la marca, una lista de reproducción existente ahora puede cambiarse, no solo crearse — sus pistas se agregan, se eliminan o se ponen en un orden diferente. Lo que estas cuatro herramientas de lista de reproducción tocan son las entradas propias de la lista nombrada, más exactamente dos filas en otro lugar: la fila propia de esa lista, cuyo lastEditTime cada edición sella para que Engine vea el cambio, y — solo para create_playlist — el enlace de la lista anterior anterior, hecho por el disparador de inserción propio de Engine. Ninguna otra lista de reproducción se renombra, vacía o elimina, y ninguna pista, cue o beatgrid es tocada por estas cuatro. update_track_metadata cambia género, comentario, etiqueta, año y calificación en las pistas nombradas — ver su sección arriba — y nada más: ninguna lista de reproducción, cue, beatgrid, título, artista, álbum, ruta o archivo es tocado.
Cada edición devuelve undo — la llamada exacta de herramienta que la revierte — y undo_complete; para las herramientas de lista de reproducción, undo se expresa contra las posiciones que la propia edición produjo, y undo_complete dice si reproducirla devuelve la lista de reproducción exactamente como estaba. Reproducir undo es la forma correcta de volver de una edición; restaurar backup_path no lo es, porque revierte la biblioteca completa a antes de la primera escritura de esta sesión, descartando cada conteo de reproducción, importación, cue y cambio de beatgrid que Engine DJ ha registrado desde entonces, junto con la única edición que realmente querías deshacer. Ver Restaurar una instantánea.
Hay exactamente una edición que undo no puede revertir, y lo dice en lugar de fingir lo contrario: eliminar una entrada cuya pista falta en la biblioteca (missing: true). Tal entrada nombra una pista que esta biblioteca no tiene, por lo que no hay id de pista para agregar de vuelta — el resultado vuelve con undo_complete: false y un undo_note nombrando esas posiciones, y los pasos que sí devuelve aún restauran todo lo demás.
Antes de la primera escritura de una sesión, la base de datos se captura en ~/.engine-dj-mcp/backups/, y cada escritura de esa sesión devuelve su ruta. Se mantienen diez instantáneas por biblioteca — por archivo de biblioteca, por lo que una biblioteca y su clon en otra unidad no comparten las diez.
Un archivo sí se crea dentro de tu carpeta Engine Library mientras una escritura está en progreso: el diario de reversión de SQLite, m.db-journal, junto a m.db. Se elimina cuando la transacción se confirma, y es lo que hace que la escritura sea todo-o-nada. Si el proceso se mata a mitad de transacción, el diario queda atrás, y tanto este servidor como Engine DJ tratan la biblioteca como necesitada de recuperación — este servidor reporta library_needs_recovery y se niega a tocar la biblioteca, incluso para lecturas, hasta que hayas iniciado Engine DJ una vez para que pueda revertir el diario. Nada más se escribe jamás en esa carpeta, y sin --allow-writes ni siquiera esto.
La escritura toma el bloqueo de escritura propio de SQLite por la duración de una transacción y no espera por él: si algo más — Engine DJ a mitad de guardado, un reproductor — está manteniendo un bloqueo conflictivo en ese momento, la escritura se niega con library_busy y nada cambia.
Sal de Engine DJ antes de escribir. Tener Engine abierto no suele ser un conflicto de bloqueo, por lo que la escritura misma normalmente pasará — pero lo que Engine luego hace con un cambio hecho debajo de él nunca se ha medido aquí. Cada verificación de aceptación de una escritura se ejecutó con Engine cerrado. Lo que sí se ha medido es que Engine hace su propio trabajo en la biblioteca mientras carga: renumera las entradas de la lista de reproducción, y copia los cambios de lista de reproducción a otra biblioteca conectada (ver abajo). Sal, escribe, relanza — Engine lee la biblioteca al inicio y muestra el cambio.
Sal, no cierres. En macOS, cerrar la ventana de Engine deja la aplicación ejecutándose: observado 2026-09-01 con el proceso principal y siete trabajadores OfflineAnalyzer — que escriben en la base de datos — aún vivos después. Usa ⌘Q.
Un deshacer cubre una biblioteca
Cada resultado de escritura lleva un campo library — el uuid y path de la biblioteca en la que la escritura realmente aterrizó. Dos bibliotecas conectadas a la vez es la configuración ordinaria: una unidad USB y su copia en la computadora. Aquí es donde verificas a cuál de ellas fue una escritura.
undo revierte la edición en esa única biblioteca, y solo allí. Cada paso de deshacer la nombra — por ruta, en el argumento library del propio paso — por lo que reproducir un paso al pie de la letra vuelve a la biblioteca en la que se hizo la edición, no a lo que sea el predeterminado en el momento de la reproducción. Eso importa porque una unidad USB y su copia tienen los mismos ids de lista de reproducción y los mismos ids de pista: una reproducción que resolviera el predeterminado podría aterrizar en el disco equivocado, y su expect_track_ids estaría de acuerdo, ambos lados habiendo sido editados de la misma manera.
Engine DJ mueve los cambios de lista de reproducción entre bibliotecas conectadas por sí mismo, por lo que una copia de tu edición aún puede terminar en algún lugar al que undo no puede llegar.
Medido el 2026-09-01. Se añadió una pista a una lista de reproducción en la biblioteca del
ordenador. Luego se lanzó Engine DJ con la unidad USB conectada, y la
misma lista de reproducción en el USB volvió con la misma pista añadida — la copia
que llevaba el mismo lastEditTime que el INSERT de este servidor había escrito. El
undo se ejecutó entonces y revirtió la edición en el ordenador. El USB la conservó.
Nada resultó dañado: ambas bibliotecas permanecieron íntegras. Pero las dos habían divergido,
y el undo informó éxito, correctamente, porque dentro de su propia biblioteca hizo
exactamente lo que prometió.
Así que: si hay una segunda biblioteca conectada, mira el campo library y deshaz
contra cada biblioteca por separado. Deshacer antes de que Engine DJ se ejecute de nuevo evita
el problema por completo.
A qué biblioteca se propaga un cambio, y en qué dirección, es asunto propio de Engine — este proyecto no lo modela ni hará suposiciones al respecto.
Restaurar una instantánea
backup_path no es un deshacer. Es una copia de la totalidad del m.db de
antes de la primera escritura de la sesión, así que restaurarla revierte toda la
biblioteca a ese momento: cada cuenta de reproducción, importación, cue, beatgrid y calificación
que Engine DJ ha escrito desde entonces se descarta junto con la única edición que querías
eliminar. Recurre a ella solo si la biblioteca en sí está dañada — el caso en el que
una escritura regresa con detail: "committed_unverified".
Las instantáneas viven en ~/.engine-dj-mcp/backups/, diez por biblioteca. Solo un nombre
que termina en .db es una instantánea. Un archivo que termina en .partial-<number> — con o
sin -journal después — es una copia que aún se está escribiendo, o una cuyo
proceso murió antes de terminar: nunca restaures una de esas. Una copia se
renombra a su nombre .db solo una vez que está completa, y una abandonada se
limpia la próxima vez que esa biblioteca se instantanee.
Para deshacer una lista de reproducción que creaste, elimínala en Engine DJ. El propio
disparador de eliminación de Engine repara la cadena de la lista de reproducción y elimina en cascada las entradas,
que es exactamente lo que debería hacer al eliminarla y no es algo que restaurar
una instantánea haga mejor. Para las herramientas de listas de reproducción, para deshacer una edición a una
lista existente, reproduce el undo que devolvió la edición en su lugar — nombra
la llamada precisa a add_tracks_to_playlist, remove_tracks_from_playlist o
reorder_playlist que devuelve la lista exactamente como estaba,
sin tocar nada más que Engine DJ haya registrado desde entonces.
run_sql acepta SQL arbitrario, pero solo se ejecuta la primera declaración,
y VACUUM, ATTACH y DETACH se rechazan de plano, así que una
declaración encadenada o de exfiltración no puede colarse a través de la conexión de solo lectura.
Si Engine DJ se cerró de forma incorrecta y dejó un diario no recuperado, este
servidor no abrirá la biblioteca para "arreglarla", con o sin
--allow-writes — avanzar un diario es una reparación en un archivo de otra
persona, y cada herramienta de escritura rechaza tal biblioteca directamente en lugar de
dejar que SQLite lo haga de paso. Informa library_needs_recovery y
te pide que lances Engine DJ una vez para que pueda recuperar su propia biblioteca.
Limitaciones
Lee esto antes de decidir en qué confiar.
Los diseños de PerformanceData están ingeniados a la inversa, así que cada campo
decodificado dice qué tipo es. Los cuatro están marcados como layout: "verified" —
derivados y verificados contra una biblioteca real de Engine DJ 3.0.x de 281
pistas analizadas, donde los offsets de cue caen dentro de la pista, el tempo
implícito del beatgrid coincide con bpmAnalyzed en las 281, y el espaciado de puntos
declarado de la forma de onda se multiplica de vuelta al recuento de muestras de la pista en las 281.
Los loops fueron los últimos en ganarlo. La cuadrícula de slots se fijó con 2248
centinelas, pero ninguna biblioteca disponible tenía un loop guardado, así que un slot poblado
permaneció sin probar y los loops llevaron layout: "unverified" a través de varias
versiones. Un loop guardado deliberadamente lo resolvió: su slot abarca 1.678321678 s
en una pista que Engine analizó a 143 BPM, que son cuatro beats con una precisión de 4e-15 s.
Solo el orden de campos, la unidad y el endianness correctos aterrizan en un recuento de beats entero.
Un marcador layout es una afirmación sobre los bytes, no sobre cada nombre que se les pone.
Cuatro etiquetas se infieren en lugar de medirse, y el código lo dice
donde se define cada una: cuál de los cuatro bytes de color de un cue es qué canal
(se devuelven tal como se almacenan, un valor de 32 bits, sin afirmación de canal); que
el segundo beatgrid es el que Engine llama "ajustado" (que es el que Engine reproduce
está medido — en siete pistas el otro corre a exactamente la mitad
del tempo analizado); que main_cue.is_adjusted es lo que significa su byte de bandera;
y que los tres bytes por punto de la forma de onda son bajo, medio y alto en ese
orden. Ninguno afecta a un valor que recibes.
Todo lo demás — títulos, artistas, tempo, tonalidad, calificaciones, historial de reproducción, rutas de archivo — se lee directamente de la base de datos y no lleva tal advertencia.
has_cues y no_cues significan "hay un cue caliente configurado". Engine escribe un
blob quickCues en cada pista analizada se use o no un pad, así que la
prueba SQL barata respondería a una pregunta sobre análisis en su lugar: en la
biblioteca de referencia, los 281 blobs contarían como que tienen cues, mientras que dos pistas
realmente los tienen. Por lo tanto, el blob se decodifica mientras se construye el índice. Eso
cuesta aproximadamente 100 ms extra a 50,000 pistas, y solo cuando tu biblioteca
cambia. El cue principal de la pista no cuenta para ello — Engine lo configura
como un marcador de reproducción en lugar de que el DJ lo coloque. has_beatgrid sí
prueba el blob: beatData no tiene un estado "escrito pero vacío".
Escribe listas de reproducción y cinco campos de pista, y solo cuando lo pides.
Sin --allow-writes la biblioteca se abre en solo lectura a nivel del sistema operativo y
no hay herramienta que pueda escribir. Con la bandera, aparecen cinco herramientas de escritura:
cuatro crean listas de reproducción y añaden, eliminan y reordenan sus pistas, y
update_track_metadata cambia género, comentario, etiqueta, año y calificación en
la base de datos de Engine — y esa es la lista completa. Ni un cue, un loop o un
beatgrid; ni un título, artista, álbum, recuento de reproducciones o ruta de archivo; ni las etiquetas
dentro de tus archivos de audio; y ni siquiera la recuperación de un diario que Engine DJ
dejó atrás.
No lee el historial de reproducción. Track.timeLastPlayed responde "¿qué no he
reproducido en seis meses?", pero la base de datos separada del historial de Engine —
sesiones, decks, qué siguió a qué — no se abre en absoluto.
Las listas inteligentes no se informan. Las listas basadas en reglas de Engine viven en una tabla
Smartlist separada con su propio orden y una columna de reglas JSON, y nada
aquí la lee. get_playlists informa solo listas de reproducción y carpetas ordinarias, así que
una lista inteligente que puedes ver en Engine no aparecerá.
Una carpeta vacía se lee como una lista de reproducción vacía. El esquema de Engine no tiene una bandera
de carpeta — una carpeta es simplemente una lista de reproducción bajo la que se asientan otras listas — así que
is_folder significa "tiene listas hijas". Una carpeta que has vaciado es
indistinguible de una lista de reproducción sin pistas.
Las pistas de una lista se pueden editar; la lista en sí no. Con
--allow-writes se puede crear una nueva lista, y una existente puede tener
pistas añadidas, eliminadas o reordenadas — pero no renombrada, eliminada, movida entre
carpetas, ni convertida en una carpeta en sí, y no hay listas fijas ni
transiciones sugeridas. Responde preguntas sobre la colección y escribe
la respuesta si lo pides; la mezcla es tuya.
Solo esquema 3.0.0 a 3.0.2. Las bibliotecas más antiguas y más nuevas se listan con su versión y se informan como no soportadas en lugar de leerse a ciegas.
Licencia
MIT — ver LICENSE. Lo que este proyecto garantiza y se niega a hacer: PRINCIPLES.md.