npm package: @heartbitai/publish-bots-mcp
Description: MCP server and automation runtime for media-platform auth and publish workflows
Latest version: 0.1.6

# @heartbitai/publish-bots-mcp

MCP server + TypeScript/Playwright runtime for media-platform auth and publish automation.

## Requirements

- Node.js 20+
- pnpm 10+ (for source development)

## Install (npm)

```bash
# global install
npm install -g @heartbitai/publish-bots-mcp

# install Playwright browser once
npx playwright install chromium
```

Run MCP server:

```bash
bots-mcp
```

Without global install:

```bash
npx -y -p @heartbitai/publish-bots-mcp bots-mcp
```

## Install (Source)

```bash
pnpm install
pnpm run build
```

## Project Structure

```text
src/
  adapters/
    legacy-worker/        # worker protocol bridge and runtime
  cli/                    # local CLI entry
  config/                 # runtime selector config
  contracts/              # shared types/messages/error codes
  core/                   # browser session, lifecycle, logging, cancellation
  platforms/
    shared/               # BaseBot/DOM action/common auth helpers
    <platform>/
      auth/               # platform auth flow
      publish/            # platform publish flow (if supported)
    # examples:
    # baijiahao/auth
    # baijiahao/publish (placeholder)
    # toutiao/auth + toutiao/publish
    # wechat/auth + wechat/publish
    registry.ts           # platform factory registry
```

## Platform Support

- Auth: `baijiahao`, `bilibili`, `bjh`, `csdn`, `douyin`, `ifeng`, `juejin`, `sina`, `sohu`, `toutiao`, `wechat`, `xhs`, `zhihu`
- Publish: `baijiahao`, `bilibili`, `bjh`, `csdn`, `douyin`, `juejin`, `sohu`, `toutiao`, `wechat`, `xhs`

Current behavior notes:

- Sohu publish supports article flow (title/body + optional cover upload), and validates publish result via toast or URL transition.
- Baijiahao / CSDN / Juejin / XHS / Douyin publish are available with practical selector fallback logic; `bjh` is an alias for `baijiahao`.
- Bilibili publish targets 专栏链路 (`platform/upload/text/new-article`) and includes york-frame precheck handling (level/real-name/phone/daily-limit block reasons).
- Toutiao `image` mode uploads images into the editor and publishes as image-rich article flow.
- Wechat article mode now auto-detects editor input (`textarea`/`contenteditable`) and writes body text.

## Scripts

```bash
pnpm run lint
pnpm run typecheck
pnpm run build
pnpm run test
pnpm run smoke
pnpm run clean
pnpm run mcp
pnpm run prepack
```

## CLI

`pnpm run cli` runs `dist/cli/index.js`, so build first (`pnpm run build`) after code changes.

```bash
# list supported platforms
pnpm run cli list

# auth (interactive; legacy user-data mode)
pnpm run cli auth toutiao --user-data /tmp/toutiao-profile

# verify with configurable timeout/poll
pnpm run cli verify wechat \
  --user-data /tmp/wechat-profile \
  --timeout-ms 10000 \
  --poll-interval-ms 300

# daemon-backed session auth start (recommended for automation)
pnpm run cli auth-start wechat --profile main

# get QR by session id
pnpm run cli auth-qr --session wechat-xxxx

# wait for session status transition
pnpm run cli auth-wait \
  --session wechat-xxxx \
  --timeout-ms 300000 \
  --poll-interval-ms 1000 \
  --until LOGGED_IN,FAILED,CANCELLED,TIMEOUT

# publish article
pnpm run cli publish toutiao \
  --user-data /tmp/toutiao-profile \
  --type article \
  --title "Title" \
  --body "Body"

# publish article by daemon profile (no --user-data)
pnpm run cli publish wechat \
  --profile main \
  --type article \
  --title "Title" \
  --body "Body"

# list known publish accounts / profiles
pnpm run cli publish-accounts --platform wechat --json

# set default publish account for a platform
pnpm run cli publish-set-default wechat --profile main --json

# set custom account label (nickname) for a platform/profile
pnpm run cli publish-set-nickname wechat --profile main --nickname "主账号A" --json

# batch publish to multiple profiles
pnpm run cli publish-batch wechat \
  --profiles main,backup \
  --type article \
  --title "Batch Title" \
  --body "Batch Body" \
  --json

# dry-run (no browser launch)
pnpm run cli publish wechat --user-data /tmp/wechat-profile --dry-run

# structured JSON output (for scripts/CI)
pnpm run cli auth-start wechat --profile ci-main --dry-run --json
```

### CLI Usage Guide (中文)

CLI 有两种使用入口：

