Skip to main content
Glama
README.md
# 抓取歌曲 MCP

个人本地音乐抓取 MCP server。通过 Claude 自然语言完成"搜歌 -> 下载 -> 配歌词 -> 整理本地库"。

## 架构

三层单向依赖(mcp -> core -> sources):
- **sources**:源适配器,统一 Protocol。网易云(自实现 weapi:搜索/下载/歌词)+ LRCLIB(`httpx`:仅歌词)。下载链路只接网易云一个源;歌词链路双源择优(synced 优于 plain)。
- **core**:编排层。SearchOrchestrator / DownloadManager / LyricsMatcher / LibraryStore(SQLite)。
- **mcp**:`fastmcp` 暴露 5 个 tool。

详见 `docs/superpowers/specs/2026-07-21-music-mcp-design.md`。

## 安装

要求 Python ≥ 3.11。

```bash
git clone <repo-url> music-mcp && cd music-mcp
pip install -e ".[dev]"      # 装主包 + 依赖(含 cryptography)+ 测试
pytest -q                    # 单测全绿即环境就绪
```

装完后 `music-mcp` 命令可用(见 `pyproject.toml` 的 `[project.scripts]`)。网易云源自实现 weapi,无需额外依赖或步骤。

### 网易云源

网易云源自实现 weapi 客户端(双层 AES + 自定义 RSA),依赖 `cryptography`,已随主包 `pip install -e ".[dev]"` 安装,无需额外步骤。

- 游客模式:默认无登录态,音质写死 `exhigh`(约 320k);无损需登录(本期未实现)。
- 下载只能拿到「游客可听」的歌:版权/VIP/付费曲 `get_song_url` 返回 `url=None`,属正常现象。
- 只能「搜索命中」后下载,不能枚举全站曲库;搜不到的曲拿不到。
- 歌词只读 `lrc.lyric`(带时间轴)+ `tlyric.lyric`(翻译);网易云的逐字歌词 `yrc` 未读,若某曲只有 `yrc` 则该源返回空,退回 LRCLIB 兜底。
- 可选注入登录态:设 `MUSIC_MCP_NETEASE_COOKIE`(如 `MUSIC_U=...`),为未来登录扩展预留。

## 配置(环境变量)

| 变量 | 默认 | 说明 |
|------|------|------|
| `MUSIC_MCP_DOWNLOAD_DIR` | `./downloads` | 歌曲下载目录(`download_song` 不传 `target_dir` 时用此值) |
| `MUSIC_MCP_LYRICS_DIR` | (空) | 歌词存储目录;设置后 `get_lyrics` 自动写 `{artist} - {title}.lrc`,无需音频文件存在。留空则歌词只写在与音频同目录(需音频已存在) |
| `MUSIC_MCP_DB_PATH` | `./library.db` | SQLite 索引路径 |
| `MUSIC_MCP_NETEASE_MIN_INTERVAL_MS` | `800` | 网易云请求最小间隔(ms) |
| `MUSIC_MCP_NETEASE_COOKIE` | (空) | 网易云登录 cookie(可选,如 `MUSIC_U=...`) |
| `MUSIC_MCP_NETEASE_TIMEOUT` | `10.0` | 网易云请求超时(秒) |
| `MUSIC_MCP_LOG_LEVEL` | `INFO` | 日志级别 |

> 歌曲与歌词存储路径即通过以上两个变量配置:`MUSIC_MCP_DOWNLOAD_DIR` 决定歌曲落盘位置,`MUSIC_MCP_LYRICS_DIR` 决定歌词落盘位置。两者均为可选,留空时歌曲默认 `./downloads`、歌词不单独落盘(仅在与音频同目录时写)。

## 接入 Claude

配置时把歌曲/歌词存储路径用环境变量设好,之后所有 tool 自动用这两个目录,无需每次传参。

### Claude Desktop

编辑配置文件(Windows: `%APPDATA%\Claude\claude_desktop_config.json`):

```json
{
  "mcpServers": {
    "music-mcp": {
      "command": "music-mcp",
      "env": {
        "MUSIC_MCP_DOWNLOAD_DIR": "D:/music/downloads",
        "MUSIC_MCP_LYRICS_DIR": "D:/music/lyrics",
        "MUSIC_MCP_DB_PATH": "D:/music/library.db"
      }
    }
  }
}
```

### Claude Code

命令行注册(无需手编 JSON):

```bash
claude mcp add music-mcp \
  -e MUSIC_MCP_DOWNLOAD_DIR=D:/music/downloads \
  -e MUSIC_MCP_LYRICS_DIR=D:/music/lyrics \
  -e MUSIC_MCP_DB_PATH=D:/music/library.db \
  -- music-mcp
```

