md2wechat-mcp
v0.2.3
Published
Markdown to WeChat HTML MCP server (Node.js)
Readme
微信 Markdown 转换 MCP
这个 MCP 服务提供以下工具:
convert_markdown_to_wechat_html: 把 Markdown 转成可用于公众号排版流程的内联样式 HTML。list_wechat_themes: 返回可用主题。open_wechat_html_in_browser: 打开转换后的缓存 HTML(cacheHtmlPath必须来自convert_markdown_to_wechat_html返回值)。wechat_get_access_token: 获取公众号接口调用凭证(读取环境变量)。wechat_upload_image: 上传“文章内图片”并返回 URL(不占素材库配额)。wechat_add_material: 上传永久素材(image | voice | video | thumb)。wechat_draft_add: 新增草稿。wechat_markdown_to_draft: 一键把 Markdown 转 HTML 并创建草稿。wechat_draft_update: 更新草稿指定文章。wechat_draft_batchget: 分页查询草稿列表。wechat_draft_delete: 删除草稿。
当前已支持常见 Markdown 语法(标题、段落、列表、引用、代码块、链接、强调)以及 GFM 风格表格。
转换成功后会自动缓存一份 HTML 到:
./.cache/wechat-mcp/
运行
开发:
npm run dev构建并运行:
npm run build
npm startCLI 转换:
node dist/cli.js ./input.md --theme defaultMCP 配置示例
npx(无需本地安装):
{
"mcpServers": {
"md2wechat-mcp": {
"command": "npx",
"args": ["-y", "md2wechat-mcp"]
}
}
}本地路径:
{
"mcpServers": {
"md2wechat-mcp": {
"command": "node",
"args": ["/absolute/path/to/md2wechat-mcp/dist/server.js"]
}
}
}工具参数
1) convert_markdown_to_wechat_html
输入:
markdown(string, optional): 直接传 Markdown 内容markdown_path(string, optional): 传本地 Markdown 文件路径(避免整篇内容走 token)theme(string, optional):default | tech | warm | apple | wechat-nativetitle(string, optional)font_size_preset(string, optional, defaultmedium):small | medium | largeaccess_token(string, optional): 传入后会自动上传本地图片并替换为微信 URL
规则:
markdown和markdown_path二选一- 如果两者都传,优先使用
markdown - 顶部一级标题(
# 标题)会在转换时从正文移除(避免正文重复显示大标题)
输出:
content[0].text:HTML 字符串(<article>...</article>)content[1].text:可见元信息文本(含cacheHtmlPath=...)meta.cacheHtmlPath返回缓存文件路径(后续open_wechat_html_in_browser.cacheHtmlPath必须使用这个字段)- 兼容说明:若客户端不展示
meta,从content[1].text解析cacheHtmlPath=...
2) list_wechat_themes
输入:空对象 输出:主题列表
3) open_wechat_html_in_browser
输入:
cacheHtmlPath(string, required): 请直接传convert_markdown_to_wechat_html返回的meta.cacheHtmlPath,不要手猜/tmp路径
输出:
- JSON 文本,包含
ok / opened / cacheHtmlPath meta.cacheHtmlPath返回缓存文件路径
4) wechat_add_material
对应官方接口:POST /cgi-bin/material/add_material
输入:
access_token(string, required)type(string, required):image | voice | video | thumbfile_path(string, required): 本地文件绝对路径title(string, optional):type=video时必填introduction(string, optional):type=video时必填
输出:
media_id(string): 新增的永久素材 IDurl(string, optional): 仅image类型返回
5) wechat_get_access_token
输入:空对象
依赖环境变量:
WECHAT_APPIDWECHAT_APPSECRET
输出:
access_tokenexpires_in
6) wechat_upload_image
对应官方接口:POST /cgi-bin/media/uploadimg
输入:
access_token(string, required)file_path(string, required): 本地图片路径,仅支持jpg/jpeg/png
输出:
url(string): 文章内可用的图片 URL
7) wechat_draft_add
对应官方接口:POST /cgi-bin/draft/add
输入:
access_token(string, required)articles(array, required): 至少 1 篇,字段与公众号草稿接口一致,核心字段:title(string, required)content(string, required): 建议使用convert_markdown_to_wechat_html生成author/digest/content_source_url/thumb_media_id/need_open_comment/only_fans_can_comment(optional)
输出:
media_id(string)
8) wechat_draft_update
对应官方接口:POST /cgi-bin/draft/update
输入:
access_token(string, required)media_id(string, required)index(number, optional, default0)article(object, required): 同wechat_draft_add单篇文章结构
输出:
errcodeerrmsg
9) wechat_markdown_to_draft
一键工具:内部自动执行 convert_markdown_to_wechat_html + wechat_draft_add。
封面自动策略(默认启用,无需传封面参数):
- 若存在
title="封面"的本地jpg/jpeg图片:该图上传为封面,并从正文移除 - 否则:尝试使用首张本地
jpg/jpeg图片作为封面 - 若无可用图片:不上传封面,草稿仍会创建
- 顶部一级标题(
# 标题)会在转换时从正文移除(避免正文重复显示大标题)
输入(核心):
access_token(string, required)article_title(string, optional):草稿标题。未传时按“首个一级标题(H1)→ 文件名(去扩展名)”自动推断markdown或markdown_path(至少一个)
输入(可选):
theme/titlefont_size_preset(可选,small | medium | large,默认medium)thumb_media_id(可选,显式覆盖自动封面)author/digest/content_source_urlneed_open_comment/only_fans_can_comment
说明:
auto_thumb_from_first_image参数已移除;封面由wechat_markdown_to_draft内置策略自动处理。
输出:
- 草稿创建结果(含
media_id) meta.cacheHtmlPath(转换缓存路径)meta.thumbMediaId(若自动或手动封面可用)
10) wechat_draft_batchget
对应官方接口:POST /cgi-bin/draft/batchget
输入:
access_token(string, required)offset(number, optional, default0)count(number, optional, default10, 范围1-20)no_content(0 | 1, optional, default0)
输出:
total_countitem_countitem
11) wechat_draft_delete
对应官方接口:POST /cgi-bin/draft/delete
输入:
access_token(string, required)media_id(string, required)
输出:
errcodeerrmsg
典型调用流程
前置:wechat_markdown_to_draft 也必须先拿到 access_token(可通过 wechat_get_access_token 获取)。
- 推荐直接调用
wechat_markdown_to_draft一步完成转换+建草稿(在已持有access_token的前提下) - 或者使用分步模式:
wechat_get_access_token→convert_markdown_to_wechat_html→wechat_draft_add - 封面默认自动处理(
title="封面"优先;否则首图);也可手动wechat_add_material(type=thumb)后在草稿里显式传thumb_media_id - 使用
wechat_draft_update / wechat_draft_batchget / wechat_draft_delete做后续管理
给 AI 的执行规范(建议写入系统提示)
当用户说“把 Markdown 上传到公众号草稿箱”时,必须遵循:
- 必须先调用
convert_markdown_to_wechat_html。 wechat_draft_add.articles[].content必须使用上一步返回的content[0].text。- 不要把原始 Markdown 直接传给
wechat_draft_add。 wechat_add_material仅用于素材上传(封面/音视频),不能替代wechat_draft_add创建草稿。- 需要手动封面时,先调用
wechat_add_material(type=thumb),再将返回media_id写入wechat_draft_add.articles[].thumb_media_id。 - 调用
open_wechat_html_in_browser时,cacheHtmlPath必须来自convert_markdown_to_wechat_html返回的meta.cacheHtmlPath。
MCP 调用示例(可直接复制)
wechat_get_access_token
{
"name": "wechat_get_access_token",
"arguments": {}
}convert_markdown_to_wechat_html
{
"name": "convert_markdown_to_wechat_html",
"arguments": {
"markdown_path": "/absolute/path/article.md",
"theme": "wechat-native",
"font_size_preset": "small",
"title": "文章标题",
"access_token": "ACCESS_TOKEN"
}
}convert_markdown_to_wechat_html -> open_wechat_html_in_browser(字段级映射)
open.arguments.cacheHtmlPath = convert.meta.cacheHtmlPath若客户端不展示 meta,可从 convert 的第二段 content 读取:
convert.content[1].text:
cacheHtmlPath=/abs/path/to/wechat-xxx.html提取规则(示例):
cacheHtmlPath = line that starts with "cacheHtmlPath="
value = text after "="{
"name": "open_wechat_html_in_browser",
"arguments": {
"cacheHtmlPath": "<convert_result.meta.cacheHtmlPath>"
}
}wechat_add_material
{
"name": "wechat_add_material",
"arguments": {
"access_token": "ACCESS_TOKEN",
"type": "thumb",
"file_path": "/absolute/path/cover.jpg"
}
}wechat_upload_image
{
"name": "wechat_upload_image",
"arguments": {
"access_token": "ACCESS_TOKEN",
"file_path": "/absolute/path/inline-image.jpg"
}
}wechat_draft_add
{
"name": "wechat_draft_add",
"arguments": {
"access_token": "ACCESS_TOKEN",
"articles": [
{
"title": "文章标题",
"author": "作者名",
"digest": "摘要",
"content": "<article>...</article>",
"thumb_media_id": "THUMB_MEDIA_ID",
"need_open_comment": 1,
"only_fans_can_comment": 0
}
]
}
}wechat_draft_update
{
"name": "wechat_draft_update",
"arguments": {
"access_token": "ACCESS_TOKEN",
"media_id": "DRAFT_MEDIA_ID",
"index": 0,
"article": {
"title": "更新后的标题",
"content": "<article>...</article>",
"thumb_media_id": "THUMB_MEDIA_ID"
}
}
}wechat_markdown_to_draft
{
"name": "wechat_markdown_to_draft",
"arguments": {
"access_token": "ACCESS_TOKEN",
"article_title": "文章标题",
"markdown_path": "/absolute/path/article.md",
"theme": "wechat-native",
"font_size_preset": "medium",
"title": "页面H1标题",
"author": "作者名",
"digest": "摘要"
}
}wechat_draft_batchget
{
"name": "wechat_draft_batchget",
"arguments": {
"access_token": "ACCESS_TOKEN",
"offset": 0,
"count": 10,
"no_content": 1
}
}wechat_draft_delete
{
"name": "wechat_draft_delete",
"arguments": {
"access_token": "ACCESS_TOKEN",
"media_id": "DRAFT_MEDIA_ID"
}
}CLI 参数
md2wechat <input> [options]
--theme <theme>--title <title>--font-size-preset <preset>--out <path>--cache-dir <path>
测试
npm test