Inside wenyan-mcp: how Markdown reaches a WeChat Official Account draft box
文颜 MCP Server 可以让 AI 自动将 Markdown 文章排版后发布至微信公众号。
At a glance
- What is it?
- wenyan-mcp is an Apache-2.0 Model Context Protocol server that hands a chat client the Wenyan rendering engine, so an agent can theme a Markdown file and upload it as a WeChat Official Account draft. Its real constraints are the WeChat IP whitelist and a flow that deliberately stops at the draft box.
- Who is it for?
- wenyan-mcp fits a writer who already publishes to a WeChat Official Account and wants the agent to own the formatting step, and it fits few others. It is the wrong tool for a workflow that needs unattended publication, since the documented result is a draft waiting for a human click, and the wrong tool for multi-account work that will not deploy the Wenyan Server.
- Can I use it commercially?
- Yes. Apache-2.0 is a permissive licence: you can use, modify and sell software built on it, as long as you keep its copyright and licence notices.
- Is it still maintained?
- Yes. The repository last received commits 11 days ago.
- What is it written in?
- Mainly JavaScript, according to GitHub's language statistics.
Answers come from the project's GitHub data, last synced on September 20, 2026, and from our analysis. They are not legal advice.
Editorial analysis
One MCP server inside a four-client Wenyan family
Wenyan is a multi-platform Markdown formatting and publishing tool, and the project behind this repository deliberately ships in four shapes: a macOS App Store desktop app, a cross-platform desktop build for Windows and Linux, a command line version aimed at CI publishing, and the Model Context Protocol server that is the subject here. That fourth package is not a standalone formatter. Its manifest depends on `@wenyan-md/core`, the same rendering engine the other clients use, and it wraps that engine in MCP tool definitions so a chat client can drive it directly. The split matters in practice. The desktop applications exist for a person sitting at a keyboard who wants to see the result before sending it anywhere. The MCP server exists for a different caller: an agent that has just written a file and needs the file out of the process. The repository topics, mcp-server, wechat and wenyan, name that audience without further explanation. Publishing targets named in the documentation go beyond WeChat to Zhihu and Toutiao, with more platforms described as in progress, so the transport is general even though the credential model is not.
Two environment variables and an IP whitelist
Nothing works until the process has credentials. The documentation marks two environment variables as mandatory and warns that the upload endpoint fails without them. Installation is a single global npm install, and the client configuration passes the two values through an `env` block.
npm install -g @wenyan-md/mcp{
"mcpServers": {
"wenyan-mcp": {
"command": "wenyan-mcp",
"env": {
"WECHAT_APP_ID": "your_app_id",
"WECHAT_APP_SECRET": "your_app_secret"
}
}
}
}Beneath the credentials sits a second requirement that no package description would tell you about: the IP address of the machine running Wenyan must already be registered in the WeChat Official Account backend whitelist, or the same upload call fails again. That condition comes from the WeChat API rather than from the MCP layer, and it drives every deployment decision in the project. A laptop on a residential connection changes address between sessions. A container acquires a fresh address each time it is recreated. A CI runner takes whatever the pool assigns it. The project's answer is a second transport, described in the next section, that moves the API call onto a machine with a stable address.
Local stdio, remote server, and the multi-account rule
The server runs in two modes with identical results. In local stdio mode the MCP process calls the WeChat API directly. In client-server mode the same process acts as a client and forwards the publishing request to a Wenyan Server running on a cloud machine, and that server makes the API call. The switch is two arguments in the client configuration.
{
"mcpServers": {
"wenyan-mcp": {
"command": "wenyan-mcp",
"args": ["--server", "https://api.example.com", "--api-key", "your-api-key"]
}
}
}The documentation lists the situations this second mode suits: a machine with no fixed address, teams that need to share publishing setup, CI pipelines, and agents publishing on their own schedule. Each of those is really a restatement of the same problem, namely that a moving IP cannot stay in a whitelist.
One feature is gated behind server mode entirely. Publishing to several Official Accounts at once requires the Wenyan Server, because the credentials for each account are configured on the server side and the request has to name which `app_id` it is aimed at. A publish instruction therefore carries the target account explicitly. Local stdio mode has no way to express that choice.
The draft box is where the process stops
It is worth being exact about what the publish tool does, because the category name can overstate it. The documented outcome of a publish call is a draft in the WeChat Official Account backend, not a public post. The worked example in the documentation reports that the article reached the draft box, names the theme applied and the media ID returned, then directs the writer to log into the WeChat console, open the draft box, check the rendering, and publish with a single click.
That boundary is a design choice rather than a limitation being worked around. The agent absorbs the mechanical work: converting Markdown into the inline styled HTML that WeChat accepts, uploading every image into the material library so the article does not depend on external hosts, and choosing a cover. A person still owns the moment of publication. Teams shopping for a pipeline that posts without review should read the documentation closely here, because the list of reasons to choose server mode includes CI and agent-driven publishing while the last click in the described flow remains manual.
Frontmatter is the whole article contract
Each article opens with a frontmatter block, and only one field is required.
---
title: 在本地跑一个大语言模型(2) - 给模型提供外部知识库
cover: /Users/xxx/image.jpg
author: xxx
source_url: http://
---`title` is the one entry marked mandatory. `cover` accepts a local path or a network URL, and when it is omitted the first image in the body is used instead. `author` and `source_url` are passed through as metadata, and two further fields control the comment settings on the resulting draft: whether comments are enabled at all, and whether only followers may comment.
Two more fields belong to a different post shape entirely. WeChat's image post, referred to in the documentation as the small green book type, is produced by setting `type: image`, which tells the tool to pull every image out of the body by itself.
---
title: 人勤春来早,读书正当时
type: image
---


