douyin-dm-mcp

douyin-dm-mcp

Enables reading and sending Douyin private messages through a persistent browser profile, with dry-run and authorization safeguards.

Category
Visit Server

README

douyin-dm-mcp

A Model Context Protocol server and local HTTP API for Douyin web direct messages, built with Playwright. Both interfaces reuse the same persistent local browser profile, read currently rendered conversations and messages, and send individual messages only when explicitly enabled.

The project uses Douyin's current standalone chat page:

https://www.douyin.com/chat?isPopup=1

Login and account status checks still use the Douyin home page. /messages currently returns a 404 page and is not used for automation.

Safety boundaries

  • DOUYIN_ALLOW_SEND defaults to false, so real sending is disabled by default.
  • send_message defaults to dryRun: true. Dry runs validate the current snapshot without opening a conversation or changing page state.
  • A real send requires both dry-run to be disabled and DOUYIN_ALLOW_SEND=true.
  • Before reading or a real send, the server verifies that the nickname is unique, the conversation position and exact nickname still match, and the open chat title matches.
  • Duplicate nicknames are marked targetable: false and are refused by both MCP tools and the nickname-based CLI.
  • If the result cannot be confirmed after clicking send, the server returns SEND_STATUS_UNKNOWN and does not retry automatically.
  • Each browser profile has an exclusive filesystem lock to prevent concurrent Chromium instances from corrupting it. MCP, the HTTP API, and the operator CLI cannot run at the same time against the same DOUYIN_PROFILE.
  • All page operations are serialized to prevent cross-conversation reads or sends.
  • The project does not modify browser fingerprints, bypass verification challenges, or call Douyin's private WebSocket/Protobuf interfaces.
  • Logs are written to stderr and redact message bodies, cookies, and password fields.

Current limitations

Douyin's rendered conversation DOM does not expose a supported stable conversation ID, user ID, sec_uid, or stable profile link. Therefore:

  • conversationKey is opaque and valid only for the latest list_conversations snapshot.
  • Calling list_conversations creates new keys and immediately expires every key from the previous snapshot.
  • Every conversation returns stableKey: false; duplicate nicknames additionally return targetable: false.
  • Call list_conversations before calling read_messages or send_message, then use a key from that exact result.
  • The conversation list contains only items currently rendered by the browser; complete is always false.
  • Fuzzy nickname matching, bulk sending, stranger search, and search-to-send fallbacks are intentionally unsupported.

Detailed live-page evidence is recorded in RESEARCH.md.

Requirements

  • Node.js 20 or newer
  • npm
  • A desktop environment capable of displaying Chromium for the initial QR-code login

Installation

npm install
npx playwright install chromium
npm run build

Configuration

Environment variable Default Description
DOUYIN_PROFILE default Profile name; letters, numbers, underscores, and hyphens only
DOUYIN_HEADLESS false Run Chromium headlessly; keep this false for initial login
DOUYIN_ALLOW_SEND false Allow real message sends
DOUYIN_DEBUG false Enable debug logging
DOUYIN_NAVIGATION_TIMEOUT_MS 60000 Navigation timeout in milliseconds
DOUYIN_ACTION_TIMEOUT_MS 10000 Page action timeout in milliseconds
DOUYIN_MIN_SEND_INTERVAL_MS 3000 Minimum interval between send attempts
DOUYIN_API_HOST 127.0.0.1 HTTP API bind address
DOUYIN_API_PORT 3000 HTTP API port
DOUYIN_API_KEY unset Bearer key, minimum 16 characters; required for non-loopback binding

These variables are read from the process environment. The project does not load .env. Use .env.example as a reference, then export the values in your shell or set them in the MCP client env block.

Browser data is stored in:

.data/profiles/<DOUYIN_PROFILE>

This directory contains authentication data. Do not commit or share it.

Login

For first use or an expired session, run:

npm run login

Scan the displayed QR code with Douyin. After login, the script prints structured status, closes Chromium safely, and keeps the authenticated session in the persistent profile.

