mcpField
E
Engine DJ Library Assistant (unofficial)

Engine DJ Library Assistant (unofficial)

本地服务
github.com游戏娱乐12 周前更新

关于 Engine DJ Library Assistant (unofficial)

Engine DJ Library Assistant (unofficial) 是面向 DJ 的 MCP 服务,可通过 AI 助手搜索、查看和审计 Engine DJ 曲库。它读取本机及 USB 驱动器上的曲库,默认禁用修改。

主要功能

  1. 01按文本、BPM、调性、评分和日期搜索曲目
  2. 02查看播放列表、提示点、节拍网格和演奏数据
  3. 03审计缺失文件、重复曲目等问题
  4. 04读取本机和 USB 驱动器上的曲库
  5. 05可选创建播放列表并编辑曲目元数据

适用场景

  1. 01按节奏、调性、流派或备注查找曲目
  2. 02检查曲库中的缺失文件和重复曲目
  3. 03查看曲目的提示点和节拍网格
  4. 04启用写入后创建播放列表或更新标签

MCP 相关

该 MCP 服务向兼容的 AI 客户端提供搜索和查看 Engine DJ 曲库的工具。只有使用 `--allow-writes` 启动时,才会提供写入工具。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"venut-technologies-engine-dj": {
"command": "npx",
"args": [
"-y",
"engine-dj-mcp"
]
}
}
}
启动命令npx -y engine-dj-mcp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmengine-dj-mcp0.18.0

工具

9

收录的工具列表 · 更新于 2 周前

参数类型必填说明
qstring否
bpmobject否
bpm.maxnumber否
bpm.minnumber否
bpm.aroundnumber否
bpm.tolerance_pctnumber否
keyobject否
key.modeenum否major | minor
key.camelotarray否
key.compatible_withstring否
addedobject否
added.afterstring否
added.beforestring否
flagsobject否
flags.analyzedboolean否
flags.has_cuesboolean否
flags.availableboolean否
flags.has_beatgridboolean否
limitinteger否
cursorstring否
fieldsarray否
playedobject否
played.afterstring否
played.neverboolean否
played.beforestring否
ratingobject否
rating.maxnumber否
rating.minnumber否
librarystring否要使用的曲库:指定 uuid,或 list_libraries 报告的路径(报告中的 ~/... 格式和绝对路径均可)。省略时使用曲目数量最多的受支持曲库。READ 始终可以省略。只有在连接了单个受支持曲库时,WRITE 才可省略;连接两个或更多曲库时,写入会拒绝执行并返回 ambiguous_library 及曲库列表,因为选择会决定更改哪个磁盘;请询问用户要使用哪个曲库,然后在此处传入。复制到另一驱动器的曲库会保留其 uuid,因此同一个 uuid 可能对应两个已连接的曲库:写入时若指定此类 uuid,也会因 ambiguous_library 而拒绝。要写入其中一个曲库,请传入其路径。
playlistobject否
playlist.idinteger否
playlist.namestring否
redact_pathsboolean否
include_totalboolean否

详细介绍

engine-dj-mcp

CI npm licence

向 Claude 或任何 AI 助手询问你的 Engine DJ 音乐库:按 BPM、调性、流派或你自己的备注查找曲目,检查收藏集中的重复项、缺失文件和没有 cue 点的曲目,读取 Engine 保存的 cue 点和节拍网格,还可以根据你的要求创建播放列表或修正曲目标签。它通过 MCP(Model Context Protocol,模型上下文协议)运行。这是 Claude Desktop、Claude Code、Cursor 和 VS Code 等 AI 应用连接你电脑上工具的标准方式:应用会启动此服务,助手在需要查询你的音乐库时调用它。

它会读取你电脑上的音乐库以及 USB 驱动器中的音乐库。它会在操作系统层面以只读方式打开这些音乐库,除非你使用 --allow-writes 启动,否则不会进行任何更改;启用后,它可以创建和编辑播放列表,也可以编辑流派、备注、标签、年份和评分,并在首次更改前将整个数据库复制到备份中。请参阅安全性。服务本身不会向任何地方发送数据;AI 应用如何处理它的回答,请参阅 PRIVACY.md。

状态:积极维护 · 1.0 之前版本。 正常使用并持续维护;在 1.0 之前,MINOR 版本可能会改变行为,PATCH 版本绝不会。更改记录在 CHANGELOG.md 中。

本项目与 inMusic Brands、Denon DJ 或 Engine DJ 产品没有关联,也未获其认可或赞助。 此处使用“Engine DJ”仅用于指明此工具读写其音乐库的软件。本项目不使用 inMusic 或 Denon DJ 的徽标或品牌图片。

你可以这样提问

连接后,你可以在聊天中直接提出这些问题:

  • “找一些氛围暗沉、速度约为 124、调性为小调,而且我过去六个月没播放过的曲目。”
  • “找出与 8A 调性兼容、速度在 138 到 142 之间的曲目。”
  • “我的收藏集有什么问题——缺失文件、重复项、没有 cue 点的曲目?”
  • “这首曲目的 cue 点在哪里?Engine 分析出的速度是多少?”
  • “帮我创建一个播放列表,包含所有 5A 调性且速度不低于 140 BPM 的曲目。”(需要 --allow-writes)
  • “将这五首曲目的流派设为 Minimal,并给它们评四星。”(需要 --allow-writes)

安装

你需要 Node.js 22.16 或更高版本。无需安装其他内容:下面列出的每个应用都会通过 npx 启动服务,并在首次运行时从 npm 下载它。

npx engine-dj-mcp

应用会运行此命令;你不需要自己运行。

Claude Desktop

前往“设置”→“开发者”→“编辑配置”,并将以下内容添加到 claude_desktop_config.json:

{
  "mcpServers": {
    "engine-dj": { "command": "npx", "args": ["-y", "engine-dj-mcp"] }
  }
}

