pixiv-mcp
# pixiv-mcp
独立的 Pixiv 插画搜索 / 过滤 / 下载工具,提供三种用法:
- **MCP server(stdio)**:给 Codex CLI、Claude Desktop / Claude Code、Cursor、Grok Bot 等任意支持 MCP 的 agent 用
- **CLI**:`pixiv-mcp search 信浓 泳装 2`,方便测试,也给不支持 MCP 的 agent 用
- **TypeScript 库**:`import { PixivService, pixivSearch } from 'pixiv-mcp'`
它是从 QQ bot 插件(`dsh-qqbot` 的 `src/pixiv`)里拆出来的,**过滤规则一条没改**,不依赖 QQ bot、cordis/cosmokit、antigravity、会话或传输层。规则细节见 [`pixiv-feature-spec.md`](./pixiv-feature-spec.md)。
运行时依赖只有 `@modelcontextprotocol/sdk` 和 `zod`。网络请求用 Node 自带的 fetch,不处理图片、不用浏览器。
## 安装
需要 Node.js ≥ 20.3(推荐 22 或 24)。
发布包里已经带了编译好的 `dist/`,服务器上**不需要** TypeScript 或 vitest:
```bash
tar xzf pixiv-mcp.tgz && cd pixiv-mcp
npm ci --omit=dev # 只装运行时依赖(约 26 MB)
node dist/bin.js --help
```
开发 / 改代码:
```bash
npm ci
npm run build # tsc → dist/,bin 入口为 dist/bin.js
npm test # 会先 build,再跑 vitest(含 stdio 端到端测试)
npm run typecheck
```
想要全局命令 `pixiv-mcp` 时,在目录里执行 `npm link` 或 `npm i -g .`。
### 低内存启动(2 vCPU / 1 GB 机器)
```bash
node --max-old-space-size=96 --max-semi-space-size=1 dist/bin.js serve
```
实测(Linux,Node 24,接本地模拟的 Pixiv):
| 启动参数 | 空闲 RSS | 跑 20 轮搜索+下载后 RSS(峰值) |
|---|---|---|
| `--max-old-space-size=128` | 77 MB | 105 MB(107) |
| `--max-old-space-size=96 --max-semi-space-size=1` | 69 MB | 94 MB(100) |
| 再加 `--lite-mode` | 59 MB | 68 MB(70),但速度慢很多,不推荐 |
光是加载官方 MCP SDK 的 stdio 部分(它会带上 zod 协议 schema)就要约 64 MB RSS,裸 Node 约 41 MB,所以用官方 SDK 时 60 MB 以下基本做不到。原图下载是流式写盘的,不会把整张图读进内存。
## 环境变量
| 变量 | 说明 |
|---|---|
| `PIXIV_REFRESH_TOKEN` | Pixiv refresh token。配了且没设 `PIXIV_PROXY_BASE_URL` 时走 app-api(推荐,R-18 需要登录) |
| `PIXIV_PHPSESSID` / `PIXIV_COOKIE` | 网页登录态,加到网页 AJAX 请求的 Cookie 上 |
| `PIXIV_PROXY_BASE_URL` | `www.pixiv.net` 的反代源站(路径同网页 AJAX)。设了它就始终走网页接口 |
| `PIXIV_MCP_HOME` | 数据目录,默认 `~/.pixiv-mcp`(Windows:`C:\Users\<你>\.pixiv-mcp`),存历史、元数据、动态标签表 |
| `PIXIV_MEDIA_DIR` | 图片目录,默认 `$PIXIV_MCP_HOME/media` |
| `PIXIV_COOLDOWN_MS` | 每个 session 的冷却,默认 `15000`;设 `0` 关闭 |
| `PIXIV_DOWNLOAD_CONCURRENCY` | 全局同时下载数,默认 `2` |
| `PIXIV_MAX_IMAGE_MB` | 单张原图上限,默认 `50` |
| `PIXIV_CACHE_MAX_MB` | 本地图片缓存上限,超出按最久未用淘汰,默认 `512` |
| `PIXIV_TIMEOUT_MS` | 单次请求超时,默认 `15000`(下载为它的 2 倍) |
| `PIXIV_LLM_TAGS=1` | 可选:把没映射的中文词交给 OpenAI 兼容模型翻成 Pixiv 标签 |
| `PIXIV_LLM_BASE_URL` / `PIXIV_LLM_API_KEY` / `PIXIV_LLM_MODEL` | 上一项用到的接口,如 `https://api.openai.com/v1`、`sk-...`、`gpt-4o-mini` |
| `NODE_USE_ENV_PROXY=1` + `HTTPS_PROXY` | 需要走 HTTP 代理时用(Node ≥ 24 的原生 fetch 支持),图片下载同样走代理 |
**LLM 不是必须的。** 推荐让调用方 agent 自己把中文描述翻成 Pixiv 常用日文标签再传进来(比如 水手服→セーラー服)。内置的中文角色映射表照样生效,没映射的词按原样当标签用。
### 怎么拿 refresh token
Pixiv 没有官方开发者入口,refresh token 只能从官方 App 的 OAuth 登录流程里拿:
1. **gppt**(最省事,会开一个浏览器让你登录):`pip install gppt`,然后运行 `gppt login-interactive`(或 `gppt login -u 邮箱 -p 密码`),输出里的 `refresh_token` 就是。
2. **pixiv_auth.py**(ZipFile 的 gist,“Retrieving Auth Token (with Selenium)”那篇):运行 `python pixiv_auth.py login`,在浏览器里登录,再按说明从开发者工具的网络面板里复制 `code`,脚本会换出 `refresh_token`。
拿到之后填进 `PIXIV_REFRESH_TOKEN`。它长期有效,本工具会自动换 access token 并缓存。token 失效时自动退回网页搜索,日志写到 stderr。
## MCP 工具
| 工具 | 作用 |
|---|---|
| `pixiv_search` | 搜索并下载。参数:`tags`(数组或空格分隔字符串,多个标签是 AND)、`count`(1–3)、`r18`、`allow_gray`(`oral` / `creampie` / `paizuri` / `footjob` / `ahegao` / `sex` / `r18`)、`session`(去重和冷却按它分开) |
| `pixiv_illust_detail` | 按 PID 读元数据,并用同一套规则给出 `compliant` 结论,不下载 |
| `pixiv_download` | 按 PID 下载原图(`pages`:`[0,1]` 或 `"all"`,最多 10 页)。不合规的作品拒绝下载 |
返回 JSON 里的字段:`localPath` / `files[].path` 是本地绝对路径,`caption`(`🎨 画师:xxx`)给 agent 展示用,`url` 是作品页地址。
过滤规则和 bot 版完全一致:
- 多标签 AND
- 互斥组蹭标签、AI(`aiType=2`)、动图、黑名单直接丢;请求里出现黑名单词就整次拒绝,而且不扣冷却。短英文关键词(如 `bl`)以及 `腐`、`グロ` 只按整词匹配
- 灰名单默认避开;显式放行的类别作品必须真有
- R-18 只认官方 `xRestrict`
- 1 年半衰期排序,同一 session 不重复出图,看完一轮自动重新循环
- 搜索按 `10000users入り` → … → 纯标签逐档降级
- 429 / 5xx 指数退避重试
## 各客户端配置
下面示例用相对路径 `./dist/bin.js`(先 `cd` 到项目根目录再启动)。若写成绝对路径,Windows / Linux / macOS 各自换成你的实际路径即可。
### Codex CLI:`~/.codex/config.toml`(Windows:`C:\Users\<你>\.codex\config.toml`)
```toml
[mcp_servers.pixiv]
command = "node"
args = ['--max-old-space-size=96', './dist/bin.js', 'serve']
startup_timeout_sec = 20
tool_timeout_sec = 120
[mcp_servers.pixiv.env]
PIXIV_REFRESH_TOKEN = "你的token"
PIXIV_MCP_HOME = '~/.pixiv-mcp'
```
TOML 里用单引号字符串,Windows 反斜杠就不用转义。
### Claude Desktop:`%APPDATA%\Claude\claude_desktop_config.json`
```json
{
"mcpServers": {
"pixiv": {
"command": "node",
"args": ["--max-old-space-size=96", "./dist/bin.js", "serve"],
"env": {
"PIXIV_REFRESH_TOKEN": "你的token",
"PIXIV_MCP_HOME": "~/.pixiv-mcp"
}
}
}
}
```
### Claude Code
```bash
claude mcp add pixiv -s user -e PIXIV_REFRESH_TOKEN=你的token -e "PIXIV_MCP_HOME=~/.pixiv-mcp" -- node --max-old-space-size=96 "./dist/bin.js" serve
```
### Cursor:`~/.cursor/mcp.json`(全局)或项目里的 `.cursor/mcp.json`
内容和 Claude Desktop 那段 `mcpServers` 一样。
### Grok Bot(本地 stdio MCP)
在 Grok Bot 添加本地 MCP 时填:
- 命令:`node`
- 参数:`--max-old-space-size=96`、`./dist/bin.js`、`serve`
- 环境变量:`PIXIV_REFRESH_TOKEN`,以及可选的 `PIXIV_MCP_HOME`
如果要求填 JSON,就用和 Claude Desktop 一样的 `mcpServers.pixiv` 那段。
### 不支持 MCP 的 agent:直接调 CLI
```bash
node dist/bin.js search 信浓 泳装 2 --json
node dist/bin.js search --tags "オリジナル 水着" --gray oral --session alice
node dist/bin.js detail 12345678 --json
node dist/bin.js download 12345678 --pages all --json
```
退出码:成功为 `0`;失败、被拦截或冷却中为 `1`;用法错误为 `2`。
## 和 bot 版的差别
- 数据目录从 `~/.dsh-qqbot` 改为 `~/.pixiv-mcp`(可用 `PIXIV_MCP_HOME` 改)
- LLM 补标签从“拉起本机 agy”改为可选的 OpenAI 兼容接口(默认关闭)
- 新增 `pixiv_illust_detail` / `pixiv_download`(按 PID),规则复用同一套过滤
- 网络失败、连续失败熔断时,结果里带 `notice` 说明原因,不再静默返回空
- 下载改为流式写盘,加了全局并发上限和单图大小上限;本地缓存上限默认从 2 GB 改为 512 MB
- 不负责发送图片:工具返回本地路径和 caption,由 agent 决定怎么展示。多图间隔 1.5 秒属于 QQ 发图的限制,这里不需要
TDQS
Scored across 3 tools
pixiv_detail (metadata only) and pixiv_download (fetch files by PID) are clearly distinct, but pixiv_search already downloads originals and returns metadata, so an agent may not know whether a subsequent pixiv_download call is needed. The descriptions partly resolve this (search = discovery + fetch, download = explicit PID fetch), but the boundary between search and download remains blurred.
All names use a consistent pixiv_ prefix with snake_case and a verb/action orientation (search, download) plus one descriptive noun form (illust_detail). Minor deviation in structure but predictable and readable.
Three tools is on the thin side but each maps to a real step of the workflow: discover, inspect, fetch. No redundant or filler tools, so the small surface is justified for the server's scope.
Search, metadata inspection, and download cover the core image-retrieval lifecycle with shared filtering rules applied consistently. Gaps exist around artist/user-based browsing or paging beyond search results, but agents can work around these by re-searching with tags.