document-converter-mcp
# @lifeng688/document-converter-mcp
A local-first MCP server for converting documents between Markdown, PDF, DOCX, and HTML, with environment diagnostics and workspace-level configuration.
> **English**: This project focuses on AI-friendly document conversion, not pixel-perfect layout reconstruction.
>
> **中文**: 本项目重点是 AI 友好的文档转换,而不是像素级版式还原。
## Features
- **7 conversion tools**: Markdown <-> PDF, Markdown <-> DOCX, Markdown <-> HTML, PDF -> Markdown
- **`doctor` tool**: Diagnose local environment (Node.js, Pandoc, Python, MarkItDown, PDF engines)
- **Configuration file**: `.document-converter.json` for workspace-level defaults
- **Dual engine support**: Pandoc (primary) + MarkItDown (enhanced PDF/DOCX extraction)
- **Safe file access**: Workspace-isolated path validation, sensitive file blocking, no-overwrite-by-default
- **Secure command execution**: Spawn-based, no shell injection, structured errors with timeouts
- **AI-friendly output**: Optional `cleanForLLM` flag for cleaner Markdown
- **Batch processing**: Convert entire directories with concurrency control, dry run, include/exclude filters
- **PDF style options**: Margin, section numbering, syntax highlighting, metadata
- **HTML style options**: Themes, embedded CSS, self-contained output, syntax highlighting
- **DOCX image extraction**: Extract embedded images with metadata reporting
- **PDF sidecar recovery**: Accurate Markdown restoration from PDFs generated with `preserveSource: true`
- **Structured results**: Consistent JSON response format across all tools
## Supported Formats
| Source | Targets |
|--------|---------|
| Markdown (`.md`) | PDF, DOCX, HTML |
| DOCX (`.docx`) | Markdown |
| PDF (`.pdf`) | Markdown |
## Installation
### Prerequisites
1. **Node.js** >= 18.0.0
2. **Pandoc** >= 3.0
3. **Python 3** >= 3.8 (optional, for MarkItDown)
#### PDF Engine (required for Markdown -> PDF)
Pandoc can convert Markdown to PDF, but it requires an external PDF engine.
| Engine | Install | Notes |
|--------|---------|-------|
| `pdflatex` (default) | MiKTeX (Windows), TeX Live (Linux/macOS) | Most common, ~2 GB install |
| `xelatex` | TeX Live / MiKTeX | Recommended for Chinese/CJK documents |
| `lualatex` | TeX Live / MiKTeX | Lua-based LaTeX engine |
| `wkhtmltopdf` | `apt install wkhtmltopdf` / `brew install wkhtmltopdf` | Lightweight HTML-to-PDF engine |
| `weasyprint` | `pip install weasyprint` | Python-based HTML-to-PDF |
| `typst` | `cargo install typst` | Modern, fast typesetting system |
> **Chinese documents**: Use `pdfEngine: "xelatex"` with a TeX Live / MiKTeX installation that includes the `ctex` package.
>
> - **Windows**: `cjkMainFont: "Microsoft YaHei"`
> - **macOS**: `cjkMainFont: "Songti SC"`
> - **Linux**: `cjkMainFont: "Noto Sans CJK SC"`
### Install Pandoc
**macOS:**
```bash
brew install pandoc
```
**Ubuntu/Debian:**
```bash
sudo apt-get update && sudo apt-get install -y pandoc
```
**Windows:**
Download from https://pandoc.org/installing.html
Verify:
```bash
pandoc --version
```
### Install MarkItDown (optional, recommended for PDF -> Markdown)
```bash
pip install markitdown
```
Verify:
```bash
python3 -c "import markitdown; print('ok')"
```
> **PDF support requires optional dependencies:**
> ```bash
> # For PDF extraction only:
> python -m pip install -U "markitdown[pdf]"
>
> # For DOCX extraction:
> python -m pip install -U "markitdown[docx]"
>
> # For all optional converters (PDF, EPUB, HTML, DOCX, etc.):
> python -m pip install -U "markitdown[all]"
> ```
>
> `markitdown` installed does not guarantee PDF or DOCX support is available.
### Install the Server
```bash
npm install -g @lifeng688/document-converter-mcp
```
Or use directly via npx:
```bash
npx @lifeng688/document-converter-mcp
```
For development, clone the repo and build locally:
```bash
git clone https://github.com/guanweiqiang/document-convert-mcp.git
cd document-convert-mcp
npm install
npm run build
```
## MCP Client Configuration
Install the package globally first:
```bash
npm install -g @lifeng688/document-converter-mcp
```
### Claude Desktop
Edit your Claude Desktop config (`~/Library/Application Support/Claude/claude_desktop_config.json` on macOS, or `%APPDATA%\Claude\claude_desktop_config.json` on Windows):
```json
{
"mcpServers": {
"document-converter": {
"command": "npx",
"args": ["-y", "@lifeng688/document-converter-mcp"],
"env": {
"DOC_CONVERTER_WORKSPACE": "E:/MCPWorkDir"
}
}
}
}
```
Or if installed globally, use the local path:
```json
{
"mcpServers": {
"document-converter": {
"command": "document-converter-mcp",
"env": {
"DOC_CONVERTER_WORKSPACE": "E:/MCPWorkDir"
}
}
}
}
```
Sample configs are in `examples/`:
- `mcp.json` -- MCP Inspector config
- `claude-desktop-config.json` -- Claude Desktop config
## Configuration File
Place `.document-converter.json` in your workspace root to set defaults for all tools.
**Example:**
```json
{
"defaults": {
"pdfEngine": "xelatex",
"cjkMainFont": "Microsoft YaHei",
"pageSize": "A4",
"theme": "github",
"cleanForLLM": true,
"overwrite": false
},
"batch": {
"maxConcurrency": 2,
"continueOnError": true
},
"security": {
"maxFileSizeMB": 50
}
}
```
**Precedence:**
```
tool args > .document-converter.json > built-in defaults
```
**Notes:**
- The config file is read from the workspace root only (not nested directories).
- Config values cannot bypass `pathGuard` -- paths must still be within the workspace.
- `overwrite` defaults to `false` in the config for safety; do not set it to `true` unless intentional.
- For Chinese/CJK PDF generation, recommended config:
- **Windows**: `"pdfEngine": "xelatex", "cjkMainFont": "Microsoft YaHei"`
- **macOS**: `"pdfEngine": "xelatex", "cjkMainFont": "Songti SC"`
- **Linux**: `"pdfEngine": "xelatex", "cjkMainFont": "Noto Sans CJK SC"`
## Tools
### 1. `doctor`
Check the local environment for document-converter-mcp dependencies.
This tool never fails due to missing dependencies -- missing tools appear as `false` in the output with warnings.
Checks:
- Node.js version
- Workspace path, existence, writability
- Pandoc availability and version
- Python availability
- MarkItDown availability and PDF support
- PDF engines: `pdflatex`, `xelatex`, `lualatex`, `wkhtmltopdf`, `weasyprint`, `typst`
- Recommendations for missing dependencies
**Example output:**
```json
{
"success": true,
"summary": "Environment check completed.",
"data": {
"node": { "available": true, "version": "v22.18.0" },
"workspace": { "path": "E:/MCPWorkDir", "exists": true, "writable": true },
"pandoc": { "available": true, "version": "pandoc 3.8.2" },
"python": { "available": true, "command": "python" },
"markitdown": { "available": true, "pdfSupport": true },
"pdfEngines": {
"pdflatex": true,
"xelatex": true,
"lualatex": true,
"wkhtmltopdf": false,
"weasyprint": false,
"typst": false
},
"recommendations": []
},
"warnings": [],
"error": null
}
```
### 2. `markdown_to_pdf`
Convert Markdown to PDF using Pandoc.
> **Note**: Pandoc requires an external PDF engine (LaTeX distribution or alternative) to generate PDFs.
> **中文文档**:`pdflatex` 不支持中文 Unicode 字符。中文 Markdown 转 PDF 请使用 `pdfEngine: "xelatex"`(推荐)并设置 `cjkMainFont`。
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `inputPath` | string | Yes | -- | Input Markdown file path (relative to workspace) |
| `outputPath` | string | No | Auto-derived | Output PDF path |
| `title` | string | No | -- | PDF document title |
| `toc` | boolean | No | false | Include table of contents |
| `pageSize` | enum | No | A4 | Page size: `A4` or `Letter` |
| `theme` | enum | No | default | Theme: `default`, `github`, `academic` |
| `pdfEngine` | enum | No | Pandoc default | PDF engine: `pdflatex`, `xelatex`, `lualatex`, `wkhtmltopdf`, `weasyprint`, `typst` |
| `cjkMainFont` | string | No | -- | CJK main font for Chinese/Japanese/Korean documents (e.g. `"Microsoft YaHei"`, `"SimSun"`, `"Noto Sans CJK SC"`) |
| `preserveSource` | boolean | No | false | Save original Markdown as sidecar files (`sample.pdf.source.md`, `sample.pdf.meta.json`) for accurate PDF-to-Markdown recovery |
| `strictMarkdown` | boolean | No | false | Reject input if Markdown has structural issues like unclosed code blocks |
| `overwrite` | boolean | No | false | Allow overwriting existing files |
| `margin` | string | No | -- | Page margin in safe format (e.g. `'1in'`, `'2cm'`, `'20mm'`, `'72pt'`) |
| `numberSections` | boolean | No | false | Number section headings in the PDF |
| `highlightStyle` | string | No | -- | Code highlight theme: `default`, `tango`, `pygments`, `kate`, `monochrome`, `github`, `darkblue`, `emacs`, `friendly`, `fruity`, `native`, `trac`, `borland` |
| `metadata` | object | No | -- | Additional metadata key-value pairs |
**Sidecar files** (when `preserveSource=true`):
- `document.pdf.source.md` -- Original Markdown content
- `document.pdf.meta.json` -- Conversion metadata
### 3. `markdown_to_docx`
Convert Markdown to DOCX using Pandoc.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `inputPath` | string | Yes | -- | Input Markdown file path |
| `outputPath` | string | No | Auto-derived | Output DOCX path |
| `referenceDocx` | string | No | -- | Word template file |
| `toc` | boolean | No | false | Include table of contents |
| `strictMarkdown` | boolean | No | false | Reject input if Markdown has structural issues |
| `overwrite` | boolean | No | false | Allow overwriting existing files |
### 4. `docx_to_markdown`
Convert DOCX to Markdown using Pandoc or MarkItDown.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `inputPath` | string | Yes | -- | Input DOCX file path |
| `outputPath` | string | No | Auto-derived | Output Markdown path |
| `extractImages` | boolean | No | false | Extract embedded images from the DOCX |
| `imageDir` | string | No | Auto-derived | Directory for extracted images (must be within workspace). If omitted, defaults to `${outputBasename}_media` |
| `engine` | enum | No | pandoc | Engine: `pandoc` or `markitdown` |
| `markdownFlavor` | enum | No | gfm | Markdown dialect: `gfm` (GitHub Flavored), `commonmark`, or `pandoc` |
| `cleanForLLM` | boolean | No | false | Clean Markdown for AI consumption |
| `overwrite` | boolean | No | false | Allow overwriting existing files |
**Image extraction:**
When `extractImages=true`, the response includes:
```json
{
"imageCount": 2,
"imageDir": "out/document_media",
"images": [
{
"filename": "media/image1.png",
"sizeBytes": 12345
}
]
}
```
Even if no images are found:
```json
{
"imageCount": 0,
"imageDir": "out/document_media",
"images": []
}
```
Supported image extensions: `.png`, `.jpg`, `.jpeg`, `.gif`, `.webp`, `.svg`, `.bmp`, `.tif`, `.tiff`.
**Path safety:** `imageDir` is validated against path traversal. Values like `../outside-media` will be rejected with an error containing "Access denied" and "workspace".
### 5. `pdf_to_markdown`
Extract text from PDF to Markdown.
> **Warning**: This is **content extraction**, not layout reconstruction. Scanned PDFs, complex tables, two-column papers, and mathematical formulas may not convert reliably. For scanned PDFs, OCR is required (not included).
>
> **PDF 转 Markdown 是内容提取,不是版式或语义结构还原。**
>
> 普通 PDF 通常不保存 Markdown 语义。标题、表格、代码块、列表、阅读顺序都可能无法可靠恢复。
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `inputPath` | string | Yes | -- | Input PDF file path |
| `outputPath` | string | No | Auto-derived | Output Markdown path |
| `engine` | enum | No | markitdown | Engine: `markitdown` or `pandoc` |
| `cleanForLLM` | boolean | No | false | Clean Markdown for AI consumption |
| `preferSourceSidecar` | boolean | No | true | First check for a `.source.md` sidecar file. If found, return original Markdown instead of extracting PDF text. |
| `overwrite` | boolean | No | false | Allow overwriting existing files |
**Sidecar recovery:**
If the PDF was generated by this server with `preserveSource: true`, the original Markdown is available as sidecar files (`document.pdf.source.md`, `document.pdf.meta.json`). The default `preferSourceSidecar: true` will automatically find and return it.
**Quality report:**
Sidecar recovery mode:
```json
{
"quality": {
"mode": "source-sidecar",
"layoutPreserved": true,
"headingsReliable": true,
"tablesReliable": true,
"codeBlocksReliable": true,
"readingOrderReliable": true
}
}
```
Plain text extraction mode:
```json
{
"quality": {
"mode": "text-extraction",
"layoutPreserved": false,
"headingsReliable": false,
"tablesReliable": false,
"codeBlocksReliable": false,
"readingOrderReliable": false
}
}
```
### 6. `markdown_to_html`
Convert Markdown to HTML using Pandoc.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `inputPath` | string | Yes | -- | Input Markdown file path |
| `outputPath` | string | No | Auto-derived | Output HTML path |
| `cssPath` | string | No | -- | External CSS file path (validated via workspace pathGuard) |
| `standalone` | boolean | No | true | Generate complete HTML document with head/body |
| `strictMarkdown` | boolean | No | false | Reject input if Markdown has structural issues |
| `overwrite` | boolean | No | false | Allow overwriting existing files |
| `theme` | string | No | -- | Pandoc HTML theme: `default`, `github`, `academic`, `monochrome`, `bookish`, `mangoe`, `slaper`, `quarto` |
| `embedCss` | boolean | No | false | Embed CSS and resources into the HTML document |
| `selfContained` | boolean | No | false | Generate a self-contained single-file HTML |
| `highlightStyle` | string | No | -- | Code highlight theme: `default`, `tango`, `pygments`, `kate`, `monochrome`, `github`, `darkblue`, `emacs`, `friendly`, `fruity`, `native`, `trac`, `borland` |
> **`theme=github`** is ideal for README-style documentation.
> **`embedCss=true`** embeds CSS directly into the HTML.
> **`selfContained=true`** produces a single HTML file with all resources inline.
### 7. `batch_convert`
Convert all matching files in a directory from one format to another.
| Parameter | Type | Required | Default | Description |
|-----------|------|----------|---------|-------------|
| `inputDir` | string | Yes | -- | Source directory (relative to workspace) |
| `outputDir` | string | Yes | -- | Destination directory (relative to workspace) |
| `from` | enum | Yes | -- | Source format: `md`, `markdown`, `docx`, `pdf` |
| `to` | enum | Yes | -- | Target format: `md`, `markdown`, `docx`, `pdf`, `html` |
| `recursive` | boolean | No | false | Traverse subdirectories |
| `overwrite` | boolean | No | false | Overwrite existing files |
| `cleanForLLM` | boolean | No | false | Clean Markdown output for LLM consumption |
| `dryRun` | boolean | No | false | Generate a conversion plan without writing files |
| `include` | string[] | No | -- | Only convert files matching these glob patterns (e.g. `["report-*.md"]`) |
| `exclude` | string[] | No | -- | Skip files matching these glob patterns (e.g. `["draft-*"]`) |
| `maxConcurrency` | number | No | 1 | Max concurrent conversions (1-8). Useful for low-memory machines. |
| `continueOnError` | boolean | No | true | Continue processing other files when one fails |
**Dry run example:**
```json
{
"inputDir": "docs/source",
"outputDir": "docs/published",
"from": "md",
"to": "pdf",
"dryRun": true
}
```
Returns a plan with `plannedCount` but does not write any files.
**Return structure:**
```json
{
"success": true,
"summary": "Batch conversion completed: 4 succeeded, 0 failed, 0 skipped.",
"total": 4,
"plannedCount": 4,
"skippedCount": 0,
"successCount": 4,
"failedCount": 0,
"durationMs": 1201,
"results": [...]
}
```
## Usage Examples
### Run doctor
```
Tool: doctor
Args: {}
```
### Create `.document-converter.json`
```json
{
"defaults": {
"pdfEngine": "xelatex",
"cjkMainFont": "Microsoft YaHei",
"overwrite": false
}
}
```
### Markdown to Chinese PDF using config
With `.document-converter.json` setting `pdfEngine: "xelatex"` and `cjkMainFont: "Microsoft YaHei"`:
```
Tool: markdown_to_pdf
Args: {
"inputPath": "docs/chinese-report.md",
"title": "季度报告",
"toc": true,
"pageSize": "A4",
"preserveSource": true,
"overwrite": true
}
```
### Markdown to PDF with `preserveSource`
```
Tool: markdown_to_pdf
Args: {
"inputPath": "docs/report.md",
"outputPath": "docs/report.pdf",
"preserveSource": true,
"overwrite": true
}
```
Generates `docs/report.pdf.source.md` and `docs/report.pdf.meta.json` for accurate recovery.
### PDF to Markdown using source sidecar
```
Tool: pdf_to_markdown
Args: {
"inputPath": "docs/report.pdf",
"preferSourceSidecar": true
}
```
Automatically finds and returns the original Markdown from the sidecar file.
### Markdown to HTML with GitHub theme
```
Tool: markdown_to_html
Args: {
"inputPath": "docs/readme.md",
"theme": "github",
"standalone": true,
"selfContained": true
}
```
### Batch convert with dry run
```
Tool: batch_convert
Args: {
"inputDir": "docs/articles",
"outputDir": "docs/html",
"from": "md",
"to": "html",
"dryRun": true
}
```
### Batch convert with include/exclude
```
Tool: batch_convert
Args: {
"inputDir": "docs/articles",
"outputDir": "docs/published",
"from": "md",
"to": "pdf",
"recursive": true,
"include": ["report-*.md"],
"exclude": ["draft-*", "internal-*"],
"maxConcurrency": 2,
"continueOnError": true,
"overwrite": true
}
```
### DOCX to Markdown with image extraction
```
Tool: docx_to_markdown
Args: {
"inputPath": "docs/presentation.docx",
"extractImages": true,
"imageDir": "docs/presentation_media",
"overwrite": true
}
```
Returns `imageCount`, `imageDir`, and `images` array in the response.
## Security
This server implements strict security measures:
- **Workspace isolation**: All file access is confined to a configured workspace directory (`DOC_CONVERTER_WORKSPACE` env var)
- **Path traversal prevention**: `..` sequences and absolute path escapes are blocked
- **Sensitive file blocking**: `.env`, `.ssh/`, `.npmrc`, etc. are never accessible
- **File size limits**: Input files over 50 MB are rejected by default (configurable via config file)
- **No shell injection**: All commands use `spawn()` with argument arrays
- **No overwrite by default**: Existing files are protected unless explicitly allowed
- **Config file cannot bypass pathGuard**: Configuration defaults respect the same path safety rules as tool arguments
See `docs/security.md` for full details.
## Recommended Workflows
### Good
- **Markdown -> PDF** -- High-quality PDF output with Pandoc
- **Markdown -> DOCX** -- High-quality Word output
- **Markdown -> HTML** -- High-quality HTML output
- **DOCX -> Markdown** -- Good text extraction with image metadata
- **PDF -> Markdown** -- For text extraction only. Use `preferSourceSidecar: true` for PDFs generated by this server.
### Not recommended
- **Markdown -> PDF -> Markdown** for structure recovery
- PDFs do not preserve Markdown semantics (headings, tables, code blocks, lists, reading order)
- The round-trip will lose structural information
- Use `preserveSource: true` instead when generating the PDF
## 推荐工作流
### 推荐
- **Markdown -> PDF** -- 高质量的 PDF 输出
- **Markdown -> DOCX** -- 高质量的 Word 输出
- **Markdown -> HTML** -- 高质量的 HTML 输出
- **DOCX -> Markdown** -- 良好的文本提取和图片元数据
- **PDF -> Markdown** -- 仅用于内容提取。对本服务生成的 PDF 请使用 `preferSourceSidecar: true` 精确恢复。
### 不推荐
- **Markdown -> PDF -> Markdown** 用于结构恢复
- PDF 不保存 Markdown 语义(标题、表格、代码块、列表、阅读顺序)
- 往返转换将丢失结构信息
- 生成 PDF 时请使用 `preserveSource: true`
## Conversion Quality
> **This project focuses on AI-friendly document conversion, not pixel-perfect layout reconstruction.**
See `docs/conversion-quality.md` for format-specific quality notes and engine comparisons.
## Development
```bash
# Install dependencies
npm install
# Build TypeScript
npm run build
# Run in development mode (hot reload)
npm run dev
# Type check without emitting
npm run typecheck
```
## License
MIT
TDQS
Scored across 6 tools
Tools are mostly distinct: batch_convert handles directory-wide conversions, while others are single-file conversions for specific pairs. However, an agent could be unsure whether to use batch_convert or a specific tool for a single file, causing minor overlap.
Most tools follow source_to_target pattern (docx_to_markdown, markdown_to_docx, etc.), but batch_convert deviates from this pattern with a verb_noun style. Still consistent enough for predictability.
With 6 tools covering common document conversions and a batch option, the count is well-scoped for the server's purpose. No excess or deficiency.
Notable gaps exist: no source HTML conversion, no direct docx-to-html or pdf-to-docx conversion. While batch_convert supports many conversions, it lacks HTML as a source. This may cause dead ends for some workflows.