The alternative is writing the paths out in `image_list`, capped at twenty entries, with the first image serving as the cover. Either way the images must exist somewhere the tool can read, which is why the next section spends time on filesystem layout.
Themes are resources the model creates and deletes
Themes are handled as a managed resource rather than a stylesheet you edit by hand. Three operations are documented, and each is issued as an ordinary sentence to the chat client. Ask which themes are available and the reply is a numbered list where every entry carries a short note on tone and suitable content, distinguishing a plain default meant for long-form reading from warmer themes aimed at lifestyle pieces. Ask to register a stylesheet from a URL under a name of your choosing and the reply confirms the new theme by name and repeats the URL it will render from. Ask to delete a theme by name and the reply confirms the deletion.
Naming is free form, and that is what makes custom styling viable at all, since a theme is identified by whatever string the writer types. Several built-in themes ship with the client, and the documented tool surface covers rendering Markdown, managing themes, and publishing drafts, so a single conversation spans styling and delivery. The trade is that theme state becomes conversation state: renaming or removing a theme is a request to a model rather than an edit to a file, which is convenient until you want to know exactly which stylesheets exist on a given account.
The Docker path contract that decides whether images upload
Packaging is a separate axis from transport, and the container route has one requirement that is easy to miss. The image installs the published npm package globally on a Node 24 Alpine base and points its entrypoint at the `wenyan-mcp` binary, with a container-side path fixed at build time.
FROM node:24-alpine AS builder
ARG NPM_REGISTRY=https://registry.npmjs.org/
ENV CONTAINERIZED=1
ENV CONTAINER_FILE_PATH=/mnt/host-downloads
WORKDIR /app
RUN npm config set registry ${NPM_REGISTRY}
RUN npm install -g @wenyan-md/mcp && npm cache clean --force
ENTRYPOINT ["wenyan-mcp"]The consequence for users is a two-part arrangement. The host directory holding your Markdown file and its images must be mounted to `/mnt/host-downloads` inside the container, and an environment variable must be set to the matching host path so the two sides can be reconciled. The documentation calls out all three pieces, including why: local image references in the article have to resolve to something the container can actually open.
{
"mcpServers": {
"wenyan-mcp": {
"command": "docker",
"args": [
"run",
"--rm",
"-i",
"-v", "/your/host/file/path:/mnt/host-downloads",
"-e", "WECHAT_APP_ID=your_app_id",
"-e", "WECHAT_APP_SECRET=your_app_secret",
"-e", "HOST_FILE_PATH=/your/host/file/path",
"caol64/wenyan-mcp"
]
}
}
}Since the image follows the `latest` tag rather than a fixed version, the container and the globally installed package can drift apart. The three documented image sources, an absolute local path, a network URL, and a path relative to the article, all resolve differently inside a container, and only the mounted directory is guaranteed to work.
A pinned protocol SDK and four test scripts
The manifest is short enough to read in full, and the dependency choices say more about the project than the feature list does. The Model Context Protocol SDK is held at an exact version, 0.6.0, with no range, while the Wenyan core engine moves on a caret range and the rest of the list covers multipart form encoding for the uploads, a DOM implementation for the rendering step, and a schema validator. The repository is TypeScript, compiled with `tsc`, and npm receives only the build output.
The test scripts map one to one onto the tool surface. Each builds the project and then runs a single file from the tests directory with credentials loaded from an environment file: one lists themes, one publishes, one registers a theme, one removes a theme. There is no unit test layer in that list, which suits a project whose correctness is defined by whether WeChat accepts the upload.
For interactive work the documentation points at the official Inspector, launched against whatever command starts your server.
npx @modelcontextprotocol/inspector <command>On success it prints a local URL carrying a proxy authentication token, and the documented sequence runs from filling in the launch command and environment variables through connecting, listing tools, and running a single call while watching the full parameters. That last step is the practical way to see which fields a tool actually requires. Release history shows tags v2.0.2, v2.0.3 and v2.0.4, with the last push landing on 2026-09-20 alongside v2.0.4, and the earlier two tags dated 2026-04-08 and 2026-04-29.
Editorial conclusion
wenyan-mcp fits a writer who already publishes to a WeChat Official Account and wants the agent to own the formatting step, and it fits few others. It is the wrong tool for a workflow that needs unattended publication, since the documented result is a draft waiting for a human click, and the wrong tool for multi-account work that will not deploy the Wenyan Server. Verify first that your machine's IP can be registered in the WeChat whitelist, or plan on server mode from the start, and read the manifest if the protocol SDK version your client speaks matters to you.
Frequently asked questions
What does wenyan-mcp need configured before it can publish anything?
Two environment variables, WECHAT_APP_ID and WECHAT_APP_SECRET, and the IP address of the machine running it must already sit in the WeChat Official Account IP whitelist. Without either, the upload call fails.
Does wenyan-mcp publish an article straight to readers?
No. The documented result is a draft in the WeChat Official Account backend, which you open in the WeChat console, review, and publish with one click.
How do I publish to more than one WeChat Official Account with wenyan-mcp?
You must deploy the Wenyan Server and point the MCP client at it. Credentials for each account are configured on the server, and multi-account publishing does not work in local stdio mode.
Why do my article images fail to upload when wenyan-mcp runs in Docker?
The host directory holding your Markdown and images has to be mounted to /mnt/host-downloads in the container, and HOST_FILE_PATH must be set to that same host path. Local image references cannot resolve without that pair.
Which frontmatter field is mandatory in wenyan-mcp?
Only title. Cover, author, source_url, need_open_comment and only_fans_can_comment are optional, and when cover is omitted the first image in the article body is used instead.
What does type: image do in a wenyan-mcp article?
It publishes as a WeChat image post rather than a text article, and the tool extracts the images from the article body on its own. Listing the paths in image_list is the manual alternative, limited to twenty images with the first used as the cover.
Official sources
Add this badge to your README
If you maintain this project, the badge below links readers to this analysis and shows its maintenance status from the daily GitHub snapshot. Paste the markdown into your README; add ?metric=license or ?metric=stars to the image URL for a different field.
[](https://hysenlabs.com/projects/caol64-wenyan-mcp)