Apple Music Playlist Curator

io.github.Z-Han-Z由 GitHub 帳號 Z-Han-Z 發布v1.5.0更新於 Oct 24, 2026

Deep Apple Music curation: language-led selection, catalog grounding, and thoughtful listening flow.

已驗證STDIO僅桌面效率與工作流媒體與設計

概覽

AI 產生的概覽

本機 MCP 伺服器,讓助理搜尋 Apple Music 目錄、核實候選曲目、審查與排序播放清單,並在使用者帳號中建立或修改播放清單。

可以做什麼
搜尋 Apple Music 目錄並把候選曲目解析成確切錄音,標記重複項目與可疑的現場版或重製版,同時查詢帳號與登入狀態。列出、檢視、建立播放清單,加入曲目,以及刪除播放清單。審查播放清單中繼資料(長度、藝人集中度、曲風、年代、重複項目),並依速度、調性、能量與情緒值分析曲目銜接。可在使用者指定的敘事分段內選擇性重新排序,並讀取最近播放紀錄與播放次數。
適用情境
適合希望助理把自然語言需求變成有憑據的 Apple Music 播放清單,或檢查、審查與調整現有播放清單和收聽紀錄的情況。適合已訂閱 Apple Music、並願意完成一次 Apple ID 登入的使用者。
執行需求
以 stdio 本機程序執行;需要 Python 3.10+,支援 Windows、macOS 與 Linux,可透過 uvx 或 pip 從 apple-music-playlists 套件啟動。需透過 am-playlist login 指令完成一次 Apple ID 登入,憑證保存在本機應用程式設定中。流程分析需要連線至 Apple Music 及第三方音訊特徵服務。
安裝前請注意
建立播放清單會同時把曲目加入使用者的資料庫,刪除播放清單不會移除這些曲目;刪除與編輯工具會變更帳號資料。Apple ID 憑證保存在本機應用程式設定中,不應寫入容器映像。曲目的 ISRC 會傳送給外部音訊特徵服務,結果會快取在本機。

安裝

在 SourceWeft 中

  1. 開啟 儀表板中的 Apple Music Playlist Curator,將其新增到工作區。
  2. 為需要使用其工具的對話啟用該服務。

Desktop only,透過 STDIO。 STDIO 服務會啟動本機處理程序,因此需要 SourceWeft 桌面主機。

其他 MCP 客戶端

參照 儲存庫 中的啟動說明。

README

Apple Music MCP Curator

网易云(NCM)公开预览:简体中文使用说明。

[Apple Music MCP — natural-language playlists grounded in Apple Music]

[Tests] [Container] [Release] [PyPI] [MCP Registry] [Python 3.10+] [License: MIT] [Glama quality score]

English | 简体中文 | 繁體中文 | 日本語 | 한국어 | Español | Português do Brasil | Deutsch | Français

Deep playlist curation for Apple Music: describe a feeling, scene, era, listening purpose, or sound; your agent grounds the selection and shapes its flow into a satisfying listening experience. You define any narrative beats; without that request, no story arc is imposed.

See a 12-track curation demo: 22 grounded candidates become a deliberately requested three-act story—and show why a lower optimizer cost can make a playlist worse. It is one possible shape, not a default template.

Pure Python standard library — no pip install required to run, and no Apple Developer Program membership needed. Requires Python 3.10+ and works on Windows / macOS / Linux.

The primary interface is the local am-mcp stdio server. The language model already running in your MCP client interprets the brief and chooses candidates; this project searches the Apple Music catalog, resolves exact tracks, and performs account operations. There is no bundled model, LLM API key, artist list, or fixed theme. The CLI remains available for login, diagnostics, scripting, audits, and advanced sequencing.

  understand  →  curate  →  ground  →  sequence with intent  →  dry-run  →  create

Why this project

  • The brief stays semantic. The host LLM reasons directly about imagery, mood, lyrical angle, era, cultural context, contrast, and the role of each song. It is not reduced to a handful of user-supplied sliders or an opaque theme-fit number.
  • Selection and sequencing stay separate. The model decides what belongs; measured BPM, key, energy, valence, and loudness can diagnose transitions or refine flow within beats you specify— otherwise, no global arc is imposed.
  • Every candidate is grounded. Catalog resolution catches missing tracks, duplicates, wrong artists, and suspicious live/remastered versions before the dry run and final create step.
  • Listening evidence remains evidence. Replay history, recent plays, dates, and play counts can inform curation without silently becoming an algorithmic definition of the user's taste.
  • Local and private by design. The server runs on your machine; credentials stay in the local app config, and there is no bundled model, telemetry service, or extra LLM API key.
  • Portable MCP stdio. Works with Codex, Claude, Cursor, VS Code/Copilot, Gemini CLI, Windsurf, Cordis/DSH, Harness, and other clients that can launch a local stdio server.

