mcpField
M
markdown-to-whatsapp

markdown-to-whatsapp

本地服务
drsound.github.io通信协作09 周前更新

关于 markdown-to-whatsapp

markdown-to-whatsapp 可将 Markdown 转为 WhatsApp 格式,提供网页工具、npm 库、命令行工具和 MCP 服务。表格会按可配置的等宽气泡宽度排版,必要时可改用列表。

主要功能

  1. 01转换 WhatsApp 格式的 Markdown 样式
  2. 02按设定的气泡宽度排版表格
  3. 03选择表格和列表布局
  4. 04在 WhatsApp 气泡中预览消息
  5. 05作为库、命令行工具或网页工具使用

适用场景

  1. 01准备适合在 WhatsApp 发送的格式化笔记
  2. 02让 Markdown 表格适配手机文本宽度
  3. 03转换文件或标准输入中的 Markdown
  4. 04分享前预览消息换行效果

MCP 相关

项目提供供 Agent 使用的 MCP 服务,可执行 Markdown 到 WhatsApp 格式的转换。提供的 README 片段未说明其 MCP 工具或设置方式。

配置

本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。

mcp.json · 通用配置
{
"mcpServers": {
"drsound-markdown-to-whatsapp": {
"command": "npx",
"args": [
"-y",
"markdown-to-whatsapp"
]
}
}
}
启动命令npx -y markdown-to-whatsapp

本地服务需要在你的电脑上安装运行环境(如 Node.js 18+)。各客户端的配置文件位置不同,请参考客户端文档。

安装包
npmmarkdown-to-whatsapp1.0.3

工具

1

收录的工具列表 · 更新于 2 周前

参数类型必填说明
markdownstring是要转换的 Markdown 文本
monoWidthinteger否读者的 WhatsApp 气泡中一行可容纳的等宽字符数;在 360 px 手机上约为 26 个(默认值 26)。表格会调整为适合此宽度。
listLayoutenum否表格转换为列表时的显示方式:rows(每行一组)、columns(每列一组,用于比较矩阵)、pairs(仅两列,显示为不带项目符号的“key: value”行)。auto 会根据表头进行判断。默认值为 auto。auto | rows | columns | pairs
tableFormatenum否auto:适合 monoWidth 时绘制表格,否则转换为项目符号列表。list:始终转换为项目符号列表。默认值为 auto。auto | list
rowSeparatorboolean否在表格行之间绘制分隔线。行内容换行时始终绘制。默认值为 false。
headingEmojisboolean否在粗体文本前添加级别表情符号(📌 🟠 🟡 …)作为标题前缀。默认值为 true。

详细介绍

Markdown 转 WhatsApp 转换器

License: MIT tests npm

将标准 Markdown 转换为 WhatsApp 的格式语法——可作为网页、npm 库、命令行工具或供 Agent 使用的 MCP 服务。

➡️ 前往在线工具

npm i markdown-to-whatsapp · npx markdown-to-whatsapp mcp

应用截图


此工具的用途

WhatsApp 使用一种非标准的文本格式语法(例如 *粗体*、_斜体_、~删除线~)。它与标准 Markdown 相似,但并不完全相同。

此工具提供了一种简单的方法,可将 Markdown 来源(如文本编辑器、Google 文档等)中的文本转换为 WhatsApp 所需的格式,省去手动修正的麻烦。

整个转换过程都通过 JavaScript 在浏览器本地运行,解析器也随页面一起提供。数据绝不会发送到服务器——离开页面的唯一请求是获取字体。

支持的转换

脚本使用 marked 库进行基于 AST 的解析,并支持以下格式:

文本样式
  • 粗体: **text** → *text*
  • 斜体: *text* 或 _text_ → _text_
  • 删除线: ~~text~~ → ~text~
  • 行内代码: `code` → `code`
  • 粗斜体: ***text*** → _*text*_(同时保留两种样式)
标题

标题会转换为粗体文本,并根据级别添加对应的表情符号前缀:

  • # H1 → *📌 H1*
  • ## H2 → *🟠 H2*
  • ### H3 → *🟡 H3*
  • 以此类推……

