flacli
PLAYLISTS · MUSICBRAINZ · SOULSEEK · ANY AGENT · A SHELL
No. 2 · MMXXVI

MUSIC ONTO DISK. FROM WHATEVER MODEL YOU HAPPEN TO BE RUNNING.

A command line that takes the songs and albums you name, or a playlist from TIDAL, Deezer, YouTube Music or an export file, works out which tracks you already own, canonicalises the rest against MusicBrainz, and fetches what is missing from Soulseek through the Nicotine+ client already running on your machine. Every command prints one JSON object and returns in seconds; the slow part runs in the background and flacli status says what to do next. Any agent that can run a command can drive it, a seven-billion parameter model included. Nothing is queued until you have seen the count, the size and the users involved and said yes. No telemetry, no hosted service, no account but the ones you already have.

THREE FRONT DOORS, ONE ENGINE
Shell
flacli get "Lorde - Royals"

Any agent with a shell tool. Small local models. Humans. The guide is one command away.

Simple MCP
flacli mcp

Fifteen coarse tools, one workflow step each. For MCP clients with a modest model.

Full MCP
flacli mcp --full

Every fine-grained tool, fifty-five of them. For models that can plan a nine-step pipeline.

HOW IT RUNS
XGET. flacli get "Artist - Title" "Artist - Album (album)". Albums are expanded to their tracklist on MusicBrainz, what you own is skipped, the rest is matched and, at or above 0.85 confidence, queued. Naming the music is the yes.
XSYNC. flacli sync playlist.csv, or a TIDAL, Deezer or YouTube Music link. Resolve, diff, match in a detached worker. Nothing downloaded.
XSTATUS. The job, the counts, the transfers, where the finished files went, and a next field naming the command to run.
XREVIEW. The doubtful matches with the reason each scored as it did. approve and skip by track id.
XQUEUE. Totals first. --yes only after the user has seen them. Then the transfers go to Nicotine+.
XM3U. The playlist in its own order. Tracks still missing are listed, never dropped.
XTIDY. Each finished download is filed as Artist/Album/NN - Title on arrival. flacli tidy plans the rest of the library; --apply after your yes.
XPLAYER. Every tidy ends by telling MPD to rescan just the folders that received files, and every M3U is also stored in MPD under its own name. Flaclify shows both without a rescan.
ANY AGENT │ ├─ shell ──────── flacli get · sync · status │ JSON out, worker in the back │ └─ MCP (stdio) ── flacli mcp | --full │ ▼ MCP BRIDGE PLUGIN inside Nicotine+
FIG. 1 — ONE ENGINE, THREE DOORS
A small model does not fail at finding a folder on Soulseek. It fails at choosing between forty-three tools and remembering to poll. So the tools became commands, the pipeline became a worker, and the next step became a field in the output.
— WHY THIS IS A FORK OF CLAUDE-MUSIC
NO DOWNLOAD
WITHOUT YOUR YES
INSTALL
sudo pacman -S --needed nicotine+ uv      # or your distro's packages
git clone https://github.com/h-3303/flacli ~/src/flacli
cd ~/src/flacli && ./install.sh

flacli doctor                              # is Nicotine+ reachable?
flacli config set music_dir ~/Music
flacli guide                               # hand this to the agent

The script copies the bridge into Nicotine+'s plugin folder and installs flacli with uv tool install. Then tick MCP Bridge in Nicotine+ → Preferences → Plugins. Settings live in ~/.config/flacli/config.toml; environment variables override them, so an MCP client entry can carry its own music folder. Any distro with Nicotine+ 3.3 or newer and uv works.

The commands, in the order you use them.
  • flacli get "A - T" ...named songs and albums; resolved, diffed, matched, queued; the request is the go-ahead
  • flacli sync <file|url|id>import, MusicBrainz, library diff, then a background match; --yes when the user asked for everything confident up front
  • flacli status [id]every playlist, or one: job, counts, transfers; files finished tracks; says what is next
  • flacli review · approve · skipcandidates with confidence 0–1, quality, queue length and a why breakdown
  • flacli queue <id> [--yes]totals without the flag; transfers only with it
  • flacli m3u <id>the file, in order, and the list of what is still missing
  • flacli tidy [--apply]the library put in order; a report and a plan first, writes only with the flag
  • flacli mpd [update · playlist]the player's MPD: is it reachable and serving the same library; a scoped rescan; a playlist stored by name. Done for you after every tidy and M3U
  • flacli avatar missing · fill · seta picture for every artist in the player: one a tagger left behind, the Wikidata portrait with its licence, a MusicBrainz image, or Deezer's; saved beside the music as artist.jpg, where Flaclify finds it
  • flacli cover missing · fill · seta cover for every album: one already in the folder or a track, the Cover Art Archive front, Deezer's, or iTunes'; saved as cover.jpg, embedded in every track that had none, written into the player's cache under the album's folder
  • flacli wiki missing · fill · setthe empty bio and wiki panels in Flaclify / Euphonica: Wikipedia where an article exists, the agent's own prose from MusicBrainz facts for the rest, each attributed, kept as Markdown beside the music, which Flaclify reads before asking anyone online
  • flacli search · download · downloadsthe bridge on its own: raw Soulseek search, a whole folder, the transfer list
  • flacli service · config · doctorTIDAL and YouTube Music logins stored 0600 on your disk; settings; the health check that says what to fix
