README.md

January 23, 2026 ยท View on GitHub

md2x

Markdown โ†’ PDF/DOCX/HTML/Image converter.

npm version

Features

  • ๐Ÿ“ฆ Supported formats: pdf, docx, html, png, jpg/jpeg, webp
  • ๐Ÿ”Œ MCP (Model Context Protocol): built-in MCP server
  • ๐Ÿง  Skills: includes an agent skill at skills/md2x/SKILL.md for repeatable conversions/workflows
  • ๐Ÿงฉ Custom templates: render md2x blocks with Vue SFC (.vue) and Svelte 5 (.svelte) templates (plus plain HTML)

CLI Usage

Export to PDF:

npx md2x input.md

Export to DOCX:

npx md2x input.md -f docx

Export to HTML:

npx md2x input.md -f html

Export to PNG:

npx md2x input.md -f png -o output.png

List themes:

npx md2x --list-themes

Use a theme:

npx md2x input.md -o output.pdf --theme academic

Help:

npx md2x -h

CLI Options

OptionAliasDescriptionDefaultValues
--help-hShow help message--
--version-vShow version number--
--output-oOutput file pathInput name with format extensionFile path
--format-fOutput formatpdfpdf, docx, html, png, jpg/jpeg, webp
--theme-tTheme namedefaultSee --list-themes
--diagram-mode-HTML/Image diagram rendering modeliveimg, live, none
--hr-page-break-Convert horizontal rules to page breakstrue for PDF/DOCX, false for HTML/Imagetrue, false
--templates-dir-Extra template dir for md2x blocks (repeatable; resolved against input dir when relative)-Directory path
--list-themes-List all available themes--

Diagram Modes (HTML/Image)

  • live (default): Render diagrams in the browser on load using online CDN scripts (Mermaid, @viz-js/viz, Vega-Lite, Infographic)
  • img: Pre-render diagrams as embedded images (offline, stable; no CDN)
  • none: Keep diagram source blocks only (no rendering)

Front Matter Options

When converting a markdown file, you can put options in YAML front matter (the CLI merges front matter with CLI flags; explicit CLI flags win).

Common (All Formats)

---
format: pdf        # pdf | docx | html | png | jpg | jpeg | webp
theme: default
hrAsPageBreak: true
---

PDF

---
format: pdf
title: "My Doc"     # used for PDF metadata/header templates
pdf:
  format: A4        # A4 | Letter | Legal | A3 | A5
  landscape: false
  margin:
    top: 1cm
    bottom: 1cm
    left: 1cm
    right: 1cm
  printBackground: true
  scale: 1
  displayHeaderFooter: false
  headerTemplate: "<div style='font-size:10px;width:100%;text-align:center;'><span class='title'></span></div>"
  footerTemplate: "<div style='font-size:10px;width:100%;text-align:center;'>Page <span class='pageNumber'></span> / <span class='totalPages'></span></div>"
---

DOCX

---
format: docx
theme: default
hrAsPageBreak: true
---

HTML

---
format: html
title: "My Doc"
standalone: true    # full HTML document (default)
baseTag: true       # emit <base href="file://.../"> for resolving relative paths (default)
diagramMode: live   # img | live | none
cdn:                # optional: override CDN URLs (used when diagramMode: live)
  mermaid: "https://cdn.jsdelivr.net/npm/mermaid@11.12.2/dist/mermaid.min.js"
  # Template blocks:
  vue: "https://unpkg.com/vue@3/dist/vue.global.js"
  vueSfcLoader: "https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"
  svelteCompiler: "https://esm.sh/svelte@5/compiler"
  svelteBase: "https://esm.sh/svelte@5/"
---

Image (PNG/JPEG/WebP)

---
format: png
diagramMode: live   # or "img" for offline (no CDN)
image:
  # selector can be a string or an array of selectors (CSS selector list).
  selector:
    - 'div.md2x-diagram[data-md2x-diagram-kind="mermaid"]'
    - 'div.md2x-diagram[data-md2x-diagram-kind="infographic"]'
  # selectorMode: first | each | union | stitch (default: stitch)
  # - union: capture the union bounding box (includes in-between page content)
  # - stitch: stack matched elements and capture only them (no in-between content)
  selectorMode: stitch
  selectorGap: 16      # optional: vertical gap (px) between stitched elements
  selectorPadding: 8   # optional: padding (px) around the stitched region
  split: auto          # optional: split very tall output into multiple images
---

When image.split produces multiple parts, outputs are written as output.part-001.png, output.part-002.png, ...

Diagram blocks are tagged with data-md2x-diagram-kind so you can target specific types via selectors:

image:
  selector: 'div.md2x-diagram[data-md2x-diagram-kind="mermaid"]'
  selectorMode: stitch

md2x Template Blocks

Besides diagram blocks (mermaid/dot/vega-lite/infographic), md2x also supports template blocks via:

```md2x
{
  type: 'vue',          // "vue" | "html" | "svelte"
  template: 'example.vue', // or "example.html" / "example.svelte"
  data: [{ title: 't', message: 'm' }]
}
```
//example.vue
<script setup>
const data = templateData;
</script>