可在界面中关闭表情符号前缀(标题 · 表情符号),仅保留纯文本 *标题*。

列表
  • 无序列表: 使用 * 前缀,嵌套级别使用 ◦
    • 第 1 级:* Item
    • 第 2 级:* ◦ Item
    • 第 3 级:* ◦ ◦ Item
  • 有序列表: 保留编号,嵌套级别使用 ◦ 标记
    • 1. A / ◦ 1. A1 / ◦ ◦ 1. A1a / 2. B
  • 任务列表: - [x] → ☑,- [ ] → ☐,有序列表中也适用(1. ☑ 已完成)
  • 松散列表项: 同一列表项中的多个段落会合并到一行
  • 列表项中的块级内容: 代码块、引用块和嵌套列表会另起一行,显示在列表项下方
气泡宽度

WhatsApp 气泡每行可容纳固定数量的等宽字符——在 360 px 的手机上约为 26 个,这是默认值。给自己发送一个代码块,数一数它在哪个位置换行,即可测出宽度,然后在栏中设置 宽度(旁边的 ? 会显示相同说明)。字段接受 10 到 80 之间的数值:没有手机会超出这个范围。

这个数值取决于手机,而非某张表格,因此适用于所有等宽内容:表格会调整以保持在该宽度以内;预览则会严格按此宽度绘制每个代码块,并在收件人的 WhatsApp 中相同的位置换行。

表格

表格会按以下 两种样式之一呈现,可在界面中为整个文档或单张表格选择:

  1. 自动(默认):在等宽代码块中绘制表格,宽度按需调整且不超过气泡宽度;无法绘制表格时则显示为项目符号列表。

    +--------+-------------+
    | Name   | Description |
    +========+=============+
    | Value  | Details     |
    +--------+-------------+
    
  2. 列表:始终显示为项目符号列表。

列表的布局方式

列表可以通过三种方式对单元格分组。转换器会根据表头和粗体单元格猜测布局,也可以针对每张表格覆盖此猜测(布局:自动 · 行 · 列 · 配对):

  • 键值对 — 2 列,表头不限:每一行都是一条 key: value。在每一行都写出表头,比起 Italy: Rome,几乎在所有表格里读起来都更糟。

    * *CPU:* Intel Xeon
    * *RAM:* 64 GB
    * *Storage:* 1 TB SSD
    
  • 按列分组 — 3 列或更多列,且第一列表头为空或是属性名称(“Feature”“Spec”“Parameter”……),或者第一列加粗:这是一个比较矩阵,被比较的对象是各列,因此每列会成为一个分组。

    * *Proxmox*
    * ◦ _Kernel:_ KVM
    * ◦ _License:_ AGPL v3
    * *ESXi*
    * ◦ _Kernel:_ VMkernel
    * ◦ _License:_ Proprietary
    
  • 按行分组 — 其他所有情况:每行一个分组,标签取自第一个单元格。

    * *Product:* Laptop
    * ◦ _Price:_ $999
    * ◦ _Stock:_ 50
    * *Product:* Smartphone
    * ◦ _Price:_ $599
    * ◦ _Stock:_ 100
    

属性词按单词逐一匹配(“Species”不算作“spec”),支持 11 种语言:英语、意大利语、西班牙语、法语、葡萄牙语、德语、俄语、阿拉伯语、印地语、孟加拉语和印度尼西亚语。键值对必须恰好有两列;如果用于更宽的表格,则会按行分组显示。

框体如何降级