Quick start

Recommended — install the MCP stdio service:

bash
pip install apple-music-playlistsam-playlist statusam-playlist login       # one-time Apple ID sign-in

Already use uv? Run the published package without a permanent install:

bash
uvx --from apple-music-playlists am-playlist statusuvx --from apple-music-playlists am-playlist loginuvx --from apple-music-playlists am-mcp

For an MCP client, the equivalent Registry-aligned configuration is:

json
{ "mcpServers": { "applemusic": { "command": "uvx",    "args": ["--from", "apple-music-playlists", "am-mcp"],    "env": { "PYTHONIOENCODING": "utf-8" } } } }

Register am-mcp in the client. The common configuration shape is:

json
{ "mcpServers": { "applemusic": { "command": "am-mcp",    "env": { "PYTHONIOENCODING": "utf-8" } } } }

Then describe the result, not the implementation:

Create a 25-track late-night driving playlist: atmospheric alternative R&B and electronic, mostly from the last ten years, no live versions, with a calm landing.

Clients with MCP Prompt support can select create_playlist_from_description. In every other client, send the same request in chat: the server instructions and typed tools expose the same status → candidate pool → catalog grounding → direct comparison → dry-run → create workflow.

From a clone (nothing to install):

bash
git clone https://github.com/Z-Han-Z/apple-music-playlists.gitcd apple-music-playlists
python am_playlist.py status     # auto-fetches the developer tokenpython am_playlist.py login      # one-time Apple ID sign-in (~6 months validity)
python am_mcp_server.py             # register this absolute script path in the MCP client

Installation also puts the CLI and MCP commands on your PATH:

bash
am-playlist status      # the CLIam-playlist login       # one-time Apple ID sign-inam-mcp                  # the MCP stdio server

For development, pip install -e . from a clone makes edits take effect without reinstalling.

See SETUP.en.md for the credential walkthrough (three ways to get the user token, including a zero-dependency one).


A real curation demo: A Machine Dreams It Is Human

This is deliberately harder than “make me a workout playlist.” The brief asks music to carry a plot, and some of its constraints cannot be expressed as tempo or mood sliders:

Build a 12-track, three-act story in which a machine wakes in a city, mistakes attention for intimacy, asks to be touched, becomes vulnerable, and sees dawn. Cross electronic music and art pop from the late 1970s to the present; use one track per artist, studio recordings only, and let the voices become progressively more human. The ending must feel quiet and earned, not merely low-energy.

The run below used the public US Apple Music catalog on 2026-09-23. It did not read or write a private library.

text
22 LLM-proposed candidates└─ 22 catalog matches returned exact title, artist, album, date, ISRC, and version metadata   ├─ rejected: “Open Eye Signal (Mixed)” — resolved to a 2025 DJ Mix, not the studio cut   ├─ rejected: “Deeper Understanding (2018 Remaster)” — not the requested original recording   └─ 12 final selections
Final create dry-run:       12/12 matched; no write performedAudio-feature coverage:     12/12 usable (100%)Flow cost, narrative locks: 44.14 → 41.14

Each title below opens the exact US catalog recording returned by the resolver, so the sequence can be auditioned rather than taken on trust.

ActGrounded orderWhat the sequence is doing
I — BootThe Robots — Kraftwerk
Technopolis — Yellow Magic Orchestra
Kid A — Radiohead
A body, then a city, then an unstable first-person voice.
II — DesireOblivion — Grimes
Digital Witness — St. Vincent
Is It Cold In The Water? — SOPHIE
Touch — Daft Punk & Paul Williams
All Is Full of Love — Björk
Public attention becomes bodily risk, transformation, a request for contact, and finally an answer.
III — Re-entryCellophane — FKA twigs
Retrograde — James Blake
Long Road Home — Oneohtrix Point Never
An Ending (Ascent) — Brian Eno
The synthetic shell fails; retreat becomes return, and the story lands at dawn.

The interesting failure happened during ordering. With only the three acts locked, the numerical optimizer cut the measured cost from 48.28 to 14.88 — but put Retrograde after the dawn and made All Is Full of Love answer a request that had not happened yet. That is cheaper and worse. The host model therefore added semantic beat boundaries (request → answer, return → dawn) and let am_optimize_order make only local changes inside those boundaries. Selection and story stayed linguistic; BPM, key, energy, and valence remained supporting evidence.

