# How to Publish Content with xiaohongshu-mcp: A Complete Technical Guide

> Publish content with xiaohongshu-mcp by validating API requests, processing media, automating browser interaction with go-rod, and submitting posts via image-text or video workflows. Get the complete technical guide.

- Repository: [zy/xiaohongshu-mcp](https://github.com/xpzouying/xiaohongshu-mcp)
- Tags: how-to-guide
- Published: 2026-03-09

---

**Publishing content with xiaohongshu-mcp involves validating API requests, processing media files, automating a headless Chrome browser with go-rod to interact with Xiaohongshu's creator platform, and submitting posts through either image-text or video workflows.**

The **xpzouying/xiaohongshu-mcp** repository provides a Model Context Protocol (MCP) server and HTTP API that automates publishing to Xiaohongshu (Little Red Book) by orchestrating browser sessions. This guide details the exact steps, source file locations, and code implementations used to publish content programmatically.

## API Entry Points and Request Structure

The publishing process begins at the HTTP layer defined in [`main/handlers_api.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/handlers_api.go). The system exposes two distinct endpoints for different content types.

### Image-Text Publishing Endpoint

The `POST /api/v1/publish` endpoint handles image-text notes. The **publishHandler** function (lines 83-101) unmarshals the JSON body into a `PublishRequest` struct containing title, content, image URLs or paths, tags, and optional scheduling parameters.

### Video Publishing Endpoint

The `POST /api/v1/publish_video` endpoint handles short videos. The **publishVideoHandler** function (lines 103-121) processes `PublishVideoRequest` structs, which replace the image array with a single video file path and include the same metadata fields as image posts.

## Service Layer Validation and Preparation

Before browser automation begins, [`main/service.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/service.go) performs critical validation and resource preparation.

### Title Length Validation

The service enforces Xiaohongshu's 20-rune title limit using **xhsutil.CalcTitleLength** from [`pkg/xhsutil/title.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/pkg/xhsutil/title.go). This function counts Unicode runes rather than bytes to properly handle Chinese characters.

### Image Processing and Downloading

The **processImages** function handles both local file paths and remote URLs. For remote resources, it invokes the **downloader.ImageProcessor** from [`pkg/downloader/images.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/pkg/downloader/images.go) to cache files locally before upload. The system verifies file existence and size constraints before proceeding.

### Scheduling Constraints

If the request includes `ScheduleAt` (ISO-8601 format), the service validates that the timestamp falls within the 1-hour to 14-day window required by Xiaohongshu. Invalid schedules return immediate errors before browser launch.

## Browser Automation with go-rod

The core publishing mechanism relies on **go-rod**, a Go driver for Chrome DevTools Protocol.

### Browser Initialization

The **newBrowser()** function in [`main/mcp_server.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/mcp_server.go) creates a `rod.Browser` instance with a temporary user data directory. Each publishing operation receives a fresh browser context to ensure isolation.

```go
b := newBrowser()
defer b.Close()
page := b.NewPage()
defer page.Close()

```

### Navigation and Tab Selection

Both publishing workflows navigate to `https://creator.xiaohongshu.com/publish/publish?source=official` (defined as `urlOfPublic` constant). The automation then switches to the appropriate content tab—"上传图文" for images or the video tab for video content—using the **mustClickPublishTab** helper.

## Publishing Workflows

### Image-Text Note Publishing

The **NewPublishImageAction** constructor in [`xiaohongshu/publish.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish.go) (lines 38-67) initializes the image publishing workflow. The **Publish** method (lines 71-95) executes the following sequence:

1. **Upload images** via **uploadImages**, which sequentially calls `SetFiles` on the file input element and waits for preview generation using **waitForUploadComplete**.
2. **Trim tags** to the maximum of 10 allowed by the platform.
3. **Submit the note** via **submitPublish** (lines 74-124), which:
   - Fills title and content fields
   - Applies optional **setSchedulePublish** (toggles switch and sets datetime picker)
   - Sets **setVisibility** via dropdown selection
   - Toggles **setOriginal** and confirms via **confirmOriginalDeclaration** modal if requested
   - Executes **bindProducts** to search and attach product links
   - Clicks the final publish button (`.publish-page-publish-btn button.bg-red`)

All DOM interactions include defensive checks such as **isElementBlocked**, **removePopCover**, and **clickEmptyPosition** to handle pop-ups and overlays.

### Video Note Publishing

The **NewPublishVideoAction** constructor in [`xiaohongshu/publish_video.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish_video.go) (lines 27-53) handles video content. The workflow mirrors image publishing with these differences:

- **Single file upload** via **uploadVideo** with a 5-minute timeout
- **Publish button polling** via **waitForPublishButtonClickable** to detect when the platform enables the submit button after processing
- **Shared submission logic** reusing **submitPublishVideo** which calls the same helper functions for scheduling, visibility, and product binding

## Response Handling and Error Management

After successful `action.Publish` or `PublishVideo` execution, the service constructs response structs:

```go
// Image response
return &PublishResponse{
    Title:   req.Title,
    Content: req.Content,
    Images:  len(imagePaths),
    Status:  "发布完成",
}, nil

```

```go
// Video response
return &PublishVideoResponse{
    Title:   req.Title,
    Content: req.Content,
    Video:   req.Video,
    Status:  "发布完成",
}, nil

```

Errors propagate using wrapped errors from `github.com/pkg/errors`. Structured logging via **logrus** and **slog** captures execution details. UI automation failures (element timeouts, blocked interactions) return `5xx` status codes with machine-readable error identifiers such as `PUBLISH_FAILED`.

## Code Examples

### Publishing Images via cURL

```bash
curl -X POST http://localhost:8080/api/v1/publish \
  -H "Content-Type: application/json" \
  -d '{
        "title":"我的春季穿搭",
        "content":"今天的穿搭分享…",
        "images":["https://example.com/img1.jpg","./local/img2.png"],
        "tags":["春季","穿搭"],
        "visibility":"公开可见",
        "is_original":true,
        "schedule_at":"2026-04-01T10:00:00Z",
        "products":["连衣裙"]
      }'

```

The server downloads remote images, launches a headless Chrome session, and returns:

```json
{
  "title":"我的春季穿搭",
  "content":"今天的穿搭分享…",
  "images":2,
  "status":"发布完成"
}

```

### Publishing Video via cURL

```bash
curl -X POST http://localhost:8080/api/v1/publish_video \
  -H "Content-Type: application/json" \
  -d '{
        "title":"旅行Vlog",
        "content":"在云南的七天行程",
        "video":"/home/user/Videos/yn.mp4",
        "tags":["旅行","云南"],
        "visibility":"仅自己可见"
      }'

```

### Direct Go Integration

```go
import (
    "context"
    "github.com/xpzouying/xiaohongshu-mcp/main/xiaohongshu"
    "github.com/xpzouying/xiaohongshu-mcp/main/service"
)

func main() {
    svc := service.NewXiaohongshuService() // uses default config
    req := &service.PublishRequest{
        Title:   "手工咖啡教程",
        Content: "一步步教你做手冲咖啡",
        Images:  []string{"./imgs/step1.jpg", "./imgs/step2.jpg"},
    }

    resp, err := svc.PublishContent(context.Background(), req)
    if err != nil {
        log.Fatalf("publish failed: %v", err)
    }
    fmt.Printf("Published, post id: %s\n", resp.Status)
}

```

## Key Source Files Reference

| File | Purpose |
|------|---------|
| [`main/handlers_api.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/handlers_api.go) | HTTP endpoints (`publishHandler`, `publishVideoHandler`) and request validation |
| [`main/service.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/service.go) | Business logic including validation, image processing, and workflow orchestration |
| [`xiaohongshu/publish.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish.go) | UI automation for image-text notes (`NewPublishImageAction`, `uploadImages`, `submitPublish`) |
| [`xiaohongshu/publish_video.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish_video.go) | UI automation for video notes (`NewPublishVideoAction`, `uploadVideo`) |
| [`pkg/downloader/images.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/pkg/downloader/images.go) | Remote image downloading and local caching (`ImageProcessor`) |
| [`pkg/xhsutil/title.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/pkg/xhsutil/title.go) | Unicode-aware title length calculation (`CalcTitleLength`) |
| [`main/mcp_server.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/mcp_server.go) | Browser factory (`newBrowser`) and global configuration |
| [`docs/API.md`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/docs/API.md) | Complete API specification and request/response schemas |

## Summary

Publishing content with xiaohongshu-mcp follows a structured pipeline:

- **API Layer**: [`main/handlers_api.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/handlers_api.go) exposes REST endpoints that accept JSON payloads for image-text or video content.
- **Validation**: [`main/service.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/service.go) enforces title length limits (20 runes), downloads remote media via [`pkg/downloader/images.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/pkg/downloader/images.go), and validates scheduling windows (1 hour to 14 days).
- **Browser Automation**: The system launches headless Chrome via `newBrowser()` in [`main/mcp_server.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/mcp_server.go), then uses [`xiaohongshu/publish.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish.go) or [`xiaohongshu/publish_video.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish_video.go) to navigate the creator platform, upload files, and submit posts.
- **Optional Features**: Supports scheduled publishing, visibility controls (public/private), originality declarations, and product binding through dedicated UI automation helpers.
- **Response**: Returns structured JSON confirming publication status or detailed error codes for debugging.

## Frequently Asked Questions

### What authentication method does xiaohongshu-mcp use?

The system operates by automating a logged-in Chrome session rather than using API tokens. You must manually log into Xiaohongshu in the headless browser instance (or use a pre-configured user data directory) before the automation can access publishing features. The `newBrowser()` function in [`main/mcp_server.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/mcp_server.go) initializes this browser context.

### Can I schedule posts for a specific time in the future?

Yes. Include the `schedule_at` field in your API request with an ISO-8601 timestamp. The service layer in [`main/service.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/service.go) validates that the time falls between 1 hour and 14 days from the current time. The `setSchedulePublish` function in [`xiaohongshu/publish.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish.go) then toggles the scheduling switch and sets the datetime picker in the browser automation.

### How does the system handle remote images versus local files?

The `processImages` function in [`main/service.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/main/service.go) detects whether image paths are URLs or local filesystem paths. For remote URLs, it invokes the `ImageProcessor` from [`pkg/downloader/images.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/pkg/downloader/images.go) to download and cache files locally before upload. Local paths are validated for existence and size constraints. All images are then passed as absolute paths to the browser automation for sequential upload via `uploadImages`.

### What happens if the Xiaohongshu UI changes and elements can't be found?

The automation includes defensive programming patterns such as `isElementBlocked`, `removePopCover`, and `clickEmptyPosition` to handle overlays and pop-ups. However, if Xiaohongshu significantly changes DOM selectors, the automation will fail with a `PUBLISH_FAILED` error code and detailed logs via **logrus** and **slog**. You would need to update the selector constants in [`xiaohongshu/publish.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish.go) or [`xiaohongshu/publish_video.go`](https://github.com/xpzouying/xiaohongshu-mcp/blob/main/xiaohongshu/publish_video.go) to match the new UI structure.