apple-music-mcp
by Pallas-Labs
README.md
# apple-music-mcp
[](https://www.npmjs.com/package/@pallas-labs/apple-music-mcp) [](https://github.com/parthmangrola/apple-music-mcp) [](./LICENSE)
MCP server for controlling Apple Music on macOS via AppleScript. Implements MCP 2026-07-28 and remains compatible with initialization-based 2025 clients over stdio. Works with Claude Code, Codex CLI, Cursor, OpenCode, and other MCP clients.
## Tools
| Tool | Description | Read/Write |
| --- | --- | --- |
| `music.capabilities` | Report server capabilities and runtime flags | Read |
| `music.health` | Check Music availability and automation permissions | Read |
| `music.list_folders` | List folder playlists | Read |
| `music.list_playlists` | List user playlists, optionally filtered by folder | Read |
| `music.get_now_playing` | Get current track info and player state | Read |
| `music.search_library` | Search library tracks by name/artist | Read |
| `music.find_tracks` | Find library tracks by text, year, or play count | Read |
| `music.get_playlist_tracks` | Get tracks in a playlist (paginated) | Read |
| `music.create_playlist` | Create a playlist, optionally in a folder | Write |
| `music.create_playlist_from_criteria` | Create a playlist from a bounded criteria match | Write |
| `music.create_folder` | Create a folder playlist, optionally nested | Write |
| `music.move_playlist` | Move a playlist into a folder | Write |
| `music.rename_playlist` | Rename a user playlist or folder | Write |
| `music.delete_playlist` | Delete a user playlist after the user confirms | Write |
| `music.playback_control` | Play, pause, next, previous, toggle | Write |
| `music.play` | Play a playlist or track, optionally shuffled | Write |
| `music.set_player` | Set volume, shuffle, repeat, or position | Write |
| `music.add_tracks_to_playlist` | Add unique track IDs with per-ID outcomes | Write |
| `music.remove_tracks_from_playlist` | Remove every instance of each unique track ID | Write |
| `music.update_track` | Set a track's favorite, dislike, or star rating | Write |
### Playlist deletion
`music.delete_playlist` deletes one user playlist; the tracks stay in the library. It refuses folders, because deleting a folder also deletes every playlist in it, and it refuses built-in and Apple Music playlists. If the client supports elicitation, the server always asks the user to confirm and names the playlist and its track count. Other clients must pass `confirm: true`, and should do so only after the user approves. A declined or cancelled prompt deletes nothing.
### Track matching and bulk mutation results
`music.find_tracks` requires at least one text or numeric filter. Music applies the most selective filter across the whole library, the server reads the candidates in bulk and applies the remaining filters, then sorts and limits. It returns `tracks`, `totalMatched`, `limit`, `scanned`, and `truncated`. `scanned` is the number of candidates Music returned, capped at 50,000 for memory safety. `truncated` is `true` only when that cap was hit; then `totalMatched` is a lower bound, not a full-library total. `music.create_playlist_from_criteria` reports the same `scanned` and `truncated` values; its `matched` field has the same lower-bound rule.
Add and remove requests reject duplicate track IDs. Add results contain `playlistId`, `requested`, `added`, `addedTrackIds`, `missingTrackIds`, and `failedTrackIds`. A missing ID was not found in the library; a failed ID was found but could not be duplicated.
Remove results contain `playlistId`, `requested`, `removed`, `removedTrackIds`, `missingTrackIds`, and `failedTrackIds`. Removal deletes every matching playlist instance, so `removed` can exceed `removedTrackIds.length`. Criteria-created playlists also report `requested`, `addedTrackIds`, `missingTrackIds`, and `failedTrackIds`.
## Resources and prompts
The server exposes two read-only resources:
- `music://guide` — safe usage, persistent IDs, and write controls
- `music://now-playing` — current track and playback state as JSON
It also provides two user-invoked prompts:
- `curate-playlist` — plan a bounded library search and approved playlist creation
- `library-overview` — summarize folders, playlists, and current playback without writes
## Environment Variables
| Variable | Default | Description |
| --- | --- | --- |
| `APPLE_MUSIC_MCP_ENABLE_WRITES` | `false` | Enable every Write tool in the table above |
| `APPLE_MUSIC_MCP_DRY_RUN` | `false` | Mutation tools return dry-run payloads without modifying Music |
| `APPLE_MUSIC_MCP_HTTP_PORT` | unset | Serve Streamable HTTP at `http://127.0.0.1:<port>/mcp` instead of stdio |
## Setup (Recommended)
### Codex CLI / Codex IDE extension
Add the server with the CLI:
```bash
codex mcp add apple_music -- npx -y @pallas-labs/apple-music-mcp
```
Then set safe timeouts in `~/.codex/config.toml` (default tool timeout is 60s, but this server can take up to ~70s for large playlist operations):
```toml
[mcp_servers.apple_music]
command = "npx"
args = ["-y", "@pallas-labs/apple-music-mcp"]
startup_timeout_sec = 20
tool_timeout_sec = 90
[mcp_servers.apple_music.env]
APPLE_MUSIC_MCP_ENABLE_WRITES = "false"
```
If you want mutation tools, set:
```toml
[mcp_servers.apple_music.env]
APPLE_MUSIC_MCP_ENABLE_WRITES = "true"
```
### Claude Code
Add the server with Claude CLI:
```bash
claude mcp add --transport stdio apple-music -- npx -y @pallas-labs/apple-music-mcp
```
`--transport`/`--env` options must come before the server name, and `--` separates Claude flags from the server command.
Project-shared setup (`.mcp.json`):
```json
{
"mcpServers": {
"apple-music": {
"command": "npx",
"args": ["-y", "@pallas-labs/apple-music-mcp"],
"env": {
"APPLE_MUSIC_MCP_ENABLE_WRITES": "false"
}
}
}
}
```
To enable writes, set `APPLE_MUSIC_MCP_ENABLE_WRITES` to `"true"`.
### Cursor
Add to `~/.cursor/mcp.json`:
```json
{
"mcpServers": {
"apple-music": {
"command": "npx",
"args": ["-y", "@pallas-labs/apple-music-mcp"],
"env": {
"APPLE_MUSIC_MCP_ENABLE_WRITES": "false"
}
}
}
}
```
### OpenCode
Add to `opencode.json`:
```json
{
"mcp": {
"servers": {
"apple_music": {
"type": "local",
"command": ["npx", "-y", "@pallas-labs/apple-music-mcp"],
"environment": {
"APPLE_MUSIC_MCP_ENABLE_WRITES": "false"
},
"protocol": "2026-07-28"
}
}
}
}
```
OpenCode runs MCP servers in Code Mode by default, and this server works with it. Use the key `apple_music` with an underscore. Scripts then call tools such as `tools.apple_music.music_find_tracks(...)`, and prompts appear as `/apple_music:curate-playlist`.
### Executor and other HTTP clients
Clients that connect by URL, such as the Executor 2 desktop app, need the HTTP mode. Start the server with a port:
```bash
APPLE_MUSIC_MCP_HTTP_PORT=4319 npx -y @pallas-labs/apple-music-mcp
```
In Executor, select **Add app**, add a custom MCP server, and enter `http://127.0.0.1:4319/mcp`. The server listens only on 127.0.0.1, accepts only `localhost` and `127.0.0.1` Host headers, and refuses requests that carry an `Origin` header, so web pages cannot call it. To keep it running after you log out and in, run it from a launchd agent.
### Hardened Read-Only (Codex)
For extra safety, keep writes disabled and hide mutation tools:
```toml
[mcp_servers.apple_music]
command = "npx"
args = ["-y", "@pallas-labs/apple-music-mcp"]
tool_timeout_sec = 90
disabled_tools = [
"music.create_playlist",
"music.create_playlist_from_criteria",
"music.create_folder",
"music.move_playlist",
"music.rename_playlist",
"music.delete_playlist",
"music.playback_control",
"music.play",
"music.set_player",
"music.add_tracks_to_playlist",
"music.remove_tracks_from_playlist",
"music.update_track"
]
```
## Permissions
This server uses AppleScript to control Music. macOS will prompt for automation permission on first use.
If you see `permission_denied` errors:
1. Open **System Settings** > **Privacy & Security** > **Automation**
2. Find your terminal app (Terminal, iTerm2, VS Code, etc.)
3. Enable **Music** under it
## Development
Development requires Bun 1.3.14.
```bash
git clone https://github.com/Pallas-Labs/apple-music-mcp.git
cd apple-music-mcp
bun install --frozen-lockfile
bun run format:check
bun run lint
bun run typecheck
bun run test
bun run build
```
### Integration Tests
Requires Music app running on macOS:
```bash
INTEGRATION=true bun test src
```
## Troubleshooting
**"Music app is not running or unavailable"** The server auto-launches Music. If it fails, open Music manually first.
**"Permission denied to control Music"** See the [Permissions](#permissions) section above.
**"AppleScript command timed out"** Large libraries can be slow. Track discovery usually finishes in a few seconds because Music filters the library and the server reads matches in bulk, but it allows up to 120 seconds for very large libraries. Playlist listing allows up to 70 seconds. In Codex, set `tool_timeout_sec = 130` for this server. If you still hit timeouts, run `music.health` first to warm up the connection.
**Mutations are non-atomic** AppleScript doesn't support transactions. Bulk operations may partially succeed. Inspect the returned added/removed, missing, and failed ID arrays instead of assuming the full request completed.
## License
MIT
This server cannot be deployed
Maintenance
ActivityMaintained
ResponsivenessNo issues