MusicSeed — Dependency & Workflow Architecture

Web, CLI, REST API, and MCP · September 2026 · SQLite only No Docker No ML runtime REST API Next.js web

This document explains every dependency MusicSeed relies on today — Python packages, data stores, network services — and how data flows through the four main workflows (import, enrich, sonic import, recommend) and the user surfaces (CLI, MCP agent tools, and the Next.js web UI via the REST API). It exists to track how small the install surface has become, so publishing MusicSeed as open source means a new user needs as little as possible.

TL;DR install surface: Python 3.12+ and SQLite 3, plus Node for building the web UI during install; seven core packages, two CLI packages, three API packages, two MCP packages, and a Next.js + React web app — one SQLite file (created automatically), and read access to two Plex SQLite databases. Local-network Plex discovery (GDM + SSDP) is stdlib socket only; servers on other subnets are found through the optional plex.tv/api/resources account lookup (httpx, needs a Plex token). uv is development-only tooling — the end-user install is plain python3 -m venv + pip. That's the whole thing — no database server, no containers, no native ML libraries.

1. The big picture

Browser → musicseed-web Next.js · React · TypeScript musicseed-cli Typer commands · Rich output musicseed-mcp MCP tools · stdio / HTTP musicseed-api handlers/ · orchestration JSON + optional static web export musicseed-core (import name: musicseed) services/ · importers/ · enrichers/ · recommender/ · sonic.py · plex_discovery.py deps: sqlalchemy · pyyaml · httpx · numpy · pydantic · rich · paramiko business logic · no Typer or HTTP framework musicseed.db (SQLite) ~/.local/share/musicseed/ · WAL read + write · MusicSeed's own state Plex metadata DB com.plexapp.plugins.library.db READ-ONLY (stdlib sqlite3) Plex blobs DB …library.blobs.db · sonic vectors READ-ONLY · import time Plex HTTP API playlists · sonic Butler trigger optional · token required External APIs ListenBrainz (preferred) MBIDs imported from Plex Spotify (optional fallback) rate-limited · enrichment only JSON / HTTP services only services (direct) services (direct) SQLAlchemy ORM import import vectors (import time) httpx · playlist writes httpx · async
Everything MusicSeed touches. Green borders are read-only SQLite files owned by Plex; the blue-bordered file is the only writable store. Amber is network I/O. Purple is the API orchestration layer — the browser UI reaches it over HTTP (directly, or through the development proxy), while the CLI and the MCP server call core services directly (MCP is an agent surface for playlist create/populate). Removed in the dependency reduction: PostgreSQL server, Docker, pgvector, psycopg, essentia-tensorflow, Jinja/HTMX server rendering, and all audio-file reads.

Python packages (core)

7

sqlalchemy, pyyaml, httpx, numpy, pydantic, rich, paramiko — all pure-Python or widely pre-built wheels.

Python packages (surfaces)

2 + 3 + 2 + Node

cli: typer, rich · api: fastapi, uvicorn, python-multipart · mcp: mcp, anyio · web: next, react, react-dom (Node). Web talks to api over HTTP; api, cli, and mcp depend on core via editable paths.

Extra database processes

0

No database server or container runtime. The product runs a local API/UI server; MCP can run as a subprocess or HTTP server.

2. Dependency ledger (before → after)

Python packages

PackageRoleStatus
sqlalchemyORM over the SQLite state filekept core
httpxHTTP to Plex API + ListenBrainz/Spotifykept core
numpySonic vector math (matmul, cosine) over the local vector storekept core
pydanticConfig + service result modelskept core
pyyamlconfig.yaml loadingkept core
richProgress rendering inside import/enrich pipelineskept core
paramikoVerified SSH execution and streaming of consistent SQLite backupskept core
typerCLI frameworkkept cli
fastapiREST API request handlingkept api
uvicornASGI server for the APIkept api
python-multipartHTML form parsing (setup wizard, credential forms)kept api
next / react / react-domNext.js + React client-rendered web UI (Node)kept web
mcpMCP server framework for the agent surfacekept mcp
anyioWorker-thread offload for synchronous core service callskept mcp
jinja2Server-rendered templatesremoved · React rewrite
htmx (vendored file)Fragment updates without a JS build stepremoved · React rewrite
essentia-tensorflowSelf-generated audio embeddings (MusiCNN)removed · Phase 1
psycopg[binary]PostgreSQL driverremoved · Phase 3
pgvectorVector column type + cosine-distance queryremoved · Phase 2

Infrastructure & system requirements

ComponentFormer roleStatus
PostgreSQL 16 serverMusicSeed state databaseremoved · Phase 3
Docker / docker-composeRan the Postgres containerremoved · Phase 3
pg_trgm extensionTrigram indexes (never queried)removed · Phase 3
TensorFlow runtimePulled in by essentia; amd64-onlyremoved · Phase 1
Audio-file accessReading music files to embed themremoved · Phase 1
Stored embeddingsVector(200) column, zero-padded from 50removed · Phase 2