- npm 全局安装后直接用：`publish-bots-cli ...`
- 源码仓库内使用：`pnpm run cli ...`（先 `pnpm run build`）

两种运行路径：

- Legacy 模式（单命令 + 本地 `user-data`）：`auth` / `verify` / `publish --user-data`
- Daemon Profile 模式（推荐自动化）：`auth-*` 会话命令 + `publish/publish-batch/publish-accounts/publish-set-default`，基于本地 daemon 持久化 profile

推荐流程（Session 模式首次登录）：

1. 启动登录会话：`publish-bots-cli auth-start wechat --profile main --json`
2. 获取二维码：`publish-bots-cli auth-qr --session <session_id> --json`
3. 轮询状态：`publish-bots-cli auth-wait --session <session_id> --until LOGGED_IN,FAILED,CANCELLED,TIMEOUT --timeout-ms 300000 --json`
4. 登录完成后关闭会话：`publish-bots-cli auth-finish --session <session_id> --json`

`sohu` 建议走手机号/验证码流程（非二维码优先）：

1. 启动会话：`publish-bots-cli auth-start sohu --profile main --json`
2. 抓取登录页：`publish-bots-cli auth-capture-page --session <session_id> --json`
3. 交互步骤：`auth-step switch_to_sms/fill_phone/send_code/fill_code/submit`
4. 轮询与结束：`auth-wait ...` + `auth-finish`

快速验证账号是否已登录（Legacy verify）：

- `publish-bots-cli verify wechat --user-data /tmp/wechat-profile --timeout-ms 10000 --poll-interval-ms 300 --json`

发布（Legacy publish）：

- `publish-bots-cli publish toutiao --user-data /tmp/toutiao-profile --type article --title "Title" --body "Body"`
- 建议先加 `--dry-run` 做参数检查

发布（Daemon Profile 模式，支持多账号）：

1. 列出平台可用发布账号：`publish-bots-cli publish-accounts --platform wechat --json`
2. 设置默认账号：`publish-bots-cli publish-set-default wechat --profile main --json`
3. 设置账号标识：`publish-bots-cli publish-set-nickname wechat --profile main --nickname "主账号A" --json`
4. 单账号发布（指定账号）：`publish-bots-cli publish wechat --profile main --type article --title "Title" --body "Body" --json`
5. 单账号发布（使用默认账号）：`publish-bots-cli publish wechat --type article --title "Title" --body "Body" --json`
6. 批量发布：`publish-bots-cli publish-batch wechat --profiles main,backup --type article --title "Batch" --body "Body" --json`

JSON 输出（脚本/CI 推荐）：

- 任意命令加 `--json`
- 成功格式：`{ ok: true, command, result }`
- 失败格式：`{ ok: false, command, error: { code, message, details } }`

daemon 管理：

- Daemon Profile 模式会自动拉起 daemon（Unix socket）
- 使用 `publish-bots-cli daemon-stop` 关闭当前 profile-root 对应 daemon

Common options:

- `--user-data <path>` required for legacy `auth` / `verify` / `publish`
- `--session <id>` session id for `auth`, `verify`, and session-style auth commands
- `--timeout-ms <ms>` + `--poll-interval-ms <ms>` for `verify` / `auth-wait`
- `--until LOGGED_IN,FAILED,...` for `auth-wait`
- `--profile <name>` for session auth and daemon publish commands
- `--profiles <a,b,c>` profile list for `publish-batch`
- `--profiles-root <path>` override daemon `BotsService` profile root
- `--task <id>` optional task id for `publish`
- `--task-start <id>` optional start task id for `publish-batch`
- `--continue-on-error <true|false>` batch publish error strategy (default `true`)
- `--limit <n>` result size limit for `publish-accounts`
- `--type article|image`
- `--media /a.png,/b.png`
- `--final-params '{"key":"value"}'`
- `--ack-timeout <ms>` publish ACK timeout (default `20000`)
- `--dry-run`
- `--json` return structured machine-readable output

Session-style auth commands (daemon-backed):

- `auth-start <platform>`
- `auth-qr --session <id>`
- `auth-capture-page --session <id>`
- `auth-status --session <id>`
- `auth-wait --session <id>`
- `auth-step --session <id> --action <...> [--value <text>]`
- `auth-finish --session <id>`
- `auth-cancel --session <id>`
- `daemon-stop` (stop local auth daemon process)

Daemon-backed publish commands:

- `publish <platform> [--profile <name>] [--task <id>] ...`
- `publish-batch <platform> [--profiles <a,b,c>] [--task-start <id>] ...`
- `publish-accounts [--platform <name>] [--limit <n>]`
- `publish-set-default <platform> --profile <name>`
- `publish-set-nickname <platform> --profile <name> --nickname <text>`

