Engine DJ MCP (engine-dj-mcp)
Pergunte a um assistente de IA sobre sua biblioteca Engine DJ (Denon): busca por BPM e tonalidade Camelot, auditorias de duplicatas e arquivos ausentes, cues e beatgrids, e edições de playlists ou tags sob solicitação (opt-in, com backup primeiro). Local, macOS, código aberto. Não oficial.
Documentação
engine-dj-mcp
Pergunte ao Claude, ou a qualquer assistente de IA, sobre sua biblioteca Engine DJ: encontre faixas por BPM, tonalidade, gênero ou seus próprios comentários, verifique a coleção em busca de duplicatas, arquivos ausentes e faixas sem cues, leia os cues e beatgrids que o Engine armazenou, e peça para criar playlists ou corrigir tags de faixas quando você solicitar. Funciona através do MCP, o Model Context Protocol, o padrão pelo qual aplicativos de IA como Claude Desktop, Claude Code, Cursor e VS Code se conectam a ferramentas no seu próprio computador: o aplicativo inicia este servidor, e o assistente o chama quando uma pergunta precisa da sua biblioteca.
Ele lê a biblioteca no seu computador e as dos seus drives USB. Ele as
abre somente leitura no nível do sistema operacional e não altera nada
a menos que você o inicie com --allow-writes — então ele pode criar e editar
playlists e editar gênero, comentário, rótulo, ano e avaliação, e copia o
banco de dados inteiro para um backup antes da primeira alteração. Veja Segurança.
O servidor em si não envia nada para lugar nenhum; o que seu aplicativo de IA faz com as
respostas está coberto em PRIVACY.md.
Status: Ativo · Pré-1.0. Em uso regular e mantido; antes da 1.0, um lançamento MINOR pode mudar o comportamento, um PATCH nunca muda. As alterações estão registradas em CHANGELOG.md.
Não afiliado, endossado ou patrocinado pela inMusic Brands, Denon DJ, ou pelo produto Engine DJ. "Engine DJ" é usado aqui apenas para nomear o software cuja biblioteca esta ferramenta lê e escreve. Nenhum logotipo ou arte de marca da inMusic ou Denon DJ é usado neste projeto.
O que você pode perguntar
Uma vez conectado, estas são perguntas comuns no chat:
- "Algo dark por volta de 124 em tom menor que não toco há seis meses."
- "Encontre algo harmonicamente compatível com 8A entre 138 e 142."
- "O que está quebrado na minha coleção — arquivos ausentes, duplicatas, faixas sem cues?"
- "Onde estão os cue points nesta faixa, e qual tempo o Engine analisou?"
- "Monte uma playlist com tudo em 5A a partir de 140 BPM." (requer
--allow-writes) - "Defina o gênero destas cinco faixas como Minimal e avalie-as com quatro estrelas." (requer
--allow-writes)
Instalação
Você precisa do Node.js 22.16 ou mais recente. Não há mais nada
para instalar: cada aplicativo abaixo inicia o servidor com npx, que o baixa
do npm na primeira vez.
npx engine-dj-mcp
Esse comando é o que os aplicativos executam; você não precisa executá-lo você mesmo.
Claude Desktop
Configurações → Desenvolvedor → Editar Config, e adicione a claude_desktop_config.json:
{
"mcpServers": {
"engine-dj": { "command": "npx", "args": ["-y", "engine-dj-mcp"] }
}
}
Reinicie o Claude Desktop.
Claude Code
claude mcp add --scope user engine-dj -- npx -y engine-dj-mcp
Cursor
Adicionar ao Cursor
— ou abra este deep link diretamente, ou adicione a mesma entrada mcpServers como para
Claude Desktop em ~/.cursor/mcp.json:
cursor://anysphere.cursor-deeplink/mcp/install?name=engine-dj&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImVuZ2luZS1kai1tY3AiXX0%3D
VS Code
Adicionar ao VS Code — ou execute:
code --add-mcp '{"name":"engine-dj","command":"npx","args":["-y","engine-dj-mcp"]}'
Permitindo escrita
Para permitir que o assistente crie e edite playlists e edite tags de faixas, adicione
--allow-writes a args — uma flag em vez de uma variável de ambiente
justamente para que fique visível na configuração que você está lendo:
{
"mcpServers": {
"engine-dj": { "command": "npx", "args": ["-y", "engine-dj-mcp@0.17.2", "--allow-writes"] }
}
}
Fixe a versão neste caso. Sem fixar, npx busca o que estiver mais recente a
cada inicialização, e esta configuração dá a esse código acesso de escrita à sua
biblioteca. Com a versão fixada, um novo lançamento só a alcança quando você mudar o número.
Saia do Engine DJ antes de pedir uma alteração; veja Escrita.
Compatibilidade
- macOS: sim. Cada verificação contra uma biblioteca real foi executada no macOS,
e ele encontra bibliotecas onde o Engine DJ as mantém lá:
~/Musice o topo de cada drive sob/Volumes. - Windows e Linux: ainda não suportados. A suíte de testes passa no Ubuntu,
mas nenhuma biblioteca real foi lida em nenhum dos dois sistemas. Fora do macOS, o servidor
procura apenas em
~/Music/Engine Library, então uma biblioteca em um drive USB não é encontrada, e não há opção para apontá-lo para outro lugar. - Node.js 22.16 ou mais recente, sem dependências nativas.
node:sqlitedeixou de precisar de uma flag na versão 22.13, mas o backup pré-escrita usa seubackup(), adicionado na 22.16. O CI executa a suíte completa no Node 22.16 e 24, no macOS e Ubuntu. - Bibliotecas Engine DJ no schema 3.0.0 até 3.0.2 — Engine DJ 4.5 e 5.x. Tudo aqui foi exercitado contra uma biblioteca real schema 3.0.2; 3.0.0 e 3.0.1 são aceitos pela verificação de versão e cobertos por fixtures geradas, mas nenhuma biblioteca real nessas versões foi lida. Qualquer coisa fora do intervalo é listada com sua versão e recusada, nunca lida por suposição.
Ferramentas
Nove ferramentas somente leitura, e cinco que escrevem — create_playlist,
add_tracks_to_playlist, remove_tracks_from_playlist, reorder_playlist
e update_track_metadata — que aparecem apenas quando você inicia o servidor com
--allow-writes. Toda ferramenta que lê dados da biblioteca também aceita um
argumento opcional library — veja Escolhendo uma biblioteca.
search_tracks
A principal. Busca em texto completo com diacríticos normalizados, além de filtros para tempo, tonalidade, avaliação, quando uma faixa foi adicionada e quando foi tocada pela última vez.
| Argumento | O que faz |
|---|---|
q | Texto completo sobre título, artista, álbum, gênero, comentário e rótulo. Diacríticos são normalizados, então bjork corresponde a Björk — a busca do próprio Engine não faz isso. |
bpm | { min, max } ou { around, tolerance_pct }. Tempo resolvido, então um BPM analisado vence sobre a tag. |
key | { camelot: [...] } para tonalidades exatas, { compatible_with: "8A" } para vizinhos harmônicos, { mode: "minor" } para um lado inteiro da roda. |
rating | { min, max }, em estrelas, 0–5. O Engine armazena 0, 20, 40, 60, 80, 100; o filtro converte, então { min: 4 } significa quatro estrelas ou mais. O campo rating devolve o número armazenado, e rating_stars o mesmo em estrelas. |
played | { never: true }, ou { before, after } aceitando uma data ISO ou uma forma relativa como -6 months. |
added | { before, after }, mesmas formas de data. |
flags | analyzed, available, has_cues, has_beatgrid. has_cues significa que um hot cue está realmente definido — veja Limitações. |
playlist | { id } ou { name } — busca dentro de uma playlist. Os resultados ainda voltam por relevância ou id; get_playlist_tracks é o que preserva a ordem da playlist. |
fields | Quais colunas retornar. Padrão é id, artist, title, bpm, camelot, rating. |
limit, cursor | Tamanho da página (padrão 25, máximo 200) e um cursor opaco para a próxima página. |
include_total | Desligado por padrão porque contar custa muito mais que a página. Limitado a 1000 — um resultado limitado carrega total_capped: true e significa "pelo menos 1000". |
get_tracks
Metadados completos para ids de faixas específicos, retornados na ordem que você pediu. Ids desconhecidos são omitidos em vez de falhar a chamada.
ids (obrigatório), fields, redact_paths.
get_playlists
Sua árvore de playlists, na ordem que o Engine DJ mostra — pastas incluídas.
A lista é plana e na ordem que você leria a barra lateral com cada
pasta expandida: depth e path carregam o aninhamento, parent_id nomeia a
pasta em que uma lista está.
| Campo | Significado |
|---|---|
name, id, path | path é o Folder/Sub/Name completo, e é único — um name simples não precisa ser. |
depth, parent_id | O aninhamento. parent_id é null no nível superior. |
is_folder | A lista tem listas filhas. O Engine não tem flag de pasta — uma pasta é uma playlist sob a qual outras playlists ficam — então uma pasta esvaziada é lida como uma playlist vazia. |
is_persisted | A flag do próprio Engine para uma lista salva no dispositivo. Ambos os valores aparecem em listas que o Engine exibe, então nada é filtrado nisso. |
track_count | Entradas apenas naquela lista, nunca somadas de seus filhos — o número que o Engine mostra ao lado dela. |
missing_count | Quantas dessas entradas nomeiam uma faixa que esta biblioteca não possui. |
limit (padrão 200, máximo 1000). warnings aparece se a cadeia de links de uma playlist
estiver danificada; nada é jamais removido da lista por causa disso.
get_playlist_tracks
As faixas de uma playlist, na ordem da playlist.
Nomeie-a com playlist_id ou com playlist_name — exatamente um dos dois.
Nomes de playlist são únicos apenas dentro de uma pasta, então um nome que corresponda a mais de
uma é recusado com o id e o caminho completo de cada candidato em vez de adivinhar;
passe o path de get_playlists para dizer qual você quis dizer.
Cada linha carrega position, sua posição baseada em 1 na playlist. Mesmas
convenções de fields, limit e cursor que search_tracks.
Uma entrada cuja faixa não está nesta biblioteca mantém seu lugar e volta como
{ position, entry_id, track_id, missing: true }, com missing_count
junto de entry_count. Isso é normal em vez de corrupção — entradas de playlist
sobrevivem às suas faixas e viajam entre drives — e são mantidas no lugar
para que o número de linhas ainda corresponda ao comprimento da própria playlist. Em uma
biblioteca de referência, uma playlist de 43 entradas contém exatamente uma faixa que essa biblioteca
pode realmente tocar.
get_track_performance
Decodifica o PerformanceData binário que o Engine armazena por faixa: hot cues, o
cue principal, loops salvos, o beatgrid e um perfil de forma de onda grosseiro.
Cada campo carrega seu próprio status de decodificação e seu próprio marcador layout.
layout: "verified" significa que o layout de bytes foi confirmado contra uma biblioteca
real, então status: "ok" é uma afirmação sobre os valores. layout: "unverified"
significaria apenas que os bytes foram analisados; nenhum campo o retorna hoje.
Posições são offsets de amostra; itens de cue e loop também carregam segundos.
items: [] com slots: 8 significa uma faixa analisada sem cues definidos.
id (obrigatório).
audit_library
Onze verificações de saúde da coleção. Retorna uma contagem e uma pequena amostra de ids por verificação, nunca o conjunto completo de resultados — uma biblioteca com milhares de faixas não analisadas não deve encher o contexto de um assistente.
| Verificação | Encontra |
|---|---|
missing_files | Faixas cujo arquivo sumiu do disco |
unavailable | Faixas que o Engine marcou como indisponíveis |
unanalyzed | Faixas que o Engine não analisou |
no_cues | Faixas sem hot cue definido |
no_beatgrid | Faixas sem dados de beatgrid |
missing_key | Faixas sem tonalidade detectada |
suspicious_bpm | Tempo analisado e tempo da tag discordam, ou tempo fora de 60–200 |
duplicates | Mesmo artista e título, comparados independentemente de maiúsculas em qualquer script |
empty_metadata | Sem artista ou sem título |
orphan_entries | Entradas de playlist apontando para faixas que não estão nesta biblioteca — get_playlist_tracks mostra onde cada uma está |
path_form_mismatch | O arquivo está no disco, mas seu nome lá — ou o de uma pasta no caminho — está em uma forma Unicode diferente do caminho que o Engine armazenou. O macOS o encontra mesmo assim; o Linux não (medido no driver exFAT do kernel), e o Engine OS em um player é Linux, então estes podem falhar ao carregar no hardware. Diferenças apenas de maiúsculas não são contadas: exFAT e Windows ignoram maiúsculas |
checks — omita para executar todas as onze.
run_sql
Uma saída de emergência para perguntas que as ferramentas acima não cobrem. Somente leitura é imposta pelo kernel, não por esta ferramenta. Os resultados são limitados independentemente do que a consulta disser.
Prefira side.track_derived.camelot e side.track_derived.tempo em cláusulas WHERE
em vez das funções SQL camelot() e tempo() — as funções executam
por linha e prejudicam os índices.
sql (obrigatório), params, limit.
list_libraries
Toda biblioteca encontrada, incluindo aquelas cujo esquema não é suportado — listadas
com sua versão, para que você possa distinguir um servidor quebrado de uma biblioteca ausente.
Re-escaneia a cada chamada, então um drive conectado depois que o servidor iniciou aparece
sem necessidade de reiniciar. Uma biblioteca temporariamente ilegível — Engine DJ
gravando nela, por exemplo — permanece listada com status: "unreadable" em vez de
desaparecer.
Sem argumentos.
refresh_index
Reconstrói o índice de busca se a biblioteca mudou. Normalmente desnecessário; o servidor verifica a desatualização por conta própria antes de responder.
create_playlist
A primeira das cinco ferramentas que gravam, nenhuma das quais é registrada
a menos que o servidor tenha sido iniciado com --allow-writes.
Cria uma nova playlist de nível superior a partir de ids de faixas — track_ids define tanto
o que está nela quanto a ordem em que está, então uma lista construída por search_tracks
chega no Engine DJ na ordem que o assistente escolheu. Nada mais muda:
nenhuma playlist é renomeada, reordenada, esvaziada ou excluída, e nenhuma faixa, cue ou
beatgrid é tocada. A única linha existente que se move é o link da playlist anterior
anterior, e o próprio trigger de inserção do Engine é o que a move.
| Argumento | O que faz |
|---|---|
title | Nome da nova playlist. Não deve já existir no nível superior — o Engine permite um nome por pasta. |
track_ids | Ids de search_tracks ou get_tracks, na ordem da playlist. Pode ser vazio, para uma playlist vazia. Uma faixa pode aparecer no máximo uma vez, que é a regra do próprio Engine. |
Cada entrada armazena a identidade de origem da faixa — (originDatabaseUuid, originTrackId), o par que o Engine corresponde — não o id da linha local, então uma
playlist construída aqui é lida da mesma forma que a do próprio Engine.
O resultado carrega playlist_id, tracks_added, library e backup_path. Para desfazer
isso, exclua a playlist no Engine DJ; backup_path é um
snapshot de toda a biblioteca para o caso de algo ter dado errado em um nível mais baixo, não um
desfazer — veja Restaurando um snapshot.
Suas próprias recusas: playlist_exists para um título já usado, unknown_track para um
id que esta biblioteca não possui, duplicate_track para o mesmo id duas vezes — além
daquelas compartilhadas por toda ferramenta de gravação.
add_tracks_to_playlist
Adiciona uma ou mais faixas a uma playlist existente — isso edita o
conteúdo dessa playlist, não cria uma nova (create_playlist faz
isso). Se a cadeia de entradas da playlist já estiver danificada, a gravação é
recusada diretamente em vez de reparada, e nada é adicionado.
Uma playlist que é uma pasta (is_folder: true — tem listas filhas) é
editada como qualquer outra: o Engine não tem um tipo separado de pasta, uma pasta pode conter
entradas próprias, e todas as três ferramentas de edição adicionam, removem e reordenam
essas entradas sem reclamar. As listas dentro dela não são tocadas de qualquer forma.
| Argumento | O que faz |
|---|---|
playlist_id / playlist_name | Exatamente um dos dois, resolvido da mesma forma que get_playlist_tracks faz: um nome que corresponde a mais de uma playlist é recusado com o id e o caminho completo de cada candidato listados, não adivinhado. |
track_ids | Ids de search_tracks ou get_tracks, na ordem em que devem aparecer. Uma faixa já na playlist é recusada como duplicate_track — o Engine permite uma faixa em uma playlist apenas uma vez. |
at | Onde as novas faixas caem, em relação às posições atuais baseadas em 1 da playlist (a mesma numeração que get_playlist_tracks relata): "start", "end" (o padrão), ou { after_position: n }. |
O resultado carrega playlist_id, tracks_added, positions — onde as
novas faixas caíram — undo, undo_complete (sempre true aqui), library
e backup_path. undo é a chamada exata de remove_tracks_from_playlist que
reverte esta edição: as posições onde as faixas caíram, mais
expect_track_ids nomeando as faixas que caíram lá, então uma playlist
que algo mais mudou enquanto isso é recusada em vez de ter as
linhas erradas removidas. Chame-a para desfazer em vez de restaurar backup_path —
veja Restaurando um snapshot, e
Um desfazer cobre uma biblioteca para o que ela
não alcança. Suas próprias recusas: playlist_not_found, playlist_chain_damaged,
invalid_position, e unknown_track / duplicate_track como para
create_playlist — além daquelas
compartilhadas por toda ferramenta de gravação.
remove_tracks_from_playlist
Remove uma ou mais faixas de uma playlist existente por posição — isso edita o conteúdo dessa playlist; nunca toca em nenhuma outra playlist. Se a cadeia de entradas já estiver danificada, a gravação é recusada diretamente em vez de reparada.
| Argumento | O que faz |
|---|---|
playlist_id / playlist_name | Exatamente um dos dois, resolvido da mesma forma que get_playlist_tracks faz. |
positions | Posições baseadas em 1 que get_playlist_tracks relata para esta playlist agora. Inclui entradas cuja faixa está ausente da biblioteca (missing: true) — remover uma é uma forma legítima de limpar um buraco, e a única remoção que undo não pode reverter (veja abaixo). |
expect_track_ids | Opcional, uma entrada por posição: verifica se cada posição nomeada ainda contém a faixa esperada antes que qualquer coisa seja removida, recusando a chamada inteira caso contrário. null significa "esta posição deve conter uma entrada cuja faixa está ausente", não "sem expectativa". |
O resultado carrega playlist_id, tracks_removed, removed — cada
posição's track_id, null para uma ausente — undo, undo_complete,
library e backup_path. undo é uma sequência de chamadas add_tracks_to_playlist,
uma por faixa removida que pode ser restaurada. Execute-as na ordem dada,
nunca em paralelo e nunca invertidas — a posição alvo de cada passo é
calculada contra a lista como ela está após o passo anterior já ter
sido executado, então dispará-las fora de ordem coloca as faixas de volta nos lugares errados.
Preferido em vez de restaurar backup_path pela mesma razão acima.
undo_complete é false quando a remoção incluiu uma entrada cuja faixa está
ausente da biblioteca: essa entrada nomeava uma faixa que esta biblioteca não
possui, então nenhuma chamada add_tracks_to_playlist pode colocá-la de volta, e um
undo_note nomeia essas posições. Os passos que são retornados ainda são executados e
ainda restauram todo o resto; as entradas ausentes são recuperáveis apenas de
backup_path, que reverte a biblioteca inteira.
Suas próprias recusas: playlist_not_found, playlist_chain_damaged, e
invalid_position — para uma posição repetida ou fora do intervalo, ou uma que
não contém o que expect_track_ids esperava — além daquelas
compartilhadas por toda ferramenta de gravação.
playlist_chain_damaged sempre significa a mesma coisa para todas as três ferramentas de
edição: a cadeia de entradas da playlist já estava quebrada antes da edição,
que é o motivo pelo qual a edição recusou tocá-la. Se em vez disso a verificação que cada edição
executa em seu próprio trabalho discorda — a cadeia não leu de volta como foi
escrita — a transação é revertida e isso volta como
library_unreadable, com detail: "not_committed". Ambos deixam a biblioteca
exatamente como estava; apenas o segundo é este servidor dizendo que não
entende o que a biblioteca acabou de fazer.
reorder_playlist
Reordena as faixas de uma playlist existente — isso muda a ordem das entradas existentes dessa playlist; não adiciona nada e não remove nada. Se a cadeia de entradas já estiver danificada, a gravação é recusada diretamente em vez de reparada.
| Argumento | O que faz |
|---|---|
playlist_id / playlist_name | Exatamente um dos dois, resolvido da mesma forma que get_playlist_tracks faz. |
order | Uma permutação completa de 1..n, sendo n a contagem atual de entradas da playlist. order[i] nomeia a posição atual baseada em 1 (de get_playlist_tracks) da faixa que deve terminar na posição i + 1. Uma instrução parcial de "mover x para y" não é aceita — nomeie cada posição, incluindo aquelas que não se movem. |
O resultado carrega playlist_id, undo, undo_complete (sempre true
aqui), library e backup_path. undo é a permutação inversa exata, como uma única
chamada reorder_playlist. Suas próprias recusas: playlist_not_found,
playlist_chain_damaged, e invalid_position se order não for uma permutação
completa das posições atuais da playlist — além daquelas
compartilhadas por toda ferramenta de gravação.
Reordenar para a ordem em que uma playlist já está é aceito e não reescreve
nenhuma entrada: ainda carimba o lastEditTime da playlist, e ainda custa o
snapshot desta sessão se nada tivesse sido gravado ainda.
update_track_metadata
Altera gênero, comentário, rótulo, ano ou classificação em faixas — os valores que o Engine DJ mostra em suas colunas. Ele grava no banco de dados do Engine, não nas tags dos arquivos de áudio. O próprio Engine grava um comentário no arquivo quando você o edita lá, mas não um gênero ou uma classificação, então outros softwares lendo as tags não verão essas edições de qualquer forma.
| Argumento | |
|---|---|
updates | Até 200 entradas, cada uma { id, genre?, comment?, label?, year?, rating_stars? }. Apenas os campos nomeados mudam. "" limpa um campo de texto; year: 0 significa desconhecido, como o Engine o armazena; rating_stars é 0–5. |
library | Obrigatório quando mais de uma biblioteca está conectada — veja Escolhendo uma biblioteca. |
Uma faixa que já contém os valores solicitados não é gravada, então repetir
uma chamada não muda nada; é contada em unchanged. O resultado carrega
updated, unchanged, changed — quais campos mudaram em quais faixas —
undo, undo_complete (sempre true), library, e backup_path sempre que
a transação de gravação foi executada — o que pode incluir updated: 0, se as faixas já
tivessem mudado para os valores solicitados no momento em que o bloqueio de gravação foi obtido.
Desfazer. undo é uma chamada update_track_metadata que restaura os
valores anteriores exatamente dos campos que mudaram, e nomeia a biblioteca.
Ela restaura valores, não lastEditTime: o próprio trigger do Engine carimba cada
edição, incluindo o desfazer. Cada entrada carrega expect definido para o que esta chamada
gravou, então um desfazer repetido depois que alguém editou a faixa novamente é recusado como
stale_value em vez de sobrescrever essa edição. rating_raw e expect
existem para isso; uma edição comum não precisa de nenhum dos dois.
Mantenha o desfazer da primeira resposta. Repetir uma chamada que já passou não encontra nada para mudar e retorna um desfazer vazio. Para trabalho distribuído em várias chamadas, reproduza os desfazeres em ordem reversa.
Suas próprias recusas: unknown_track; track_not_editable — uma faixa cuja origem
está vazia (o trigger do Engine reescreve uma origem vazia em qualquer atualização, o que
a desanexaria das entradas de playlist em outros drives), ou um campo contendo um valor
que esta ferramenta não poderia colocar de volta, como uma classificação fora de 0–255; stale_value —
a faixa mudou após os valores em expect terem sido lidos (até 20 incompatibilidades
voltam em um campo estruturado mismatches, com a contagem total na
mensagem em prosa); e invalid_argument. Em stale_value, diga ao usuário quais
faixas e campos mudaram — não reconstrua expect a partir de uma nova leitura para forçar a
gravação sem o consentimento do usuário, ou isso sobrescreve silenciosamente a edição que o
DJ fez desde então. Além daquelas
compartilhadas por toda ferramenta de gravação, exceto
index_stale e os erros de consulta: esta ferramenta aborda faixas por id e nunca
toca no índice de busca.
Buscando logo após uma edição. Gênero, comentário e rótulo estão no índice
de busca, que é reconstruído na próxima leitura. Enquanto o Engine DJ mantém a biblioteca
aberta, ele não pode ser reconstruído, então uma busca pode continuar mostrando os valores antigos, e
refresh_index não pode ajudar até o Engine soltar. A edição em si está no
banco de dados.
Playlists inteligentes. Uma playlist inteligente cujas regras correspondem ao gênero muda o que ela contém quando um gênero é renomeado, embora nenhuma de suas próprias linhas tenha sido tocada.
Duas bibliotecas conectadas. Não presuma que uma edição de tag se propaga da mesma forma que uma edição de playlist (veja Um undo cobre uma biblioteca): medido uma vez, uma edição de tag feita na biblioteca USB não foi copiada para a biblioteca do computador em uma inicialização nova do Engine DJ. A outra direção não foi medida para tags. Faixas editadas são marcadas para sincronização (isMetadataOfPackedTrackChanged) da mesma forma que o Engine DJ marca suas próprias edições de tag — medido em 2026-09-15. Que uma sincronização explícita para uma unidade então carregue a edição é para o que a flag parece existir, mas não foi medido.
Recusas que todas as ferramentas de escrita compartilham
Elas vêm do que acontece antes da própria escrita — escolher a biblioteca, atualizar seu índice, resolver a playlist — e das próprias verificações da escrita.
| Código | Significado | Nada foi escrito? |
|---|---|---|
invalid_argument | Os argumentos não fazem sentido — ambos playlist_id e playlist_name, uma lista vazia onde uma é obrigatória, ou um playlist_name que corresponde a várias playlists (cada candidato é listado). | sim |
library_not_found | library não nomeia nada conectado — a recusa lista o que está — ou o cabeçalho da biblioteca não pôde ser lido. | sim |
ambiguous_library | Nenhum library fornecido, e mais de uma biblioteca suportada está conectada — ou o uuid fornecido é compartilhado por cópias em unidades diferentes. Lista-os — veja Escolhendo uma biblioteca. | sim |
unsupported_schema | A versão da biblioteca está fora do que este servidor suporta. | sim |
library_needs_recovery | O Engine DJ deixou um diário não recuperado. Inicie o Engine uma vez. | sim |
library_busy | Algo está segurando um bloqueio conflitante agora. Tente novamente. | sim |
index_stale | O índice ainda não pôde ser construído, tipicamente porque o Engine segura um bloqueio na primeira execução. Carrega retry_after_ms. | sim |
query_timeout, query_process_crashed | A busca que resolve uma playlist falhou. Apenas ferramentas de edição. | sim |
library_unreadable | A biblioteca não pôde ser lida; o snapshot tirado antes da primeira escrita não pôde ser feito (um disco cheio, ou um Node mais antigo que 22.16); ou a releitura de uma escrita discordou do que foi escrito, e foi revertida. | veja detail |
detail nesses erros. Uma vez que a própria escrita começou, detail é exatamente uma de duas strings, e um cliente pode lê-la para decidir se a biblioteca mudou: not_committed — a biblioteca é o que era — ou committed_unverified — o raro: a escrita pode ter sido aplicada, mas não pôde ser confirmada, e apenas este caso devolve um backup_path. Recusas levantadas antes desse ponto — cada linha acima marcada como "sim" — nunca abriram a biblioteca para escrita, seja qual for seu detail: pode estar ausente, not_committed, ou texto explicativo como os candidatos que um playlist_name ambíguo lista.
Recursos
engine://schema— a semântica de campos que um assistente precisa antes de escrever SQL: como o Engine codifica a tonalidade musical, por que o tempo éCOALESCE(bpmAnalyzed, bpm), queTrack.pathé relativo, onde a ordem da playlist realmente vive, e quais colunas auxiliares são indexadas.engine://libraries— o que foi descoberto na inicialização e se o esquema de cada biblioteca é suportado. Um snapshot;list_librariesé a visão ao vivo.
Escolhendo uma biblioteca
O Engine DJ mantém uma biblioteca no seu computador e outra em cada unidade para a qual você exporta, então mais de uma geralmente está conectada. list_libraries reporta cada uma com um uuid e um path, e toda ferramenta que lê dados da biblioteca aceita um argumento opcional library. Passe qualquer forma exatamente como impressa — o caminho ~/… é aceito junto com o absoluto. Um valor que não corresponde a nenhum retorna como library_not_found, listando o que você pode escolher.
Deixe de fora e o servidor usa a biblioteca suportada com mais faixas. Isso importa: a biblioteca local que o Engine DJ cria na instalação é escaneada primeiro e geralmente está vazia, então "a primeira encontrada" esconderia a unidade com a qual você realmente trabalha.
Essa regra é suficiente para uma leitura, que não muda nada: com duas bibliotecas conectadas, uma leitura escolhe uma. Passe library quando importar qual.
Uma escrita recusa em vez disso, assim que mais de uma biblioteca suportada está conectada — independentemente de suas contagens de faixas. ambiguous_library lista cada candidato com sua contagem de faixas, e detail: "not_committed".
A contagem nunca foi a pergunta certa. Uma versão anterior recusava apenas um empate exato, raciocinando que uma unidade USB e sua cópia empatam precisamente porque uma é cópia da outra — medido em 2026-09-01, ambas bibliotecas reais em 257. Mas importe uma faixa de um lado e o empate desaparece, e o padrão silenciosamente pega a maior. Uma playlist escrita na unidade errada é pelo menos visível lá; o gênero de uma faixa não é, e você fica acreditando que a edição não funcionou.
Com uma única biblioteca nada muda: você nunca precisa nomeá-la.
Cópias compartilham um uuid. Copie uma pasta Engine Library para outra unidade — um pendrive reserva para um show — e a cópia mantém o uuid do original, então com ambas conectadas um uuid nomeia duas bibliotecas. Uma escrita nomeando esse uuid é recusada da mesma forma, com ambiguous_library listando ambos os caminhos, em vez de cair na unidade escaneada primeiro. Passe o caminho em vez disso — ele distingue as cópias — e releia desse caminho qualquer coisa da qual a escrita depende, já que uma leitura nomeando o uuid pode ter vindo da outra cópia. Leituras nomeando um uuid compartilhado não são recusadas: elas respondem de uma das cópias. Antes de cada escrita, as unidades são escaneadas novamente, então uma cópia conectada após o servidor iniciar é contada — desde que sua biblioteca possa ser lida.
A recusa diz ao assistente para perguntar a você em vez de escolher. Caso contrário, "passe library, aqui estão as duas" é um convite para pegar a primeira, o que coloca a escrita de volta em um disco arbitrário e torna a recusa inútil.
Cada biblioteca tem seu próprio índice e sua própria conexão, aberta na primeira vez que você pergunta algo àquela biblioteca. Comparar duas bibliotecas entre si — "o que está nesta unidade mas não naquela?" — não é algo que este servidor faz.
Segurança
As garantias que este servidor faz sobre sua biblioteca estão listadas em PRINCIPLES.md; esta seção é como elas funcionam.
Sua biblioteca é aberta somente leitura no nível do sistema operacional, não por convenção e não por um PRAGMA que uma consulta poderia desligar. Escritas são recusadas pelo próprio SQLite, e sem --allow-writes nenhum arquivo é jamais criado dentro da sua pasta Engine Library. O índice de busca vive em ~/.engine-dj-mcp/.
Escrevendo
Sem --allow-writes o servidor não tem nenhuma ferramenta que possa escrever, e o parágrafo acima vale exatamente como escrito: o próprio SQLite recusa.
Com a flag, cinco ferramentas aparecem. create_playlist adiciona uma nova playlist e nada mais. add_tracks_to_playlist, remove_tracks_from_playlist e reorder_playlist vão além: com a flag, uma playlist existente pode agora ser alterada, não apenas criada — suas faixas adicionadas, removidas, ou colocadas em uma ordem diferente. O que essas quatro ferramentas de playlist tocam são as entradas da própria playlist nomeada, mais exatamente duas linhas em outro lugar: a linha da própria playlist, cujo lastEditTime toda edição carimba para o Engine ver a mudança, e — apenas para create_playlist — o link da última playlist anterior, feito pelo trigger de inserção do próprio Engine. Nenhuma outra playlist é renomeada, esvaziada ou excluída, e nenhuma faixa, cue ou beatgrid é tocada por essas quatro. update_track_metadata muda gênero, comentário, rótulo, ano e classificação nas faixas nomeadas — veja sua seção acima — e nada mais: nenhuma playlist, cue, beatgrid, título, artista, álbum, caminho ou arquivo é tocado.
Toda edição retorna undo — a chamada de ferramenta exata que a reverte — e undo_complete; para as ferramentas de playlist, undo é expresso contra as posições que a própria edição produziu, e undo_complete diz se reproduzi-la coloca a playlist de volta exatamente como estava. Reproduzir undo é o caminho certo de volta de uma edição; restaurar backup_path não é, porque reverte a biblioteca inteira para antes da primeira escrita desta sessão, descartando toda contagem de reprodução, importação, cue e mudança de beatgrid que o Engine DJ registrou desde então, junto com a única edição que você realmente queria desfazer. Veja Restaurando um snapshot.
Há exatamente uma edição que undo não pode reverter, e ele diz isso em vez de fingir o contrário: remover uma entrada cuja faixa está ausente da biblioteca (missing: true). Tal entrada nomeia uma faixa que esta biblioteca não tem, então não há id de faixa para adicionar de volta — o resultado retorna com undo_complete: false e um undo_note nomeando essas posições, e os passos que ele retorna ainda restauram todo o resto.
Antes da primeira escrita de uma sessão, o banco de dados é snapshotado para ~/.engine-dj-mcp/backups/, e toda escrita dessa sessão retorna seu caminho. Dez snapshots são mantidos por biblioteca — por arquivo de biblioteca, então uma biblioteca e seu clone em outra unidade não compartilham os dez.
Um arquivo é criado dentro da sua pasta Engine Library enquanto uma escrita está em andamento: o diário de rollback do SQLite, m.db-journal, ao lado de m.db. Ele é removido quando a transação é confirmada, e é o que torna a escrita tudo-ou-nada. Se o processo for morto no meio de uma transação, o diário é deixado para trás, e tanto este servidor quanto o Engine DJ tratam a biblioteca como precisando de recuperação — este servidor reporta library_needs_recovery e recusa tocar na biblioteca, incluindo para leituras, até que você tenha iniciado o Engine DJ uma vez para que ele possa reverter o diário. Nada mais é jamais escrito nessa pasta, e sem --allow-writes nem mesmo isso.
A escrita pega o bloqueio de escrita do próprio SQLite pela duração de uma transação e não espera por ele: se algo mais — Engine DJ no meio de um salvamento, um player — estiver segurando um bloqueio conflitante naquele momento, a escrita é recusada com library_busy e nada é alterado.
Saia do Engine DJ antes de escrever. Ter o Engine aberto geralmente não é um conflito de bloqueio, então a escrita em si normalmente passará — mas o que o Engine então faz com uma mudança feita por baixo dele nunca foi medido aqui. Cada verificação de aceitação de uma escrita foi executada com o Engine fechado. O que foi medido é que o Engine faz seu próprio trabalho na biblioteca ao carregá-la: ele renumera entradas de playlist, e copia mudanças de playlist para outra biblioteca conectada (veja abaixo). Saia, escreva, reinicie — o Engine lê a biblioteca na inicialização e mostra a mudança.
Saia, não feche. No macOS, fechar a janela do Engine deixa o aplicativo rodando: observado em 2026-09-01 com o processo principal e sete workers OfflineAnalyzer — que escrevem no banco de dados — ainda vivos depois. Use ⌘Q.
Um undo cobre uma biblioteca
Todo resultado de escrita carrega um campo library — o uuid e path da biblioteca na qual a escrita realmente caiu. Duas bibliotecas conectadas ao mesmo tempo é a configuração comum: uma unidade USB e sua cópia no computador. É aqui que você verifica em qual delas uma escrita foi.
undo reverte a edição naquela única biblioteca, e somente lá. Cada passo de undo a nomeia — por caminho, no argumento library do próprio passo — então reproduzir um passo literalmente volta para a biblioteca na qual a edição foi feita, não para o que quer que seja o padrão no momento da reprodução. Isso importa porque uma unidade USB e sua cópia têm os mesmos ids de playlist e os mesmos ids de faixa: uma reprodução que resolvesse o padrão poderia cair no disco errado, e seu expect_track_ids concordaria, ambos os lados tendo sido editados da mesma forma.
O Engine DJ move mudanças de playlist entre bibliotecas conectadas por conta própria, então uma cópia da sua edição ainda pode acabar em algum lugar que undo não pode alcançar.
Medido em 2026-09-01. Uma faixa foi adicionada a uma playlist na biblioteca do
computador. O Engine DJ foi então iniciado com o drive USB conectado, e a
mesma playlist no USB voltou com a mesma faixa adicionada — a cópia
carregando exatamente o lastEditTime que o INSERT deste servidor havia escrito. O
undo foi então executado e reverteu a edição no computador. O USB manteve a alteração.
Nada foi danificado: ambas as bibliotecas permaneceram íntegras. Mas as duas haviam divergido,
e o undo relatou sucesso, corretamente, porque dentro de sua própria biblioteca ele
fez exatamente o que prometeu.
Então: se uma segunda biblioteca estiver conectada, observe o campo library e desfaça
contra cada biblioteca separadamente. Desfazer antes da próxima execução do Engine DJ evita
o problema por completo.
Para qual biblioteca uma alteração se propaga, e em qual direção, é assunto do próprio Engine — este projeto não modela isso e não fará suposições.
Restaurando um snapshot
backup_path não é um desfazer. É uma cópia do todo do m.db de
antes da primeira escrita da sessão, então restaurá-lo reverte a biblioteca
inteira para aquele momento: cada contagem de reprodução, importação, cue, beatgrid e classificação
que o Engine DJ escreveu desde então é descartada junto com a única edição que você queria
remover. Use-o apenas se a própria biblioteca estiver danificada — o caso em que uma
escrita retorna com detail: "committed_unverified".
Os snapshots ficam em ~/.engine-dj-mcp/backups/, dez por biblioteca. Apenas um nome
terminando em .db é um snapshot. Um arquivo terminando em .partial-<number> — com ou
sem -journal depois — é uma cópia ainda sendo escrita, ou uma cujo
processo morreu antes de terminar: nunca restaure um desses. Uma cópia é
renomeada para seu nome .db somente quando estiver completa, e uma abandonada é
limpa na próxima vez que essa biblioteca for snapshotada.
Para desfazer uma playlist que você criou, exclua-a no Engine DJ. O próprio gatilho de
exclusão do Engine repara a cadeia de playlists e remove as entradas em cascata,
que é exatamente o que removê-la deveria fazer e não é algo que restaurar
um snapshot faz melhor. Para as ferramentas de playlist, para desfazer uma edição em uma
playlist existente, reproduza o undo que a edição retornou — ele nomeia
a chamada precisa de add_tracks_to_playlist, remove_tracks_from_playlist ou
reorder_playlist que coloca a playlist de volta exatamente como estava,
sem tocar em nada mais que o Engine DJ registrou desde então.
run_sql aceita SQL arbitrário, mas apenas a primeira instrução é sempre
executada, e VACUUM, ATTACH e DETACH são rejeitados de imediato, então uma
instrução encadeada ou de exfiltração não pode passar pela conexão somente leitura.
Se o Engine DJ foi fechado de forma inadequada e deixou um diário não recuperado, este
servidor não abrirá a biblioteca para "corrigi-la", com ou sem
--allow-writes — avançar um diário é um reparo em um arquivo de outra pessoa,
e toda ferramenta de escrita recusa tal biblioteca de imediato, em vez de
deixar o SQLite fazer isso no caminho. Ele relata library_needs_recovery e
pede que você inicie o Engine DJ uma vez para que ele possa recuperar sua própria biblioteca.
Limitações
Leia isto antes de decidir no que confiar.
Os layouts de PerformanceData são engenharia reversa, então cada campo
decodificado diz de que tipo é. Todos os quatro são marcados como layout: "verified" —
derivados e verificados contra uma biblioteca real do Engine DJ 3.0.x de 281
faixas analisadas, onde os offsets de cue caem dentro da faixa, o tempo implícito do
beatgrid corresponde ao bpmAnalyzed em todas as 281, e o espaçamento de pontos declarado
da forma de onda se multiplica de volta para a contagem de amostras da faixa em todas as 281.
Os loops foram os últimos a conquistar isso. A grade de slots foi determinada por 2248
sentinelas, mas nenhuma biblioteca disponível tinha um loop salvo, então um slot preenchido
permaneceu não testado e os loops carregaram layout: "unverified" por várias
versões. Um loop salvo deliberadamente resolveu: seu slot abrange 1,678321678 s
em uma faixa que o Engine analisou a 143 BPM, o que é quatro batidas com precisão de 4e-15 s.
Apenas a ordem correta de campos, unidade e endianness chegam a uma contagem inteira de batidas.
Um marcador layout é uma afirmação sobre os bytes, não sobre cada nome atribuído a
eles. Quatro rótulos são inferidos em vez de medidos, e o código diz isso
onde cada um é definido: qual dos quatro bytes de cor de um cue é qual canal
(eles são retornados como armazenados, um valor de 32 bits, sem afirmação de canal); que
o segundo beatgrid é o que o Engine chama de "ajustado" (que é o que o Engine
reproduz é medido — em sete faixas o outro roda exatamente na metade
do tempo analisado); que main_cue.is_adjusted é o que seu byte de flag significa;
e que os três bytes por ponto da forma de onda são baixo, médio e alto nessa
ordem. Nenhum afeta um valor que você recebe de volta.
Todo o resto — títulos, artistas, tempo, tonalidade, classificações, histórico de reprodução, caminhos de arquivo — é lido diretamente do banco de dados e não carrega tal ressalva.
has_cues e no_cues significam "um cue quente está definido". O Engine escreve um
blob quickCues em cada faixa analisada, independentemente de um pad ser usado, então o
teste SQL barato responderia a uma pergunta sobre análise em vez disso: na
biblioteca de referência, todos os 281 blobs contariam como tendo cues, enquanto duas faixas
realmente têm. O blob é portanto decodificado enquanto o índice é construído. Isso
custa aproximadamente 100 ms extras em 50.000 faixas, e apenas quando sua biblioteca
muda. O cue principal da faixa não conta para isso — o Engine define
isso como um marcador de reprodução em vez de o DJ colocá-lo. has_beatgrid ainda
testa o blob: beatData não tem estado "escrito mas vazio".
Ele escreve playlists e cinco campos de faixa, e somente quando você pede.
Sem --allow-writes a biblioteca é aberta somente leitura no nível do SO e
não há ferramenta que possa escrever. Com o flag, cinco ferramentas de escrita aparecem:
quatro criam playlists e adicionam, removem e reordenam suas faixas, e
update_track_metadata altera gênero, comentário, rótulo, ano e classificação no
banco de dados do Engine — e essa é a lista completa. Nem um cue, um loop ou um
beatgrid; nem um título, artista, álbum, contagem de reprodução ou caminho de arquivo; nem as tags
dentro dos seus arquivos de áudio; e nem mesmo a recuperação de um diário que o Engine DJ
deixou para trás.
Ele não lê o histórico de reprodução. Track.timeLastPlayed responde "o que eu não
toquei em seis meses?", mas o banco de dados separado de histórico do Engine —
sessões, decks, o que seguiu o quê — não é aberto de forma alguma.
Smartlists não são relatadas. As listas baseadas em regras do Engine vivem em uma tabela
separada Smartlist com sua própria ordenação e uma coluna de regras JSON, e nada
aqui a lê. get_playlists relata apenas playlists e pastas comuns, então
uma smartlist que você pode ver no Engine não aparecerá.
Uma pasta vazia é lida como uma playlist vazia. O esquema do Engine não tem flag de
pasta — uma pasta é simplesmente uma playlist sob a qual outras playlists ficam — então
is_folder significa "tem listas filhas". Uma pasta que você esvaziou é
indistinguível de uma playlist sem faixas.
As faixas de uma playlist podem ser editadas; a playlist em si não pode. Com
--allow-writes uma nova playlist pode ser criada, e uma existente pode ter
faixas adicionadas, removidas ou reordenadas — mas não renomeada, excluída, movida entre
pastas, ou transformada em uma pasta em si, e não há listas definidas ou
transições sugeridas. Ele responde perguntas sobre a coleção e escreve
a resposta se você pedir; a mixagem é sua.
Somente esquema 3.0.0 a 3.0.2. Bibliotecas mais antigas e mais novas são listadas com sua versão e relatadas como não suportadas, em vez de lidas por suposição.
Licença
MIT — veja LICENSE. O que este projeto garante e se recusa a fazer: PRINCIPLES.md.