重启 Claude Desktop。

Claude Code
claude mcp add --scope user engine-dj -- npx -y engine-dj-mcp
Cursor

添加到 Cursor ——也可以直接打开此深层链接,或将与 Claude Desktop 相同的 mcpServers 条目添加到 ~/.cursor/mcp.json:

cursor://anysphere.cursor-deeplink/mcp/install?name=engine-dj&config=eyJjb21tYW5kIjoibnB4IiwiYXJncyI6WyIteSIsImVuZ2luZS1kai1tY3AiXX0%3D
VS Code

添加到 VS Code ——也可以运行:

code --add-mcp '{"name":"engine-dj","command":"npx","args":["-y","engine-dj-mcp"]}'
Gemini CLI
gemini extensions install https://github.com/Venut-Technologies/engine-dj-mcp

扩展清单(gemini-extension.json)会启动与上述配置相同的只读服务。

允许写入

若要让助手创建和编辑播放列表,以及编辑曲目标签,请在 args 中添加 --allow-writes。它是一个标志,而不是环境变量,正是为了让它能在你查看的配置中清晰可见:

{
  "mcpServers": {
    "engine-dj": { "command": "npx", "args": ["-y", "[email protected]", "--allow-writes"] }
  }
}

请在此处固定版本。不固定时,npx 每次启动都会获取最新版本,而此配置会让该代码获得对你曲库的写入权限。固定版本后,只有在更改版本号时,新版本才会生效。 要求更改前,请先退出 Engine DJ;参见写入。

兼容性

  • macOS:支持。 所有针对真实曲库的检查都在 macOS 上运行,并能找到 Engine DJ 存放在 ~/Music 和各个驱动器根目录下 /Volumes 中的曲库。
  • Windows 和 Linux:暂不支持。 测试套件在 Ubuntu 上通过了,但尚未在这两个系统上读取过真实曲库。在 macOS 以外的系统上,服务只会在 ~/Music/Engine Library 中查找,因此无法找到 USB 驱动器上的曲库,也没有其他选项可指定曲库位置。
  • Node.js 22.16 或更高版本,且没有原生依赖。node:sqlite 从 22.13 起不再需要标志,但写入前备份会用到 backup(),此方法在 22.16 中新增。CI 会在 macOS 和 Ubuntu 上使用 Node 22.16 和 24 运行完整测试套件。
  • Engine DJ 曲库架构版本为 3.0.0 至 3.0.2——Engine DJ 4.5 和 5.x。此处所有功能都已针对真实的 3.0.2 架构曲库进行测试;版本检查接受 3.0.0 和 3.0.1,生成的测试数据也覆盖了这两个版本,但尚未读取过这些版本的真实曲库。超出此范围的曲库会连同版本信息一起列出并被拒绝,不会贸然读取。

工具

共有九个只读工具,以及五个写入工具——create_playlist、 add_tracks_to_playlist、remove_tracks_from_playlist、reorder_playlist 和 update_track_metadata——仅在使用 --allow-writes 启动服务时才会出现。每个读取曲库数据的工具也都接受可选的 library 参数——参见选择曲库。

search_tracks

主要工具。支持折叠变音符号的全文搜索,以及按速度、调性、评分、曲目添加时间和最后播放时间筛选。

参数功能
q对标题、艺人、专辑、流派、备注和厂牌进行全文搜索。会折叠变音符号,因此 bjork 可以匹配 Björk——Engine 自带的搜索无法做到这一点。
bpm{ min, max } 或 { around, tolerance_pct }。使用解析后的速度,因此分析得出的 BPM 优先于标签中的值。
key{ camelot: [...] } 用于匹配精确调性,{ compatible_with: "8A" } 用于匹配和声相邻调性,{ mode: "minor" } 用于匹配调性转盘上的整个小调一侧。
rating{ min, max },以星级计,范围为 0–5。Engine 存储的值为 0、20、40、60、80、100;筛选器会进行转换,因此 { min: 4 } 表示四星及以上。rating 字段返回存储的数值,rating_stars 返回以星级表示的同一评分。
played{ never: true },或 { before, after },接受 ISO 日期或 -6 months 之类的相对时间格式。
added{ before, after },日期格式相同。
flagsanalyzed、available、has_cues、has_beatgrid。has_cues 表示确实设置了热提示点——参见限制。
playlist{ id } 或 { name }——在单个播放列表中搜索。结果仍按相关性或 ID 返回;只有 get_playlist_tracks 会保留播放列表顺序。
fields要返回的列。默认为 id, artist, title, bpm, camelot, rating。
limit, cursor每页条目数(默认为 25,最大为 200),以及用于获取下一页的不透明游标。
include_total默认关闭,因为计数的开销远高于获取当前页。最多计数 1000 条——达到上限时,结果会带有 total_capped: true,表示“至少 1000 条”。
get_tracks

获取指定曲目 ID 的完整元数据,并按请求顺序返回。 未知 ID 会被省略,不会导致调用失败。

ids(必需)、fields、redact_paths。

get_playlists

按 Engine DJ 显示顺序排列的播放列表树——包括文件夹。

列表是扁平的,顺序与展开所有文件夹后从侧边栏向下浏览的顺序一致:depth 和 path 表示嵌套关系,parent_id 指明列表所在的文件夹。

字段含义
name、id、pathpath 是完整的 Folder/Sub/Name,且唯一——单独的 name 不一定唯一。
depth、parent_id嵌套关系。顶层的 parent_id 为 null。
is_folder列表包含子列表。Engine 没有文件夹标记——文件夹本身就是其他播放列表所在的播放列表——因此,已清空的文件夹会显示为空播放列表。
is_persistedEngine 自带的标记,表示列表已保存到设备上。Engine 显示的列表中这两种值都会出现,因此不会据此筛选。
track_count仅统计该列表中的条目,不包括其子列表——也就是 Engine 在列表旁显示的数量。
missing_count这些条目中,有多少指向此媒体库中不存在的曲目。