## Worker Bridge

`LegacyWorkerBridge` accepts inbound worker messages and dispatches to runtime:

- `START_AUTH`
- `FOCUS_AUTH`
- `STOP_AUTH`
- `RUN_PUBLISH`
- `ACK`
- `DISPOSE`

On failure, bridge emits `BRIDGE_ERROR` with structured payload:

- `code`
- `message`
- `inboundType`
- `context`
- `timestamp`

Core files:

- `src/adapters/legacy-worker/LegacyWorkerRuntime.ts`
- `src/adapters/legacy-worker/LegacyWorkerBridge.ts`
- `src/platforms/registry.ts`

## Tests

- Unit tests: `tests/*.test.cjs`
- Smoke tests: `tests/*.smoke.cjs`

Smoke tests use CLI `--dry-run` to validate command wiring without real account login.

## MCP Server

Run MCP server over stdio:

```bash
bots-mcp
```

If not globally installed:

```bash
npx -y -p @heartbitai/publish-bots-mcp bots-mcp
```

Source mode:

```bash
pnpm run build
pnpm run mcp
```

### Configure MCP Client

Claude / OpenClaude-like JSON config (recommended via `npx`):

```json
{
  "mcpServers": {
    "bots": {
      "command": "npx",
      "args": ["-y", "-p", "@heartbitai/publish-bots-mcp", "bots-mcp"],
      "env": {
        "BOTS_PROFILES_ROOT": "/absolute/path/bots-profiles"
      }
    }
  }
}
```

Codex desktop (`~/.codex/config.toml`) example:

```toml
[mcp_servers.bots]
type = "stdio"
command = "npx"
args = ["-y", "-p", "@heartbitai/publish-bots-mcp", "bots-mcp"]

[mcp_servers.bots.env]
BOTS_PROFILES_ROOT = "/absolute/path/bots-profiles"
```

If your MCP host cannot resolve `npx`/`node` from `PATH`, use absolute binary paths.

Available tools:

- `bots_list_platforms`
- `bots_auth_list`
- `bots_account_check`
- `bots_auth_start`
- `bots_auth_get_qr`
- `bots_auth_focus`
- `bots_auth_capture_page`
- `bots_auth_step`
- `bots_auth_refresh_qr`
- `bots_auth_status`
- `bots_auth_wait`
- `bots_auth_finish`
- `bots_auth_cancel`
- `bots_publish_accounts_list`
- `bots_publish_accounts_set_default`
- `bots_publish`
- `bots_publish_batch`

Available resources:

- `bots://platforms`
- `bots://auth/sessions/recent`
- `bots://mcp/policy`

Available resource templates:

- `bots://auth/sessions/{platform}`
- `bots://auth/sessions/{platform}/{profile}`

`bots_account_check` returns `state=LOGGED_IN|NEED_QR|FAILED` and can include QR snapshot (`qr`) when login is required.

Tool usage notes:

- `bots_account_check` is a quick check helper (`start -> wait -> finish`) and is best for "already logged in?" checks.
- For first-time login with human interaction, prefer session-style auth tools:
  - `bots_auth_start`
  - `bots_auth_get_qr` / `bots_auth_refresh_qr`
  - `bots_auth_focus` (bring auth window to front)
  - `bots_auth_capture_page` (full-page snapshot when QR is not the right artifact)
  - `bots_auth_step` (`switch_to_sms|fill_phone|send_code|fill_code|submit`)
  - `bots_auth_status` / `bots_auth_wait`
  - `bots_auth_finish` (or `bots_auth_cancel`)

### Agent Call Templates

Health check (MCP connectivity):

1. call `bots_list_platforms`
2. call `bots_auth_list` (optional)
3. call MCP `resources/list` and confirm `bots://platforms` exists (optional)
4. call `bots_publish` with `dry_run=true` (optional)

First-time login (human scans QR):

1. `bots_auth_start` with `platform` + `profile`
2. `bots_auth_get_qr` with returned `session_id`
3. show `qr.imagePath` image to user for scanning
4. poll `bots_auth_status` (or use `bots_auth_wait`)
5. if QR expired, call `bots_auth_refresh_qr` and re-show QR
6. when status reaches `LOGGED_IN`, call `bots_auth_finish`
7. if status becomes `FAILED` / `TIMEOUT` / `CANCELLED`, call `bots_auth_cancel`

Example first-time login tool arguments:

```json
{
  "platform": "wechat",
  "profile": "real-main"
}
```

```json
{
  "session_id": "wechat-xxxx",
  "include_base64": false
}
```

