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

CI npm licence

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í: ~/Music y 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:sqlite dejó de necesitar una bandera en 22.13, pero la copia de seguridad previa a la escritura usa su backup(), 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.

ArgumentoQué hace
qTexto 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.
flagsanalyzed, 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.
fieldsQué columnas devolver. Por defecto id, artist, title, bpm, camelot, rating.
limit, cursorTamaño de página (por defecto 25, máximo 200) y un cursor opaco para la siguiente página.
include_totalDesactivado 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.

CampoSignificado
name, id, pathpath es el Folder/Sub/Name completo, y es único — un name simple no necesita serlo.
depth, parent_idEl anidamiento. parent_id es null en el nivel superior.
is_folderLa 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_persistedLa 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_countEntradas en esa lista sola, nunca acumuladas de sus hijas — el número que Engine muestra junto a ella.
missing_countCuá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ónEncuentra
missing_filesPistas cuyo archivo ha desaparecido del disco
unavailablePistas que Engine ha marcado como no disponibles
unanalyzedPistas que Engine no ha analizado
no_cuesPistas sin hot cue configurado
no_beatgridPistas sin datos de beatgrid
missing_keyPistas sin tonalidad detectada
suspicious_bpmTempo analizado y etiquetado en desacuerdo, o tempo fuera de 60–200
duplicatesMismo artista y título, comparados sin importar mayúsculas en cualquier escritura
empty_metadataSin artista o sin título
orphan_entriesEntradas 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_mismatchEl 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.

ArgumentoQué hace
titleNombre de la nueva lista. No debe estar ya en uso en el nivel superior — Engine permite un nombre por carpeta.
track_idsIds 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.

ArgumentoQué hace
playlist_id / playlist_nameExactamente 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_idsIds 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.
atDó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.

ArgumentoQué hace
playlist_id / playlist_nameExactamente uno de los dos, resuelto de la misma manera que get_playlist_tracks.
positionsPosiciones 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_idsOpcional, 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.

ArgumentoQué hace
playlist_id / playlist_nameExactamente uno de los dos, resuelto de la misma manera que get_playlist_tracks.
orderUna 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
updatesHasta 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.
libraryRequerido 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ódigoSignificado¿Nada escrito?
invalid_argumentLos 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_foundlibrary no nombra nada conectado — la negativa enumera qué hay — o no se pudo leer el encabezado de la biblioteca.sí
ambiguous_libraryNo 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_schemaLa versión de la biblioteca está fuera de lo que este servidor soporta.sí
library_needs_recoveryEngine DJ dejó un diario no recuperado. Inicia Engine una vez.sí
library_busyAlgo mantiene un bloqueo conflictivo en este momento. Reintenta.sí
index_staleEl í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_crashedLa búsqueda que resuelve una lista de reproducción falló. Solo herramientas de edición.sí
library_unreadableLa 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 es COALESCE(bpmAnalyzed, bpm), que Track.path es 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_libraries es 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.