What remains external (and can't be removed)

DependencyWhy it existsAccess
Plex metadata DBSource of truth for artists/albums/tracks/tags/play historyread-only, stdlib sqlite3
Plex blobs DBPlex's own 50-dim sonic analysis vectorsread-only, import time (copied into musicseed.db)
Plex HTTP APICreating/populating playlists; triggering sonic analysisoptional, token-gated
ListenBrainzPopularity enrichment via recording MBIDsoptional, rate-limited
Spotify Web APIFallback popularity/metadata when MBIDs are missingoptional, credentialed
Why sonic vectors are stored locally: Plex already computes a 50-dimensional vector per track during its own sonic analysis. MusicSeed copies those vectors out of the Plex blobs database into its own track_vectors table (import-plex-sonic / POST /sonic/import), then rebuilds the in-memory L2-normalized matrix (sonic.py → SonicVectors) from that local store when a recommendation runs. The blobs database is read only at import time, so there is no vector index, no ANN index, and no embedding pipeline to maintain.

3. Workflows

3.1 Import — Plex metadata → musicseed.db

Plex metadata DB read-only sqlite3 importers/plex.py row mapping · year fallback services/library.import_library batch commits · resumable musicseed.db metadata · state · vectors
musicseed-cli import — one-way copy of artists, albums, tracks, genres, moods, styles, play history. The Plex file is never written to.

3.2 Enrich — popularity signals (async, resumable)

tracks with MBID musicseed.db queue enrichers/pipeline.py async · bounded concurrency · retries ListenBrainz preferred (MBID-keyed) Spotify optional fallback · credentials popularity columns musicseed.db · batch commits
musicseed-cli enrich --source listenbrainz|spotify — attempted tracks are flagged so interrupted runs resume. This is the only workflow that spends third-party API calls, which is why old Postgres data was migrated rather than re-fetched.

3.3 Sonic import — copy vectors into musicseed.db, read local store at query time

Plex blobs DB read-only · import time import-plex-sonic decode 50-dim · upsert musicseed.db track_vectors table SonicVectors raw + normalized · RAM get() scoring
Vectors are copied into the local track_vectors table at import time, then cached in raw/normalized matrices. Recommendations score every eligible track using local vector lookups. Cache memory scales with vector count; query latency is measured, not assumed instantaneous. See docs/resolvers/retrieval-decision.md for benchmarks and limits. No ANN index or pgvector column is needed.

3.4 Recommend — six signals, explainable

seed tracks IDs or text SeedProfile resolve_seed_tracks score_eligible_tracks year/seed filters · scalar SQL ConstrainedTopK shared score_signals · artist cap playlist max 3 / artist signals: sonic · popularity · style · genre · era · novelty — every score is explainable via --explain
musicseed-cli recommend previews; playlist / populate confirm and apply the exact approved IDs over Plex HTTP, without recomputing. Full retrieval hydrates only seeds and selected tracks. sonic-refresh is a separate, explicitly confirmed remote-analysis mutation.

4. What a brand-new user needs (open-source readiness)

Prerequisites

· Python 3.12+, SQLite 3, Node.js/npm (install only)

· Plex Media Server with a music library, sonically analyzed

· Optional: Spotify API credentials (fallback enrichment only)

Install (from a git clone)

./scripts/install.sh

musicseed then open http://127.0.0.1:8789

The first-run wizard discovers Plex, initializes the database, and walks through import/enrichment. Node is not needed after install.

Still on the road to pip install musicseed

GapNotes
PyPI packagingcore, api, cli, and mcp are separate Python distributions; the Next.js UI is built with npm. Source installs join Python packages through editable paths.
Plex DB discoveryServer discovery is solved (GDM/SSDP; plex.tv/api/resources for other subnets). On-disk db_path probes macOS and Linux candidates; Windows is untested.
Sonic analysis coverageRecommendation quality depends on Plex having analyzed the library; sonic-probe reports coverage, sonic-refresh triggers the Butler task.

5. Verify the current state

# From the repository root, after syncing each app environment:
uv run --project core ruff check core/src
uv run --project api pytest api/tests -q
uv run --project cli pytest cli/tests -q
uv run --project mcp pytest mcp/tests -q
python3 -m compileall -q core/src/musicseed api/src/musicseed_api cli/src/musicseed_cli mcp/src/musicseed_mcp
.venv-docs/bin/mkdocs build --strict
# Read-only checks against a configured local library:
uv run --project cli musicseed-cli status
uv run --project cli musicseed-cli recommend --seed-id 123 --limit 20 --explain