重构为Monorepo:拆分xhs/xhh应用与core包并完成双服务部署改造
This commit is contained in:
@@ -1,286 +1,127 @@
|
||||
# Social MCP
|
||||
# Social Auto Hub (Monorepo)
|
||||
|
||||
[中文文档](./README.zh-CN.md)
|
||||
|
||||
Multi-platform social media automation service that exposes browser-based actions as both MCP (Model Context Protocol) tools and a REST API. Current platform support: **Xiaohongshu** (Little Red Book).
|
||||
This repository is now a **workspace monorepo** with clear visual separation:
|
||||
|
||||
## Features
|
||||
- `apps/xhs-mcp`: Xiaohongshu MCP service (`xhs_*` tools)
|
||||
- `apps/xhh-mcp`: Xiaoheihe MCP service (`xhh_*` tools)
|
||||
- `packages/core`: shared infrastructure (`browser/config/cookie/server/utils`)
|
||||
|
||||
- **22 MCP tools** for Xiaohongshu (login, browsing, publishing, interactions, notifications, automation)
|
||||
- **REST API** with Bearer token authentication and per-route rate limiting
|
||||
- **Browser automation** via `rebrowser-playwright` with per-platform serial queueing
|
||||
- **Cookie persistence** with file-based storage (`0600`, atomic writes)
|
||||
- **Notification task state** in SQLite (`$COOKIE_DIR/xiaohongshu/automation.db`) for idempotent auto-reply workflows
|
||||
- **Web dashboard** (React + Vite) for login, feed exploration, publishing, and API testing
|
||||
- **Security controls**: timing-safe token comparison, bind-address safety gate, error sanitization, log redaction
|
||||
- **Docker deployment** support with hardened runtime defaults
|
||||
- **Plugin architecture** for adding additional social platforms
|
||||
## Workspace Layout
|
||||
|
||||
```text
|
||||
apps/
|
||||
xhs-mcp/
|
||||
xhh-mcp/
|
||||
packages/
|
||||
core/
|
||||
```
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Prerequisites
|
||||
|
||||
- Node.js >= 22.0.0
|
||||
- Node.js >= 22
|
||||
- pnpm
|
||||
|
||||
### Install and Run (Backend)
|
||||
### Install
|
||||
|
||||
```bash
|
||||
# Install dependencies
|
||||
pnpm install
|
||||
npx rebrowser-playwright install chromium
|
||||
```
|
||||
|
||||
# Install Playwright browser (first time only)
|
||||
npx playwright install chromium
|
||||
### Build all packages
|
||||
|
||||
# Build backend
|
||||
```bash
|
||||
pnpm build
|
||||
|
||||
# Start server
|
||||
pnpm start
|
||||
```
|
||||
|
||||
The server listens on `http://127.0.0.1:9527` by default in local mode.
|
||||
A REST API Bearer token is printed on startup and persisted at `~/.social-mcp/.api-token`.
|
||||
|
||||
### Build With Web Dashboard
|
||||
### Run XHS service
|
||||
|
||||
```bash
|
||||
# Build backend + web dashboard bundle
|
||||
pnpm build:all
|
||||
|
||||
# Start server (serves dashboard from /)
|
||||
pnpm start
|
||||
pnpm start:xhs
|
||||
```
|
||||
|
||||
### Development
|
||||
Default runtime values:
|
||||
|
||||
- `PORT=9527`
|
||||
- `COOKIE_DIR=~/.social-mcp-xhs`
|
||||
|
||||
### Run XHH service
|
||||
|
||||
```bash
|
||||
# Backend watch build
|
||||
pnpm dev
|
||||
|
||||
# Frontend dev server
|
||||
pnpm dev:web
|
||||
|
||||
# Type check
|
||||
pnpm lint
|
||||
|
||||
# Tests
|
||||
pnpm test
|
||||
pnpm start:xhh
|
||||
```
|
||||
|
||||
## MCP Integration
|
||||
Default runtime values:
|
||||
|
||||
### Claude Desktop
|
||||
- `PORT=9528`
|
||||
- `COOKIE_DIR=~/.social-mcp-xhh`
|
||||
|
||||
Add this in `claude_desktop_config.json`:
|
||||
## Compatibility Root Scripts
|
||||
|
||||
Root scripts are kept as forwarding wrappers:
|
||||
|
||||
- `pnpm build`
|
||||
- `pnpm lint`
|
||||
- `pnpm test`
|
||||
- `pnpm start:xhs`
|
||||
- `pnpm start:xhh`
|
||||
- `pnpm dev:xhs`
|
||||
- `pnpm dev:xhh`
|
||||
|
||||
You can also run package-level commands directly:
|
||||
|
||||
```bash
|
||||
pnpm --filter @social/core build
|
||||
pnpm --filter @social/xhs-mcp start
|
||||
pnpm --filter @social/xhh-mcp start
|
||||
```
|
||||
|
||||
## MCP Endpoints
|
||||
|
||||
- XHS MCP: `http://127.0.0.1:9527/mcp`
|
||||
- XHH MCP: `http://127.0.0.1:9528/mcp`
|
||||
|
||||
## REST Endpoints
|
||||
|
||||
- XHS REST: `http://127.0.0.1:9527/api/xhs/*`
|
||||
- XHH REST: `http://127.0.0.1:9528/api/xhh/*`
|
||||
|
||||
Each service uses its own bearer token file under its `COOKIE_DIR`.
|
||||
|
||||
## Claude Desktop Example
|
||||
|
||||
```json
|
||||
{
|
||||
"mcpServers": {
|
||||
"social-mcp": {
|
||||
"url": "http://127.0.0.1:9527/sse"
|
||||
"mcp-xhs": {
|
||||
"url": "http://127.0.0.1:9527/mcp"
|
||||
},
|
||||
"mcp-xhh": {
|
||||
"url": "http://127.0.0.1:9528/mcp"
|
||||
}
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Available MCP Tools (Xiaohongshu)
|
||||
## Docker
|
||||
|
||||
| Tool | Description |
|
||||
|------|-------------|
|
||||
| `xhs_check_login` | Check Xiaohongshu login status |
|
||||
| `xhs_get_login_qrcode` | Get login QR code for phone scanning |
|
||||
| `xhs_delete_cookies` | Delete cookies and reset login session |
|
||||
| `xhs_list_feeds` | Get explore page recommended feed list |
|
||||
| `xhs_search` | Search notes by keyword with filters |
|
||||
| `xhs_get_feed_detail` | Get note detail (supports passing note URL directly) |
|
||||
| `xhs_get_sub_comments` | Load sub-comments for a parent comment (keyset cursor pagination) |
|
||||
| `xhs_get_user_profile` | Get user profile with recent notes (supports passing profile URL directly) |
|
||||
| `xhs_list_my_notes` | List current account's published notes |
|
||||
| `xhs_publish_image` | Publish an image note |
|
||||
| `xhs_publish_video` | Publish a video note |
|
||||
| `xhs_post_comment` | Post a comment on a note |
|
||||
| `xhs_reply_comment` | Reply to a comment |
|
||||
| `xhs_set_like_state` | Set like state on a note (idempotent) |
|
||||
| `xhs_set_favorite_state` | Set favorite state on a note (idempotent) |
|
||||
| `xhs_get_unprocessed_notifications` | Get unprocessed notification tasks from local SQLite state |
|
||||
| `xhs_mark_notification_task` | Manually mark notification task status (new/pending/ignored/replied/failed) |
|
||||
| `xhs_mark_notification_tasks` | Batch mark notification task statuses |
|
||||
| `xhs_list_failed_notification_tasks` | List failed notification tasks for triage/retry |
|
||||
| `xhs_retry_notification_task` | Retry a failed notification task by fingerprint |
|
||||
| `xhs_retry_notification_tasks` | Batch retry failed notification tasks |
|
||||
| `xhs_reply_notification` | Reply to a specific notification |
|
||||
Single Dockerfile supports dual app targets via build arg:
|
||||
|
||||
## REST API
|
||||
- `APP_NAME=xhs-mcp`
|
||||
- `APP_NAME=xhh-mcp`
|
||||
|
||||
All `/api/*` endpoints require:
|
||||
Compose files already define both services:
|
||||
|
||||
- `Authorization: Bearer <token>`
|
||||
- `Content-Type: application/json` (for POST bodies)
|
||||
- `docker-compose.yml`
|
||||
- `deploy/docker-compose.yml`
|
||||
|
||||
Example:
|
||||
## Migration Notes
|
||||
|
||||
```bash
|
||||
# Login status
|
||||
curl -H "Authorization: Bearer <token>" \
|
||||
http://127.0.0.1:9527/api/xhs/login/status
|
||||
|
||||
# Search notes
|
||||
curl -X POST \
|
||||
-H "Authorization: Bearer <token>" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{"keyword":"travel","filters":{"sort":"popularity_descending"}}' \
|
||||
http://127.0.0.1:9527/api/xhs/search
|
||||
```
|
||||
|
||||
### Endpoint List
|
||||
|
||||
Read endpoints are limited to **60/min** per IP. Write endpoints are limited to **10/min** per IP.
|
||||
|
||||
| Method | Path | Description | Limit |
|
||||
|--------|------|-------------|-------|
|
||||
| `GET` | `/api/xhs/login/status` | Check login status | 60/min |
|
||||
| `GET` | `/api/xhs/login/qrcode` | Get login QR code | 60/min |
|
||||
| `DELETE` | `/api/xhs/login/cookies` | Delete cookies | 10/min |
|
||||
| `GET` | `/api/xhs/login/cookie-check` | Check whether cookie file exists | 60/min |
|
||||
| `GET` | `/api/xhs/feeds` | Get recommended feeds | 60/min |
|
||||
| `POST` | `/api/xhs/search` | Search notes | 60/min |
|
||||
| `POST` | `/api/xhs/feeds/detail` | Get note detail | 60/min |
|
||||
| `POST` | `/api/xhs/feeds/sub-comments` | Load sub-comments for parent comment | 60/min |
|
||||
| `POST` | `/api/xhs/user/profile` | Get user profile | 60/min |
|
||||
| `GET` | `/api/xhs/my-notes` | List my published notes | 60/min |
|
||||
| `POST` | `/api/xhs/publish/image` | Publish image note | 10/min |
|
||||
| `POST` | `/api/xhs/publish/video` | Publish video note | 10/min |
|
||||
| `POST` | `/api/xhs/comment` | Post a comment | 10/min |
|
||||
| `POST` | `/api/xhs/comment/reply` | Reply to a comment | 10/min |
|
||||
| `POST` | `/api/xhs/like` | Toggle like | 10/min |
|
||||
| `POST` | `/api/xhs/favorite` | Toggle favorite | 10/min |
|
||||
| `GET` | `/api/xhs/notifications/comments` | Get comment notifications | 60/min |
|
||||
| `GET` | `/api/xhs/notifications/unprocessed` | Get unprocessed notification tasks from SQLite | 60/min |
|
||||
| `POST` | `/api/xhs/notifications/reply` | Reply to notification | 10/min |
|
||||
|
||||
### Response Format
|
||||
|
||||
```json
|
||||
{
|
||||
"success": true,
|
||||
"data": {}
|
||||
}
|
||||
```
|
||||
|
||||
```json
|
||||
{
|
||||
"success": false,
|
||||
"error": {
|
||||
"code": "VALIDATION_ERROR",
|
||||
"message": "keyword: Required"
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### Public Endpoints (No Bearer Token)
|
||||
|
||||
| Method | Path | Description |
|
||||
|--------|------|-------------|
|
||||
| `GET` | `/health` | Health check (uptime/memory/plugin status) |
|
||||
| `GET` | `/sse` | MCP SSE transport |
|
||||
| `POST` | `/messages` | MCP JSON-RPC message endpoint |
|
||||
|
||||
## Docker Deployment
|
||||
|
||||
### Using Compose (Recommended)
|
||||
|
||||
```bash
|
||||
cd deploy
|
||||
docker compose up -d
|
||||
|
||||
# View logs
|
||||
docker compose logs -f
|
||||
|
||||
# Find Bearer token in logs
|
||||
docker compose logs social-mcp | grep "Bearer Token"
|
||||
```
|
||||
|
||||
### Docker Run
|
||||
|
||||
```bash
|
||||
docker build -t social-mcp .
|
||||
|
||||
docker run -d \
|
||||
--name social-mcp \
|
||||
-p 127.0.0.1:3000:3000 \
|
||||
--shm-size=1gb \
|
||||
--memory=2g \
|
||||
--cpus=2.0 \
|
||||
--security-opt=no-new-privileges:true \
|
||||
--cap-drop=ALL \
|
||||
--read-only \
|
||||
--tmpfs /tmp:size=512m \
|
||||
-v social-mcp-data:/home/appuser/.social-mcp \
|
||||
social-mcp
|
||||
```
|
||||
|
||||
Note: Docker defaults expose port `3000` because container env sets `PORT=3000`.
|
||||
|
||||
## Environment Variables
|
||||
|
||||
| Variable | Default (local) | Description |
|
||||
|----------|------------------|-------------|
|
||||
| `PORT` | `9527` | HTTP server port |
|
||||
| `HOST` | `127.0.0.1` | Bind address (`0.0.0.0` requires `ALLOW_REMOTE`) |
|
||||
| `HEADLESS` | `true` | Run browser in headless mode |
|
||||
| `BROWSER_BIN` | (auto) | Custom Chromium executable path |
|
||||
| `LOG_LEVEL` | `info` | Pino log level (`debug`, `info`, `warn`, `error`) |
|
||||
| `NODE_ENV` | `development` | Runtime environment |
|
||||
| `COOKIE_DIR` | `~/.social-mcp` | Cookie/token storage directory |
|
||||
| `MAX_QUEUE_DEPTH` | `10` | Max pending operations per platform queue |
|
||||
| `ALLOW_REMOTE` | (unset) | Must be `yes-i-understand-the-risk` to allow public bind |
|
||||
| `XHS_NOTIFICATION_POLL_ENABLED` | `true` | Enable periodic notification sync into SQLite |
|
||||
| `XHS_NOTIFICATION_POLL_INTERVAL_SEC` | `60` | Notification sync interval (seconds, min 15) |
|
||||
| `XHS_NOTIFICATION_POLL_MAX_COUNT` | `20` | Max notifications fetched per sync |
|
||||
|
||||
## Project Structure
|
||||
|
||||
```text
|
||||
social-mcp/
|
||||
├── src/
|
||||
│ ├── index.ts
|
||||
│ ├── server/
|
||||
│ │ ├── app.ts
|
||||
│ │ └── middleware.ts
|
||||
│ ├── browser/
|
||||
│ │ └── manager.ts
|
||||
│ ├── cookie/
|
||||
│ │ └── store.ts
|
||||
│ ├── config/
|
||||
│ │ └── index.ts
|
||||
│ ├── utils/
|
||||
│ │ ├── logger.ts
|
||||
│ │ ├── errors.ts
|
||||
│ │ └── downloader.ts
|
||||
│ └── platforms/
|
||||
│ └── xiaohongshu/
|
||||
│ ├── index.ts
|
||||
│ ├── routes.ts
|
||||
│ ├── schemas.ts
|
||||
│ ├── selectors.ts
|
||||
│ ├── login.ts
|
||||
│ ├── feeds.ts
|
||||
│ ├── search.ts
|
||||
│ ├── feed-detail.ts
|
||||
│ ├── user-profile.ts
|
||||
│ ├── my-notes.ts
|
||||
│ ├── publish.ts
|
||||
│ ├── publish-video.ts
|
||||
│ ├── comment.ts
|
||||
│ ├── interaction.ts
|
||||
│ ├── notification.ts
|
||||
│ ├── notification-state.ts
|
||||
│ └── notification-sync.ts
|
||||
└── web/
|
||||
└── src/
|
||||
```
|
||||
|
||||
## License
|
||||
|
||||
ISC
|
||||
- Old root runtime entry (`src/index.ts`) is removed.
|
||||
- Service code now lives inside `apps/*`.
|
||||
- Shared runtime logic now lives inside `packages/core`.
|
||||
- Lockfile strategy is pnpm-only (`pnpm-lock.yaml`).
|
||||
|
||||
Reference in New Issue
Block a user