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

> Easily create PDFs from markdown or edit existing PDFs with Desktop Commander MCP. Leverage automated document generation and modification using Chrome rendering and pdf-lib.

- Repository: [Eduard Ruzga/DesktopCommanderMCP](https://github.com/wonderwhy-er/DesktopCommanderMCP)
- Tags: how-to-guide
- Published: 2026-07-17

---

**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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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

```typescript
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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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.

```typescript
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));
})();

```

```typescript
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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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:

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

  ]
}

```

## Summary

- Use **`parseMarkdownToPdf`** in [`src/tools/pdf/markdown.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/src/tools/pdf/manipulations.ts) to perform page-level deletions and insertions using **pdf-lib** operations.
- The **`PdfFileHandler`** in [`src/utils/files/pdf.ts`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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`](https://github.com/wonderwhy-er/DesktopCommanderMCP/blob/main/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.