框体不会以任意宽度绘制;它会逐步缩减,直到适配 monoWidth,如果没有任何框体能适配,则会变成列表。无法要求表格宽于气泡。

  1. 完整框体,逐列移除内边距(先移除右侧,再移除左侧)。
  2. 紧凑无边框样式,同样逐步移除内边距:
     Head1|Head2       |Head-N
    ------+------------+------
     A    |BBBBBBBBBBBB|C
    
  3. 自动换行框体,先尝试完整边框,再尝试紧凑样式:每列至少保留其最长单词所需的宽度,剩余宽度按比例分配,并对单元格内容进行自动换行。行会按需增高,不设上限;行与行之间始终绘制分隔线,因为两行自动换行的内容若没有分隔线,会连在一起。单元格内容在行内顶部对齐。
    +---------+--------------+
    | Feature | Notes here   |
    +=========+==============+
    | Alpha   | short note   |
    +---------+--------------+
    | Beta    | a slightly   |
    |         | longer note  |
    +---------+--------------+
    
  4. 项目符号列表,当连最长的单词都无法容纳时(例如很长的 URL,或有 5 列且每列宽 26 个字符……)。

自动换行的高框体是否比列表更易读,可以通过预览来判断:该表格自己的面板可以将其切换为列表。无法适配任何框体的表格会在自己的面板中说明这一点,并提供列表布局,而不是提供无法产生任何变化的样式。

表格的其他行为

  • 列宽按显示单元格计算,因此表情符号和 CJK 文本仍能对齐(✅、日本語均计为 2 列)——但手机的显示能力有限:这些字形也来自备用字体,因此对齐只能尽力而为,不同于 ASCII 边框。
  • 框体和紧凑样式会遵循列对齐方式(:---、:---:、---:)。
  • 只有表头的表格不会渲染空表体或重复边框。
  • 单元格内的 <br> 会变为空格,转义后的 \| 会变成 ¦,以免被误认为多出一列。
  • 边框使用纯 ASCII 字符(+-|=),这是有意为之。WhatsApp 的等宽字体不包含制表符号:手机会从任意备用字体中获取 ─ 和 ┌,其宽度也由该字体决定;由 26 个此类字符组成的分隔线会折成两行,而旁边的文本行却不会。+-| 是等宽字体唯一承诺宽度一致的字符——这也是转义后的竖线会变成 ¦ 的原因,它是与 à 使用同一字体的 Latin-1 字符。
  • 行分隔线(默认关闭)会在表体各行之间绘制分隔线,适用于所有有边框和紧凑样式;自动换行表格则始终绘制分隔线。
  • 样式、行分隔线和列表布局都可以按表格单独设置:在预览中将鼠标悬停在表格上,会显示该表格自己的控件。这些控件初始值取自文档默认设置,并只覆盖该表格的设置;控件只显示仍然适用的选项(框体显示分隔线选项,列表显示布局选项)。宽度不在这些选项之中——气泡只有一个,所有表格使用相同宽度。带有独立设置的表格会保留虚线标记,因为面板顶部的控件会有意跳过它;表格的“重置”按钮会将设置交还给这些顶部控件。覆盖设置会根据表头文本跟随表格,因此在其上方添加或删除表格不会改变设置对应的表格。嵌套在列表项或引用块中的表格始终遵循文档默认设置。
代码块

围栏代码块和缩进代码块会原样传到 WhatsApp:转换器不会重新换行或调整缩进,因为代码中的换行属于内容,而非排版。WhatsApp 会自行在长行中间换行,聊天气泡也没有水平滚动功能,因此预览会在 monoWidth 处模拟换行,而不是滚动,并准确显示收件人会看到的换行位置。

