Skip to main content

mcp-docgen

A Markdown-driven Model Context Protocol (MCP) server to create, read, edit and convert Word (.docx), Excel (.xlsx), PowerPoint (.pptx), PDF and HTML documents.

Built entirely on mature, permissively-licensed Python libraries (python-docx, python-pptx, openpyxl, reportlab, pypdf, markdown-it-py, markdownify, beautifulsoup4) — no proprietary dependencies. MIT licensed.

Part of the Touka project: giving AI agents the ability to produce, read and edit real Office documents using only open-source building blocks.

Why

LLMs are great at producing Markdown. mcp-docgen converts Markdown to polished Office documents — and reads them back to Markdown — so an MCP-capable assistant (Claude Desktop, Touka, …) can run a full read → edit → write loop on real .docx / .xlsx / .pptx / .pdf / .html files, and convert between any of them.

Install & run

uvx mcp-docgen          # once published to PyPI
# or, from a local checkout:
uv sync && uv run mcp-docgen

The server speaks MCP over stdio.

MCP client configuration

{
  "mcpServers": {
    "docgen": {
      "command": "uvx",
      "args": ["mcp-docgen"],
      "env": { "MCP_DOCGEN_OUTPUT_DIR": "/absolute/path/to/workdir" }
    }
  }
}

From a local checkout, swap the command for:

{ "command": "uv", "args": ["run", "--directory", "/path/to/mcp-docgen", "mcp-docgen"] }

Tools

Create (Markdown / structured data → file)

Tool Input → Output
create_docx(markdown, output_path, title?) Markdown → Word
create_pptx(markdown, output_path, title?) Markdown → PowerPoint
create_pdf(markdown, output_path, title?) Markdown → PDF
create_html(markdown, output_path, title?) Markdown → HTML
create_xlsx(sheets, output_path) structured rows → Excel

Markdown features: headings, bold / italic / inline code, bullet & numbered lists (nested), tables, block quotes, fenced code blocks, horizontal rules.

PowerPoint slide convention: # Heading starts a new slide (its title); content below becomes bullet points; --- forces a slide break; title adds a leading title slide.

Excel sheets: [{ "name": str, "rows": [[cell, …], …], "header"?: bool }]. Cells may be strings / numbers / booleans / null; the first row is a bold, frozen header unless "header": false.

Read (file → Markdown / structured data)

Tool Returns
read_docx(input_path, base_dir?) { "markdown": … }
read_pptx(input_path, base_dir?) { "markdown": … }
read_xlsx(input_path, base_dir?) { "sheets": [{ "name", "rows" }] } (round-trips with create_xlsx)
read_pdf(input_path, base_dir?) { "num_pages", "pages": […], "text" }
read_html(input_path, base_dir?) { "markdown": … }

Reading docx/pptx to Markdown enables editing without in-place tools: read → edit the Markdown → create_* to regenerate.

Edit (in-place, preserving the rest)

Tool Effect
edit_xlsx(input_path, output_path, edits) set cells / append rows / add sheets, keeping other sheets, formulas & formatting
append_docx(input_path, output_path, markdown) append Markdown content to the end
append_pptx(input_path, output_path, markdown) append Markdown-derived slides to the end

edits = { "set_cells": [{"sheet","cell","value"}], "append_rows": [{"sheet","rows"}], "add_sheet": [{"name","rows"}] }.

PDF page operations

Tool Effect
pdf_merge(input_paths, output_path) concatenate PDFs in order
pdf_split(input_path, output_dir?) one file per page
pdf_extract(input_path, pages, output_path) extract a page subset (e.g. "1-3,5")

Note on PDF "editing": clean open-source PDF editing means page operations (merge / split / extract), not reflowing or replacing body text — PDFs are not designed for in-place text editing. To revise PDF content, regenerate with create_pdf.

Create/edit tools return {"path": <absolute path>}; pdf_split returns {"paths": […]}.

Convert (any format → any format)

Tool Effect
convert_document(input_path, output_path) convert between formats — both inferred from the file extensions

Conversions among html / docx / pptx read straight into a rich intermediate model, so colour, font, size, alignment, images and table cell spans survive — no lossy Markdown round-trip. xlsx → xlsx is lossless (styles, formulas, merges). Other pairings degrade gracefully. The result reports an honest fidelity plus a warnings list of what that particular pairing drops:

Fidelity When What it means
rich html · docx · pptx among themselves, and xlsx → xlsx styling preserved (colour / font / align / images / cell spans; formulas for xlsx)
clean md → a flow format structure preserved (Markdown carries no styling to begin with)
lossy any → pptx; xlsx ↔ documents slides split on # headings; only tables map to / from worksheets
text-only pdf as a source pypdf extracts plain text only; layout, styling and tables are lost