> 若 Claude 报找不到 `music-mcp` 命令,把 `command` 换成 `python -m music_mcp.server`,或指向 venv 里该命令的绝对路径。

## 使用

接入后直接用自然语言驱动,5 个 tool 都支持「`norm_key`(来自搜索结果)」或「`title + artist`」两种入参形态。

```
帮我搜周杰伦的晴天                  # search_songs
下载这首歌                          # download_song(用上一步的 norm_key)
下载 晴天 周杰伦                    # download_song(直接 title+artist)
给我这首歌的歌词                    # get_lyrics
扫描 D:/music 这个目录并补歌词      # scan_library
本地库里有哪些没歌词的              # query_library
```

典型流程:**搜歌 → 下载 → 配歌词 → 扫描整理本地库 → 查询库**。下载默认落 `MUSIC_MCP_DOWNLOAD_DIR`,歌词默认落 `MUSIC_MCP_LYRICS_DIR`(设了的话)。

## 提供的 tool

| Tool | 说明 |
|------|------|
| `search_songs` | 按关键词搜索歌曲,标记是否已入库 |
| `download_song` | 下载歌曲到本地(幂等,传 `norm_key` 或 `title+artist`);`target_dir` 省略时用 `MUSIC_MCP_DOWNLOAD_DIR` |
| `get_lyrics` | 获取歌词(LRC 优先);`audio_dir` 提供时写同名 `.lrc`,否则写 `MUSIC_MCP_LYRICS_DIR`(设了的话) |
| `scan_library` | 扫描本地音频目录,补元数据并触发歌词补全 |
| `query_library` | 查询本地库,可按关键词/有无歌词过滤 |

## 测试

```bash
pytest -q          # 单元测试(不发真网络请求)
```

### 代码探索(可选,开发者)

仓库可用 [codegraph](https://www.npmjs.com/package/@colbymchenry/codegraph) 查调用链与影响面。未装可 `npm i -g @colbymchenry/codegraph`;索引数据 `.codegraph/` 已 git 忽略(本地产物,clone 后需重建):

```bash
codegraph init           # clone 后首次建索引
codegraph sync           # 改代码后增量同步
codegraph status         # 查看索引是否最新
codegraph impact <符号>   # 改某符号前看影响面
```

## 手动 smoke test

```bash
python scripts/smoke_test.py "晴天" "周杰伦"
```

真实连网易云 + LRCLIB 跑搜索/下载/歌词全流程。不进自动化测试。

## 能力边界

> 以下是当前实现的真实边界,避免对能力过度预期。

**下载**
- 只能下载网易云「游客可听」的曲;版权/VIP 曲 `url=None` 会失败。
- 音质写死 `exhigh`;无损需登录态(未实现)。
- 只接网易云一个下载源(LRCLIB 不提供音频),该源限流/不可用时无备胎。

**歌词**
- 双源择优:网易云(带时间轴 LRC 优先)-> LRCLIB 兜底;多数热门曲能拿到带时间轴 LRC。
- **不制作时间轴**:只转发源里现成的 LRC。若两源都只有纯文本,落盘即纯文本歌词,系统不做音频对齐/强制时间戳。
- 网易云逐字歌词 `yrc` 未读,仅读 `lrc`(时间轴)+ `tlyric`(翻译)。
- 纯器乐 / 极冷门 / 刚发行的曲可能两边都无歌词,返回查不到。

## 风险说明

- 网易云抓取违反其 ToS,账号有被封风险,仅供个人学习与本地使用。
- 反爬细节集中在 `netease/` 包内(`weapi.py` 加密 + `client.py` 请求),核心业务层不碰加密参数;接口变更时改动集中可见。
- 不分发版权音乐。

TDQS

B3.2/5.0

Scored across 5 tools

Disambiguation4/5

The tools are mostly distinct: search finds remote songs, download fetches them, get_lyrics retrieves lyrics, scan and query operate on the local library. Search/download/get_lyrics relate to remote content while scan/query relate to local content; these boundaries are fairly clear. The only minor overlap is that download and get_lyrics both accept the same identifying params, but their purposes differ.

Naming Consistency5/5

All five tools follow a consistent verb_noun pattern: search_songs, download_song, get_lyrics, scan_library, query_library. The verbs and nouns are unambiguous and consistently styled in snake_case.

Tool Count4/5

Five tools is a reasonable, compact set for a music management server. It could arguably benefit from a couple more operations (e.g., remove/delete songs), but the current count is well-scoped for the apparent purpose.

Completeness4/5

The surface covers search, download, lyrics retrieval, library scanning, and querying. Minor gaps include no delete/remove tool for downloaded songs or library entries, and no play or status operation, but core workflows are covered and agents can accomplish primary tasks.

Maintenance

ActivitySlowing
ResponsivenessNo issues