Content
Tool List
`xhs-mcp` provides a unified command-line entry `xhs-mcp` with built-in subcommands. For Xiaohongshu (xiaohongshu.com) Model Context Protocol (MCP) server and CLI tool, supporting automation capabilities such as login, publishing, search, and recommendations (based on Python + [CloakBrowser](https://github.com/CloakHQ/CloakBrowser) stealth Chromium).
[](https://opensource.org/licenses/MIT)
> This project is a rewrite of the original TypeScript + Puppeteer version, **completely retaining all original functionality**: same CLI subcommands and parameters, same MCP tool names and JSON Schema, same resource URIs, same output JSON structure.
>
> One intentional difference: **login state changed to persistent browser profile, no longer using `cookies.json`** (the original scheme injected cookies into a new incognito context each time, which is a clear automation feature). Old configurations will be automatically migrated, see [Porting Notes](./docs/PORTING_NOTES.md) for details.
## 📦 Installation
- Package name: `xhs-mcp`
- Run CLI (recommended): `uvx xhs-mcp <subcommand>`
- Start MCP: `uvx xhs-mcp mcp [--mode stdio|http] [--port 3000]`
```bash
pip install xhs-mcp
# or
uv tool install xhs-mcp
```
Requires Python >= 3.10.
## ✨ Features
- Authentication: login, logout, status check
- Publishing: image and video publishing
- **Image publishing**: title ≤ 20 characters (40 display units), content ≤ 1000, up to 18 images
- **Video publishing**: supports MP4, MOV, AVI, MKV, WebM, FLV, WMV formats
- ⭐ Supports automatic download of image URLs (HTTP/HTTPS)
- ⭐ Precise title width verification (CJK characters 2 units, ASCII characters 1 unit)
- Supports local image paths
- Supports mixed use of URLs and local paths
- Intelligent caching mechanism to avoid repeated downloads
- Discovery: recommendations, search, details, comments
- User notes: list view, deletion management
- Automation: **CloakBrowser driver (source-level stealth patch Chromium)**, headless mode, **persistent browser profile to maintain login state**
- Verification: publishing function verification script, supports HTML report generation
## 📋 Available Tools
- `xhs_auth_login`, `x_logout`, `_auth_status`
-hs_discover_feeds`, `xhs_search `xhs_get_note_detail`
- `xhs_comment_on_note`
- `xhs_get_user_notes`, `xhs_delete_note` (user note management)
- `xhs_publish_content` (unified publishing interface: `type`, `title`, `content`, `media_paths`, `tags`) — **asynchronous**, returns `taskId`
- `xhs_task_status`, `xhs_task_list` (query background tasks)
- **Image publishing**: 1-18 image files or URLs
- **Video publishing**: exactly 1 video file
- **Mixed use**: supports mixed use of image URLs and local paths
## 🚀 Quick Start (MCP)
### Stdio Mode (default)
```bash
uvx xhs-mcp mcp
# Debug logging
XHS_ENABLE_LOGGING=true uvx xhs-mcp mcp
```
> First run prompt: if CloakBrowser's stealth Chromium is not downloaded, execute
>
> ```bash
> xhs-mcp browser # automatically checks and downloads Chromium (about 200MB), displays executable path
> ```
>
> Output example:
> ```json
> {
> "success": true,
> "message": "Chromium is ready",
> "data": {
> "installed": true,
> "executablePath": "/path/to/chromium"
> }
> }
> ```
Verify MCP connection:
```bash
echo '{"jsonrpc": "2.0", "id": 1, "method": "tools/list"}' | uvx xhs-mcp
```
### HTTP Mode
```bash
# Start HTTP server (default port 3000)
xhs-mcp mcp --mode http
# Specify port
xhs-mcp mcp --mode http --port 8080
# Debug mode
XHS_ENABLE_LOGGING=true xhs-mcp mcp --mode http
```
HTTP server supports:
- **Streamable HTTP** (protocol version 2025-03-26) - endpoint: `/mcp`
- **SSE** (protocol version 2024-11-05) - endpoints: `/sse` and `/messages`
- **Health check** - endpoint: `/health`
For detailed documentation, refer to: [HTTP Transports](./docs/HTTP_TRANSPORTS.md)
## 🧰 CLI Subcommands
```bash
# Authentication
xhs-mcp login --timeout 120
xhs-mcp logout
xhs-mcp status
# Browser dependencies
xhs-mcp browser [--with-deps] # check and download Chromium, display executable path
# Discovery and search
xhs-mcp feeds [-b /path/to/chromium]
xhs-mcp search -k keyword [-b /path/to/chromium]
# Current user notes
xhs-mcp usernote list [-l 20] [--cursor <cursor>] [-b /path/to/chromium]
# Delete user notes
xhs-mcp usernote delete --note-id <id> [-b /path/to/chromium]
xhs-mcp usernote delete --last-published [-b /path/to/chromium]
# Interaction
xhs-mcp comment --feed-id <id> --xsec-token <token> -n "Nice!" [-b /path/to/chromium]
# Publishing
# Use local images
xhs-mcp publish --type image --title title --content content -m path1.jpg,path2.png --tags a,b
# ⭐ Use image URLs (automatic download)
xhs-mcp publish --type image --title title --content content -m "https://example.com/img,https://example2.png" --tags a,b
# Mixed use of URLs and local paths
xhs-mcp publish --type image --title title --content content -m "https://example.com/img1.jpg,./local/img2.jpg" --tags a,b
# Publish video
xhs-mcp publish --type video --title video title --content video description -m path/to/video.mp4 --tags a,b
# View available tools
xhs-mcp tools [--detailed] [--json]
# Start MCP
xhs-mcp mcp [--mode stdio|http] [--port 3000]
```
## 🔧 Client Access (Cursor)
### Stdio Mode
`.cursor/mcp.json`:
```json
{
"mcpServers": {
"xhs-mcp": {
"command": "uvx",
"args": ["xhs-mcp", "mcp"],
"env": { "XHS_ENABLE_LOGGING": "true" }
}
}
}
```
### HTTP Mode
`.cursor/mcp.json`:
```json
{
"mcpServers": {
"xhs-mcp-http": {
"command": "uvx",
"args": ["xhs-mcp", "mcp", "--mode", "http", "--port", "3000"],
"env": { "XHS_ENABLE_LOGGING": "true" }
}
}
}
```
Or use an HTTP client to connect directly:
```json
{
"mcpServers": {
"xhs-mcp-http": {
"url": "http://localhost:3000/mcp"
}
}
}
```
## ⚙️ Environment Variables
| Variable | Default value | Description |
| --- | --- | --- |
| `XHS_ENABLE_LOGGING` | `false` | Set to `true` to output debug logs to stderr |
| `XHS_HEADLESS` | `true` | Default headless mode |
| `XHS_BROWSER_TIMEOUT` | `30000` | Default browser timeout (milliseconds) |
| `XHS_LOGIN_TIMEOUT` | `300` | Login timeout (seconds) |
| `XHS_SERVER_NAME` | `xhs-mcp` | MCP server name |
| `XHS_HOST`127.0.0.1` | Default host |
| `XHS `8000` | Default port |
| `XHS_LOG_LEVEL` | `INFO` | Log level |
| `XHS_LOG_FILE` | `false` | Whether to write logs to a file |
| `XHS_BROWSER_ARGS` | empty | Additional Chromium arguments (comma-separated), e.g., `--no-sandbox` |
| `XHS_HUMANIZE` | `true` | Humanize behavior (mouse curves, keystroke rhythm, scrolling). Disabling can significantly speed up |
| `XHS_USER_DATA_DIR` | `~/.xhs-mcp/profile` | Browser profile directory (where login state is stored), see below |
### 🏗 Architecture: Entry layer / Instance management layer / Browser layer
```
Entry layer CLI · MCP stdio · MCP HTTP(SSE)
│ (only sends requests, doesn't touch the browser)
Instance management layer BrowserSessionManager
│ Ensures "one profile = one browser instance"
Browser layer BrowserManager → CloakBrowser + Playwright
```
The entry layer **only holds services**, not any browser objects, and doesn't start the browser itself; it calls the management layer's `shutdown_all()` when needed, rather than going into the service's internal browser. The management layer organizes by profile directory:
- **In-process**: same profile reuses the same instance (reference counting), multiple tabs run in parallel
- **Cross-process**: when the browser starts, Chromium writes the debugging port to the profile directory's `DevToolsActivePort`; later processes read it and **take over the existing instance**, rather than starting another one and failing
- **Ownership**: whoever starts is responsible for shutting down; the taker only disconnects when exiting — so running `xhs-mcp status` in the terminal won't kill the browser of the resident MCP service
- **Startup contention**: when two processes start simultaneously, the one that loses contention will automatically take over the winner's instance
Therefore, regardless of the entry point changes, this layer doesn't need to be modified; the browser layer below can assume it's the only instance for that profile.
> This layer has no switch. "One profile, one instance" is its purpose, and adding a switch to turn it off would only make usable scenarios fail. For truly independent browsers, use a different `XHS_USER_DATA_DIR` — that's the semantically correct approach.
### ⏳ Long tasks: asynchronous queue
With humanization enabled, the body is typed character by character (about 1.25 seconds/character), and publishing a thousand-character note takes over ten minutes — no MCP client will wait that long. Therefore, **publishing and login go through a background queue**:
```jsonc
// 1. Call xhs_publish_content, returns immediately (measured 0.01s)
{ "success": true, "status": "queued", "taskId": "24b31f72...", "queuePosition": 0 }
// 2. Poll with xhs_task_status
{ "taskId": "24b31f72...", "kind": "publish_image", "status": "running",
"detail": { "type": "image", "title": "Today's food", "mediaCount": 1 } }
// 3. After completion
{ "status": "succeeded", "durationMs": 812340, "result": { "noteId": "..." } }
```
- The queue **executes serially**: the browser is a shared resource, and Xiaohongshu has for consecutive posts within a short time, so running one long task at a time is intentional.
- Read-only operations (`status` / `feeds` / `search` / details) **do not enter the queue**, still running concurrently in their respective tabs.
- The CLI's `xhs-mcp publish` is still blocking — it's more natural to wait in the terminal, and the process exiting the queue.
- Tasks only exist in process memory and are lost after service restart.
### 🔐 Login state: persistent browser profile
The login state is stored in a **real Chromium user directory** (default `~/.xhs-mcp/profile`), with cookies, localStorage, and Indexed managed by the browser itself:
```bash
xhs-mcp login # scan once
xhs-mcp status # reuse without scanning again
```
**Why not use a cookie file**: earlier versions (and the TypeScript original that was ported) stored the login state as `~/.xhs-mcp/cookies.json`, injecting cookies into a new incognito context each time. A real user's browser is unlikely to have a brand-new private window every time — this is a strong automation feature that's easily detectable. **This mode has been completely removed**.
Description:
- **No need to re-login for old users**: if an old `cookies.json` is detected during the first run, it will automatically import the profile and retire the file (no longer writing or applying duplicate configurations).
- `xhs-mcp logout` deletes the entire profile directory. To prevent accidental deletion, **only directories with a `.xhs-mcp-profile` marker file are deleted** (created by this tool); if you point `XHS_USER_DATA_DIR` to a real Chrome profile, logout will refuse to delete and error.
- **Concurrency**: in-process multi-tab parallelism (tested 3 tool calls succeed simultaneously, total time approximately equal to the slowest one, not cumulative); cross-process instances are managed by the management layer to take over the same instance, see above.
- The profile directory is about 10-50 MB.
- `xhs://cookies`'s `cookieCount` reads from the Chromium cookie library on disk; during browser runtime, this value may be low (Chromium buffers in memory and writes periodically), and after the browser exits. This field is for reference only and does not affect any behavior.
## ⚠️ Precautions
- **Image publishing**: title ≤ 20, content ≤ 1000, images ≤ 18
- **Video publishing**: supports multiple formats, file size recommended500MB
- Avoid simultaneous web login from multiple devices
- Reasonably control posting frequency
URLs are automatically downloaded to the `./temp_images/` directory (automatic caching)
- Image URL: JPEG, PNG, GIF, WebP, BMP
- **`-b/--browser / `browser_path` parameter reserved but not CloakBrowser always uses its own stealth Chromium, pointing to a regular Chrome would break its fingerprint patches. See [Porting Notes](./docs/PORTING_NOTES.md) for details.
## 📖 Documentation and Examples
### 📚 Documentation
- [Complete User Guide](./docs/USAGE_GUIDE.md) - detailed usage instructions and best practices
- [HTTP Transport Documentation](./docs/HTTP_TRANSPORTS.md) - HTTP/SSE mode configuration
- [Porting Notes](./docs/PORTING_NOTES.md) - from TypeScript/Puppeteer to Python/CloakBrowser comparison
### 🧪 Testing
- Run all tests: `pytest`
- script**: `python scripts/cli_validation.py` - publishing function verification test, generates HTML report
## 🛠️ Development
```bash
uv venv
uv pip install -e ".[dev]"
pytest
ruff check src tests
```
## 🙏 Acknowledgements
Based on [xhs-mcp](https://github.com/Algovate/xcp) (TypeScript + Puppeteer) rewritten + Cloak original project is [xiaohongshu-mcp](https:///xpzouying/xiaohong).
MCP Config
Below is the configuration for this MCP Server. You can copy it directly to Cursor or other MCP clients.
mcp.json
Connection Info
You Might Also Like
No recommendations available