Skip to main content
Glama
README.md
# xhs-read-mcp

本地单用户、只读的小红书 MCP 服务。项目使用 Python、Playwright 驱动的 Google Chrome(本机)或 Chromium(容器)和官方 MCP Python SDK,通过正常加载小红书网页并读取 `window.__INITIAL_STATE__` 提供结构化数据。

## 功能范围

- 检查网页登录状态。
- 返回二维码并在后台等待扫码,或保留有界面 Chrome 供用户完成验证码。
- 原子保存和恢复 Playwright storage state。
- 清除本机登录状态。
- 按关键词和网页筛选项读取初始搜索结果。
- 使用搜索结果配套的 `note_id` 和 `xsec_token` 读取详情。
- 返回初始评论,或按上限滚动加载更多父评论。
- 默认 stdio,可选 Streamable HTTP。

本项目不发布内容、不点赞、不收藏、不关注、不发表评论,不调用需要逆向签名的私有 API,也不绕过验证码或风控。

## 环境要求

- Python 3.11 或更高版本。
- 已安装 Google Chrome 的本机环境。
- 能正常访问小红书网页的网络。

使用 Docker Compose 部署时,本机不需要安装 Python 或 Google Chrome,只需要 Docker Compose v2。

## 安装

Windows PowerShell 示例:

```powershell
git clone https://github.com/KZI-22/xhs_mcp.git
cd xhs_mcp
python -m venv .venv
.\.venv\Scripts\python.exe -m pip install -e ".[test]"
.\.venv\Scripts\python.exe -m playwright install chrome
```

也可以使用 uv 安装;项目使用标准 `pyproject.toml`,不依赖特定包管理器。

## Docker Compose 一键部署

仓库镜像由 GitHub Actions 自动构建并发布到 `ghcr.io/kzi-22/xhs_mcp`。下载本仓库的 `compose.yaml` 后运行:

Docker 镜像使用 Playwright 自带的 Chromium,以同时支持 `linux/amd64` 和 `linux/arm64`;本机运行仍默认使用 Google Chrome。

> 仓库维护者首次发布镜像后,需要在 GitHub Package 设置中将 `xhs_mcp` 的可见性改为 Public;否则匿名用户无法通过 Compose 拉取镜像。

```powershell
docker compose up -d
docker compose logs xhs-mcp
```

首次启动会自动生成 MCP Bearer Token,并将 Token 和访问地址写入容器日志。默认访问地址:

```text
http://127.0.0.1:8765/mcp
```

调用方需要发送日志中显示的 Token:

```http
Authorization: Bearer <token>
```

二维码登录仍通过 `xhs_start_login` 返回,不需要为容器配置桌面或 VNC。Docker 默认无头运行,仅支持可在 MCP 客户端完成的二维码流程;如果页面要求短信验证码或其他人工验证,请改用本机默认的有界面模式。登录状态和自动生成的 Token 保存在 `xhs-data` Docker Volume 中,容器升级或重启不会丢失。

更新镜像:

```powershell
docker compose pull
docker compose up -d
```

默认 Compose 只把端口发布到宿主机 `127.0.0.1`。如需远程访问,应使用 HTTPS 反向代理,并同步配置允许的 Host 和 Origin;不要把明文 Bearer Token 暴露在公网 HTTP 上。

## 启动

### stdio(默认)

```powershell
xhs-read-mcp
```

或:

```powershell
python -m xhs_read_mcp --transport stdio
```

stdio 模式下 stdout 只用于 MCP 协议,程序日志写入 stderr。

通用 MCP 客户端配置示意:

```json
{
  "command": "D:\\path\\to\\.venv\\Scripts\\python.exe",
  "args": ["-m", "xhs_read_mcp"],
  "cwd": "D:\\path\\to\\xhs-read-mcp"
}
```

### Streamable HTTP

```powershell
xhs-read-mcp --transport streamable-http
```

默认地址:

```text
http://127.0.0.1:8765/mcp
```

服务默认启用 DNS rebinding 防护,只监听本机回环地址。若显式绑定非回环地址,必须同时设置:

```text
XHS_MCP_ALLOW_NON_LOOPBACK=true
XHS_MCP_AUTH_TOKEN=<strong-random-token>
```

此模式只提供静态 Bearer 边界,不等同于远程多用户 OAuth 架构。

## MCP 工具

| 工具 | 作用 |
| --- | --- |
| `xhs_check_login` | 检查已保存登录状态是否有效 |
| `xhs_start_login` | 创建或复用网页登录会话 |
| `xhs_get_login_status` | 根据 `login_id` 查询登录状态 |
| `xhs_cancel_login` | 取消登录会话 |
| `xhs_logout` | 清除本机状态并重置浏览器上下文 |
| `xhs_search_notes` | 搜索首次加载的笔记结果 |
| `xhs_get_note_detail` | 读取详情及可选评论 |

成功结果通过 MCP `structuredContent` 返回。整体失败使用 `isError=true`,并返回稳定错误码;局部评论加载失败等情况进入 warnings。

## 推荐调用流程