limit(默认值为 200,最大值为 1000)。如果播放列表的链接链已损坏,则会出现 warnings;不会因此从列表中删除任何内容。

get_playlist_tracks

单个播放列表中的曲目,按播放列表顺序排列。

使用 playlist_id 或 playlist_name 指定播放列表——两者必须且只能提供一个。播放列表名称仅在文件夹内唯一,因此如果名称匹配多个播放列表,系统会拒绝并返回每个候选项的 ID 和完整路径,而不会自行猜测;传入 get_playlists 返回的 path,即可指定目标播放列表。

每一行都包含 position,表示该条目在播放列表中的位置,从 1 开始计数。fields、limit 和 cursor 的用法与 search_tracks 相同。

如果条目对应的曲目不在此媒体库中,该条目仍会保留原位置,并以 { position, entry_id, track_id, missing: true } 的形式返回,同时在 entry_count 旁提供 missing_count。这是正常情况,不代表数据损坏——播放列表条目的存在时间可能超过曲目,也可能随驱动器迁移——因此它们会留在原位,使返回的行数仍与播放列表自身的长度一致。在一个参考媒体库中,一个包含 43 个条目的播放列表中,恰好只有一个曲目可由该媒体库实际播放。

get_track_performance

解码 Engine 为每首曲目存储的二进制 PerformanceData:热点提示、主提示点、已保存的循环、节拍网格和粗略的波形概况。

每个字段都带有自己的解码状态 和 layout 标记。layout: "verified" 表示已通过真实媒体库确认字节布局,因此 status: "ok" 代表这些值可信。layout: "unverified" 则表示只解析了字节;目前没有字段会返回此值。

位置以采样偏移量表示;提示点和循环条目也包含秒数。对于已分析但未设置提示点的曲目,items: [] 且 slots: 8。

id(必填)。

audit_library

十一项集合健康检查。每项检查仅返回计数和少量 ID 样本,而不会返回完整结果集——包含数千首未经分析曲目的曲库不应占满助手的上下文。

检查查找内容
missing_files文件已从磁盘中消失的曲目
unavailableEngine 已标记为不可用的曲目
unanalyzedEngine 尚未分析的曲目
no_cues未设置热提示点的曲目
no_beatgrid没有节拍网格数据的曲目
missing_key未检测到调性的曲目
suspicious_bpm分析所得速度与标记的速度不一致,或速度超出 60–200
duplicates艺术家和标题相同的曲目,不区分大小写,也不区分文字体系
empty_metadata缺少艺术家或标题的曲目
orphan_entries指向不在此曲库中的曲目的播放列表条目——get_playlist_tracks 会显示每个条目所在的位置
path_form_mismatch文件在磁盘上,但文件名或路径中的某个文件夹与 Engine 存储的路径采用了不同的 Unicode 形式。macOS 仍能找到该文件;Linux 则不能(根据内核的 exFAT 驱动实测),而播放器上的 Engine OS 使用 Linux,因此这些文件可能无法在硬件上加载。仅大小写不同不计入:exFAT 和 Windows 不区分大小写

省略 checks 即运行全部十一项检查。

run_sql

用于处理上述工具无法覆盖的问题。只读限制由内核强制执行,而非由此工具强制。无论查询内容如何,结果都会受到限制。

在 WHERE 子句中,优先使用 side.track_derived.camelot 和 side.track_derived.tempo,而不是 camelot() 和 tempo() SQL 函数——这些函数会逐行运行,导致索引失效。

sql(必填)、params、limit。

list_libraries

列出找到的所有曲库,包括架构不受支持的曲库——并显示其版本,以便区分服务故障和曲库缺失。

每次调用都会重新扫描,因此服务器启动后连接的驱动器也会显示出来,无需重启。如果某个曲库暂时无法读取——例如 Engine DJ 正在向其中写入——它仍会保留在列表中,状态为 status: "unreadable",而不会消失。

无参数。

refresh_index

如果曲库已更改,则重建搜索索引。通常不需要手动调用;服务器会在响应前自行检查索引是否过期。

create_playlist

五个写入工具中的第一个。只有服务器以 --allow-writes 启动时,这些工具才会注册。

根据曲目 ID 创建一个新的顶层播放列表——track_ids 同时决定播放列表包含哪些曲目以及它们的顺序,因此由 search_tracks 生成的列表会按助手选择的顺序出现在 Engine DJ 中。除此之外,不会更改任何内容:不会重命名、重新排序、清空或删除播放列表,也不会触碰曲目、提示点或节拍网格。唯一发生移动的现有行是上一个末尾播放列表的链接,而移动该链接的是 Engine 自带的插入触发器。

参数作用
title新播放列表的名称。顶层不能已有此名称——Engine 允许每个文件夹中存在一个同名项。
track_ids来自 search_tracks 或 get_tracks 的 ID,顺序即播放列表顺序。可以为空,即创建空播放列表。每首曲目最多出现一次,这是 Engine 自身的规则。

每个条目存储曲目的来源身份——(originDatabaseUuid, originTrackId),即 Engine 用于匹配的二元组——而不是本地行 ID,因此在此处创建的播放列表与 Engine 自行创建的播放列表读取结果相同。

结果包含 playlist_id、tracks_added、library 和 backup_path。如需撤销,请在 Engine DJ 中删除播放列表;backup_path 是整个曲库的快照,用于处理底层出错的情况,并非撤销机制——请参阅恢复快照。

此工具特有的拒绝原因:标题已被占用时返回 playlist_exists;此曲库中不存在该 ID 时返回 unknown_track;同一 ID 出现两次时返回 duplicate_track——此外还有所有写入工具共用的拒绝原因。

