How to Create PDFs from Markdown and Modify Existing PDFs Using Desktop Commander MCP

Desktop Commander MCP provides native PDF manipulation through parseMarkdownToPdf for markdown conversion and editPdf for page-level editing, enabling automated document generation and modification via Chrome-based rendering and pdf-lib operations.

Desktop Commander MCP, the open-source repository by wonderwhy-er, treats PDFs as first-class file types with dedicated tools for creating documents from markdown and performing surgical page-level modifications. This guide examines the actual source implementation in wonderwhy-er/DesktopCommanderMCP to show you how to generate PDFs programmatically and modify existing documents using the built-in MCP server capabilities.

Generating PDFs from Markdown

The parseMarkdownToPdf function in src/tools/pdf/markdown.ts (lines 87-103) handles the conversion of markdown strings to PDF buffers. This process relies on headless Chrome rendering through the md-to-pdf library.

Chrome Discovery and Rendering Pipeline

Before rendering occurs, the system ensures a Chrome executable is available via getChromePath. This function checks the private Puppeteer cache first, falls back to system-installed Chrome, and finally installs a fresh Chrome build via @puppeteer/browsers if needed. The discovered Chrome path is injected into launch_options so md-to-pdf can launch without searching the system.

The conversion accepts optional layout parameters including page size and margins, returning a buffer that can be written directly to disk or passed to other operations.

The parseMarkdownToPdf Implementation

import { parseMarkdownToPdf } from './src/tools/pdf/markdown.js';

const markdown = `

# Hello, Desktop Commander

This PDF is generated from **markdown** using the built‑in PDF tool.
`;

(async () => {
  const pdfBuffer = await parseMarkdownToPdf(markdown);
  // Write the buffer wherever you need it
  await import('fs/promises').then(fs => fs.writeFile('hello.pdf', pdfBuffer));
})();

Modifying Existing PDFs

For page-level modifications, Desktop Commander MCP implements editPdf in src/tools/pdf/manipulations.ts (lines 95-131). This function uses pdf-lib to load documents, execute sequential operations, and return modified buffers.

The editPdf Workflow

The function loads the target PDF using PDFDocument.load, then processes an array of operations sequentially. Each operation is either a deletion or insertion, validated against Zod schemas before execution. After processing all operations, pdfDoc.save() produces a modified Uint8Array.

Delete and Insert Operations

Delete operations remove pages using pdfDoc.removePage after normalizing negative indices via normalizePageIndexes. Insert operations support two content sources: a markdown string (converted on-the-fly via parseMarkdownToPdf) or an existing PDF file. Pages are copied from the source document and inserted at the requested index.

import { editPdf } from './src/tools/pdf/manipulations.js';

// Example: Delete pages 3–5 from an existing PDF
(async () => {
  const modified = await editPdf('source.pdf', [
    { type: 'delete', pageIndexes: [2, 3, 4] }   // zero‑based indexes
  ]);
  await import('fs/promises').then(fs => fs.writeFile('trimmed.pdf', modified));
})();
import { editPdf } from './src/tools/pdf/manipulations.js';

const markdownPage = `

## New Section

Added via markdown → PDF conversion.
`;

// Example: Insert a markdown-generated page after page 2
(async () => {
  const result = await editPdf('source.pdf', [
    {
      type: 'insert',
      pageIndex: 2,                // after the second page (0‑based)
      markdown: markdownPage
    }
  ]);
  await import('fs/promises').then(fs => fs.writeFile('updated.pdf', result));
})();

Unified File Handler Interface

The PdfFileHandler in src/utils/files/pdf.ts (lines 14-93) exposes a uniform read/write interface for PDFs within the generic file system. Read operations leverage parsePdfToMarkdown (implemented in src/tools/pdf/lib/pdf2md.ts) to extract text, images, and metadata using @opendocsg/pdf2md. Write operations dispatch to either parseMarkdownToPdf (when content is a string) or editPdf (when content is an array of operations).

Schema Validation

PDF editing operations are validated against strict Zod schemas defined in src/tools/schemas.ts. The PdfInsertOperationSchema (lines 84-90) validates insertion parameters including pageIndex and optional markdown content, while PdfDeleteOperationSchema (lines 92-95) validates the pageIndexes array for deletions.

When using the generic tool dispatcher, payloads are validated against WritePdfArgsSchema before processing:

{
  "path": "report.pdf",
  "content": [
    { "type": "delete", "pageIndexes": [0] },
    { "type": "insert", "pageIndex": 0, "markdown": "# Executive Summary" }

  ]
}

Summary

  • Use parseMarkdownToPdf in src/tools/pdf/markdown.ts to convert markdown strings to PDF buffers using Chrome/Chromium rendering via md-to-pdf.
  • Leverage editPdf in src/tools/pdf/manipulations.ts to perform page-level deletions and insertions using pdf-lib operations.
  • The PdfFileHandler in src/utils/files/pdf.ts provides a unified read/write interface that leverages pdf2md for parsing existing documents.
  • Operations are validated against Zod schemas defined in src/tools/schemas.ts, ensuring type safety for both insert and delete commands.

Frequently Asked Questions

Does Desktop Commander MCP require Chrome to be installed?

No, Chrome is not strictly required at installation. The system uses getChromePath to locate a Chrome binary, checking first for a cached Puppeteer build, then system-installed Chrome, and finally downloading Chrome on-demand via @puppeteer/browsers if needed for the md-to-pdf rendering pipeline.

Can I insert pages from existing PDF files or only from markdown?

Both options are supported. When inserting pages via editPdf, you can provide a markdown property to generate new content on the fly, or supply an existing PDF path. The function copies page objects from the source document and inserts them at the specified index using pdf-lib's copy and insert methods.

How does page indexing work when deleting or inserting pages?

Desktop Commander MCP uses zero-based indexing throughout its PDF manipulation functions. The editPdf function normalizes negative indices via normalizePageIndexes before calling pdfDoc.removePage, ensuring consistent behavior whether you specify absolute positions or negative offsets from the end of the document.

What validation is performed on PDF operations before execution?

All PDF editing operations are validated against Zod schemas defined in src/tools/schemas.ts. Specifically, PdfInsertOperationSchema (lines 84-90) and PdfDeleteOperationSchema (lines 92-95) validate the shape of insert and delete operations, respectively, ensuring type safety before editPdf processes the operations array.

Have a question about this repo?

These articles cover the highlights, but your codebase questions are specific. Give your agent direct access to the source. Share this with your agent to get started:

Share the following with your agent to get started:
curl -s "https://instagit.com/install.md"

Works with
Claude Codex Cursor VS Code OpenClaw Any MCP Client

Maintain an open-source project? Get it listed too →