Final dry-run input
json
[  "The Robots - Kraftwerk",  "Technopolis - Yellow Magic Orchestra",  "Kid A - Radiohead",  "Oblivion - Grimes",  "Digital Witness - St. Vincent",  "Is It Cold In The Water? - SOPHIE",  "Touch - Daft Punk",  "All Is Full of Love - Björk",  "Cellophane - FKA twigs",  "Retrograde - James Blake",  "Long Road Home - Oneohtrix Point Never",  "An Ending (Ascent) - Brian Eno"]

This is the normal MCP workflow: the host model interprets the brief and proposes more candidates than it needs; am_resolve_candidates grounds them; the model chooses tracks and interprets narrative roles only when the brief calls for them; am_optimize_order optionally checks local flow without crossing semantic boundaries; and am_create_playlist(dry_run=true) verifies the exact final recordings before the write.

To compare that workflow with a one-shot baseline, use the six difficult multilingual briefs and blind-listening protocol in examples/. The suite provides auditable constraints, not predetermined “correct” songs.


What's in the box

FilePurpose
am_playlist.pyCore: token management, catalog search, create / edit / delete playlists, track resolution
am_mcp_server.pyPrimary MCP stdio service: one description-to-playlist prompt plus 14 tools
playlist_audit.pyMetadata audit: length, artist concentration, genres, eras, durations, duplicates, interludes
playlist_flow.pyAudio-feature audit: BPM / key / loudness / energy / valence, adjacency checks, arc shape
playlist_optimize.pySimulated-annealing track ordering against the measured rules
listening_stats.pyListening history: recently played, and per-track/album/artist play counts (Apple Music Replay backend)
profile_library.pyDescriptive sample profile: measured BPM / energy / valence spread and sample-relative quadrants; it does not define the user's taste
am_library.pyYour library: paged export of every catalog-backed song you own, enriched with ISRC / year / genre
build_pool.pyListening-evidence pool: merge recent plays with multiple Replay years, retaining dates and play counts for the LLM to interpret

Supporting modules:

FilePurpose
playlist_core.pyPlatform-neutral core: Camelot, BPM folding, the four adjacency rules, the six narrative shapes. Imports nothing from this project and nothing third-party
am_paths.pyPlatform-neutral paths and version — where config and cache live
am_meta.pyThe single catalog_meta implementation (batched catalog lookups)

All three analysis modules are importable as libraries:

python
import playlist_flow, playlist_audit, playlist_optimize
print(playlist_flow.flow_report("My Playlist"))          # -> strprint(playlist_audit.audit_report("My Playlist"))        # -> strids, report = playlist_optimize.optimize("stack.json")   # -> (list[str], str)

MCP tools

The standard prompt create_playlist_from_description asks for a natural-language brief and optional name, track count, and response language. Curation stays in the host model; the tools are the grounded Apple Music execution layer:

am_status · am_search_songs · am_resolve_candidates · am_list_playlists · am_show_playlist · am_create_playlist · am_add_tracks · am_delete_playlist · am_audit_playlist · am_analyze_flow · am_optimize_order · am_recently_played · am_top_played

am_resolve_candidates grounds a generous LLM-proposed pool in real catalog metadata, flags duplicates and suspicious versions, and deliberately does not score theme fit. The host model compares candidates directly with the user's words and explains their playlist roles. am_analyze_flow diagnoses transitions; am_optimize_order can optionally refine ordering inside already chosen narrative blocks. It never decides which songs belong in the playlist.

Mount it in a Cordis agent preset with the template in preset/, or wire it into any other MCP client with:

json
{ "mcpServers": { "applemusic": {    "command": "python", "args": ["/abs/path/am_mcp_server.py"] } } }

Complete tested examples for Codex, Claude, Cursor, VS Code/Copilot, Gemini CLI, Windsurf, Docker, Cordis/DSH, and Harness are in docs/client-setup.md.

Build the non-root local container with:

bash
docker build -t apple-music-playlists:1.5.0 .

The client must run it attached with docker run --rm -i; mount only the app config directory and a writable cache as shown in the client guide. Config stays writable so token refresh can persist. Never bake Apple credentials into the image.


Tests

bash
python -m unittest discover -s tests -v