add_tracks_to_playlist

向现有播放列表添加一首或多首曲目——这会编辑该播放列表的内容,不会新建播放列表(新建请使用 create_playlist)。如果播放列表的条目链已损坏,操作会直接被拒绝,而不会尝试修复,也不会添加任何内容。

作为文件夹的播放列表(is_folder: true——包含子列表)与其他播放列表一样编辑:Engine 没有单独的文件夹类型,文件夹本身可以包含条目,三个编辑工具都会照常添加、移除和重新排序这些条目。无论哪种操作,文件夹内的列表都不会受到影响。

参数作用
playlist_id / playlist_name两者必须且只能提供一个,解析方式与 get_playlist_tracks 相同:如果名称匹配多个播放列表,操作会被拒绝,并列出每个候选项的 id 和完整路径,不会猜测。
track_ids来自 search_tracks 或 get_tracks 的 id,顺序即为曲目应显示的顺序。如果某首曲目已在播放列表中,操作会以 duplicate_track 为由被拒绝——Engine 只允许同一首曲目在一个播放列表中出现一次。
at新曲目的添加位置,按播放列表当前从 1 开始的位置计算(与 get_playlist_tracks 报告的编号相同):"start"、"end"(默认值),或 { after_position: n }。

结果包含 playlist_id、tracks_added、positions(新曲目添加的位置)、undo、undo_complete(在此处始终为 true)、library 和 backup_path。undo 是用于撤销此编辑的精确 remove_tracks_from_playlist 调用:它包含曲目添加到的位置,以及用于标识这些曲目的 expect_track_ids,因此如果播放列表在此期间被其他操作修改,撤销操作会被拒绝,而不会误删其他条目。请调用它来撤销操作,而不要恢复 backup_path——请参阅恢复快照,并查看一次撤销仅涉及一个库了解它无法覆盖的内容。此工具自身可能返回的拒绝原因包括:playlist_not_found、playlist_chain_damaged、invalid_position,以及与 create_playlist 相同的 unknown_track / duplicate_track——此外还有所有写入工具共用的拒绝原因。

remove_tracks_from_playlist

按位置从现有播放列表中移除一首或多首曲目——这会编辑该播放列表的内容;绝不会触碰其他播放列表。如果条目链已经损坏,则会直接拒绝写入,而不是尝试修复。

参数作用
playlist_id / playlist_name必须二选一,解析方式与 get_playlist_tracks 相同。
positionsget_playlist_tracks 当前报告的该播放列表中的 1 起始位置。也包括曲目已从媒体库中缺失的条目(missing: true)——移除这类条目是清理空缺的有效方式,也是唯一一种无法通过一次 undo 撤销的移除(见下文)。
expect_track_ids可选,每个位置对应一个条目:在移除任何内容之前,验证指定位置仍是预期的曲目;否则拒绝整个调用。null 表示“此位置应包含一个曲目缺失的条目”,而不是“没有预期值”。

结果包含 playlist_id、tracks_removed、removed(其中每个条目都包含对应位置的 track_id,若曲目缺失则为 null)、undo、undo_complete、library 和 backup_path。undo 是一组 add_tracks_to_playlist 调用,每首可恢复的已移除曲目对应一次。按给定顺序运行,切勿并行运行或倒序运行——每一步的目标位置都是根据前一步运行后的列表计算得出的,因此乱序执行会把曲目放回错误位置。出于同样的原因,优先使用 undo,而不是恢复 backup_path。

如果移除的条目中包含曲目已从媒体库中缺失的条目,undo_complete 为 false:该条目指向的曲目不在此媒体库中,因此无法通过 add_tracks_to_playlist 调用将其放回;undo_note 会指出这些位置。返回的步骤仍可执行,并会恢复其他所有内容;缺失的条目只能通过 backup_path 恢复,而这会回滚整个媒体库。

此工具特有的拒绝原因:playlist_not_found、playlist_chain_damaged 和 invalid_position——当位置重复、超出范围,或该位置的曲目与 expect_track_ids 中的预期不符时会出现此错误;此外,还有所有写入工具共有的拒绝原因。

对于所有三个编辑工具,playlist_chain_damaged 始终表示同一情况:播放列表的条目链在编辑之前就已损坏,因此编辑操作拒绝触碰它。如果编辑工具对自身工作内容执行的检查结果不一致——即回读的条目链与写入内容不符——则会回滚事务,并返回 library_unreadable,且 detail: "not_committed"。这两种情况都会使媒体库保持原样;只有第二种情况表示此服务不理解媒体库刚才执行了什么操作。

reorder_playlist

重新排列现有播放列表中的曲目——这会更改该播放列表现有条目的顺序;不会添加或移除任何内容。如果条目链已损坏,则会直接拒绝写入,而不会尝试修复。

参数作用
playlist_id / playlist_name两者必须恰选其一,解析方式与 get_playlist_tracks 相同。
order1..n 的完整排列,其中 n 为播放列表当前的条目数。order[i] 指定曲目在当前位置(从 get_playlist_tracks 获取)中的 1 起始位置,该曲目应排在第 i + 1 位。不接受部分“将 x 移至 y”的指令——必须列出每个位置,包括不变的位置。

结果包含 playlist_id、undo、undo_complete(此处始终为 true)、library 和 backup_path。undo 是精确的逆排列,可通过单次 reorder_playlist 调用完成。此工具自身可能拒绝的情况包括:playlist_not_found、playlist_chain_damaged,以及当 order 不是播放列表当前位置的完整排列时的 invalid_position;此外还有所有写入工具共有的拒绝情况。

即使播放列表已经是目标顺序,也接受重新排列请求,且不会重写任何条目:它仍会更新播放列表的 lastEditTime;如果本会话此前尚未写入任何内容,该操作仍会占用本会话的快照。