Check the current session:

npm run status

Example successful result:

{
  "ok": true,
  "browserRunning": true,
  "loggedIn": true,
  "currentUrl": "https://www.douyin.com/jingxuan"
}

Starting the MCP server

The compiled entry point is:

node dist/index.js

Codex CLI example:

codex mcp add douyin-dm -- node /absolute/path/to/douyin-dm-mcp/dist/index.js

Generic MCP client configuration:

{
  "mcpServers": {
    "douyin-dm": {
      "command": "node",
      "args": ["/absolute/path/to/douyin-dm-mcp/dist/index.js"],
      "env": {
        "DOUYIN_PROFILE": "default",
        "DOUYIN_ALLOW_SEND": "false"
      }
    }
  }
}

For an authorized real send, set DOUYIN_ALLOW_SEND to true for that MCP process and restart it. Do not leave sending globally enabled.

Do not start this process while the HTTP API or CLI already holds the same profile lock.

Starting the HTTP API

Run from source:

npm run api

Or run the compiled entry point:

node dist/api.js

Do not start this process while MCP or the CLI already holds the same profile lock.

The default base URL is http://127.0.0.1:3000. The unauthenticated health check is:

curl http://127.0.0.1:3000/health

API routes:

Method Path Input Purpose
GET /health None Process liveness; no auth, no browser
GET /api/v1/status None Login / browser session
GET /api/v1/conversations?limit=20 Query parameter limit, 1–100 Current rendered snapshot + new keys
POST /api/v1/messages/read JSON { "conversationKey": "...", "limit": 20 } Visible messages for a snapshot key
POST /api/v1/messages/send JSON { "conversationKey": "...", "text": "...", "dryRun": true } Dry-run by default; real send needs both gates

POST requests require Content-Type: application/json. Sending remains a dry run by default. A real send still requires both "dryRun": false and DOUYIN_ALLOW_SEND=true.

Example:

curl "http://127.0.0.1:3000/api/v1/conversations?limit=20"

curl -X POST http://127.0.0.1:3000/api/v1/messages/read \
  -H "Content-Type: application/json" \
  -d '{"conversationKey":"fallback:...:0","limit":20}'