THE GUIDE, FOR THE AGENT

flacli guide prints about ninety lines: a quick reference, the two workflows, the rules, and how to read the output. It is written for the smallest model that might plausibly follow it. Paste it into a system prompt, an AGENTS.md, a CLAUDE.md, or hand the model a shell and tell it to run the command first. The rules are few: never pass --yes without the user's yes in the same conversation, never delete by hand, do not fight the Soulseek rate limit, prefer FLAC, do not echo tokens.

For MCP clients the repository has the configuration for Claude Code, Codex CLI, Gemini CLI, opencode, Goose, Claude Desktop, and local models through Ollama or llama.cpp. Keep small models on the simple server; the full one has too many near-identical tools for them to pick between.

A SESSION
$ flacli get "Boards of Canada - Geogaddi (album)"
{ "understood": [ { "album": "Geogaddi",
    "release": "2002-02-18", "tracks": 23 } ],
  "already_in_library": 0, "to_fetch": 23,
  "job_id": 4, "note": "fetching 23 track(s)
  in the background; run flacli status 1" }

$ flacli status 1
{ "job": { "status": "finished",
    "queued": 23, "for_review": 0 },
  "downloads": { "done": 23 },
  "tidied": { "moved": 23, "moved_to":
    [ "Boards of Canada/Geogaddi" ] },
  "next": "all done: flacli m3u 1" }

The worker is a detached process with its pid in the job row. A later call can tell a live job from a dead one, and flacli cancel sends it a signal instead of hoping.

THE PLAYER

flacli files music; Flaclify, an MPD client, plays it. They meet through MPD and through files beside the music, nothing else. A finished download is filed and MPD is asked to rescan that one folder, so the album is in the library before the agent has finished its sentence. flacli m3u writes the M3U8 and stores the same playlist in MPD by name, so it is in the player's Playlists view with the tracks that exist and a list of the ones that do not. Bios, wikis and artist pictures are artist.md, wiki.md and artist.jpg next to the files; Flaclify reads them itself, before any online provider, and again whenever one changes, so the text and the pictures no longer depend on anything being written into the player's own database.

MPD is found through $MPD_HOST, then the usual local sockets, then localhost:6600; flacli config set mpd pins a socket or host, or turns it off. Over a local socket flacli reads MPD's music directory and stays quiet when it is not the library it files into. An MPD that is not there is a skipped field in the output, never a failure. The next steps, in the player rather than the shell, are on the roadmap.

AGENT ──▶ flacli ──▶ Nicotine+ ──▶ ~/Music │ files land │ Artist/Album/NN - Title ▼ MPD update "Artist/Album" playlistadd "Road trip" │ ▼ FLACLIFY reads artist.md · wiki.md · artist.jpg beside the music, before anyone online
FIG. 2 — WHERE THE TWO MEET

WHAT IT WILL NOT DO.

It will not queue a transfer before you have seen the totals and agreed, except when you named the music yourself. It will not raise the search rate limit, or suggest it. It will not touch the Spotify API: their terms forbid feeding platform data to a model, so the data export is the route. It will not delete a file the plan did not list, nor write a tag without a backup of the old one. It will not send anything anywhere but MusicBrainz, your own Nicotine+, and a streaming service you have connected yourself. It does not care which model is driving.

— the agent guide, rules section

Tested against Nicotine+ 3.3.10, 3.3.11 and master. 201 tests: a headless Nicotine+ core in-process, fake Soulseek peers, all three MCP servers over stdio, the CLI end to end through a real detached worker, a synthetic library tidied, the service connectors on recorded fixtures. Nothing touches the network.

everything stays on your machine

github.com/h-3303/flacli · llms.txt · a fork of claude-music · GPL-3.0-or-later