update_track_metadata

更改曲目的流派、备注、厂牌、年份或评分——这些是 Engine DJ 在其列中显示的值。它写入 Engine 的数据库,而不是音频文件的标签。Engine 本身会在你编辑备注时将其写入文件, 但不会写入流派或评分,因此其他读取标签的软件也不会看到这些编辑。

参数
updates最多 200 个条目,每个条目为 { id, genre?, comment?, label?, year?, rating_stars? }。仅更改指定的字段。"" 会清空文本字段;year: 0 表示未知,这是 Engine 的存储方式;rating_stars 的范围为 0–5。
library连接了多个库时必填——请参阅选择库。

如果曲目已经具有请求的值,就不会写入,因此重复调用不会产生任何更改;该曲目会计入 unchanged。结果包含 updated、unchanged、changed——哪些曲目的哪些字段发生了更改—— undo、undo_complete(始终为 true)、library,以及写入事务运行时的 backup_path——如果在获取写入锁时曲目已经具有请求的值,也可能出现 updated: 0。

撤销。 undo 是一次 update_track_metadata 调用,用于恢复恰好发生更改的字段的先前值,并指明库。 它恢复的是值,而不是 lastEditTime:Engine 自己的触发器会为每次编辑(包括撤销)添加时间戳。每个条目都带有 expect,其值为本次调用写入的内容,因此,如果有人再次编辑曲目后重放撤销操作,该操作会以 stale_value 被拒绝,而不会覆盖那次编辑。rating_raw 和 expect 专为此用途而设; 普通编辑都不需要它们。

请保留首次响应中的撤销数据。重复调用已经成功执行过的操作时,不会找到需要更改的内容,并会返回空撤销数据。对于分多次调用完成的工作,请按相反顺序重放撤销操作。

此工具自身的拒绝原因:unknown_track;track_not_editable——曲目的来源为空(Engine 的触发器会在任何更新时重写空来源,这会使曲目与其他驱动器上的播放列表条目脱离),或字段中存有此工具无法还原的值,例如超出 0–255 范围的评分;stale_value——曲目在读取 expect 中的值后发生了更改(最多 20 个不匹配项会在结构化的 mismatches 字段中返回,总数则会在文本消息中说明);以及 invalid_argument。遇到 stale_value 时,请告知用户哪些曲目和字段发生了更改——未经用户同意,不要根据重新读取的值重建 expect 来强行写入,否则会悄悄覆盖 DJ 之后所做的编辑。另有 所有写入工具共有的拒绝原因,但不包括 index_stale 和查询错误:此工具通过 ID 定位曲目,从不触碰搜索索引。

编辑后立即搜索。 流派、备注和厂牌都在搜索索引中,索引会在下一次读取时重建。当 Engine DJ 正在占用库时,无法重建索引,因此搜索可能继续显示旧值;在 Engine 释放库之前,refresh_index 也无济于事。编辑本身已写入数据库。

智能播放列表。 如果智能播放列表的规则按流派匹配,重命名流派会改变其包含的内容,但播放列表自身的行并未被触及。

连接了两个库。 不要假设标签编辑会像播放列表编辑那样传播(请参阅一次撤销只涵盖一个库): 曾有一次测量发现,在 U 盘库中进行的标签编辑,在全新启动 Engine DJ 后并未复制到电脑上的库中。标签编辑的反向传播情况尚未测量。编辑过的曲目会像 Engine DJ 标记其自身标签编辑那样标记为需要同步(isMetadataOfPackedTrackChanged)——测量日期为 2026-09-15。此标记似乎用于在显式同步到驱动器时携带编辑,但这一点尚未测量。

所有写入工具都会遇到的拒绝原因

这些问题发生在实际写入之前——选择库、更新库索引、解析播放列表——以及写入本身的检查过程中。

代码含义未写入任何内容?
invalid_argument参数不合理——同时提供了 playlist_id 和 playlist_name、需要列表时却提供了空列表,或 playlist_name 匹配多个播放列表(会列出所有候选项)。是
library_not_foundlibrary 指定的库未连接——拒绝信息会列出已连接的库——或者根本没有连接任何库;或者该路径下的库报告的 uuid 与 expect_library_uuid 不同;或者无法读取库的标头。是
ambiguous_library未提供 library,且连接了多个受支持的库;或者给定的 uuid 被不同驱动器上的副本共用。信息会列出这些库——参见选择库。是
unsupported_schema库的版本不在此服务支持的范围内——无论是指定的库、唯一连接的库,还是共享 uuid 所指向的所有副本。信息会说明支持哪些版本。是
library_needs_recoveryEngine DJ 留下了尚未恢复的日志。请启动一次 Engine。是
library_busy当前有对象持有冲突锁。请重试。是
index_stale暂时无法构建索引,通常是因为首次运行时 Engine 持有锁。会附带 retry_after_ms。是
query_timeout、query_process_crashed用于解析播放列表的查询失败。仅编辑工具会出现。是
library_unreadable无法读取库;无法在首次写入前创建快照(磁盘已满,或 Node 版本低于 22.16);或者写入后的回读结果与写入内容不一致,因此已回滚。参见 detail

这些错误中的 detail。 无论写入工具返回的错误来自哪一层,detail 都恰好是以下两个字符串之一,客户端可以据此判断库是否发生了变化:not_committed——库保持原样;或 committed_unverified——这种情况很罕见:写入可能已经完成,但无法确认,只有这种情况会返回 backup_path。上表中标记为“是”的每一行都是 not_committed。拒绝原因需要说明的任何信息——例如含糊的 playlist_name 匹配到的候选项、受支持库可能采用的版本——都在 message 中。早期版本允许写入开始前发生的拒绝在 detail 中携带说明文字或留空,这与工具说明中的承诺不符。