The suite is entirely offline — no network, no credentials. Two kinds:

  • Behaviour: the pure math that decides what "sounds good" — Camelot mapping, BPM folding, arc classification, each adjacency penalty asserted in isolation, annealing determinism and the block-order constraint. All six narrative archetypes must classify as themselves.
  • Structural regressions, each pinned to a bug that actually shipped: no hardcoded catalog region, exactly one catalog_meta, no non-None --storefront default, cache outside the repo, every MCP tool wired to a handler, and the optimizer free of platform imports.

They earn their keep immediately — the suite caught a syntax error in a file written minutes earlier, before it was ever run.


The interesting part: audio features

Apple's catalog API exposes no audio features at all — no tempo, key, loudness, energy, or valence. Spotify's audio-features endpoint was shut off for new apps on 2024-11-27, and AcousticBrainz retired in 2022.

playlist_flow.py bridges the gap with a free, key-less chain built on ISRC, which Apple does return:

Apple Music track  ──►  ISRC                          │                          ├─► api.reccobeats.com/v1/track?ids=<ISRC>   → track UUID                          │        └─► /v1/audio-features?ids=<UUID>                          │                 → tempo, key, mode, loudness, energy, valence,                          │                   danceability, acousticness, instrumentalness,                          │                   liveness, speechiness                          └─ (fallback) musicbrainz.org ISRC lookup

Results are cached locally, so the network cost is paid once per playlist.

Coverage is reported, never silently dropped. Every consumer prints a funnel saying why each track could not be measured:

Audio-feature coverage: 1,395/1,581 usable (88%)  ·  80 tracks have no ISRC — the feature chain cannot start, so changing sources will not help  · 106 tracks are not in the current source — switch sources or analyse the audio locally

That distinction is the point. No ISRC means the chain cannot start at all — a different feature source will not help. Not in the source means the ISRC is fine and switching sources (or analysing the audio locally) would fix it. Collapsing both into "missing features" discards the only information that tells you what to do next.

It matters more than it looks: tempo / key / energy / valence are the only things the adjacency rules and the arc can act on, so coverage is the ceiling on how good an ordering can be. At 60% coverage, four positions in ten were never evaluated — while the cost number still looks excellent. The optimizer therefore prints coverage above its cost lines, and warns below 90%.


Sequencing rules the optimizer enforces

Derived from the research collected in docs/ — including a PLOS ONE study in which 130 music professionals sequenced albums, and a randomized trial on mood-adaptive music ordering.

Hard adjacency rules

  • No two slow tracks adjacent
  • Avoid "only slightly slower" transitions (0–12% drop makes the slower track feel like it drags)
  • Adjacent tracks must not be similar in both tempo and key
  • No unjustified large BPM jumps (>40%); no jarring energy shifts under incompatible keys

These four have exactly one definition, in playlist_core.check_pair(), and both the audit and the optimizer call it. They used to be implemented twice, and the copies disagreed: the audit called a track "slow" below the tempo's 25th percentile while the optimizer used a fixed 100 BPM, and the audit never checked the BPM-jump rule at all. That is a tool diagnosing against one standard and repairing against another — so the count it reported could not be trusted.

Note on the "slow" threshold. It is absolute (100 BPM), not data-driven, deliberately: the optimizer evaluates the same sequence thousands of times while annealing, and a percentile threshold would drift as the permutation changes, so the cost would never settle. The cost is that a uniformly slow playlist flags every adjacent pair — that is real, not a sequencing failure, and the report says so.

Global narrative arc is optional. With no --arc, the optimizer only considers local adjacency and does not impose an emotional or tempo trajectory. The host LLM should interpret a free-form story from the brief and preserve its order through blocks; use a named preset only when the user explicitly asks for that kind of curve.

bash
python playlist_optimize.py stack.json --arc cinderellapython playlist_optimize.py --list-shapes
AxisTarget
valence, energy, loudnessthe chosen archetype, only when --arc is explicit
tempoinverted U — fast in the middle, only when --arc is explicit

Six optional shapes: rags-to-riches, tragedy, man-in-a-hole, icarus, cinderella, oedipus. The target curve and the shape the audit classifies come from the same table in playlist_core.ARCHETYPES. The audit may still report which preset a playlist resembles; that diagnosis does not make the preset a recommendation or an ordering target.

When a preset is explicitly requested, valence / energy / loudness follow its emotional trajectory and tempo uses a separate inverted-U target. With no --arc, neither global target is applied.

An arc cost is a distance from an explicitly requested feature curve, not a measure of whether a playlist is good. Coverage limits what it describes, and a lower cost cannot override the brief or prove that one sequence sounds better.