其他元素
  • 链接: [text](url) → text (url);自动链接、<https://x>、[url](url) 和 <[email protected]> 会显示为不重复的纯 URL 或地址(不会泄露 mailto:)
  • HTML 实体: 十进制和十六进制引用,以及常见的命名实体——Latin-1 字母、标点和符号(caf&eacute; → café、&copy; → ©、&#65; → A)。较少见的引用(希腊字母、数学符号)会保留原样。
  • 行内 HTML: <b>/<strong> → *,<i>/<em> → _,<s>/<del> → ~,<code> → `,<br> → 换行;注释和其他标签会被移除
  • HTML 块: 移除标签,将块边界转换为换行,并解码实体
  • 引用块: 保留 > 前缀,支持嵌套(> > nested)
  • 代码块: 使用三个反引号保留;内容中的三个反引号会替换为 ˋˋˋ,以免提前结束代码块
  • 水平分隔线: --- → ───────────────
  • 转义字符: 使用相似的 Unicode 字符(∗、_、∼),避免 WhatsApp 将其识别为格式标记
WhatsApp 特定处理
  • 忽略单词中间的格式: super**bold**ly → superboldly(WhatsApp 不支持单词中间的格式)
  • 标点符号可作为边界: **Name**: value → *Name*: value,(**x**) 和 **end**. 也同样适用

如何使用

  1. 打开网页: https://drsound.github.io/markdown-to-whatsapp/
  2. 将 .md 文件粘贴、输入或拖放到左侧面板中。“试用示例”会填入一条示例消息。
  3. 右侧面板会在 WhatsApp 聊天气泡中显示消息,效果与收件人看到的完全一致;“查看原始语法”会显示将要复制的文本。两个面板会同步滚动——鼠标指针所在面板会带动另一个面板滚动;输入时,预览也会跟随光标移动。
  4. 选择**“复制到 WhatsApp”,或选择“在 WhatsApp 中分享”**,通过 wa.me 打开一条已准备好消息的聊天。特别长的消息无法放入链接中——浏览器会截断几千个字符之后的 URL——因此“分享”选项会改为提示你复制。

界面会遵循操作系统的浅色或深色主题;标题栏中的切换按钮可覆盖此设置,选择也会被记住。选项栏按内容类型分为不同部分,只有文本包含相应内容时才会显示:气泡(存在表格或代码块时显示宽度选项)、表格(样式和分隔符)以及标题(表情符号前缀)。如果其他设置让某个控件失去作用——例如列表样式下的分隔符,或没有单色内容可绘制时的宽度——该控件会在原位变暗,而不是被移除,以保持选项栏形状不变。选项会与主题一同存储在 localStorage 中。每个表格的设置不会被存储,因为它们属于正在转换的文本。

在代码、Shell 或 Agent 中使用

同一转换器也以 markdown-to-whatsapp 的名称发布在 npm 上(需要 Node 20 或更高版本)。可用选项与上述相同,名称和默认值也一致,并会在本节末尾完整列出。

库
npm install markdown-to-whatsapp
import { convertTextToWhatsapp, convertToBlocks } from 'markdown-to-whatsapp';

convertTextToWhatsapp('# Hi **there**');
// → '*📌 Hi there*'

convertTextToWhatsapp(markdown, { monoWidth: 30, tableFormat: 'auto', headingEmojis: false });

// The same conversion with the blocks kept apart: each has the source `line` it starts on,
// and each table its `key`, `columns`, `fitsBox`, `asList` and `listLayout`
const { text, blocks } = convertToBlocks(markdown, { monoWidth: 30 });
命令行
npx markdown-to-whatsapp notes.md                  # a file…
cat notes.md | npx markdown-to-whatsapp            # …or stdin
npx markdown-to-whatsapp notes.md --width 32 --tables list --no-emoji
npx markdown-to-whatsapp notes.md --json           # the blocks, for scripting
npx markdown-to-whatsapp --help

--width(10–80)、--tables auto|list、--layout auto|rows|columns|pairs、--separator、--no-emoji、--json,以及 -h, --help 和 -v, --version。输出会写入 stdout;遇到无效选项时,会在 stderr 中说明原因并以状态码 2 退出。

MCP 服务

此程序包通过 stdio 作为 Model Context Protocol 服务运行,因此 Agent 可以自行转换文本——这对表格很重要:根据 26 个字符的气泡宽度计算列数,是模型容易出错、而此工具能正确处理的事情。

claude mcp add markdown-to-whatsapp -- npx -y markdown-to-whatsapp mcp

或者,对于 Claude Desktop 和其他接受 JSON 配置的客户端:

{
  "mcpServers": {
    "markdown-to-whatsapp": {
      "command": "npx",
      "args": ["-y", "markdown-to-whatsapp", "mcp"]
    }
  }
}

它提供一个工具 convert_markdown_to_whatsapp,接受 markdown,以及可选的 monoWidth、tableFormat、listLayout、rowSeparator 和 headingEmojis。转换后的文本会作为工具内容返回,结构化结果则会将其作为 text 返回,并附带 tables:每个表格对应一条记录,包含 key、columns、fitsBox、asList 和 listLayout,以便 Agent 判断哪些表格转换成了方框,哪些转换成了列表。此工具为只读且幂等。

选项

选项包括 tableFormat(auto | list)、monoWidth、rowSeparator、headingEmojis、listLayout(auto | rows | columns | pairs)和 tableOverrides——它可以是一个按表格在文档中的位置索引的数组,也可以是一个以表格的 key 为键的对象(将表头文本用 | 连接;重复的表头会附加 #2、#3……);每个条目都可以单独覆盖该表格的其他任何选项。页面仅按表格提供 listLayout 选项。

输入仍接受旧名称:tableThreshold 对应 monoWidth,ascii / always 对应 tableFormat(ascii 从不绘制宽于气泡的方框,因此会映射到 auto)。同时提供新旧名称时,以新名称为准,旧名称会被丢弃。过去用于选择 Unicode 方框线条样式的 borderStyle 仍会被接受,但会被忽略。

开发

运行测试

Node 20 或更新版本;在仓库根目录运行:

npm install
npm test

测试套件使用基于文件的测试:

  • tests/inputs/*.md - Markdown 输入文件
  • tests/inputs/*.json - 可选的每个测试样例的转换器选项(例如 { "monoWidth": 40 })
  • tests/expected/*.txt - 预期的 WhatsApp 输出

此外,还会检查一些不变量:随附的解析器与已安装的解析器一致,convertToBlocks 报告正确的源代码行,程序包入口为页面脚本。

每次推送和拉取请求都会在 CI 中运行测试(.github/workflows/test.yml)。

项目结构
  • docs/converter.js - 转换器本身:一个纯 ES 模块,不依赖 DOM,选项通过参数传入。它既是页面导入的脚本,也是 npm 程序包的入口(exports["."]),因此只需保留一份,无需构建步骤。它提供 convertTextToWhatsapp(markdown, options)、convertToBlocks(markdown, options)——执行相同的转换,但将顶层区块分开,并为每个区块标记其所属的表格,这是按表格设置选项的基础——以及 UI 用于仅在适用时显示选项的 mdContainsTable / mdContainsHeading / mdContainsCode 查询。convertToBlocks 的每个区块都会报告其起始源代码 line——这能让两个面板保持同步滚动——每个表格区块还会报告其 key、columns、fitsBox(可以绘制方框)、asList(实际输出的内容)和 listLayout,以便界面准确提供剩余可选项。
  • docs/ui.js - 页面逻辑:主题、上下文选项、WhatsApp 预览、面板间滚动同步、复制和分享
  • docs/index.html、docs/style.css - 标记和手写样式表(不使用 CSS 框架)
  • docs/vendor/ - marked 的 ES 构建版本,由 npm run vendor 从 node_modules 复制而来;页面的导入映射会将 marked 解析到此处
  • bin/markdown-to-whatsapp.js - 命令行工具;bin/mcp.js - 在 mcp 上启动的 MCP 服务,仅在此时加载,因此转换文件时不会加载协议 SDK
  • scripts/vendor.js - 将 marked 的 ES 构建版本复制到 docs/vendor/(npm run vendor)
  • tests/ - 测试样例和运行器
marked 依赖

marked 的版本在 package.json 中固定为 18.0.10,测试套件会核对页面从 docs/vendor/ 加载的副本是否与之匹配,因此页面、包和测试始终以相同的方式解析 Markdown。要升级版本:更改固定版本,运行 npm install、npm run vendor 和 npm test。

发布
npm test
npm pack --dry-run   # docs/converter.js, bin/, README, LICENSE — nothing else
npm publish
本地开发
cd docs
python3 -m http.server 8080
# Open http://localhost:8080

许可证

MIT,参见 LICENSE 文件。

评论

支持 Markdown

第一次接触 MCP?

从协议是什么,到在客户端里装上第一个服务,这几条回答覆盖了大多数入门问题。

一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。