资源

  • engine://schema — 助手写 SQL 前需要了解的字段语义:Engine 如何编码调性、为什么速度使用 COALESCE(bpmAnalyzed, bpm)、Track.path 是相对路径、播放列表顺序实际存储在哪里,以及哪些辅助列已建立索引。
  • engine://libraries — 启动时发现的库,以及各库的架构是否受支持。这是一份快照;list_libraries 提供实时视图。

选择库

Engine DJ 会在电脑上保留一个库,并在每个用于导出的驱动器上保留一个库,因此通常会连接多个库。list_libraries 会为每个库报告一个 uuid 和一个 path,每个读取库数据的工具都接受可选的 library 参数。请按打印出来的格式原样传入任一形式——除了绝对路径,也接受 ~/… 路径。与两种形式都不匹配的值会返回 library_not_found,并列出可供选择的库。

省略该参数时,服务会使用曲目数量最多的受支持库。这一点很重要:Engine DJ 安装时创建的本地库会首先扫描,而且通常为空,因此“使用找到的第一个库”会让你实际使用的驱动器被忽略。

对于读取操作,这条规则已经足够,因为读取不会改变任何内容:连接了两个库时,读取会选用其中一个。需要指定使用哪个库时,请传入 library。 如果连接了多个受支持的库,写入就会被拒绝,无论它们的曲目数量是多少。ambiguous_library 会列出每个候选库及其曲目数量,并附上 detail: "not_committed"。

数量从来就不是该问的问题。早期版本只在数量完全相同时才拒绝,理由是 USB 驱动器及其副本之所以数量相同,正是因为其中一个是副本——在 2026-09-01 测得,两边都是实际库,各有 257 首曲目。但只要在其中一边导入一首曲目,数量就不再相同,默认行为便会悄悄选择较大的库。写入错误驱动器的播放列表至少能在那里看到;曲目的流派却看不到,你只会以为编辑没有生效。

只有一个库时,一切照旧:你不必指定库。

副本会共享同一个 uuid。 将 Engine Library 文件夹复制到另一块驱动器上——比如演出用的备用 U 盘——副本会保留原库的 uuid,因此两块驱动器同时连接时,一个 uuid 会对应两个库。指定该 uuid 的写入会以相同方式被拒绝,ambiguous_library 会列出两个路径,而不是写入最先扫描到的驱动器。改为传入路径即可区分这些副本;由于指定 uuid 的读取可能来自另一个副本,因此写入所依赖的数据也应从该路径重新读取。指定共享 uuid 的读取不会被拒绝:它会从其中一个副本返回结果。每次写入前都会重新扫描驱动器,因此服务器启动后插入的副本也会被计入——前提是该库可读取。若副本的架构不受此服务器支持,则不会计入,无论是否指定该副本:写入不可能落在其中,因此与一个受支持库共享的 uuid 会解析到该库;当有两个受支持副本时,拒绝信息会说明列表中遗漏了多少个不受支持的副本。若指定不受支持副本自身的路径,仍会被拒绝,并返回 unsupported_schema。

路径指定的是挂载点,而不是驱动器。 macOS 会将两个带有相同卷标的驱动器——默认标签 Untitled 或 NO NAME,或整盘克隆——按插入顺序挂载为 /Volumes/Untitled 和 /Volumes/Untitled 1,因此重新插拔后,相同路径可能指向另一块驱动器。服务器会检测已使用过的路径上是否出现了不同的库;如果出现,就会为它重新开始(建立新连接、使用独立索引,并在每个结果和备份名称中使用它自己的 uuid),而不是继续沿用之前使用该路径的库所建立的状态。每个写入工具还会接受 expect_library_uuid:它不是选择器,而是一项检查,用于核对所选库报告的 uuid;如果报告的 uuid 不同,就会以 library_not_found 拒绝写入,且不会写入任何内容。每个撤销步骤都会将它与路径一并保存,因此如果重放操作会写入当前位于该路径的另一块驱动器,就会被拒绝,而不会仅仅相信 expect_track_ids 一致——对于 Engine DJ 已经复制到另一块驱动器上的播放列表编辑而言,这些 ID 确实会一致。

拒绝信息会提示助手向你询问,而不是自行选择。否则,“传入 library,这是两个候选项”就等于邀请助手选择第一个,写入也就会再次落到任意磁盘上,使拒绝失去意义。

每个库都有自己的索引和连接;只有在你首次请求查询该库时,才会打开连接。对比两个库——“这块驱动器上有哪些内容是那块驱动器上没有的?”——不是此服务器提供的功能。

安全性

此服务器对你的库作出的保证列在 PRINCIPLES.md 中;本节说明这些保证如何实现。

你的库在操作系统层面以只读方式打开,这不是约定俗成的限制,也不是可被查询重新关闭的 PRAGMA。写入会由 SQLite 自身拒绝;如果没有 --allow-writes,Engine Library 文件夹内绝不会创建任何文件。搜索索引位于 ~/.engine-dj-mcp/。

写入

如果没有 --allow-writes,服务器就没有任何可写入的工具;上文所述完全属实:SQLite 自身会拒绝写入。 启用该标志后,会出现五个工具。create_playlist 只会新建播放列表。add_tracks_to_playlist、remove_tracks_from_playlist 和 reorder_playlist 的作用更进一步:启用该标志后,现有播放列表也可以更改,而不只是新建——可以向其中添加曲目、移除曲目或调整曲目顺序。这四个播放列表工具只会更改指定播放列表自身的条目,以及其他位置恰好两行:该播放列表自身的行,每次编辑都会更新其 lastEditTime,让 Engine 感知到更改;以及仅当使用 create_playlist 时,由 Engine 自己的插入触发器创建的前一个末尾播放列表的链接。其他播放列表都不会被重命名、清空或删除;这四个工具也不会更改任何曲目、提示点或节拍网格。update_track_metadata 只会更改指定曲目的流派、评论、标签、年份和评分——参见上文对应章节——除此之外,不会更改任何内容:不会更改播放列表、提示点、节拍网格、标题、艺人、专辑、路径或文件。