The optimizer preserves your grouping (movements / eras / moods) and only reorders within groups, so thematic structure survives the loudness tuning. Drop the grouping and it reorders freely — measurably "smoother", at the cost of your narrative.

Example from a real run: cost 104.46 → 19.41 with grouping preserved, → 1.28 ungrouped.


Gotchas worth knowing before you debug

  • Responses can be gzip-compressed even when you never sent Accept-Encoding. Decoding the raw bytes as UTF-8 yields garbage that looks like an empty body. (This is fixed in am_playlist.py.)
  • DELETE only works on amp-api.music.apple.com. The documented host api.music.apple.com returns 401 for playlist and library-song deletion.
  • Do not send x-apple-client-version to amp-api — it turns into a 500.
  • Creating a playlist also adds its tracks to the library. Deleting the playlist does not remove them.
  • BPM estimates have octave ambiguity (90 vs 180 for the same track). Fold into [70,160) before comparing, or "two slow tracks adjacent" over-reports by ~4×.
  • Never pass "Title - Artist" straight into catalog search — you get live/remastered takes. Search on the space-separated form and score versions afterwards.
  • An artist missing from a storefront's search ≠ the song is unavailable there. Look it up by ISRC.
  • Only the client that created a playlist can modify it — Apple-side restriction.

More in docs/apple-music-api-notes.md and skill/reference.md.


Documentation

DocContents
SETUP.en.mdCredentials: what tokens exist, how to get each one, security notes, troubleshooting
docs/client-setup.mdClient-specific MCP, Docker, Cordis/DSH, generic harness, and Harness Platform setup
docs/publishing.mdMaintainer-only PyPI and official MCP Registry publishing checklist
docs/apple-music-api-notes.mdToken model, endpoint contracts, measured API behaviour, eval of 7 automation approaches
docs/how-to-build-a-good-playlist.mdCuration methodology: adjacency physics, arc data, six narrative shapes, the ISO principle
docs/playlist-curation-survey.mdSurvey of published curation guidance (platform rules, DJ methods, academic findings)
docs/evaluation-signals.mdLLM-native curation: direct candidate comparison, catalog grounding, readable constraints, and why scalar theme scores stay out of the critical path
docs/natural-language-curation-evidence.mdPrimary research on broad music intent, reference-vs-request errors, intent hallucination, scrutable language profiles, and the reusable validation protocol
examples/Reproducible multilingual curation evaluations: six difficult briefs, a baseline protocol, auditable evidence, privacy rules, and no golden track lists
docs/algorithm-review.mdReview boundary for early heuristics: which decisions belong to the LLM and which deterministic algorithms should retain
docs/platform-adapters.mdThe platform-adapter boundary: what is platform-neutral, what an adapter must provide, and what breaks on a service that exposes no ISRC
llms.txtConcise agent-readable map of the curation boundary and the most useful project documents
skill/Agent skill: workflow + the accumulated gotcha list
CHANGELOG.mdRelease history, including behaviour changes between versions
preset/Cordis agent preset template that mounts the MCP server

For the complete Simplified Chinese guide, see README_ZH_CN.md.


Community

  • Contributing — development workflow, architecture boundaries, and review expectations
  • Support — where to ask questions, report bugs, or propose features
  • Code of Conduct — participation standards and private reporting route
  • Security policy — supported versions and confidential vulnerability reporting

License

MIT — see LICENSE.

Unofficial community tooling. Not affiliated with or endorsed by Apple. Uses your own Apple Music account for personal use; follow Apple's terms of service.

來源:README.md,提交 12f82de

工具

0
SourceWeft 尚未在沙箱中啟動過該軟體包,因此其工具未知。

版本歷史

2
  1. v1.5.0最新Sep 28, 2026
  2. v1.4.0Sep 23, 2026

評分

已滿足適用檢查項的 50%

該評分衡量此收錄條目的完整程度,依據註冊中心條目、端點與公開來源計算得出。它不是對伺服器本身的評價,也不表示伺服器安全或目前可用:可連通性是帶日期的檢查結果,評分也不包含安裝量或使用者評價。

  • 近 30 天內可連通25 分

    請保持端點能夠回應 MCP 握手;SourceWeft 每 30 天探測一次。

  • 已宣告認證方式10 分

    請在註冊中心條目中宣告憑證請求標頭,或保持端點能夠回應探測,以便判定其認證方式。

  • 已列出工具15 分

    請讓伺服器在無憑證時回應 tools/list,或發布可在沙箱中無憑證啟動的軟體包。

  • 描述必需15 分
  • 圖示10 分
  • 文件15 分
  • 授權條款10 分