{"id":49700310,"url":"https://github.com/lelabdev/spotify-graph","last_synced_at":"2026-05-08T07:10:16.674Z","repository":{"id":355265433,"uuid":"1227368358","full_name":"lelabdev/spotify-graph","owner":"lelabdev","description":"Music taste graph DB — Rust CLI for artist discovery via graph traversal","archived":false,"fork":false,"pushed_at":"2026-05-02T17:59:07.000Z","size":27,"stargazers_count":0,"open_issues_count":4,"forks_count":0,"subscribers_count":0,"default_branch":"main","last_synced_at":"2026-05-02T19:13:42.353Z","etag":null,"topics":["mcp","mcp-server","music-discovery","music-graph","rust","sqlite"],"latest_commit_sha":null,"homepage":null,"language":"Rust","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/lelabdev.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null,"governance":null,"roadmap":null,"authors":null,"dei":null,"publiccode":null,"codemeta":null,"zenodo":null,"notice":null,"maintainers":null,"copyright":null,"agents":"AGENTS.md","dco":null,"cla":null}},"created_at":"2026-05-02T15:32:03.000Z","updated_at":"2026-05-02T17:59:09.000Z","dependencies_parsed_at":null,"dependency_job_id":null,"html_url":"https://github.com/lelabdev/spotify-graph","commit_stats":null,"previous_names":["lelabdev/spotify-graph"],"tags_count":null,"template":false,"template_full_name":null,"purl":"pkg:github/lelabdev/spotify-graph","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Fspotify-graph","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Fspotify-graph/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Fspotify-graph/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Fspotify-graph/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/lelabdev","download_url":"https://codeload.github.com/lelabdev/spotify-graph/tar.gz/refs/heads/main","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/lelabdev%2Fspotify-graph/sbom","scorecard":null,"host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":286080680,"owners_count":32770591,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2026-05-08T02:36:36.067Z","status":"ssl_error","status_checked_at":"2026-05-08T02:36:07.210Z","response_time":54,"last_error":"SSL_read: unexpected eof while reading","robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":false,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":["mcp","mcp-server","music-discovery","music-graph","rust","sqlite"],"created_at":"2026-05-08T07:10:13.226Z","updated_at":"2026-05-08T07:10:16.659Z","avatar_url":"https://github.com/lelabdev.png","language":"Rust","funding_links":[],"categories":[],"sub_categories":[],"readme":"# spotify-graph\n\nRust-powered musical graph — SQLite-backed, MCP-compatible, with a built-in local dashboard.\n\n## Why\n\nStatic 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.\n\n## Architecture\n\n```\nspotify-graph (single binary)\n├── mcp        → MCP stdio server (current mode)\n├── tracker    → Poll Last.fm (primary) + Spotify (fallback) → SQLite\n└── serve      → axum HTTP API + Askama/HTMX dashboard\n```\n\nEverything in Rust. No Python, no Node, no separate services.\n\n### Data flow\n\n```\n  Spotify (listen) ──auto-scrobble──→ Last.fm (hub)\n                                          │\n                        ┌─────────────────┤\n                        ▼                 ▼\n                  plays history    similar artists + tags\n                        │                 │\n                        └────────┬────────┘\n                                 ▼\n                           SQLite (local)\n                                 │\n                    ┌────────────┼────────────┐\n                    ▼            ▼            ▼\n               MCP tools    REST API    Dashboard\n              (stdio)      (axum)     (Askama+HTMX)\n```\n\n## Stack\n\n- **Rust** — axum (HTTP), rmcp (MCP), rusqlite (SQLite), reqwest (API calls)\n- **SQLite** — single file, no ORM\n- **Askama** — compile-time HTML templates\n- **HTMX** — server-driven interactivity (no JS framework)\n- **Cytoscape.js** — graph visualization (bonus, post-v1)\n- **Last.fm API** — primary source for plays, tags, similar artists\n- **Spotify API** — fallback for plays + audio features\n\n## Data Model\n\n### Nodes — Artists / Tracks / Labels\n\nEach node has 6 continuous axes (1–10) describing its sonic profile:\n\n| Axis | 1 | 10 |\n|------|---|----|\n| **Energy** | Silence | Full power |\n| **Tempo** | No beat | 140+ BPM |\n| **Repetition** | Varied | Pure hypnotic |\n| **Organic** | 100% synth | 100% acoustic |\n| **Depth** | Surface | Immersive / meditative |\n| **Brightness** | Dark | Bright |\n\nPlus CSV tags, free-form notes, and an optional Spotify URI.\n\n### Edges — Relations\n\nTypes: `similar_vibe`, `same_scene`, `same_label`, `influenced_by`, `collaborated_with`, `lastfm_similar`\n\nEach edge carries a weight (0–1) representing link strength.\n\n### Plays\n\nTracks played, sourced from Last.fm (primary) and Spotify (fallback). Artist, track name, timestamp, duration, URI.\n\n### Feedback (post-MVP)\n\n`liked` / `disliked` / `neutral` per context (coding, chill, morning…).\n\n## CLI\n\n```bash\n# MCP server (stdio) — for Hermes/any MCP client\nspotify-graph mcp\n\n# Track plays — poll Last.fm + Spotify, enrich graph\nspotify-graph tracker\n\n# Serve dashboard + API\nspotify-graph serve --port 3030\n```\n\nDefault (no subcommand) starts MCP mode for backward compatibility.\n\n## API Endpoints\n\n### Reads\n- `GET /api/stats` — graph statistics\n- `GET /api/plays?limit=50\u0026offset=0\u0026artist=` — play history\n- `GET /api/artists` — artist list with tags\n- `GET /api/artists/:name` — artist detail + relations\n- `GET /api/discover/:name?depth=2` — BFS traversal\n\n### Writes\n- `POST /api/nodes` — add a node\n- `POST /api/edges` — add a relation\n- `PATCH /api/artists/:name` — edit tags/axes/note\n- `DELETE /api/nodes/:name` — remove a node (cascade)\n\n## MCP Tools\n\n| Tool | Description |\n|------|-------------|\n| `spotify_graph_add_node` | Add a node with audio attributes |\n| `spotify_graph_link` | Link two nodes with a typed relation |\n| `spotify_graph_discover` | BFS traversal filtered by minimum weight |\n| `spotify_graph_log_play` | Log a single play event |\n| `spotify_graph_log_plays_batch` | Log multiple plays at once |\n| `spotify_graph_seed` | Bulk JSON import of nodes + edges |\n| `spotify_graph_stats` | Graph statistics |\n\n## Configuration\n\n```toml\n# ~/.config/spotify-graph/config.toml\n\n[server]\nport = 3030\nhost = \"0.0.0.0\"\n\n[database]\npath = \"~/.local/share/spotify-graph/graph.sqlite\"\n\n[lastfm]\napi_key = \"...\"\nusername = \"...\"\nrate_limit_ms = 250\n\n[spotify]\nclient_id = \"...\"\nauth_path = \"~/.hermes/auth.json\"\n\n[tracker]\npoll_interval_min = 120\nmax_similar_match = 0.4\n```\n\nDefaults are used if the file is missing. Override via `SPOTIFY_GRAPH_*` env vars.\n\n## Build\n\n```bash\ncargo build --release\n# binary: target/release/spotify-graph\n```\n\n## Dashboard Pages (v1)\n\n| Route | Description |\n|-------|-------------|\n| `/` | Quick stats |\n| `/plays` | Play history, filters, pagination |\n| `/artists` | Artist list + tags, inline edit |\n| `/artists/:name` | Detail: tags, axes, relations |\n| `/discover` | BFS discovery — interactive |\n| `/graph` | Force-directed visualization (Cytoscape.js, post-v1) |\n\n## Project Structure (target)\n\n```\nspotify-graph/\n├── src/\n│   ├── main.rs           # CLI (clap subcommands)\n│   ├── config.rs         # TOML config loading\n│   ├── db/\n│   │   ├── mod.rs\n│   │   ├── schema.sql\n│   │   └── queries.rs    # SQLite queries\n│   ├── mcp/\n│   │   ├── mod.rs\n│   │   └── tools.rs      # MCP tool handlers\n│   ├── tracker/\n│   │   ├── mod.rs\n│   │   ├── lastfm.rs     # Last.fm API client\n│   │   └── spotify.rs    # Spotify API client\n│   └── api/\n│       ├── mod.rs\n│       ├── routes.rs     # axum routes\n│       ├── handlers.rs   # route handlers\n│       ├── error.rs      # error types\n│       └── templates/    # Askama HTML templates\n│           ├── base.html\n│           ├── index.html\n│           ├── plays.html\n│           ├── artists.html\n│           ├── detail.html\n│           └── discover.html\n├── static/               # Cytoscape.js, HTMX, Tailwind\n├── Cargo.toml\n└── schema.sql\n```\n\n## Roadmap\n\n### V0.1 — Foundation ✅\n- [x] Schema + SQLite DB\n- [x] 5 MCP tools (add_node, link, discover, seed, stats)\n- [x] Initial dataset (Ludo's artists)\n- [x] Python tracker (Spotify polling)\n\n### V2 — Full Rust + Dashboard 🚧\n- [ ] #21 — CLI subcommands (mcp / tracker / serve)\n- [ ] #22 — Last.fm tracker (primary) + Spotify fallback (Rust)\n- [ ] #23 — REST API (axum)\n- [ ] #24 — Dashboard (Askama + HTMX)\n- [ ] #25 — Graph visualization (Cytoscape.js)\n- [ ] #26 — Remove Python scripts\n- [ ] #27 — Config file (TOML)\n\n### Backlog\n- [ ] #17 — MCP tool: search\n- [ ] #18 — MCP tool: Graphviz export\n- [ ] Audio features from Spotify API (energy, tempo, etc.)\n- [ ] Feedback system (liked/disliked per context)\n\n## License\n\n[MIT](LICENSE) — [lelabdev](https://github.com/lelabdev)\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flelabdev%2Fspotify-graph","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Flelabdev%2Fspotify-graph","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Flelabdev%2Fspotify-graph/lists"}