https://github.com/lelabdev/spotify-graph
Music taste graph DB — Rust CLI for artist discovery via graph traversal
https://github.com/lelabdev/spotify-graph
mcp mcp-server music-discovery music-graph rust sqlite
Last synced: about 2 months ago
JSON representation
Music taste graph DB — Rust CLI for artist discovery via graph traversal
- Host: GitHub
- URL: https://github.com/lelabdev/spotify-graph
- Owner: lelabdev
- License: mit
- Created: 2026-05-02T15:32:03.000Z (about 2 months ago)
- Default Branch: main
- Last Pushed: 2026-05-02T17:59:07.000Z (about 2 months ago)
- Last Synced: 2026-05-02T19:13:42.353Z (about 2 months ago)
- Topics: mcp, mcp-server, music-discovery, music-graph, rust, sqlite
- Language: Rust
- Size: 26.4 KB
- Stars: 0
- Watchers: 0
- Forks: 0
- Open Issues: 4
-
Metadata Files:
- Readme: README.md
- License: LICENSE
- Agents: AGENTS.md
Awesome Lists containing this project
README
# spotify-graph
Rust-powered musical graph — SQLite-backed, MCP-compatible, with a built-in local dashboard.
## Why
Static playlists don't capture the fluid nature of musical tastes. spotify-graph models artists as a weighted graph where discovery happens through proximity — not platform algorithms.
## Architecture
```
spotify-graph (single binary)
├── mcp → MCP stdio server (current mode)
├── tracker → Poll Last.fm (primary) + Spotify (fallback) → SQLite
└── serve → axum HTTP API + Askama/HTMX dashboard
```
Everything in Rust. No Python, no Node, no separate services.
### Data flow
```
Spotify (listen) ──auto-scrobble──→ Last.fm (hub)
│
┌─────────────────┤
▼ ▼
plays history similar artists + tags
│ │
└────────┬────────┘
▼
SQLite (local)
│
┌────────────┼────────────┐
▼ ▼ ▼
MCP tools REST API Dashboard
(stdio) (axum) (Askama+HTMX)
```
## Stack
- **Rust** — axum (HTTP), rmcp (MCP), rusqlite (SQLite), reqwest (API calls)
- **SQLite** — single file, no ORM
- **Askama** — compile-time HTML templates
- **HTMX** — server-driven interactivity (no JS framework)
- **Cytoscape.js** — graph visualization (bonus, post-v1)
- **Last.fm API** — primary source for plays, tags, similar artists
- **Spotify API** — fallback for plays + audio features
## Data Model
### Nodes — Artists / Tracks / Labels
Each node has 6 continuous axes (1–10) describing its sonic profile:
| Axis | 1 | 10 |
|------|---|----|
| **Energy** | Silence | Full power |
| **Tempo** | No beat | 140+ BPM |
| **Repetition** | Varied | Pure hypnotic |
| **Organic** | 100% synth | 100% acoustic |
| **Depth** | Surface | Immersive / meditative |
| **Brightness** | Dark | Bright |
Plus CSV tags, free-form notes, and an optional Spotify URI.
### Edges — Relations
Types: `similar_vibe`, `same_scene`, `same_label`, `influenced_by`, `collaborated_with`, `lastfm_similar`
Each edge carries a weight (0–1) representing link strength.
### Plays
Tracks played, sourced from Last.fm (primary) and Spotify (fallback). Artist, track name, timestamp, duration, URI.
### Feedback (post-MVP)
`liked` / `disliked` / `neutral` per context (coding, chill, morning…).
## CLI
```bash
# MCP server (stdio) — for Hermes/any MCP client
spotify-graph mcp
# Track plays — poll Last.fm + Spotify, enrich graph
spotify-graph tracker
# Serve dashboard + API
spotify-graph serve --port 3030
```
Default (no subcommand) starts MCP mode for backward compatibility.
## API Endpoints
### Reads
- `GET /api/stats` — graph statistics
- `GET /api/plays?limit=50&offset=0&artist=` — play history
- `GET /api/artists` — artist list with tags
- `GET /api/artists/:name` — artist detail + relations
- `GET /api/discover/:name?depth=2` — BFS traversal
### Writes
- `POST /api/nodes` — add a node
- `POST /api/edges` — add a relation
- `PATCH /api/artists/:name` — edit tags/axes/note
- `DELETE /api/nodes/:name` — remove a node (cascade)
## MCP Tools
| Tool | Description |
|------|-------------|
| `spotify_graph_add_node` | Add a node with audio attributes |
| `spotify_graph_link` | Link two nodes with a typed relation |
| `spotify_graph_discover` | BFS traversal filtered by minimum weight |
| `spotify_graph_log_play` | Log a single play event |
| `spotify_graph_log_plays_batch` | Log multiple plays at once |
| `spotify_graph_seed` | Bulk JSON import of nodes + edges |
| `spotify_graph_stats` | Graph statistics |
## Configuration
```toml
# ~/.config/spotify-graph/config.toml
[server]
port = 3030
host = "0.0.0.0"
[database]
path = "~/.local/share/spotify-graph/graph.sqlite"
[lastfm]
api_key = "..."
username = "..."
rate_limit_ms = 250
[spotify]
client_id = "..."
auth_path = "~/.hermes/auth.json"
[tracker]
poll_interval_min = 120
max_similar_match = 0.4
```
Defaults are used if the file is missing. Override via `SPOTIFY_GRAPH_*` env vars.
## Build
```bash
cargo build --release
# binary: target/release/spotify-graph
```
## Dashboard Pages (v1)
| Route | Description |
|-------|-------------|
| `/` | Quick stats |
| `/plays` | Play history, filters, pagination |
| `/artists` | Artist list + tags, inline edit |
| `/artists/:name` | Detail: tags, axes, relations |
| `/discover` | BFS discovery — interactive |
| `/graph` | Force-directed visualization (Cytoscape.js, post-v1) |
## Project Structure (target)
```
spotify-graph/
├── src/
│ ├── main.rs # CLI (clap subcommands)
│ ├── config.rs # TOML config loading
│ ├── db/
│ │ ├── mod.rs
│ │ ├── schema.sql
│ │ └── queries.rs # SQLite queries
│ ├── mcp/
│ │ ├── mod.rs
│ │ └── tools.rs # MCP tool handlers
│ ├── tracker/
│ │ ├── mod.rs
│ │ ├── lastfm.rs # Last.fm API client
│ │ └── spotify.rs # Spotify API client
│ └── api/
│ ├── mod.rs
│ ├── routes.rs # axum routes
│ ├── handlers.rs # route handlers
│ ├── error.rs # error types
│ └── templates/ # Askama HTML templates
│ ├── base.html
│ ├── index.html
│ ├── plays.html
│ ├── artists.html
│ ├── detail.html
│ └── discover.html
├── static/ # Cytoscape.js, HTMX, Tailwind
├── Cargo.toml
└── schema.sql
```
## Roadmap
### V0.1 — Foundation ✅
- [x] Schema + SQLite DB
- [x] 5 MCP tools (add_node, link, discover, seed, stats)
- [x] Initial dataset (Ludo's artists)
- [x] Python tracker (Spotify polling)
### V2 — Full Rust + Dashboard 🚧
- [ ] #21 — CLI subcommands (mcp / tracker / serve)
- [ ] #22 — Last.fm tracker (primary) + Spotify fallback (Rust)
- [ ] #23 — REST API (axum)
- [ ] #24 — Dashboard (Askama + HTMX)
- [ ] #25 — Graph visualization (Cytoscape.js)
- [ ] #26 — Remove Python scripts
- [ ] #27 — Config file (TOML)
### Backlog
- [ ] #17 — MCP tool: search
- [ ] #18 — MCP tool: Graphviz export
- [ ] Audio features from Spotify API (energy, tempo, etc.)
- [ ] Feedback system (liked/disliked per context)
## License
[MIT](LICENSE) — [lelabdev](https://github.com/lelabdev)