Honest limits: a PDF source is plain text (the open-source ceiling); Markdown can't carry styling, so any .md step is structure-only. A few write-side gaps remain (docx clickable hyperlinks and cell shading), and only inline-style HTML is read.

convert_document returns { "path", "source_format", "target_format", "fidelity", "warnings" }.

Directories & safety

  • Output files are written inside MCP_DOCGEN_OUTPUT_DIR (default ./out).
  • Input files (read / edit) are read from MCP_DOCGEN_INPUT_DIR (default = the output dir), so a read → edit → write loop shares one working directory.
  • Every tool accepts an optional base_dir to use as the root for that single call (it roots both reads and writes), e.g. a host that jails each call to one user's directory. When MCP_DOCGEN_INPUT_DIR is configured, a per-call base_dir may only narrow within it — one that escapes the configured boundary is rejected.
  • Every path is interpreted relative to its base; any path escaping it (via .. or an absolute path) is rejected, missing inputs and wrong suffixes raise errors.
  • When a per-call base_dir is supplied without MCP_DOCGEN_INPUT_DIR set, it is trusted as given — the host is responsible for validating it (the trust boundary is the host). A host that would rather fail closed can set MCP_DOCGEN_STRICT_BASE_DIR=1 to cap any base_dir at the output root even when no input root is configured.
  • The server makes no network calls and spawns no subprocesses.
  • Generated output is hardened against injection from untrusted source content: HTML text, inline styles and URLs are escaped / scheme-allowlisted, and spreadsheet text that looks like a formula (=…) is neutralized so it can't execute in Excel.
  • Process only trusted input files. Documents are parsed by third-party libraries (python-docx, openpyxl, python-pptx, pypdf); a hostile archive could attempt a decompression / XML-expansion bomb. Keep the pinned dependency versions current.

Examples

uv run python examples/generate_samples.py   # create report.docx / review.pptx / sales.xlsx
uv run python examples/roundtrip_demo.py      # create → read → edit → PDF → convert round-trip

Development

uv sync
uv run pytest
uv run ruff check .

License

MIT © 2026 Touka Project — see LICENSE.

Powered by python-docx, python-pptx, openpyxl, reportlab and pypdf; Markdown parsing by markdown-it-py; HTML reading by markdownify and BeautifulSoup. All MIT/BSD licensed.

Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

mcp_docgen-0.7.0.tar.gz (36.8 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

mcp_docgen-0.7.0-py3-none-any.whl (45.8 kB view details)

Uploaded Python 3

File details

Details for the file mcp_docgen-0.7.0.tar.gz.

File metadata

  • Download URL: mcp_docgen-0.7.0.tar.gz
  • Upload date:
  • Size: 36.8 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.5 {"installer":{"name":"uv","version":"0.10.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mcp_docgen-0.7.0.tar.gz
Algorithm Hash digest
SHA256 ab6d62123da1cdcf3d2abbdab6f7271b7d5fdc950d6f38327f9d8f6f927faa16
MD5 23acbc171faf83b553c04512978dd085
BLAKE2b-256 a04c95cc80b9c6853a5dda46612e04c4bf30c3316239b3fdf08e3b88716b57b9

See more details on using hashes here.

File details

Details for the file mcp_docgen-0.7.0-py3-none-any.whl.

File metadata

  • Download URL: mcp_docgen-0.7.0-py3-none-any.whl
  • Upload date:
  • Size: 45.8 kB
  • Tags: Python 3
  • Uploaded using Trusted Publishing? No
  • Uploaded via: uv/0.10.5 {"installer":{"name":"uv","version":"0.10.5","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":null,"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}

File hashes

Hashes for mcp_docgen-0.7.0-py3-none-any.whl
Algorithm Hash digest
SHA256 8a524cfcb688585f27d6843b0980ed971905e21d31edd83d5288820a7f6f6259
MD5 15203873b4a7b2cff65a1dd9a51840de
BLAKE2b-256 6d41cdad77d8f171bec821258f7f69370b5102f36f9f0dbefde25f073592edba

See more details on using hashes here.

Release history Release notifications | RSS feed

0.8.0

2 files

This release

0.7.0 This release

2 files

0.6.0

2 files

0.4.0

2 files

0.2.0

2 files

0.1.0

2 files

Anthropic, PBC Visionary sponsor Bloomberg Visionary sponsor Hudson River Trading Visionary sponsor Meta Visionary sponsor NVIDIA Visionary sponsor Microsoft Sustainability sponsor Depot Continuous Integration AWS Cloud computing and Security Sponsor Datadog Monitoring Fastly CDN Google Download Analytics Sentry Error logging StatusPage Status page