```text
xhs_check_login
  -> 未登录:xhs_start_login
  -> 用户扫码;或在已打开的 Chrome 中输入手机号、验证码
  -> xhs_get_login_status
  -> xhs_search_notes
  -> 从同一条结果取 note_id + xsec_token
  -> xhs_get_note_detail
```

搜索和详情默认要求有效登录,不会自动弹出二维码或偷偷退回匿名抓取。

本机默认以有界面模式启动固定的 Google Chrome,并使用独立的
`chrome-storage_state.json`。如果小红书展示短信验证码或其他安全验证,
`xhs_start_login` 会保持该 Chrome 窗口和登录会话;用户直接在窗口中完成验证,
随后通过 `xhs_get_login_status` 查询结果。项目不会代填、绕过或破解验证码。

### 搜索筛选枚举

```text
sort_by:
  relevance | latest | most_liked | most_commented | most_collected

note_type:
  any | video | image

publish_time:
  any | day | week | half_year

search_scope:
  any | viewed | unviewed | following

location:
  any | same_city | nearby
```

第一版只返回网页首次加载结果,不滚动、不分页,也不声称返回全部搜索结果。

### 评论模式

```text
none     只返回笔记详情
initial  返回初始状态已有评论,默认值
load     主动滚动加载更多评论
```

`load` 默认参数:

```json
{
  "max_parent_comments": 100,
  "expand_replies": false,
  "max_reply_count_to_expand": 10,
  "scroll_speed": "normal",
  "timeout_seconds": 300
}
```

结果会说明 `partial` 和 `stop_reason`,不会把“达到上限”误称为“全部评论”。

## 配置

配置优先级:

```text
CLI > 环境变量 > .env > 默认值
```

常用环境变量:

| 变量 | 默认值 |
| --- | --- |
| `XHS_MCP_TRANSPORT` | `stdio` |
| `XHS_MCP_HOST` | `127.0.0.1` |
| `XHS_MCP_PORT` | `8765` |
| `XHS_MCP_PATH` | `/mcp` |
| `XHS_MCP_AUTH_TOKEN` | 空 |
| `XHS_MCP_ALLOW_NON_LOOPBACK` | `false` |
| `XHS_MCP_ALLOWED_HOSTS` | 空,逗号分隔 |
| `XHS_MCP_ALLOWED_ORIGINS` | 空,逗号分隔 |
| `XHS_BROWSER_HEADLESS` | `false`;本机建议保留有界面以完成人工验证 |
| `XHS_BROWSER_CHANNEL` | `chrome`;可设为 `chromium` 使用 Playwright 自带浏览器,Docker 镜像已设置 |
| `XHS_BROWSER_PATH` | 空;仅用于非标准安装位置的 Google Chrome |
| `XHS_PROXY` | 空 |
| `XHS_AUTH_STATE_PATH` | 平台用户数据目录 |
| `XHS_MAX_CONCURRENT_OPERATIONS` | `2` |
| `XHS_LOGIN_TIMEOUT_SECONDS` | `240` |
| `XHS_STATUS_TIMEOUT_SECONDS` | `30` |
| `XHS_SEARCH_TIMEOUT_SECONDS` | `60` |
| `XHS_DETAIL_TIMEOUT_SECONDS` | `60` |
| `XHS_COMMENT_TIMEOUT_SECONDS` | `300` |
| `XHS_TIMEZONE` | `Asia/Shanghai` |
| `XHS_LOG_LEVEL` | `INFO` |

Windows 默认状态文件:

```text
%LOCALAPPDATA%\xhs-read-mcp\chrome-storage_state.json
```

该文件包含敏感登录状态,不应上传、分享或提交到版本控制。`xhs_logout` 只删除本机状态,不声称吊销小红书服务器端 Cookie。

从旧版本升级时,原来的 `storage_state.json` 不会自动注入 Google Chrome;请通过 `xhs_start_login` 重新登录。旧文件不会被自动删除。

## 测试

默认单元测试,不访问小红书,也不启动 Google Chrome:

```powershell
python -m pytest
```

本地 Google Chrome 集成测试,不访问小红书:

```powershell
python -m pytest -m browser
```

真实页面冒烟测试必须显式开启:

```powershell
$env:XHS_RUN_LIVE_TESTS="1"
python -m pytest -m live_xhs
```

真实测试会访问小红书,可能需要扫码,也可能受到网页改版或风控影响。

## 架构

```text
MCP tools
    -> XhsReadService
        -> LoginAction / SearchAction / FeedDetailAction / CommentLoader
            -> BrowserManager / AuthStateStore / PageContract
                -> Playwright Google Chrome / Chromium
                    -> 小红书 DOM 与 window.__INITIAL_STATE__
```

一个 MCP 进程长期运行一个 Chromium 系浏览器和一个共享登录 BrowserContext;每次普通调用使用独立 Page,默认最多两个并发浏览器操作。

## 当前验证状态

- 单元测试覆盖配置、模型、错误、状态存储、浏览器管理、登录状态机、搜索、详情、评论、Service、CLI 和 MCP schema。
- 本地 Google Chrome 生命周期测试已覆盖 Page 回收、状态保存和浏览器重建。
- 真实小红书页面测试必须由使用者显式运行;网页选择器和内部状态路径可能随网站更新而变化。