Loopback access does not require an API key. Binding to any other host is refused unless DOUYIN_API_KEY is set to at least 16 characters. When configured, send it on every /api/v1/* request:

curl http://127.0.0.1:3000/api/v1/status \
  -H "Authorization: Bearer YOUR_API_KEY"

The API returns the same structured success and Douyin error objects as MCP. Request parsing errors use INVALID_REQUEST, INVALID_JSON, UNSUPPORTED_MEDIA_TYPE, or PAYLOAD_TOO_LARGE; authentication failures use UNAUTHORIZED.

MCP tools

browser_status

Checks whether the persistent Douyin browser profile is authenticated.

Input: none.

list_conversations

Opens the standalone chat page and returns currently rendered conversations with opaque conversationKey values for the new snapshot.

{
  "limit": 20
}

Conversation fields:

  • conversationKey
  • stableKey, currently always false
  • position
  • nickname
  • preview
  • timestamp
  • targetable, false when duplicate nicknames make safe selection impossible

read_messages

Reads currently visible messages from a conversation returned by list_conversations.

{
  "conversationKey": "fallback:550e8400-e29b-41d4-a716-446655440000:0",
  "limit": 20
}

Message fields:

  • direction: incoming or outgoing, from verified sender-side DOM evidence
  • type: text, or unsupported for unrecognized message types
  • content: visible text, or null when empty

Conversations with targetable: false are refused.

send_message

Sends one message to a verified conversation.

{
  "conversationKey": "fallback:550e8400-e29b-41d4-a716-446655440000:0",
  "text": "Test message",
  "dryRun": true
}

A real send requires all of the following:

  1. DOUYIN_ALLOW_SEND=true.
  2. dryRun=false.
  3. The target nickname is unique in the current snapshot.
  4. The conversation position and exact nickname still match the snapshot.
  5. The open chat title exactly matches the target nickname.
  6. The message has no leading or trailing whitespace.
  7. The logical Slate editor text exactly matches the requested text.

After clicking send, the server waits for a new outgoing message with the exact canonical text. If confirmation fails, it returns SEND_STATUS_UNKNOWN; callers must inspect the conversation manually instead of retrying automatically. The minimum send interval is retained across conversation-list refreshes.

Operator CLI

List currently rendered conversations:

npm run chat -- list

Read messages by an exact, unique nickname:

npm run chat -- read "Exact nickname"

Real sends also require DOUYIN_ALLOW_SEND. PowerShell example:

$env:DOUYIN_ALLOW_SEND="true"
npm run chat -- send "Exact nickname" "Test message"
Remove-Item Env:DOUYIN_ALLOW_SEND

The CLI accepts exact nicknames only and refuses to continue when no match or multiple matches are found.

Do not run the CLI while MCP or the HTTP API already holds the same profile lock.

Development

npm run lint
npm test
npm run build
npm run smoke:mcp

Tests cover configuration parsing, structured errors, profile locking, page-operation serialization, snapshot expiry, duplicate refusal, target verification, message direction, dry-run isolation, composer rollback, successful send confirmation, unknown send status, persistent rate limiting, and package-safe defaults.

Project structure

src/
  browser/          Browser lifecycle, profile locking, and operation serialization
  douyin/           DouyinService, centralized selectors, and page objects
  index.ts          MCP stdio server
  api.ts            HTTP API process entry point
  api/              Versioned HTTP routes, validation, and authentication
scripts/
  login.ts          QR-code login
  status.ts         Authentication status check
  chat.ts           Operator CLI
  mcp-smoke.ts      MCP transport smoke check
tests/unit/         Repeatable behavioral tests
RESEARCH.md         Live-page evidence and engineering research

License

Licensed under the permissive MIT License.

Recommended Servers

playwright-mcp

playwright-mcp

A Model Context Protocol server that enables LLMs to interact with web pages through structured accessibility snapshots without requiring vision models or screenshots.

Official
Featured
TypeScript
Magic Component Platform (MCP)

Magic Component Platform (MCP)

An AI-powered tool that generates modern UI components from natural language descriptions, integrating with popular IDEs to streamline UI development workflow.

Official
Featured
Local
TypeScript
Audiense Insights MCP Server

Audiense Insights MCP Server

Enables interaction with Audiense Insights accounts via the Model Context Protocol, facilitating the extraction and analysis of marketing insights and audience data including demographics, behavior, and influencer engagement.

Official
Featured
Local
TypeScript
VeyraX MCP

VeyraX MCP

Single MCP tool to connect all your favorite tools: Gmail, Calendar and 40 more.

Official
Featured
Local
graphlit-mcp-server

graphlit-mcp-server

The Model Context Protocol (MCP) Server enables integration between MCP clients and the Graphlit service. Ingest anything from Slack to Gmail to podcast feeds, in addition to web crawling, into a Graphlit project - and then retrieve relevant contents from the MCP client.

Official
Featured
TypeScript
Kagi MCP Server

Kagi MCP Server

An MCP server that integrates Kagi search capabilities with Claude AI, enabling Claude to perform real-time web searches when answering questions that require up-to-date information.

Official
Featured
Python
E2B

E2B

Using MCP to run code via e2b.

Official
Featured
Neon Database

Neon Database

MCP server for interacting with Neon Management API and databases

Official
Featured
Exa Search

Exa Search

A Model Context Protocol (MCP) server lets AI assistants like Claude use the Exa AI Search API for web searches. This setup allows AI models to get real-time web information in a safe and controlled way.

Official
Featured
Qdrant Server

Qdrant Server

This repository is an example of how to create a MCP server for Qdrant, a vector search engine.

Official
Featured