每次编辑都会返回 undo——用于撤销该编辑的准确工具调用——以及 undo_complete;对于播放列表工具,undo 会针对编辑本身产生的位置来表达,而 undo_complete 会说明重放它是否能让播放列表完全恢复原状。要撤销编辑,正确的做法是重放 undo;恢复 backup_path 则不行,因为这会将整个曲库恢复到本会话首次写入之前的状态,丢弃 Engine DJ 此后记录的所有播放次数、导入内容、提示点和节拍网格更改,以及你实际想撤销的那一次编辑。请参阅恢复快照。

只有一种编辑无法通过 undo 撤销,而且它会明确说明,而不会假装可以撤销:移除曲库中缺失的曲目条目(missing: true)。此类条目指向曲库中不存在的曲目,因此没有曲目 ID 可供重新添加——返回结果中的 undo_complete: false 和 undo_note 会列出这些位置,而返回的步骤仍会恢复其他所有内容。

会话首次写入前,数据库会创建快照并保存到 ~/.engine-dj-mcp/backups/,该会话中的每次写入都会返回其路径。每个曲库最多保留十个快照——按曲库文件分别计算,因此,某个曲库及其位于另一块驱动器上的副本不会共享这十个快照。

写入进行期间,确实会在 Engine Library 文件夹中创建一个文件:SQLite 的回滚日志 m.db-journal,位于 m.db 旁边。事务提交后,该文件会被删除;它用于确保写入要么全部完成,要么完全不生效。如果进程在事务中途被终止,日志会留在原处,此时本服务和 Engine DJ 都会将曲库视为需要恢复——本服务会报告 library_needs_recovery,并拒绝操作曲库,包括读取,直到你启动 Engine DJ 一次,让它回滚日志。该文件夹中不会写入其他任何内容;未使用 --allow-writes 时,连这个文件也不会写入。

写入会在一个事务期间持有 SQLite 自身的写锁,并且不会等待:如果当时有其他进程或程序(例如正在保存的 Engine DJ、播放器)持有冲突的锁,写入就会因 library_busy 而被拒绝,不会更改任何内容。

写入前请退出 Engine DJ。 Engine 处于打开状态通常不会造成锁冲突,因此写入一般仍会成功——但 Engine 随后会如何处理在其运行期间发生的更改,此处从未进行过测试。所有写入验收检查都是在 Engine 关闭时进行的。已经确认的是,Engine 加载曲库时会执行自己的操作:它会重新编号播放列表条目,并将播放列表更改复制到另一台已连接的曲库(见下文)。退出、写入、重新启动——Engine 会在启动时读取曲库并显示更改。

请退出,而不是关闭窗口。在 macOS 上,关闭 Engine 窗口不会退出应用:2026-09-01 的观察发现,主进程和七个 OfflineAnalyzer 工作进程仍在运行——这些进程会写入数据库。请使用 ⌘Q。

一次撤销只作用于一个库

每个写入结果都包含一个 library 字段,其中记录了实际写入的库的 uuid 和 path。同时连接两个库是很常见的情况:比如一块 USB 驱动器和电脑上的副本。这时,你可以查看写入的是哪一个库。

**undo 只会在那个库中撤销编辑。**每个撤销步骤都会标明目标库:通过路径、步骤自身的 library 参数以及 expect_library_uuid 中的 uuid 来指定。因此,原样重放某个步骤时,会回到进行该编辑的库,而不是重放时的默认库;如果该路径后来对应了另一个库,操作就会被拒绝。这一点很重要,因为 USB 驱动器和它的副本具有相同的播放列表 ID 和曲目 ID:如果重放时解析到了默认库,操作就可能落在错误的磁盘上,而其中的 expect_track_ids 仍然会匹配,因为两边都以相同方式进行了编辑。

Engine DJ 会自行在已连接的库之间同步播放列表更改,因此,你的编辑副本仍可能出现在 undo 无法触及的位置。

测量日期:2026-09-01。某首曲目被添加到电脑上库中的一个播放列表。随后,在连接 USB 驱动器的情况下启动了 Engine DJ,USB 上的同一播放列表也出现了这首曲目——该副本带有本服务的 INSERT 写入的同一个 lastEditTime。随后运行了 undo,并撤销了电脑上的编辑。USB 上的更改则保留了下来。

没有任何内容受损:两个库都保持完好。但它们已不再一致,而 undo 报告成功也是正确的,因为在它自己的库中,它确实做到了所承诺的事情。

因此:如果连接了第二个库,请查看 library 字段,并分别针对每个库执行撤销。在 Engine DJ 下次运行前撤销,就能完全避免这个问题。

更改会传播到哪个库、传播方向如何,属于 Engine 自身的行为——本项目既不对此建模,也不会对此做出猜测。

恢复快照

backup_path 不是撤销操作。它是会话首次写入前整个 m.db 的副本,因此将其放回会把整个曲库还原到那个时刻:此后 Engine DJ 写入的每一次播放记录、导入、提示点、节拍网格和评分都会一并丢失,而不只是你想撤销的那项编辑。只有在曲库本身受损时才使用它——也就是写入返回 detail: "committed_unverified" 的情况。

快照位于 ~/.engine-dj-mcp/backups/,每个曲库最多保留 10 个。只有以 .db 结尾的文件才是快照。以 .partial-<number> 结尾的文件(后面可能还带有 -journal)仍在写入,或者写入进程在完成前已退出:绝不要恢复这类文件。副本只有在完整写入后才会重命名为 .db 文件;遗留的未完成副本会在下次为该曲库创建快照时清理。

