关于 Engine DJ Library Assistant (unofficial)
Engine DJ Library Assistant (unofficial) 是面向 DJ 的 MCP 服务,可通过 AI 助手搜索、查看和审计 Engine DJ 曲库。它读取本机及 USB 驱动器上的曲库,默认禁用修改。
主要功能
- 01按文本、BPM、调性、评分和日期搜索曲目
- 02查看播放列表、提示点、节拍网格和演奏数据
- 03审计缺失文件、重复曲目等问题
- 04读取本机和 USB 驱动器上的曲库
- 05可选创建播放列表并编辑曲目元数据
适用场景
- 01按节奏、调性、流派或备注查找曲目
- 02检查曲库中的缺失文件和重复曲目
- 03查看曲目的提示点和节拍网格
- 04启用写入后创建播放列表或更新标签
MCP 相关
该 MCP 服务向兼容的 AI 客户端提供搜索和查看 Engine DJ 曲库的工具。只有使用 `--allow-writes` 启动时,才会提供写入工具。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
工具
9收录的工具列表 · 更新于 2 周前
| 参数 | 类型 | 必填 | 说明 |
|---|---|---|---|
| q | string | 否 | |
| bpm | object | 否 | |
| bpm.max | number | 否 | |
| bpm.min | number | 否 | |
| bpm.around | number | 否 | |
| bpm.tolerance_pct | number | 否 | |
| key | object | 否 | |
| key.mode | enum | 否 | major | minor |
| key.camelot | array | 否 | |
| key.compatible_with | string | 否 | |
| added | object | 否 | |
| added.after | string | 否 | |
| added.before | string | 否 | |
| flags | object | 否 | |
| flags.analyzed | boolean | 否 | |
| flags.has_cues | boolean | 否 | |
| flags.available | boolean | 否 | |
| flags.has_beatgrid | boolean | 否 | |
| limit | integer | 否 | |
| cursor | string | 否 | |
| fields | array | 否 | |
| played | object | 否 | |
| played.after | string | 否 | |
| played.never | boolean | 否 | |
| played.before | string | 否 | |
| rating | object | 否 | |
| rating.max | number | 否 | |
| rating.min | number | 否 | |
| library | string | 否 | 要使用的曲库:指定 uuid,或 list_libraries 报告的路径(报告中的 ~/... 格式和绝对路径均可)。省略时使用曲目数量最多的受支持曲库。READ 始终可以省略。只有在连接了单个受支持曲库时,WRITE 才可省略;连接两个或更多曲库时,写入会拒绝执行并返回 ambiguous_library 及曲库列表,因为选择会决定更改哪个磁盘;请询问用户要使用哪个曲库,然后在此处传入。复制到另一驱动器的曲库会保留其 uuid,因此同一个 uuid 可能对应两个已连接的曲库:写入时若指定此类 uuid,也会因 ambiguous_library 而拒绝。要写入其中一个曲库,请传入其路径。 |
| playlist | object | 否 | |
| playlist.id | integer | 否 | |
| playlist.name | string | 否 | |
| redact_paths | boolean | 否 | |
| include_total | boolean | 否 |
详细介绍
engine-dj-mcp
向 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 },日期格式相同。 |
flags | analyzed、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、path | path 是完整的 Folder/Sub/Name,且唯一——单独的 name 不一定唯一。 |
depth、parent_id | 嵌套关系。顶层的 parent_id 为 null。 |
is_folder | 列表包含子列表。Engine 没有文件夹标记——文件夹本身就是其他播放列表所在的播放列表——因此,已清空的文件夹会显示为空播放列表。 |
is_persisted | Engine 自带的标记,表示列表已保存到设备上。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 | 文件已从磁盘中消失的曲目 |
unavailable | Engine 已标记为不可用的曲目 |
unanalyzed | Engine 尚未分析的曲目 |
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 相同。 |
positions | get_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 相同。 |
order | 1..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_found | library 指定的库未连接——拒绝信息会列出已连接的库——或者根本没有连接任何库;或者该路径下的库报告的 uuid 与 expect_library_uuid 不同;或者无法读取库的标头。 | 是 |
ambiguous_library | 未提供 library,且连接了多个受支持的库;或者给定的 uuid 被不同驱动器上的副本共用。信息会列出这些库——参见选择库。 | 是 |
unsupported_schema | 库的版本不在此服务支持的范围内——无论是指定的库、唯一连接的库,还是共享 uuid 所指向的所有副本。信息会说明支持哪些版本。 | 是 |
library_needs_recovery | Engine 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。
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。