markdown-to-whatsapp
Conversor de Markdown a WhatsApp
Convierte Markdown estándar a la sintaxis de formato de WhatsApp: como página web, biblioteca npm, herramienta de línea de comandos o servidor MCP para agentes.
➡️ Ir a la herramienta en vivo
npm i markdown-to-whatsapp · npx markdown-to-whatsapp mcp

Propósito de esta herramienta
WhatsApp utiliza una sintaxis no estándar para el formato de texto (p. ej., *negrita*, _cursiva_, ~tachado~). Es similar, pero no idéntica, al Markdown estándar.
Esta herramienta ofrece una forma sencilla de convertir texto de fuentes Markdown (editores de texto, Google Docs, etc.) al formato que espera WhatsApp, evitando la necesidad de corregirlo manualmente.
Todo el proceso de conversión se ejecuta localmente en tu navegador mediante JavaScript, y el analizador se incluye con la página. Ningún dato se envía jamás a un servidor: la única petición que sale de la página es la de la fuente.
Related MCP server: AI Group Markdown to Word MCP Server
Conversiones admitidas
El script utiliza la biblioteca marcada para un análisis adecuado basado en AST y gestiona:
Estilos de texto
Negrita:
**texto**→*texto*Cursiva:
*texto*o_texto_→_texto_Tachado:
~~texto~~→~texto~Código en línea:
`código`→`código`Negrita+Cursiva:
***texto***→_*texto*_(conserva ambos estilos)
Encabezados
Los encabezados se convierten a texto en negrita con prefijos de emoji según el nivel:
# H1→*📌 H1*## H2→*🟠 H2*### H3→*🟡 H3*Y así sucesivamente...
El prefijo de emoji se puede desactivar en la interfaz (Encabezados · Emoji), dejando el título simple en *Título*.
Listas
Listas desordenadas: utiliza el prefijo
*con◦para los niveles anidadosNivel 1:
* ElementoNivel 2:
* ◦ ElementoNivel 3:
* ◦ ◦ Elemento
Listas ordenadas: conserva la numeración, con
◦marcando los niveles anidados1. A/◦ 1. A1/◦ ◦ 1. A1a/2. B
Listas de tareas:
- [x]→☑,- [ ]→☐, también dentro de listas ordenadas (1. ☑ hecho)Elementos sueltos: varios párrafos de un mismo elemento se unen en una sola línea
Contenido en bloque dentro de elementos: los bloques de código, las citas y las listas anidadas se emiten en sus propias líneas debajo del elemento
Anchura de la burbuja
Una burbuja de WhatsApp admite un número fijo de caracteres monoespaciados por línea: unos 26 en un teléfono de 360 px, que es el valor por defecto. Mide la tuya enviándote un bloque de código a ti mismo y contando dónde se parte, y luego fija Anchura en la barra con ese valor (el ? al lado dice lo mismo). El campo acepta de 10 a 80: ningún teléfono queda fuera de ese rango.
El número es una propiedad del teléfono, no de ninguna tabla en particular, por lo que rige todo lo monoespaciado: las tablas se degradan para mantenerse por debajo de él, y la vista previa dibuja cada bloque de código exactamente con esa anchura, ajustando donde se ajustará el WhatsApp del destinatario.
Tablas
Una tabla se representa en dos estilos, elegidos en la interfaz para todo el documento o para una sola tabla a la vez:
Automático (por defecto): una tabla dibujada dentro de un bloque monoespaciado, tan ancha como sea necesario y nunca más ancha que la burbuja — y la lista con viñetas cuando no se puede dibujar ninguna caja.
+--------+-------------+ | Name | Description | +========+=============+ | Value | Details | +--------+-------------+Lista: siempre la lista con viñetas.
Cómo se distribuye la lista
Una lista puede agrupar las celdas de tres maneras. El conversor deduce automáticamente a partir de los encabezados y las celdas en negrita, y esa suposición se puede anular tabla por tabla (Distribución: Automático · Filas · Columnas · Parejas):
Parejas — 2 columnas, sean cuales sean los encabezados: cada fila es una línea de
clave: valor. Escribir los encabezados completos en cada fila se lee peor queItalia: Romaen casi cualquier tabla.* *CPU:* Intel Xeon * *RAM:* 64 GB * *Storage:* 1 TB SSDColumnas — 3 o más columnas cuyo primer encabezado está vacío o nombra una propiedad ("Característica", "Spec", "Parámetro"...), o cuya primera columna está en negrita: una matriz de comparación, donde las cosas comparadas son las columnas, así que cada columna se convierte en un grupo.
* *Proxmox* * ◦ _Kernel:_ KVM * ◦ _License:_ AGPL v3 * *ESXi* * ◦ _Kernel:_ VMkernel * ◦ _License:_ ProprietaryFilas — todo lo demás: un grupo por fila, etiquetado por su primera celda.
* *Product:* Laptop * ◦ _Price:_ $999 * ◦ _Stock:_ 50 * *Product:* Smartphone * ◦ _Price:_ $599 * ◦ _Stock:_ 100
Las palabras de propiedad se comparan palabra por palabra ("Especie" no cuenta como "spec") en 11 idiomas: inglés, italiano, español, francés, portugués, alemán, ruso, árabe, hindi, bengalí e indonesio. Las parejas necesitan exactamente dos columnas; si se piden en una tabla más ancha, se interpreta como Filas.
Cómo se degrada la caja
La caja no se dibuja con cualquier ancho: se degrada hasta caber en monoWidth, y se convierte en una lista cuando nada cabe. No hay forma de pedir una tabla más ancha que la burbuja.
Caja completa, eliminando el relleno columna a columna (primero por la derecha, luego por la izquierda).
Estilo compacto sin bordes, de nuevo eliminando el relleno de forma progresiva:
Head1|Head2 |Head-N ------+------------+------ A |BBBBBBBBBBBB|CCaja con ajuste, primero con bordes completos y luego compacta: cada columna recibe al menos su palabra más larga, el ancho restante se distribuye proporcionalmente y las celdas se ajustan. Las filas crecen tanto como necesiten — no hay límite — y siempre se dibuja una regla entre ellas, porque dos filas ajustadas sin ella se mezclan. Las celdas se alinean a la parte superior de su fila.
+---------+--------------+ | Feature | Notes here | +=========+==============+ | Alpha | short note | +---------+--------------+ | Beta | a slightly | | | longer note | +---------+--------------+Lista con viñetas, cuando ni siquiera las palabras más largas caben (una URL larga, cinco columnas a 26 caracteres…).
Si una caja ajustada y alta se lee mejor que la lista es un juicio que la vista previa te permite hacer: el panel propio de esa tabla la cambia a Lista. Una tabla que no puede alojar ninguna caja lo indica en su panel y ofrece la distribución de lista en lugar de un estilo que no podría cambiar nada.
Comportamiento adicional de las tablas
Los anchos de columna se miden en celdas de visualización, por lo que los emojis y el texto CJK se alinean (✅, 日本語 cuentan como dos columnas) — en la medida en que el teléfono lo permita: esos símbolos vienen también de una fuente de reserva, así que la alineación es lo mejor posible, a diferencia de los bordes ASCII.
La alineación de columnas (
:---,:---:,---:) se respeta en los estilos de caja y compacto.Las tablas de solo encabezado se renderizan sin cuerpo vacío ni borde duplicado.
<br>dentro de una celda se convierte en un espacio, y una barra vertical con escape\|se convierte en¦para que no pueda simular una columna de más.Los bordes son ASCII plano (
+|-), a propósito. La fuente monoespaciada de WhatsApp no tiene glifos de dibujo de cajas: un teléfono toma─y┌de cualquier fuente de reserva que tenga, con el ancho que esa fuente les dé, y una línea de 26 de ellas se envuelve en dos líneas, mientras que las filas de texto a su lado no lo hacen.+-son los únicos caracteres cuyo ancho una fuente monoespaciada realmente promete — que es también por eso que la barra escapada se convierte en¦, un carácter latino-1 de la misma fuente queà.El separador de filas (desactivado por defecto) dibuja una regla entre las filas del cuerpo, en todos los estilos de caja y compactos; una tabla ajustada lo dibuja independientemente.
La estilo, el separador de filas y la distribución de la lista se pueden configurar por tabla: al pasar el ratón sobre una tabla de la vista previa aparecen sus propios controles, que parten de ajuste por defecto del documento y lo sustituyen solo para esa tabla, mostrando únicamente los que siguen siendo relevantes (el separador en una caja, la distribución en una lista). La anchura no está entre esas opciones: hay una sola burbuja y es la misma para todas las tablas. Una tabla con sus propias configuraciones mantiene une marca discontinua, ya que los controles de la parte superior del panel deliberadamente no la modifican — su botón "restablecer" se la devuelve a ellos. Las anulaciones siguen a la tabla por el texto de su encabezado, de modo que añadir o eliminar una tabla más arriba no las desplaza. Una tabla anidada dentro de un elemento de lista o una cita sigue siempre la configuración predeterminada del documento.
Bloques de código
Los bloques delimitados e indentados llegan a WhatsApp tal cual: el conversor nunca los reajusta ni reindenta, porque un salto de línea dentro del código es contenido, no estructura. WhatsApp envuelve las líneas largas por sí solo, a mitad de palabra, y una burbuja de chat no tiene desplazamiento horizontal — por lo que la vista previa reproduce ese ajuste a monoWidth en lugar de desplazarse y muestra exactamente donde el destinatario verá el corte.
Otros elementos
Enlaces:
[texto](url)→texto (url); los enlaces automáticos,<https://x.com>,[url](url)y<me@x.com>se muestran como la URL o dirección desnuda (sin duplicados ni fugasmailto:)Entidades HTML: referencias decimales y hexadecimales además de las comunes con nombre — letras latino-1, puntuación y símbolos (
café→café,©;A→A). Las referencias más raras (griegas y matemáticas) se dejan tal cual.HTML en línea:
<b>/<strong>→*, el<i>/<em>→_,<s>/<del>→~,<code>→`;<br>→ nueva línea; los comentarios y otras etiquetas se eliminanBloques HTML: las etiquetas se eliminan, los límites de los bloques se convierten en nuevas líneas y las entidades se decodifican
Citas: conserva el prefijo
>, admite anidamiento (> > citada)Bloques de código: se conservan con triple comilla invertida; una triple comilla invertida dentro del contenido se sustituye por
ˋˋˋpara que no pueda cerrar el bloque prematuramenteLínea horizontal:
---→───────────────Caracteres de escape: utiliza sustitutos Unicode parecidos (
∗,_,∼) para que WhatsApp no los interprete como formato
Gestión específica de WhatsApp
El formato parcial de palabras se ignora:
super**bold**ly→superboldly(WhatsApp no admite formato a mitad de palabra)La puntuación es un límite válido:
**Nombre**: valor de→*Name*: value, y también lo son(**x**)y**fin**.
Cómo usarlo
Abre la página web: https://drsound.github.io/markdown-to-whatsapp/
Pega, escribe o suelta un archivo
.mden el panel izquierdo. "Probar con un ejemplo" la rellena con un mensaje de muestra.El panel derecho muestra el mensaje en una burbuja de WhatsApp, tal cual lo verá el destinatario; "ver sintaxis sin procesar" muestra el texto que se copiará. Los dos paneles se desplazan juntos — el que esté bajo el cursor dirige — y mientras escribes, la vista previa sigue al cursor.
"Copiar para WhatsApp", o "Compartir en WhatsApp" para abrir un chat con el mensaje listo mediante
wa.me. Los mensajes muy largos no caben en un enlace — los navegadores cortan las URL a partir de unos pocos miles de caracteres — así que "Compartir" se retira y sugiere copiar.
La interfaz sigue el tema claro u oscuro del sistema operativo; la alternancia de la cabecera lo anula y esa elección se recuerda. La barra de opciones tiene una sección por tipo de contenido, cada una visible solo mientras el texto lo contiene: Burbuja (la anchura, cuando hay una tabla o un bloque de código), Tablas (estilo y separador) y Encabezados (el prefijo de emoji). Un control que otra configuración convierte en inútil — el separador en estilo "Lista", la anchura sin nada monoespaciado que dibujar — se atenúa in situ en lugar de eliminarse, de modo que la barra mantenga su forma. Las opciones se guardan en localStorage junto con el tema. Las opciones de cada tabla no se guardan: pertenecen al texto que se convierte.
Uso desde el código, la terminal o un generador de contenido
El mismo convertidor se publica en npm con el nombre markdown-to-whatsapp (Node 20 o posterior). Las opciones son las descritas anteriormente, con los mismos nombres y valores por defecto, y se enumeran completas al final de esta sección.
Biblioteca
npm install markdown-to-whatsappimport { 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 });Línea de comandos
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, además de -h, --help y -v, --version. La salida va a stdout; una opción incorrecta explica el porqué en stderr y sale con código 2.
Servidor MCP
El paquete se ejecuta como un servidor de protocolo Model Context sobre stdio, de modo que un generador pueda convertir texto por sí mismo — algo importante en las tablas: contar columnas frente a una burbuja de 26 caracteres es lo que el modelo hace mal y esta herramienta hace bien.
claude mcp add markdown-to-whatsapp -- npx -y markdown-to-whatsapp mcpo, para Claude Desktop y otros clientes que admiten una configuración JSON:
{
"mcpServers": {
"markdown-to-whatsapp": {
"command": "npx",
"args": ["-y", "markdown-to-whatsapp", "mcp"]
}
}
}Expone una única herramienta, convert_markdown_to_whatsapp, que recibe markdown y los opcionales monoWidth, tableFormat, listLayout, rowSeparator y headingEmojis. El texto se devuelve como contenido de la herramienta, y el resultado estructurado lo lleva como text, junto con tables: una entrada por tabla, con key, columns, fitsBox, asList y listLayout, para que el agente pueda saber qué tablas acabaron siendo una caja y cuáles una lista. La herramienta es de solo lectura e idempotente.
Opciones
Las opciones son tableFormat (auto | list), monoWidth, rowSeparator, headingEmojis, listLayout (auto | rows | columns | pairs) y tableOverrides: ya sea un array indexado por la posición de la tabla en el documento o un objeto indexado por la key de la tabla (sus textos de encabezado unidos con |, más #2, #3… para encabezados repetidos), and cada entrada anula cualquira de la demás únicamente para la esa tabla. La página expone listLayout únicamente per tabla.
Los nombres anteriores se siguen aceptando en la entrada: tableThreshold por monoWidth, y ascii / always para tableFormat (ascii nunca dibujó una caja más ancha que el globo, así que se asigna a auto). Cuando se proporción both the old and the new name, the new one, the old is dropped. borderStyle, que solía elegir el dibujo de cajas con Unicode, se accepta y se ignora.
Development
Running the tests
Node 20 or above, from the root of the repository:
npm install
npm testThe test suite uses a valid version:
tests/inputs/*.md- Markdown input filestests/inputs/*.json- optional converter options per fixture (e.g.{ "monoWidth": 4 })tests/expected/*.txt- Expected Whatsapp output
adás de algunos invariants: el análisis de la vendor coincides with the installed one, convertToBlocks reports the correct source lines, and the entry point of the package is the page script.
Las pruebas also/correr en CI en cada push and pull request (.github/workflows/test.yml).
Standard structure
docs/converter.js: the converter itself: a pure ES module, sin DOM, with the options passed as a parameter. It is at the same time the script that the page imports and the entry point of the npm package (exports["."]), so there is one copy and no build step. ExposesconvertToWhatsapp(markdown, options),convertToBlocks(markdown, options)— the same conversion but with the top-level blocks separated and each one tagged with the block it is for, which is what the per-table options are built on — and themdContainsTable/mdContainsHeading/mdContainsCodequeries that the UI uses to show an option only when it applies. InconvertToBlocks, all the blocks with thelinesource from which it starts — what keeps the two panels in sync — and each table block also itskey,columns,fitsBox(if a box is possible),does. Each entry aslistLayout, so that the interface can offer exactly the choices left.docs/ui.js- page wirings: theme, contextual options, WhatsApp preview, scroll sync between panels, copy and sharedocs/index.html,docs/style.css- markup and hand-written style, no CSS frameworkdocs/vendor/- the ES build of marked, copied fromnode_modulesbynpm run build; the page's map importsmarkedinto itbin/markdown-to-whatsapp.js- the command-line tool;bin/mcp.js- the MCP server, loaded only then, so converting a file never loads the protocol SDKscripts/vendor.js- copys the ES build of marked to/vendor(npm run vendor)tests/- the fixtures and the runner
La dep encia de marked
The version of marcado is fixed at 18.0.0 in package.json, and the copy that the page loads from docs/vendor/ is checked against it by the test suite, so that the page, the package and the tests always mark the same Markdown. To update it: change the fixed version, npm install, npm run .`.,
in
npm test
npm pack --dry-run # docs/converter.js, bin/, README, LICENSE — nothing else
npm publishLocal development
cd docs
python3 -m http.server 8080
# Open http://localhost:8080Licencia
MIT, consulta el archivo LICENSE.
Available Tools
1 toolconvert_markdown_to_whatsappConvert Markdown to WhatsAppARead-onlyIdempotent
Convert Markdown into the formatting WhatsApp renders (bold, italic, strike, code, lists, quotes), ready to paste or send.
Use it instead of hand-writing WhatsApp syntax whenever the text has tables, nested lists, headings or inline formatting next to punctuation.
It applies WhatsApp's real rules: markers only on word boundaries, escapes that WhatsApp does not interpret, no mid-word formatting.
Each table is drawn as a monospace box sized to the reader's phone, degrading in this order: cell padding removed, then borders, then cells word-wrapped. It becomes a bulleted list only when no box fits.
Counting columns against a 26-character bubble is exactly what this tool does and a model does not.
| Name | Required | Description | Default |
|---|---|---|---|
| markdown | Yes | The Markdown text to convert | |
| monoWidth | No | Monospace characters that fit on one line of the reader's WhatsApp bubble; about 26 on a 360 px phone (default 26). Tables are drawn to fit this width. | |
| listLayout | No | How a table reads when it is a list: rows (one group per row), columns (one group per column, for comparison matrices), pairs (bare "key: value" lines, two columns only). auto guesses from the headers. Default auto. | |
| tableFormat | No | auto: a drawn table when it fits monoWidth, a bulleted list otherwise. list: always a bulleted list. Default auto. | |
| rowSeparator | No | Draw a rule between table rows. Always drawn when a row wraps. Default false. | |
| headingEmojis | No | Prefix headings with a level emoji (📌 🟠 🟡 …) before the bold text. Default true. |
Output Schema
| Name | Required | Description |
|---|---|---|
| text | Yes | The WhatsApp-formatted message |
| tables | Yes | One entry per table in the document, in order |
TDQS
Does the description disclose side effects, auth requirements, rate limits, or destructive behavior?
Discloses real behavioral rules: markers only on word boundaries, escapes that WhatsApp does not interpret, no mid-word formatting, and a detailed table-degradation order (padding removed, then borders, then word-wrapping, then becoming a bulleted list). The annotations already indicate read-only and idempotent behavior, and the description adds substantial context beyond that without contradicting it.
Agents need to know what a tool does to the world before calling it. Descriptions should go beyond structured annotations to explain consequences.
Is the description appropriately sized, front-loaded, and free of redundancy?
Four sentences carry the core purpose, usage triggers, formatting rules, and table behavior without filler. The main conversion statement is front-loaded, and each sentence contributes distinct, necessary information.
Shorter descriptions cost fewer tokens and are easier for agents to parse. Every sentence should earn its place.
Given the tool's complexity, does the description cover enough for an agent to succeed on first attempt?
For a tool with 6 parameters, a complete input schema, and an output schema, the description covers purpose, when to use it, behavioral nuances, and table degradation fallbacks. Nothing significant is missing for an agent to select and invoke the tool correctly.
Complex tools with many parameters or behaviors need more documentation. Simple tools need less. This dimension scales expectations accordingly.
Does the description clarify parameter syntax, constraints, interactions, or defaults beyond what the schema provides?
Schema description coverage is 100%, so the baseline is 3. The description adds general context about the 26-character bubble and table-fitting behavior, but it does not explain individual parameters beyond what the schema already provides.
Input schemas describe structure but not intent. Descriptions should explain non-obvious parameter relationships and valid value ranges.
Does the description clearly state what the tool does and how it differs from similar tools?
States a specific verb and resource: 'Convert Markdown into the formatting WhatsApp renders' and lists concrete formatting constructs (*bold*, _italic_, ~strike~, `code`, lists, quotes). The scope is precise and there are no sibling tools to confuse it with.
Agents choose between tools based on descriptions. A clear purpose with a specific verb and resource helps agents select the right tool.
Does the description explain when to use this tool, when not to, or what alternatives exist?
Explicitly tells when to use it: 'Use it instead of hand-writing WhatsApp syntax whenever the text has tables, nested lists, headings or inline formatting next to punctuation.' It names the alternative (hand-writing) and gives concrete trigger conditions, though it does not state when not to use the tool or list exclusions for simple text.
Agents often have multiple tools that could apply. Explicit usage guidance like "use X instead of Y when Z" prevents misuse.
Tool Schema Changelog
Recent tool additions, removals, and schema changes observed during successful MCP inspections.
1 tool update
v1.0.2- First observed
convert_markdown_to_whatsapp
TDQS
Scored across 1 tool
With only one tool, there is no possibility of confusion or misselection. The tool's purpose is entirely unambiguous within the server's scope.
The single tool name uses a clear snake_case verb_noun pattern that directly describes its action and target. Even without other tools for comparison, the naming is predictable and self-consistent.
The server has just one tool, but its purpose is extremely focused: converting Markdown to WhatsApp formatting. This is slightly below the typical 3-15 tool range, yet the single tool fully covers the server's stated domain.
The tool comprehensively handles the full conversion scenario, including tables, nested lists, headings, inline formatting, and WhatsApp's real formatting rules. Within the narrow markdown-to-WhatsApp domain, there are no obvious missing operations.
Maintenance
Related MCP Connectors
Markdown to PDF: headings, bold, code, lists, rules. A4/Letter/Legal. Free 30/hr. MCP + REST.
Use your own Word templates to convert Markdown → DOCX/PDF/HTML from any MCP-compatible AI.
Free deterministic PDF, Markdown, citation, invisible-character, and formatting cleanup for agents.
11PDF/Word/Excel/HTML to clean Markdown for LLMs and RAG. Keys $19/mo; 50 free calls a day.
Related MCP Servers
- AlicenseNot gradedqualityDmaintenanceConverts various file formats to Markdown using the MarkItDown utility and can be integrated with MCP clients for seamless document processing and conversion.88MIT
- AlicenseAqualityCmaintenanceConverts Markdown documents to professional Word documents with advanced formatting capabilities including mathematical formulas, custom styling, tables, images, headers/footers, and watermarks.480 npm14MIT
- FlicenseNot gradedqualityDmaintenanceConverts documents (PDF, DOCX, images, etc.) to Markdown using Microsoft's Markitdown library, with no local setup required. Integrates with AI agents via MCP for seamless document conversion.1-
- AlicenseNot gradedqualityAmaintenanceA stdio MCP server that converts CommonMark/GFM Markdown into Slack mrkdwn, Microsoft Teams HTML, Jira wiki markup, and plain text, enabling agents to transform content for downstream platforms without posting or sending messages.7 npmMIT