```json
{
  "session_id": "wechat-xxxx",
  "timeout_ms": 300000,
  "poll_interval_ms": 1500,
  "until": ["LOGGED_IN", "FAILED", "CANCELLED", "TIMEOUT"]
}
```

Publish with existing profile (already logged in):

1. call `bots_publish` with `platform` + `profile` + content
2. recommend `dry_run=true` first, then real publish

Multiple accounts / default account / batch publish:

1. call `bots_publish_accounts_list` to view known profiles for a platform
2. call `bots_publish_accounts_set_default` to set default profile
3. call `bots_publish` without `profile` to use default profile
4. call `bots_publish_batch` with `profiles` array for bulk publish

Example publish arguments:

```json
{
  "platform": "wechat",
  "profile": "real-main",
  "type": "article",
  "title": "Demo Title",
  "body": "Demo Body",
  "media": ["/Users/troy/Desktop/projects/bots/auto-publish-bot/cover.jpg"]
}
```

Dry-run example:

```json
{
  "platform": "wechat",
  "profile": "real-main",
  "dry_run": true
}
```

Batch publish example:

```json
{
  "platform": "wechat",
  "profiles": ["real-main", "backup-account"],
  "type": "article",
  "title": "Batch Demo Title",
  "body": "Batch Demo Body",
  "continue_on_error": true
}
```

Error contract (`tools/call` result with `isError: true`):

```json
{
  "isError": true,
  "content": [{ "type": "text", "text": "[ERROR_CODE] readable message" }],
  "structuredContent": {
    "ok": false,
    "error": {
      "code": "MCP_INVALID_ARGUMENT | MCP_TOOL_UNSUPPORTED | MCP_TOOL_FORBIDDEN | MCP_INTERNAL_ERROR | BOTS_*",
      "message": "readable message",
      "tool": "bots_auth_start",
      "details": {}
    }
  }
}
```

MCP permission policy (env vars, optional):

- `BOTS_MCP_POLICY=full|read_only|auth_only|publish_only|none` (default `full`)
- `BOTS_MCP_ALLOW_TOOLS=bots_list_platforms,bots_auth_list` (allowlist, optional)
- `BOTS_MCP_DENY_TOOLS=bots_publish` (denylist, optional)
- `BOTS_PROFILES_ROOT=/absolute/path` (override MCP profile storage root)

Example:

```bash
# deny publish in this process
export BOTS_MCP_POLICY=read_only
bots-mcp
```

Auth QR screenshots are stored under:

- `resources/auth-qr/`

Default profile root used by `BotsService`:

- when `BOTS_PROFILES_ROOT` is set: `<BOTS_PROFILES_ROOT>/<platform>/<profile>`
- when `BOTS_PROFILES_ROOT` is not set: `<server_package_root>/.profiles/<platform>/<profile>`
- session index: `<profiles_root>/.service-state/auth-sessions.json` (persisted auth session index)
  - Writes are debounced and atomically replaced via temp-file + rename.

MCP server startup note:

- MCP resolves default profile root from server package location (`<server_package_root>/.profiles`) instead of current working directory, so it can start correctly even when host cwd is `/`.
- for npm global installs, always set `BOTS_PROFILES_ROOT` to a writable directory.

Profile locking:

- Same profile path is single-owner at a time (auth/publish are mutually exclusive).
- Concurrent conflict returns `BOTS_PROFILE_BUSY`.

Common troubleshooting:

- "Tool not found in shell"
  - MCP tools (e.g. `bots_list_platforms`) are not shell commands; call them via MCP `tools/call`.
- `BOTS_PROFILE_BUSY`
  - another auth/publish flow is holding the same profile path; finish/cancel existing session first.
- no QR image in response
  - call `bots_auth_get_qr` again after `bots_auth_start`, or use `bots_auth_refresh_qr` when QR changes.

## Publish To npm (Maintainer)

```bash
npm login
npm publish --access public
```

Notes:

- `prepublishOnly` will run `pnpm run clean && pnpm run build`.
- validate package files before publishing: `npm pack --dry-run`.

### TUI Release Script (latest / beta)

This repo includes an interactive terminal release helper:

```bash
pnpm run release:tui
```

You can also force channel selection:

```bash
pnpm run release:latest
pnpm run release:beta
```

What it does:

- checks `npm whoami` and git working tree status
- interactive choose version strategy (`patch/minor/major` or `pre*`)
- optional input OTP
- optional pre-publish checks (`lint + typecheck + build`)
- runs `npm version --no-git-tag-version`
- publishes:
  - stable: `npm publish --access public` (`latest`)
  - beta: `npm publish --access public --tag beta`