要撤销你创建的播放列表,请在 Engine DJ 中删除它。 Engine 自带的删除触发器会修复播放列表链,并级联删除其中的条目;这正是删除它应有的效果,恢复快照并不会做得更好。对于播放列表工具,要撤销对现有播放列表的编辑,请重放该编辑返回的 undo——它会指出精确的 add_tracks_to_playlist、remove_tracks_from_playlist 或 reorder_playlist 调用,将播放列表恢复到原样,而不触及 Engine DJ 此后记录的任何其他内容。

run_sql 接受任意 SQL,但只会执行第一条语句;VACUUM、ATTACH 和 DETACH 则会被直接拒绝,因此,链式语句或用于导出的语句无法绕过只读连接。

如果 Engine DJ 异常关闭并留下未恢复的日志,本服务不会打开曲库来“修复”它,无论是否使用 --allow-writes 都一样——向前应用日志是在修复他人的文件,因此所有写入工具都会直接拒绝处理此类曲库,而不会让 SQLite 在打开时执行此操作。本服务会报告 library_needs_recovery,并提示你启动一次 Engine DJ,让它自行恢复曲库。

限制

在决定信任哪些内容之前,请阅读本节。

PerformanceData 的布局是通过逆向工程得出的,因此每个解码后的字段都会标明其类型。四种布局的标记均为 layout: "verified"——它们源自并经过真实 Engine DJ 3.0.x 曲库的验证,该曲库包含 281 首已分析曲目:提示点偏移均位于曲目范围内,节拍网格所隐含的节奏与全部 281 首曲目的 bpmAnalyzed 相符,波形声明的点间距乘以点数后也都与全部 281 首曲目的采样数相符。

循环是最后一个通过验证的布局。通过 2248 个哨兵值确定了槽位网格,但当时没有可用曲库保存过循环,因此经过填充的槽位一直未经测试,循环布局在多个版本中都标记为 layout: "unverified"。后来有人特意保存了一个循环,验证结果由此确定:该槽位跨越 1.678321678 s,而 Engine 分析的曲目节奏为 143 BPM,换算后恰好是 4 拍,误差在 4e-15 s 以内。只有正确的字段顺序、单位和字节序才能得出整数拍数。

layout 标记描述的是字节布局,并不代表赋予这些字节的每个名称都已得到验证。有四个标签是推断得出的,而代码在各自定义处都明确说明了这一点:提示点的 4 个颜色字节中,哪个对应哪个通道(返回值保持存储形式,作为一个 32 位值,不声明通道含义);第二个节拍网格是 Engine 称为“调整后”的网格(经测量确认它是 Engine 实际播放的网格——在 7 首曲目中,另一个网格的速度恰好只有分析速度的一半);main_cue.is_adjusted 对应标志字节的含义;以及波形每个点的 3 个字节依次代表低、中、高频。这些推断都不会影响你获得的值。

其他所有内容——标题、艺人、节奏、调性、评分、播放记录、文件路径——都直接从数据库读取,不存在此类保留说明。 has_cues 和 no_cues 表示“已设置热提示点”。 Engine 会为每首经过分析的曲目写入一个 quickCues blob,无论是否使用提示垫,因此简单的 SQL 检查回答的其实是关于分析的问题:在 参考曲库中,281 个 blob 都会被计为有提示点,但实际只有两首曲目有。因而,索引构建时会解码这个 blob。在 50,000 首曲目的情况下,这会额外花费约 100 ms,并且只会在曲库发生变化时产生开销。曲目的主提示点不计入其中——Engine 将其设为播放标记,而不是由 DJ 设置的提示点。has_beatgrid 仍会检查 blob:beatData 没有“已写入但为空”的状态。

**它会写入播放列表和五个曲目字段,而且只有在你要求时才会写入。**不使用 --allow-writes 时,曲库会在操作系统层面以只读方式打开,并且没有任何工具可以写入。启用该标志后,会出现五个写入工具:四个用于创建播放列表,以及添加、移除和重新排列其中的曲目;update_track_metadata 则会更改 Engine 数据库中的流派、备注、厂牌、年份和评分——仅此而已。不会改提示点、循环或节拍网格;不会改标题、艺人、专辑、播放次数或文件路径;不会改音频文件中的标签;甚至不会恢复 Engine DJ 留下的日志文件。

它不会读取播放历史。Track.timeLastPlayed 可以回答“过去六个月我没播放过哪些曲目?”,但不会打开单独的 Engine 历史数据库——其中的会话、唱盘以及曲目之间的播放顺序都不会读取。

**不会报告智能播放列表。**Engine 的规则式列表位于单独的 Smartlist 表中,该表有自己的排序方式和 JSON 规则列;这里不会读取这些内容。get_playlists 只会报告普通播放列表和文件夹,因此你在 Engine 中能看到的智能播放列表不会出现在这里。

**空文件夹会被读取为空播放列表。**Engine 的架构没有文件夹标志——文件夹只是其他播放列表的上级播放列表——所以 is_folder 表示“有子列表”。清空的文件夹与没有曲目的播放列表无法区分。

**可以编辑播放列表中的曲目,但不能编辑播放列表本身。**启用 --allow-writes 后,可以创建新播放列表,也可以向现有播放列表添加、移除或重新排列曲目;但不能重命名、删除、移到其他文件夹,也不能将其转换为文件夹;此外,也没有套曲列表或建议的过渡方式。它会回答关于曲库的问题,并在你要求时记录答案;混音由你负责。

**仅支持架构版本 3.0.0 至 3.0.2。**旧版和新版曲库会列出其版本,并报告为不受支持,而不会猜测着读取。

许可证

MIT — 参见 LICENSE。本项目的保证与不做事项: PRINCIPLES.md。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。