High-performance command-line tool and MCP server for iTunes/Apple Music integration on macOS
A Go-based tool that bridges command-line interfaces and AI applications with your Apple Music library. Features ultra-fast search (<7ms via SQLite FTS5), smart playback control, and integration with Large Language Models through the Model Context Protocol (MCP).
- Ultra-Fast Search: SQLite FTS5 database with <7ms query performance
- Smart Playback: ID-based track lookup with playlist context support
- Audio Control: EQ presets and output device management (local/AirPlay)
- MCP Integration: 14 specialized tools for AI/LLM applications
- Database-First: Normalized SQLite schema keyed by persistent Apple Music IDs
- Context-Aware:
context.Contextflows from MCP requests down to SQLite queries and JXA processes, so cancellation propagates end to end - Radio Stations: Apple Music stations with database-backed CRUD and search
- Vim Plugin: fzf-powered library search and playback from inside Vim/Neovim (
vim-plugin/)
This project evolved from a legacy VIM plugin for iTunes integration. The original plugin remains untouched in the master branch as a reference. The current implementation is a complete rewrite focused on modern Apple Music integration, database performance, and AI/LLM compatibility through the MCP protocol — and includes a rewritten Vim plugin (vim-plugin/) that fronts the Go CLI instead of JXA scripts.
+-------------------+ +--------------------+ +-------------------+
| CLI Tool | | MCP Server | | Apple Music |
| (itunes) | | (mcp-itunes) | | Desktop App |
+---------+---------+ +---------+----------+ +---------+---------+
| | |
+----------+-------------+ |
| |
+----------v------------+ +----------v--------+
| itunes.Client | | JXA Scripts |
| (core library) |<-------------+ (via osascript |
+----------+------------+ | stdin) |
| +-------------------+
+----------v------------+
| SQLite Database |
| FTS5 Search |
+-----------------------+
- Search path: queries hit the local SQLite database directly — the Apple Music app is not involved.
- Playback path: Go pipes embedded JXA scripts to
osascriptvia stdin (no temp files) to drive the Apple Music app. - Library refresh: Apple Music → JXA bulk property fetch → SQLite (~15–30 seconds even for 10k+ track libraries).
- macOS (required for Apple Music integration)
- Go 1.25+
- Apple Music app (installed and configured)
- Terminal access with Automation permissions for AppleScript/JXA
git clone <repository-url>
cd itunes
go build -o bin/itunes . # CLI tool
go build -o bin/mcp-itunes ./mcp-server # MCP server
go build -o bin/itunes-migrate ./cmd/migrate # Migration tool# First-time setup or library refresh (~15-30 seconds)
./bin/itunes-migrate -from-script
# Validate existing database
./bin/itunes-migrate -validateWhen to refresh: first install, library changes, metadata updates, or search issues.
# Search your music library
./bin/itunes search "jazz"
./bin/itunes search "Miles Davis"
./bin/itunes search --json "Miles Davis" # JSON output (used by the Vim plugin)
# Play tracks: play <playlist> [album] [track] [trackID]
./bin/itunes play "My Playlist" "" "" "B258396D58E2ECC9" # ID with playlist context
./bin/itunes play "" "Kind of Blue" "So What" # album + track name
# Current playback status
./bin/itunes now-playing # or: status
# Radio stations
./bin/itunes search-stations "jazz"
./bin/itunes list-stations
./bin/itunes add-station --name "Name" --url "itmss://..." [--genre g --description d --homepage url]
./bin/itunes update-station <id> [--name/--url/--genre/--description/--homepage ...]
./bin/itunes delete-station <id>
./bin/itunes import-stations stations.json
./bin/itunes export-stations stations.json# Start MCP server (stdio transport)
./bin/mcp-itunesClaude Desktop / Claude Code configuration:
{
"mcpServers": {
"itunes": {
"command": "/path/to/bin/mcp-itunes"
}
}
}Search and play from inside Vim/Neovim via fzf:
Plug 'junegunn/fzf', { 'do': { -> fzf#install() } }
Plug 'chew-z/itunes.vim', { 'branch': 'main', 'rtp': 'vim-plugin' }| Command | Description |
|---|---|
:Tunes [query] |
Fuzzy-search the library and play the selected track |
:TunesStatus |
Show the currently playing track |
:TunesPlay [playlist] |
Play an entire playlist |
The plugin shells out to bin/itunes (auto-detected, or set g:itunes_bin).
See vim-plugin/README.md for configuration and
docs/VIM-PLUGIN.md for design details.
Music Library:
search_itunes— search library (<7ms FTS5 performance)search_advanced— filtered search (genre, artist, album, rating, starred, streaming)play_track— play with ID-based lookup and optional playlist contextnow_playing— current playback status and track inforefresh_library— rebuild database from Apple Music (~15–30 s, resource-intensive)
Playlists:
list_playlists— all playlists with metadataget_playlist_tracks— tracks in a specific playlist
Radio & Streams:
search_stations— find Apple Music radio stations by genre/nameplay_stream— play streaming URLs (itmss://,https://)
EQ & Audio Output:
check_eq— current EQ status and available presetsset_eq— apply EQ presets (Rock, Jazz, Classical, …) or enable/disableget_output_device— current audio output (local/AirPlay)list_output_devices— available output devicesset_output_device— switch to local output (AirPlay selection must be manual)
MCP Resources: itunes://database/stats, itunes://database/playlists
Best practice is ID-based playback with playlist context for continuous play:
{"track_id": "B258396D58E2ECC9", "playlist": "My Jazz Collection"}Track IDs come from search results and are Apple Music persistent IDs, which avoids name-matching and encoding issues.
- Search: <7ms queries, <5µs cache hits
- Database: ~760 bytes per track, ~800 tracks/sec migration
- Storage: ~1MB per 1,000 tracks including FTS5 indexes
- Driver: pure Go SQLite (
modernc.org/sqlite, no CGO)
| Variable | Default | Description |
|---|---|---|
ITUNES_DB_PATH |
~/Music/iTunes/itunes_library.db |
Primary database path |
ITUNES_BACKUP_DB_PATH |
~/Music/iTunes/itunes_library_backup.db |
Backup database path |
ITUNES_SEARCH_LIMIT |
15 |
Maximum search results |
Developer docs live in docs/: architecture,
database & FTS5 internals,
JXA automation (including the bulk-fetch rule —
required reading before touching any JXA script), and the
Vim plugin.
├── itunes.go # CLI application (command dispatch + handlers)
├── mcp-server/ # MCP server (14 tools, grouped registration)
├── itunes/ # Core library: Client type, playback, EQ, JXA scripts
│ └── scripts/ # Embedded JXA automation scripts
├── database/ # SQLite + FTS5: schema, search, migrations
├── vim-plugin/ # Vim/Neovim frontend (fzf + CLI)
├── docs/ # Developer documentation
├── logging/ # zap logger setup
└── cmd/migrate/ # Database migration/validation tool
The core library exposes itunes.NewClient(logger) owning the database connection and search manager; playback and audio-control functions are package-level and take a context.Context.
./run_test.sh # go test -v ./...
./run_lint.sh # golangci-lint (zero findings expected)
./run_format.sh # gofmt -w .
# Or directly:
go test ./...
go test ./database -bench=BenchmarkSearchDatabase issues:
./bin/itunes-migrate -from-script # Initialize/refresh
./bin/itunes-migrate -validate # Check statusPlayback issues:
- Use
track_idfrom search results (most reliable) - Ensure the Apple Music app is running
- Grant Automation permissions: System Settings → Privacy & Security → Automation
EQ/Audio issues:
- EQ status is unavailable while using AirPlay (macOS limitation)
- Specific AirPlay device selection must be done manually in the Music app
Radio station playback:
- Stations must use
itmss://protocol URLs for playback;https://URLs are homepage links only
MIT License — see LICENSE file for details.
Thanks to Apple Music, SQLite FTS5, the MCP Protocol, and modernc.org/sqlite.