<template>
<div class="my-component">Hello md2x! This is vue template</div>
<div v-for="(item, index) in data" :key="index">
  <h2>{{ item.title }}</h2>
  <p>{{ item.message }}</p>
</div>
</template>

<style scoped>
.my-component {
  color: red;
}
</style>
<!-- example.svelte (Svelte 5) -->
<script>
  const data = templateData;
</script>

<div class="my-component">Hello md2x! This is svelte template</div>
{#each data as item, index}
  <div>
    <h2>{item.title}</h2>
    <p>{item.message}</p>
  </div>
{/each}

<style>
  .my-component { color: red; }
</style>

Config Fields

  • type: "vue", "html", or "svelte" (Svelte 5)
  • template: template file name/path
    • if you only pass a filename (e.g. example.vue), it is treated as ${type}/${template} (e.g. vue/example.vue)
  • data: arbitrary JSON-serializable data (injected by replacing the templateData placeholder)
  • allowTemplateAssets (optional, unsafe): when true, allow templates to load extra JS/CSS URLs declared in the template file header:
    • <!-- TemplateConfig: {"assets":{"scripts":["..."],"styles":["..."]}} -->
    • Useful for UMD/IIFE globals (e.g. window.dayjs), not npm-style import.
    • Backward compat: allowCdn is accepted as an alias.
  • allowScripts (optional, unsafe, html only): when exporting images in diagramMode: "img", set allowScripts: true to execute inline <script> blocks before rendering to PNG.
    • not supported: <script type="module">
    • external <script src="..."> is not supported for image rendering (use inline scripts)

Svelte Notes (Svelte 5 + esm.sh)

  • Svelte templates are compiled at runtime using the Svelte compiler, loaded from esm.sh via import().
  • This means Svelte template rendering requires network access (even in diagramMode: "img"), unless you override cdn.svelteCompiler/cdn.svelteBase to another ESM CDN that works in your environment.
  • Templates are expected to be self-contained .svelte files (no preprocessors like TypeScript/Sass, and avoid local relative imports unless you provide an ESM-resolvable URL).

Template Resolution (External Templates)

To load templates from outside, use:

  • CLI: --templates-dir /path/to/templates (repeatable; CLI reads files and passes them as templates)
  • Library API: pass templates: Record<string, string> (you can load files yourself if you want)
  • Front matter: templates: supports inline template sources (YAML map)

CDN Overrides (Live Mode)

When exporting HTML/Image with diagramMode: live, you can override CDN URLs in front matter:

cdn:
  vue: "https://unpkg.com/vue@3/dist/vue.global.js"
  vueSfcLoader: "https://cdn.jsdelivr.net/npm/vue3-sfc-loader/dist/vue3-sfc-loader.js"
  svelteCompiler: "https://esm.sh/svelte@5/compiler"
  svelteBase: "https://esm.sh/svelte@5/"

md2x Skill

This repo also includes a skill for driving md2x from an agent:

  • Skill: skills/md2x/SKILL.md
  • What it does: guides an agent to run npx md2x ..., pick formats/themes, and use front matter correctly.
  • Install (example):
npx skills add larchliu/md2x

or

npx add-skill larchliu/md2x

MCP Server (Model Context Protocol)

The md2x CLI includes a built-in MCP server that exposes markdown conversion as MCP tools via stdio transport.

Starting the MCP Server

npx md2x --mcp

Or if installed globally:

md2x --mcp

Available Tools

  • convert_markdown: Convert markdown files to PDF, DOCX, HTML, or Image formats

    • Takes a markdownFilePath parameter
    • Saves output to the same directory as the source file (or custom outputPath)
    • Returns the file path to the converted document
  • list_themes: List all available themes with their categories and featured status

Integration with Claude Desktop

Add to your Claude Desktop configuration (~/Library/Application Support/Claude/claude_desktop_config.json on macOS):

{
  "mcpServers": {
    "md2x": {
      "command": "node",
      "args": ["/path/to/markdown-viewer-extension/node/dist/md2x.js", "--mcp"]
    }
  }
}

Testing

node --test test/mcp-server.test.mjs

See node/MCP-SERVER.md for detailed documentation.


HTTP-based MCP Server (Separate Package)

There's also a separate Express-based MCP server in the mcp/ directory that exposes md2x as MCP tools over HTTP:

pnpm -C mcp install
pnpm -C mcp start

See mcp/README.md for details.

Puppeteer / Chrome install

This package depends on puppeteer. On first install, Puppeteer downloads a compatible "Chrome for Testing" build (cached under your user directory). Set PUPPETEER_SKIP_DOWNLOAD=1 to skip download and use a system Chrome via PUPPETEER_EXECUTABLE_PATH.

Open Source License

This project is open source under ISC license. Welcome to Star, report issues, suggest features, and contribute code.

Project URL: https://github.com/LarchLiu/md2x

Acknowledgements