{
  "type": "guide",
  "id": "convert-documents-to-markdown-for-agents",
  "title": "Convert documents to Markdown for agents with MarkItDown and Docling",
  "summary": "Turn PDFs, Office files and other documents into Markdown or chunks an agent can read, with Microsoft's MarkItDown for speed and Docling for layout and tables, then expose either one to an agent over MCP.",
  "author": "Agentica Author",
  "tags": [
    "documents",
    "pdf",
    "markdown",
    "extraction",
    "rag",
    "chunking",
    "mcp",
    "ocr"
  ],
  "published": "2026-10-18",
  "last_verified": "2026-10-18",
  "difficulty": "beginner",
  "time_estimate": "30 min",
  "entries": [
    "markitdown",
    "docling",
    "mcp-inspector",
    "model-context-protocol",
    "jina-reader",
    "firecrawl-api",
    "crawl4ai",
    "llamaindex",
    "haystack",
    "crewai"
  ],
  "links": {
    "html": "https://indexagentica.com/guides/convert-documents-to-markdown-for-agents/",
    "markdown": "https://indexagentica.com/guides/convert-documents-to-markdown-for-agents.md",
    "json": "https://indexagentica.com/api/longform/guides/convert-documents-to-markdown-for-agents.json",
    "source": "https://github.com/Drudley/indexagentica/blob/main/content-long/guides/convert-documents-to-markdown-for-agents.md"
  },
  "status": "published",
  "prerequisites": [
    "Python 3.10 or later (MarkItDown supports 3.10 to 3.14), and uv or pip",
    "About 2 GB of free disk for Docling, its CPU PyTorch build and the models it downloads",
    "Node.js, only for the MCP Inspector checks"
  ],
  "entries_detail": [
    {
      "id": "markitdown",
      "name": "MarkItDown",
      "summary": "Microsoft's lightweight Python utility for converting PDF, Office, images, audio, HTML and more into structure-preserving Markdown for LLMs.",
      "url": "https://indexagentica.com/entries/markitdown/",
      "json": "https://indexagentica.com/api/entries/markitdown.json"
    },
    {
      "id": "docling",
      "name": "Docling",
      "summary": "Open-source document parser that converts PDF, Office, HTML, audio, images and more into Markdown, JSON or DocTags for gen-AI pipelines; includes an MCP server.",
      "url": "https://indexagentica.com/entries/docling/",
      "json": "https://indexagentica.com/api/entries/docling.json"
    },
    {
      "id": "mcp-inspector",
      "name": "MCP Inspector",
      "summary": "Official interactive developer tool for testing and debugging MCP servers in the browser, on the command line or in the terminal.",
      "url": "https://indexagentica.com/entries/mcp-inspector/",
      "json": "https://indexagentica.com/api/entries/mcp-inspector.json"
    },
    {
      "id": "model-context-protocol",
      "name": "Model Context Protocol (MCP)",
      "summary": "Open protocol that standardizes how AI applications connect to external tools, data sources and prompts via MCP servers.",
      "url": "https://indexagentica.com/entries/model-context-protocol/",
      "json": "https://indexagentica.com/api/entries/model-context-protocol.json"
    },
    {
      "id": "jina-reader",
      "name": "Jina Reader",
      "summary": "Converts any URL into LLM-friendly Markdown by prefixing it with https://r.jina.ai/.",
      "url": "https://indexagentica.com/entries/jina-reader/",
      "json": "https://indexagentica.com/api/entries/jina-reader.json"
    },
    {
      "id": "firecrawl-api",
      "name": "Firecrawl API",
      "summary": "Web data API for AI agents: search the web, scrape any page to clean data and interact with it through one API.",
      "url": "https://indexagentica.com/entries/firecrawl-api/",
      "json": "https://indexagentica.com/api/entries/firecrawl-api.json"
    },
    {
      "id": "crawl4ai",
      "name": "Crawl4AI",
      "summary": "Open-source LLM-friendly web crawler and scraper that turns any website into clean Markdown; self-host or use the hosted cloud.",
      "url": "https://indexagentica.com/entries/crawl4ai/",
      "json": "https://indexagentica.com/api/entries/crawl4ai.json"
    },
    {
      "id": "llamaindex",
      "name": "LlamaIndex",
      "summary": "Document-processing platform for AI (open-source LlamaIndex library plus hosted LlamaParse APIs that turn PDFs, scans and Office files into markdown or JSON).",
      "url": "https://indexagentica.com/entries/llamaindex/",
      "json": "https://indexagentica.com/api/entries/llamaindex.json"
    },
    {
      "id": "haystack",
      "name": "Haystack",
      "summary": "deepset's open-source orchestration framework for production-ready, context-engineered LLM applications and agents.",
      "url": "https://indexagentica.com/entries/haystack/",
      "json": "https://indexagentica.com/api/entries/haystack.json"
    },
    {
      "id": "crewai",
      "name": "CrewAI",
      "summary": "Python framework for orchestrating role-playing, autonomous AI agents that collaborate as crews and flows.",
      "url": "https://indexagentica.com/entries/crewai/",
      "json": "https://indexagentica.com/api/entries/crewai.json"
    }
  ],
  "related": [
    {
      "type": "comparison",
      "id": "vector-databases-for-agents",
      "title": "Vector databases for agents compared",
      "url": "https://indexagentica.com/compare/vector-databases-for-agents/",
      "json": "https://indexagentica.com/api/longform/compare/vector-databases-for-agents.json"
    },
    {
      "type": "guide",
      "id": "give-an-agent-long-term-memory",
      "title": "Give an agent long-term memory",
      "url": "https://indexagentica.com/guides/give-an-agent-long-term-memory/",
      "json": "https://indexagentica.com/api/longform/guides/give-an-agent-long-term-memory.json"
    },
    {
      "type": "stack",
      "id": "research-agent",
      "title": "Research agent stack",
      "url": "https://indexagentica.com/stacks/research-agent/",
      "json": "https://indexagentica.com/api/longform/stacks/research-agent.json"
    },
    {
      "type": "comparison",
      "id": "web-search-apis",
      "title": "Web search APIs for agents",
      "url": "https://indexagentica.com/compare/web-search-apis/",
      "json": "https://indexagentica.com/api/longform/compare/web-search-apis.json"
    },
    {
      "type": "guide",
      "id": "connect-an-agent-to-a-remote-mcp-server",
      "title": "Connect an agent to a remote MCP server",
      "url": "https://indexagentica.com/guides/connect-an-agent-to-a-remote-mcp-server/",
      "json": "https://indexagentica.com/api/longform/guides/connect-an-agent-to-a-remote-mcp-server.json"
    },
    {
      "type": "guide",
      "id": "run-untrusted-code-in-a-sandbox",
      "title": "Run agent-generated code safely in a sandbox",
      "url": "https://indexagentica.com/guides/run-untrusted-code-in-a-sandbox/",
      "json": "https://indexagentica.com/api/longform/guides/run-untrusted-code-in-a-sandbox.json"
    },
    {
      "type": "stack",
      "id": "data-analysis-agent",
      "title": "Data analysis agent stack",
      "url": "https://indexagentica.com/stacks/data-analysis-agent/",
      "json": "https://indexagentica.com/api/longform/stacks/data-analysis-agent.json"
    }
  ],
  "sources": [
    {
      "title": "MarkItDown README",
      "url": "https://github.com/microsoft/markitdown",
      "accessed": "2026-10-18"
    },
    {
      "title": "markitdown-mcp README",
      "url": "https://github.com/microsoft/markitdown/tree/main/packages/markitdown-mcp",
      "accessed": "2026-10-18"
    },
    {
      "title": "MarkItDown pyproject.toml (optional dependencies)",
      "url": "https://github.com/microsoft/markitdown/blob/main/packages/markitdown/pyproject.toml",
      "accessed": "2026-10-18"
    },
    {
      "title": "markitdown on PyPI",
      "url": "https://pypi.org/project/markitdown/",
      "accessed": "2026-10-18"
    },
    {
      "title": "markitdown-mcp on PyPI",
      "url": "https://pypi.org/project/markitdown-mcp/",
      "accessed": "2026-10-18"
    },
    {
      "title": "Docling README",
      "url": "https://github.com/docling-project/docling",
      "accessed": "2026-10-18"
    },
    {
      "title": "Docling, Supported formats",
      "url": "https://docling-project.github.io/docling/usage/supported_formats/",
      "accessed": "2026-10-18"
    },
    {
      "title": "Docling, Chunking",
      "url": "https://docling-project.github.io/docling/concepts/chunking/",
      "accessed": "2026-10-18"
    },
    {
      "title": "Docling, MCP",
      "url": "https://docling-project.github.io/docling/usage/mcp/",
      "accessed": "2026-10-18"
    },
    {
      "title": "docling-mcp README",
      "url": "https://github.com/docling-project/docling-mcp",
      "accessed": "2026-10-18"
    },
    {
      "title": "docling-serve README",
      "url": "https://github.com/docling-project/docling-serve",
      "accessed": "2026-10-18"
    },
    {
      "title": "docling on PyPI",
      "url": "https://pypi.org/project/docling/",
      "accessed": "2026-10-18"
    },
    {
      "title": "docling-mcp on PyPI",
      "url": "https://pypi.org/project/docling-mcp/",
      "accessed": "2026-10-18"
    },
    {
      "title": "Docling Technical Report (arXiv 2408.09869), the test PDF",
      "url": "https://arxiv.org/abs/2408.09869",
      "accessed": "2026-10-18"
    }
  ],
  "front_matter": {
    "id": "convert-documents-to-markdown-for-agents",
    "type": "guide",
    "title": "Convert documents to Markdown for agents with MarkItDown and Docling",
    "summary": "Turn PDFs, Office files and other documents into Markdown or chunks an agent can read, with Microsoft's MarkItDown for speed and Docling for layout and tables, then expose either one to an agent over MCP.",
    "description": "A hands-on guide to the two open-source converters most often put in front of agents. It installs MarkItDown and Docling, runs both on the same 9-page PDF and two Office files, shows what each output looks like, and covers Docling's chunk export for retrieval. It then wires each one to an agent through its MCP server (markitdown-mcp and docling-mcp), including a docling-mcp default that makes conversions fail, and ends with the security notes both projects publish. Commands were run on 2026-10-18 with MarkItDown 0.1.8, markitdown-mcp 0.0.1a7, Docling 2.140.0 and docling-mcp 3.3.0.",
    "author": "Agentica Author",
    "difficulty": "beginner",
    "time_estimate": "30 min",
    "prerequisites": [
      "Python 3.10 or later (MarkItDown supports 3.10 to 3.14), and uv or pip",
      "About 2 GB of free disk for Docling, its CPU PyTorch build and the models it downloads",
      "Node.js, only for the MCP Inspector checks"
    ],
    "tags": [
      "documents",
      "pdf",
      "markdown",
      "extraction",
      "rag",
      "chunking",
      "mcp",
      "ocr"
    ],
    "entries": [
      "markitdown",
      "docling",
      "mcp-inspector",
      "model-context-protocol",
      "jina-reader",
      "firecrawl-api",
      "crawl4ai",
      "llamaindex",
      "haystack",
      "crewai"
    ],
    "sources": [
      {
        "title": "MarkItDown README",
        "url": "https://github.com/microsoft/markitdown",
        "accessed": "2026-10-18"
      },
      {
        "title": "markitdown-mcp README",
        "url": "https://github.com/microsoft/markitdown/tree/main/packages/markitdown-mcp",
        "accessed": "2026-10-18"
      },
      {
        "title": "MarkItDown pyproject.toml (optional dependencies)",
        "url": "https://github.com/microsoft/markitdown/blob/main/packages/markitdown/pyproject.toml",
        "accessed": "2026-10-18"
      },
      {
        "title": "markitdown on PyPI",
        "url": "https://pypi.org/project/markitdown/",
        "accessed": "2026-10-18"
      },
      {
        "title": "markitdown-mcp on PyPI",
        "url": "https://pypi.org/project/markitdown-mcp/",
        "accessed": "2026-10-18"
      },
      {
        "title": "Docling README",
        "url": "https://github.com/docling-project/docling",
        "accessed": "2026-10-18"
      },
      {
        "title": "Docling, Supported formats",
        "url": "https://docling-project.github.io/docling/usage/supported_formats/",
        "accessed": "2026-10-18"
      },
      {
        "title": "Docling, Chunking",
        "url": "https://docling-project.github.io/docling/concepts/chunking/",
        "accessed": "2026-10-18"
      },
      {
        "title": "Docling, MCP",
        "url": "https://docling-project.github.io/docling/usage/mcp/",
        "accessed": "2026-10-18"
      },
      {
        "title": "docling-mcp README",
        "url": "https://github.com/docling-project/docling-mcp",
        "accessed": "2026-10-18"
      },
      {
        "title": "docling-serve README",
        "url": "https://github.com/docling-project/docling-serve",
        "accessed": "2026-10-18"
      },
      {
        "title": "docling on PyPI",
        "url": "https://pypi.org/project/docling/",
        "accessed": "2026-10-18"
      },
      {
        "title": "docling-mcp on PyPI",
        "url": "https://pypi.org/project/docling-mcp/",
        "accessed": "2026-10-18"
      },
      {
        "title": "Docling Technical Report (arXiv 2408.09869), the test PDF",
        "url": "https://arxiv.org/abs/2408.09869",
        "accessed": "2026-10-18"
      }
    ],
    "related": [
      "vector-databases-for-agents",
      "give-an-agent-long-term-memory",
      "research-agent",
      "web-search-apis",
      "connect-an-agent-to-a-remote-mcp-server",
      "run-untrusted-code-in-a-sandbox",
      "data-analysis-agent"
    ],
    "last_verified": "2026-10-18",
    "published": "2026-10-18"
  },
  "markdown": "\nAgents read text. Most of what people hand them is not: PDFs, Word files, slide decks, spreadsheets, scans. A converter in front of the agent turns those into Markdown, which keeps headings, lists and tables in a form models handle well and that costs few tokens. [MarkItDown](https://indexagentica.com/entries/markitdown/) (Microsoft) and [Docling](https://indexagentica.com/entries/docling/) (started at IBM Research, now an LF AI & Data project) are the two open-source converters with MIT licenses and their own MCP servers.\n\nWhat was run for this guide, on 2026-10-18, on a Linux box with 8 vCPUs and no GPU: MarkItDown 0.1.8 and Docling 2.140.0 on the 9-page [Docling Technical Report](https://arxiv.org/abs/2408.09869) PDF and on a small `.xlsx` and `.docx`; Docling's chunk export; and the [MCP Inspector](https://indexagentica.com/entries/mcp-inspector/) CLI against markitdown-mcp 0.0.1a7 and docling-mcp 3.3.0. Timings below are from that one machine and one file, so treat them as a rough guide.\n\n## Which one to use\n\n| | MarkItDown | Docling |\n|---|---|---|\n| Approach | Format-specific libraries (pdfminer.six and pdfplumber for PDF, mammoth for Word, pandas and openpyxl for Excel), no ML models by default | Layout analysis, reading order, table-structure and OCR models; optional vision-language models |\n| Formats (from each README) | PDF, PowerPoint, Word, Excel, images (EXIF and OCR), audio (transcription), HTML, CSV/JSON/XML, ZIP, YouTube URLs, EPub | PDF, DOCX/XLSX/PPTX, legacy Office and RTF (via LibreOffice), ODF, EPUB, Apple iWork, HTML, LaTeX, images, audio and video (with the `asr` extra), email, and more |\n| Outputs | Markdown | Markdown, HTML, lossless JSON, DocTags, DocLang, plain text, WebVTT, LaTeX, and JSONL chunks |\n| Install size in our test | 229 MB virtualenv with `[all]` | 1.5 GB virtualenv with CPU PyTorch, plus about 0.5 GB of models downloaded on first run |\n| Our 9-page PDF | 2.2 s, plain text with no headings and a flattened table | 35 s, 22 headings and real Markdown tables |\n| MCP server | `markitdown-mcp`: one tool, `convert_to_markdown(uri)` | `docling-mcp`: 21 tools for converting, editing and exporting documents |\n\nIn short: use MarkItDown for Office files, HTML and quick text out of born-digital PDFs. Use Docling when PDF layout matters, meaning multi-column pages, tables, scans, or chunks headed for a [vector database](https://indexagentica.com/compare/vector-databases-for-agents/). For web pages, a URL-to-Markdown service such as [Jina Reader](https://indexagentica.com/entries/jina-reader/), [Firecrawl](https://indexagentica.com/entries/firecrawl-api/) or [Crawl4AI](https://indexagentica.com/entries/crawl4ai/) is the better fit; the [research agent stack](https://indexagentica.com/stacks/research-agent/) covers that side.\n\n## 1. MarkItDown\n\nInstall it in a virtual environment. `[all]` pulls in every optional converter; you can also pick extras such as `[pdf, docx, pptx]`.\n\n```bash\nuv venv --python 3.12 .venv && source .venv/bin/activate\nuv pip install 'markitdown[all]'\nmarkitdown report.pdf -o report.md      # or: markitdown report.pdf > report.md\ncat report.pdf | markitdown             # stdin works too\n```\n\nFrom Python:\n\n```python\nfrom markitdown import MarkItDown\n\nmd = MarkItDown(enable_plugins=False)\nresult = md.convert_local(\"pricing.xlsx\")   # local files only; see Security below\nprint(result.markdown)\n```\n\nOn Office files the output was clean. A one-sheet workbook came back as a heading per sheet plus a Markdown table:\n\n```markdown\n## Pricing\n| Plan | Price per month (USD) | Seats |\n| --- | --- | --- |\n| Free | 0 | 1 |\n| Team | 20 | 5 |\n```\n\nOn the PDF it was fast (2.2 seconds) but shallow. The output had no Markdown headings. Words in justified lines often ran together (\"state-of-the-artspecializedAImodels\"), and the report's runtime table became loose lines of numbers. Pages that reproduce another paper's layout turned into wide pseudo-tables of letter fragments. That is what text-layer extraction gives you, and the README says as much: the output is meant for text-analysis tools, not high-fidelity conversion.\n\nFor better PDF and scan handling, MarkItDown can hand conversion to Azure Document Intelligence (`-d -e <endpoint>`) or Azure Content Understanding (`--use-cu`). Both are billable Azure API calls. The `markitdown-ocr` plugin adds OCR of embedded images through an LLM vision client you pass in. If you pass no client, the plugin loads but silently skips OCR.\n\n## 2. Docling\n\n```bash\nuv venv --python 3.12 .venv-docling && source .venv-docling/bin/activate\nuv pip install docling      # on CPU-only Linux, add the PyTorch CPU index to skip CUDA wheels:\n# uv pip install docling --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match\ndocling report.pdf --image-export-mode placeholder --output out/\n```\n\nTwo defaults to know about:\n\n- **Images.** With the CLI defaults, our Markdown file was 581 KB, because the figures were embedded as base64 data URIs. `--image-export-mode placeholder` replaces each one with `<!-- image -->` and brought the file down to 34 KB. Use `referenced` to write PNG files and link to them instead.\n- **First run.** The first conversion downloads the layout and table models to the Hugging Face cache and the RapidOCR models into the package. That run took 60 seconds; later runs of the same file took about 35 seconds wall time, of which Docling reported 29 seconds converting.\n\nThe output had real structure: `## 1 Introduction`-style headings, proper paragraphs, and Table 1 as a Markdown table whose column headers are joined from the original's two header rows (`native backend - TTS`, `pypdfium backend - Mem`). Rows that share cells in the original were merged into one Markdown row, so check complex tables before you trust a number in them.\n\nOCR is on by default. `--no-ocr` cut the run to 8 seconds of conversion, but the Markdown was 33 lines shorter: it lost part of the reproduced DocLayNet paper pages at the end of the report, including that paper's Table 2. Only switch OCR off after you've compared the output on your own documents.\n\nFrom Python:\n\n```python\nfrom docling.document_converter import DocumentConverter\n\nresult = DocumentConverter().convert(\"report.pdf\")   # a local path or a URL\nprint(result.document.export_to_markdown())\n```\n\nDocling also has a VLM pipeline (`docling --pipeline vlm --vlm-model granite_docling <file>`) and integrations with [LlamaIndex](https://indexagentica.com/entries/llamaindex/), [Haystack](https://indexagentica.com/entries/haystack/), LangChain and [CrewAI](https://indexagentica.com/entries/crewai/). To run it as a service, [docling-serve](https://github.com/docling-project/docling-serve) provides an HTTP API on port 5001 (`pip install docling-serve` then `docling-serve run`, or the `docling-serve-cpu` container image, listed at 4.4 GB).\n\n## 3. Chunks for retrieval\n\nIf the text is going into a vector store, chunk it with Docling rather than splitting the Markdown yourself. Its chunkers work on the document structure, so each chunk carries its headings, captions and page numbers:\n\n```bash\ndocling report.pdf --to chunks --output chunks/\n```\n\nThat wrote one JSONL file with 48 chunks for our PDF. Each line has `text` (the chunk with its heading prepended, ready to embed), `raw_text`, `num_tokens`, `headings`, `captions`, `page_numbers` and the origin file. The default is the `HybridChunker`, which starts from the document hierarchy, splits chunks that are too long for the tokenizer and merges small neighbours that share headings. When a table spans chunks, it repeats the table header by default. Set `--chunks-tokenizer` (a Hugging Face model id) and `--chunks-max-tokens` to match your embedding model. The first chunking run downloaded the `sentence-transformers/all-MiniLM-L6-v2` tokenizer as the default.\n\nLoad those lines into any store from the [vector database comparison](https://indexagentica.com/compare/vector-databases-for-agents/), and keep `page_numbers` in the metadata so the agent can cite where an answer came from. [Give an agent long-term memory](https://indexagentica.com/guides/give-an-agent-long-term-memory/) covers the memory side.\n\n## 4. Give the converter to an agent over MCP\n\nBoth projects ship an MCP server, so an agent can convert files on demand instead of you running a pipeline first (background: [Connect an agent to a remote MCP server](https://indexagentica.com/guides/connect-an-agent-to-a-remote-mcp-server/)).\n\n### markitdown-mcp\n\n```bash\nuv pip install markitdown-mcp\nmarkitdown-mcp                                      # stdio, the default\nmarkitdown-mcp --http --host 127.0.0.1 --port 3001  # Streamable HTTP at /mcp, SSE at /sse\n```\n\n```json\n{\n  \"mcpServers\": {\n    \"markitdown\": { \"command\": \"markitdown-mcp\" }\n  }\n}\n```\n\nIt exposes one tool, `convert_to_markdown`, taking an `http:`, `https:`, `file:` or `data:` URI. Checked with the Inspector:\n\n```bash\nnpx @modelcontextprotocol/inspector --cli markitdown-mcp \\\n  --method tools/call --tool-name convert_to_markdown --tool-arg uri=file:///path/to/pricing.xlsx\n```\n\nIt returned the same table as the CLI. The README recommends its Docker image for Claude Desktop, mounting only the directory you want converted (`-v /home/user/data:/workdir`).\n\n### docling-mcp\n\nThe client snippet in Docling's docs is `uvx --from=docling-mcp docling-mcp-server`. With docling-mcp 3.3.0 that is not enough, for two reasons we hit:\n\n1. **Conversion mode defaults to `remote`.** Without `DOCLING_MCP_SERVICE_URL`, every conversion failed with \"DOCLING_MCP_SERVICE_URL is not set but DOCLING_MCP_CONVERSION_MODE=remote\". To convert on your own machine, install the `local` extra and set the mode.\n2. **Transport defaults to `streamable-http`** on `localhost:8000`. A client that launches the command and expects stdio needs `--transport stdio`.\n\nThis configuration converted our PDF (the first call took about 40 seconds, including server start-up):\n\n```json\n{\n  \"mcpServers\": {\n    \"docling\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"docling-mcp[local]\", \"docling-mcp-server\", \"--transport\", \"stdio\"],\n      \"env\": { \"DOCLING_MCP_CONVERSION_MODE\": \"local\" }\n    }\n  }\n}\n```\n\nThe server keeps converted documents in an in-memory cache. `convert_document_into_docling_document` returns a `document_key`, and the agent then calls `export_docling_document_to_markdown` (with an optional `max_size` in characters) or the anchor tools (`get_overview_of_document_anchors`, `search_for_text_in_document_anchors`, `get_text_of_document_item_at_anchor`) to read parts of a long document instead of all of it. The other tools create and edit documents. Load fewer tool groups with the positional argument (`conversion`, `generation`, `manipulation`, plus optional RAG groups for LlamaIndex and Llama Stack). For a shared deployment, run docling-serve once and point each docling-mcp at it in remote mode with `DOCLING_MCP_SERVICE_URL` and `DOCLING_MCP_SERVICE_API_KEY`.\n\n## 5. Security\n\nBoth converters read whatever their process can read.\n\n- MarkItDown's README warns that `convert()` accepts local paths, remote URIs and streams, so untrusted input can make it read local files or fetch internal URLs. It recommends validating input, blocking private, loopback, link-local and metadata-service addresses, and calling the narrowest method you need (`convert_local()`, `convert_response()` or `convert_stream()`).\n- markitdown-mcp has no authentication and binds to localhost in HTTP mode. Its README says the tool can read any file the server's user can access. In our test, `convert_to_markdown` with `file:///etc/hostname` returned the file. Run it in a container or VM with only the documents mounted, and do not bind it to other interfaces.\n- docling-mcp takes local file paths and URLs too, so the same advice applies.\n- Converted text is untrusted input. A PDF can carry instructions aimed at the agent that reads it. Keep converted content separate from instructions, and keep risky tools behind approval. [Run untrusted code in a sandbox](https://indexagentica.com/guides/run-untrusted-code-in-a-sandbox/) covers isolation.\n",
  "raw": "---\nid: convert-documents-to-markdown-for-agents\ntype: guide\ntitle: Convert documents to Markdown for agents with MarkItDown and Docling\nsummary: \"Turn PDFs, Office files and other documents into Markdown or chunks an agent can read, with Microsoft's MarkItDown for speed and Docling for layout and tables, then expose either one to an agent over MCP.\"\ndescription: \"A hands-on guide to the two open-source converters most often put in front of agents. It installs MarkItDown and Docling, runs both on the same 9-page PDF and two Office files, shows what each output looks like, and covers Docling's chunk export for retrieval. It then wires each one to an agent through its MCP server (markitdown-mcp and docling-mcp), including a docling-mcp default that makes conversions fail, and ends with the security notes both projects publish. Commands were run on 2026-10-18 with MarkItDown 0.1.8, markitdown-mcp 0.0.1a7, Docling 2.140.0 and docling-mcp 3.3.0.\"\nauthor: Agentica Author\ndifficulty: beginner\ntime_estimate: 30 min\nprerequisites:\n  - Python 3.10 or later (MarkItDown supports 3.10 to 3.14), and uv or pip\n  - About 2 GB of free disk for Docling, its CPU PyTorch build and the models it downloads\n  - Node.js, only for the MCP Inspector checks\ntags: [documents, pdf, markdown, extraction, rag, chunking, mcp, ocr]\nentries: [markitdown, docling, mcp-inspector, model-context-protocol, jina-reader, firecrawl-api, crawl4ai, llamaindex, haystack, crewai]\nsources:\n  - title: MarkItDown README\n    url: https://github.com/microsoft/markitdown\n    accessed: 2026-10-18\n  - title: markitdown-mcp README\n    url: https://github.com/microsoft/markitdown/tree/main/packages/markitdown-mcp\n    accessed: 2026-10-18\n  - title: MarkItDown pyproject.toml (optional dependencies)\n    url: https://github.com/microsoft/markitdown/blob/main/packages/markitdown/pyproject.toml\n    accessed: 2026-10-18\n  - title: markitdown on PyPI\n    url: https://pypi.org/project/markitdown/\n    accessed: 2026-10-18\n  - title: markitdown-mcp on PyPI\n    url: https://pypi.org/project/markitdown-mcp/\n    accessed: 2026-10-18\n  - title: Docling README\n    url: https://github.com/docling-project/docling\n    accessed: 2026-10-18\n  - title: Docling, Supported formats\n    url: https://docling-project.github.io/docling/usage/supported_formats/\n    accessed: 2026-10-18\n  - title: Docling, Chunking\n    url: https://docling-project.github.io/docling/concepts/chunking/\n    accessed: 2026-10-18\n  - title: Docling, MCP\n    url: https://docling-project.github.io/docling/usage/mcp/\n    accessed: 2026-10-18\n  - title: docling-mcp README\n    url: https://github.com/docling-project/docling-mcp\n    accessed: 2026-10-18\n  - title: docling-serve README\n    url: https://github.com/docling-project/docling-serve\n    accessed: 2026-10-18\n  - title: docling on PyPI\n    url: https://pypi.org/project/docling/\n    accessed: 2026-10-18\n  - title: docling-mcp on PyPI\n    url: https://pypi.org/project/docling-mcp/\n    accessed: 2026-10-18\n  - title: Docling Technical Report (arXiv 2408.09869), the test PDF\n    url: https://arxiv.org/abs/2408.09869\n    accessed: 2026-10-18\nrelated: [vector-databases-for-agents, give-an-agent-long-term-memory, research-agent, web-search-apis, connect-an-agent-to-a-remote-mcp-server, run-untrusted-code-in-a-sandbox, data-analysis-agent]\nlast_verified: 2026-10-18\npublished: 2026-10-18\n---\n\nAgents read text. Most of what people hand them is not: PDFs, Word files, slide decks, spreadsheets, scans. A converter in front of the agent turns those into Markdown, which keeps headings, lists and tables in a form models handle well and that costs few tokens. [MarkItDown](https://indexagentica.com/entries/markitdown/) (Microsoft) and [Docling](https://indexagentica.com/entries/docling/) (started at IBM Research, now an LF AI & Data project) are the two open-source converters with MIT licenses and their own MCP servers.\n\nWhat was run for this guide, on 2026-10-18, on a Linux box with 8 vCPUs and no GPU: MarkItDown 0.1.8 and Docling 2.140.0 on the 9-page [Docling Technical Report](https://arxiv.org/abs/2408.09869) PDF and on a small `.xlsx` and `.docx`; Docling's chunk export; and the [MCP Inspector](https://indexagentica.com/entries/mcp-inspector/) CLI against markitdown-mcp 0.0.1a7 and docling-mcp 3.3.0. Timings below are from that one machine and one file, so treat them as a rough guide.\n\n## Which one to use\n\n| | MarkItDown | Docling |\n|---|---|---|\n| Approach | Format-specific libraries (pdfminer.six and pdfplumber for PDF, mammoth for Word, pandas and openpyxl for Excel), no ML models by default | Layout analysis, reading order, table-structure and OCR models; optional vision-language models |\n| Formats (from each README) | PDF, PowerPoint, Word, Excel, images (EXIF and OCR), audio (transcription), HTML, CSV/JSON/XML, ZIP, YouTube URLs, EPub | PDF, DOCX/XLSX/PPTX, legacy Office and RTF (via LibreOffice), ODF, EPUB, Apple iWork, HTML, LaTeX, images, audio and video (with the `asr` extra), email, and more |\n| Outputs | Markdown | Markdown, HTML, lossless JSON, DocTags, DocLang, plain text, WebVTT, LaTeX, and JSONL chunks |\n| Install size in our test | 229 MB virtualenv with `[all]` | 1.5 GB virtualenv with CPU PyTorch, plus about 0.5 GB of models downloaded on first run |\n| Our 9-page PDF | 2.2 s, plain text with no headings and a flattened table | 35 s, 22 headings and real Markdown tables |\n| MCP server | `markitdown-mcp`: one tool, `convert_to_markdown(uri)` | `docling-mcp`: 21 tools for converting, editing and exporting documents |\n\nIn short: use MarkItDown for Office files, HTML and quick text out of born-digital PDFs. Use Docling when PDF layout matters, meaning multi-column pages, tables, scans, or chunks headed for a [vector database](https://indexagentica.com/compare/vector-databases-for-agents/). For web pages, a URL-to-Markdown service such as [Jina Reader](https://indexagentica.com/entries/jina-reader/), [Firecrawl](https://indexagentica.com/entries/firecrawl-api/) or [Crawl4AI](https://indexagentica.com/entries/crawl4ai/) is the better fit; the [research agent stack](https://indexagentica.com/stacks/research-agent/) covers that side.\n\n## 1. MarkItDown\n\nInstall it in a virtual environment. `[all]` pulls in every optional converter; you can also pick extras such as `[pdf, docx, pptx]`.\n\n```bash\nuv venv --python 3.12 .venv && source .venv/bin/activate\nuv pip install 'markitdown[all]'\nmarkitdown report.pdf -o report.md      # or: markitdown report.pdf > report.md\ncat report.pdf | markitdown             # stdin works too\n```\n\nFrom Python:\n\n```python\nfrom markitdown import MarkItDown\n\nmd = MarkItDown(enable_plugins=False)\nresult = md.convert_local(\"pricing.xlsx\")   # local files only; see Security below\nprint(result.markdown)\n```\n\nOn Office files the output was clean. A one-sheet workbook came back as a heading per sheet plus a Markdown table:\n\n```markdown\n## Pricing\n| Plan | Price per month (USD) | Seats |\n| --- | --- | --- |\n| Free | 0 | 1 |\n| Team | 20 | 5 |\n```\n\nOn the PDF it was fast (2.2 seconds) but shallow. The output had no Markdown headings. Words in justified lines often ran together (\"state-of-the-artspecializedAImodels\"), and the report's runtime table became loose lines of numbers. Pages that reproduce another paper's layout turned into wide pseudo-tables of letter fragments. That is what text-layer extraction gives you, and the README says as much: the output is meant for text-analysis tools, not high-fidelity conversion.\n\nFor better PDF and scan handling, MarkItDown can hand conversion to Azure Document Intelligence (`-d -e <endpoint>`) or Azure Content Understanding (`--use-cu`). Both are billable Azure API calls. The `markitdown-ocr` plugin adds OCR of embedded images through an LLM vision client you pass in. If you pass no client, the plugin loads but silently skips OCR.\n\n## 2. Docling\n\n```bash\nuv venv --python 3.12 .venv-docling && source .venv-docling/bin/activate\nuv pip install docling      # on CPU-only Linux, add the PyTorch CPU index to skip CUDA wheels:\n# uv pip install docling --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match\ndocling report.pdf --image-export-mode placeholder --output out/\n```\n\nTwo defaults to know about:\n\n- **Images.** With the CLI defaults, our Markdown file was 581 KB, because the figures were embedded as base64 data URIs. `--image-export-mode placeholder` replaces each one with `<!-- image -->` and brought the file down to 34 KB. Use `referenced` to write PNG files and link to them instead.\n- **First run.** The first conversion downloads the layout and table models to the Hugging Face cache and the RapidOCR models into the package. That run took 60 seconds; later runs of the same file took about 35 seconds wall time, of which Docling reported 29 seconds converting.\n\nThe output had real structure: `## 1 Introduction`-style headings, proper paragraphs, and Table 1 as a Markdown table whose column headers are joined from the original's two header rows (`native backend - TTS`, `pypdfium backend - Mem`). Rows that share cells in the original were merged into one Markdown row, so check complex tables before you trust a number in them.\n\nOCR is on by default. `--no-ocr` cut the run to 8 seconds of conversion, but the Markdown was 33 lines shorter: it lost part of the reproduced DocLayNet paper pages at the end of the report, including that paper's Table 2. Only switch OCR off after you've compared the output on your own documents.\n\nFrom Python:\n\n```python\nfrom docling.document_converter import DocumentConverter\n\nresult = DocumentConverter().convert(\"report.pdf\")   # a local path or a URL\nprint(result.document.export_to_markdown())\n```\n\nDocling also has a VLM pipeline (`docling --pipeline vlm --vlm-model granite_docling <file>`) and integrations with [LlamaIndex](https://indexagentica.com/entries/llamaindex/), [Haystack](https://indexagentica.com/entries/haystack/), LangChain and [CrewAI](https://indexagentica.com/entries/crewai/). To run it as a service, [docling-serve](https://github.com/docling-project/docling-serve) provides an HTTP API on port 5001 (`pip install docling-serve` then `docling-serve run`, or the `docling-serve-cpu` container image, listed at 4.4 GB).\n\n## 3. Chunks for retrieval\n\nIf the text is going into a vector store, chunk it with Docling rather than splitting the Markdown yourself. Its chunkers work on the document structure, so each chunk carries its headings, captions and page numbers:\n\n```bash\ndocling report.pdf --to chunks --output chunks/\n```\n\nThat wrote one JSONL file with 48 chunks for our PDF. Each line has `text` (the chunk with its heading prepended, ready to embed), `raw_text`, `num_tokens`, `headings`, `captions`, `page_numbers` and the origin file. The default is the `HybridChunker`, which starts from the document hierarchy, splits chunks that are too long for the tokenizer and merges small neighbours that share headings. When a table spans chunks, it repeats the table header by default. Set `--chunks-tokenizer` (a Hugging Face model id) and `--chunks-max-tokens` to match your embedding model. The first chunking run downloaded the `sentence-transformers/all-MiniLM-L6-v2` tokenizer as the default.\n\nLoad those lines into any store from the [vector database comparison](https://indexagentica.com/compare/vector-databases-for-agents/), and keep `page_numbers` in the metadata so the agent can cite where an answer came from. [Give an agent long-term memory](https://indexagentica.com/guides/give-an-agent-long-term-memory/) covers the memory side.\n\n## 4. Give the converter to an agent over MCP\n\nBoth projects ship an MCP server, so an agent can convert files on demand instead of you running a pipeline first (background: [Connect an agent to a remote MCP server](https://indexagentica.com/guides/connect-an-agent-to-a-remote-mcp-server/)).\n\n### markitdown-mcp\n\n```bash\nuv pip install markitdown-mcp\nmarkitdown-mcp                                      # stdio, the default\nmarkitdown-mcp --http --host 127.0.0.1 --port 3001  # Streamable HTTP at /mcp, SSE at /sse\n```\n\n```json\n{\n  \"mcpServers\": {\n    \"markitdown\": { \"command\": \"markitdown-mcp\" }\n  }\n}\n```\n\nIt exposes one tool, `convert_to_markdown`, taking an `http:`, `https:`, `file:` or `data:` URI. Checked with the Inspector:\n\n```bash\nnpx @modelcontextprotocol/inspector --cli markitdown-mcp \\\n  --method tools/call --tool-name convert_to_markdown --tool-arg uri=file:///path/to/pricing.xlsx\n```\n\nIt returned the same table as the CLI. The README recommends its Docker image for Claude Desktop, mounting only the directory you want converted (`-v /home/user/data:/workdir`).\n\n### docling-mcp\n\nThe client snippet in Docling's docs is `uvx --from=docling-mcp docling-mcp-server`. With docling-mcp 3.3.0 that is not enough, for two reasons we hit:\n\n1. **Conversion mode defaults to `remote`.** Without `DOCLING_MCP_SERVICE_URL`, every conversion failed with \"DOCLING_MCP_SERVICE_URL is not set but DOCLING_MCP_CONVERSION_MODE=remote\". To convert on your own machine, install the `local` extra and set the mode.\n2. **Transport defaults to `streamable-http`** on `localhost:8000`. A client that launches the command and expects stdio needs `--transport stdio`.\n\nThis configuration converted our PDF (the first call took about 40 seconds, including server start-up):\n\n```json\n{\n  \"mcpServers\": {\n    \"docling\": {\n      \"command\": \"uvx\",\n      \"args\": [\"--from\", \"docling-mcp[local]\", \"docling-mcp-server\", \"--transport\", \"stdio\"],\n      \"env\": { \"DOCLING_MCP_CONVERSION_MODE\": \"local\" }\n    }\n  }\n}\n```\n\nThe server keeps converted documents in an in-memory cache. `convert_document_into_docling_document` returns a `document_key`, and the agent then calls `export_docling_document_to_markdown` (with an optional `max_size` in characters) or the anchor tools (`get_overview_of_document_anchors`, `search_for_text_in_document_anchors`, `get_text_of_document_item_at_anchor`) to read parts of a long document instead of all of it. The other tools create and edit documents. Load fewer tool groups with the positional argument (`conversion`, `generation`, `manipulation`, plus optional RAG groups for LlamaIndex and Llama Stack). For a shared deployment, run docling-serve once and point each docling-mcp at it in remote mode with `DOCLING_MCP_SERVICE_URL` and `DOCLING_MCP_SERVICE_API_KEY`.\n\n## 5. Security\n\nBoth converters read whatever their process can read.\n\n- MarkItDown's README warns that `convert()` accepts local paths, remote URIs and streams, so untrusted input can make it read local files or fetch internal URLs. It recommends validating input, blocking private, loopback, link-local and metadata-service addresses, and calling the narrowest method you need (`convert_local()`, `convert_response()` or `convert_stream()`).\n- markitdown-mcp has no authentication and binds to localhost in HTTP mode. Its README says the tool can read any file the server's user can access. In our test, `convert_to_markdown` with `file:///etc/hostname` returned the file. Run it in a container or VM with only the documents mounted, and do not bind it to other interfaces.\n- docling-mcp takes local file paths and URLs too, so the same advice applies.\n- Converted text is untrusted input. A PDF can carry instructions aimed at the agent that reads it. Keep converted content separate from instructions, and keep risky tools behind approval. [Run untrusted code in a sandbox](https://indexagentica.com/guides/run-untrusted-code-in-a-sandbox/) covers isolation.\n",
  "html": "<p>Agents read text. Most of what people hand them is not: PDFs, Word files, slide decks, spreadsheets, scans. A converter in front of the agent turns those into Markdown, which keeps headings, lists and tables in a form models handle well and that costs few tokens. <a href=\"/entries/markitdown/\">MarkItDown</a> (Microsoft) and <a href=\"/entries/docling/\">Docling</a> (started at IBM Research, now an LF AI &amp; Data project) are the two open-source converters with MIT licenses and their own MCP servers.</p>\n<p>What was run for this guide, on 2026-10-18, on a Linux box with 8 vCPUs and no GPU: MarkItDown 0.1.8 and Docling 2.140.0 on the 9-page <a href=\"https://arxiv.org/abs/2408.09869\">Docling Technical Report</a> PDF and on a small <code>.xlsx</code> and <code>.docx</code>; Docling&#39;s chunk export; and the <a href=\"/entries/mcp-inspector/\">MCP Inspector</a> CLI against markitdown-mcp 0.0.1a7 and docling-mcp 3.3.0. Timings below are from that one machine and one file, so treat them as a rough guide.</p>\n<h2>Which one to use</h2>\n<table><thead><tr><th scope=\"col\"></th><th scope=\"col\">MarkItDown</th><th scope=\"col\">Docling</th></tr></thead><tbody><tr><td>Approach</td><td>Format-specific libraries (pdfminer.six and pdfplumber for PDF, mammoth for Word, pandas and openpyxl for Excel), no ML models by default</td><td>Layout analysis, reading order, table-structure and OCR models; optional vision-language models</td></tr><tr><td>Formats (from each README)</td><td>PDF, PowerPoint, Word, Excel, images (EXIF and OCR), audio (transcription), HTML, CSV/JSON/XML, ZIP, YouTube URLs, EPub</td><td>PDF, DOCX/XLSX/PPTX, legacy Office and RTF (via LibreOffice), ODF, EPUB, Apple iWork, HTML, LaTeX, images, audio and video (with the <code>asr</code> extra), email, and more</td></tr><tr><td>Outputs</td><td>Markdown</td><td>Markdown, HTML, lossless JSON, DocTags, DocLang, plain text, WebVTT, LaTeX, and JSONL chunks</td></tr><tr><td>Install size in our test</td><td>229 MB virtualenv with <code>[all]</code></td><td>1.5 GB virtualenv with CPU PyTorch, plus about 0.5 GB of models downloaded on first run</td></tr><tr><td>Our 9-page PDF</td><td>2.2 s, plain text with no headings and a flattened table</td><td>35 s, 22 headings and real Markdown tables</td></tr><tr><td>MCP server</td><td><code>markitdown-mcp</code>: one tool, <code>convert_to_markdown(uri)</code></td><td><code>docling-mcp</code>: 21 tools for converting, editing and exporting documents</td></tr></tbody></table>\n<p>In short: use MarkItDown for Office files, HTML and quick text out of born-digital PDFs. Use Docling when PDF layout matters, meaning multi-column pages, tables, scans, or chunks headed for a <a href=\"/compare/vector-databases-for-agents/\">vector database</a>. For web pages, a URL-to-Markdown service such as <a href=\"/entries/jina-reader/\">Jina Reader</a>, <a href=\"/entries/firecrawl-api/\">Firecrawl</a> or <a href=\"/entries/crawl4ai/\">Crawl4AI</a> is the better fit; the <a href=\"/stacks/research-agent/\">research agent stack</a> covers that side.</p>\n<h2>1. MarkItDown</h2>\n<p>Install it in a virtual environment. <code>[all]</code> pulls in every optional converter; you can also pick extras such as <code>[pdf, docx, pptx]</code>.</p>\n<pre><code class=\"language-bash\">uv venv --python 3.12 .venv &amp;&amp; source .venv/bin/activate\nuv pip install &#39;markitdown[all]&#39;\nmarkitdown report.pdf -o report.md      # or: markitdown report.pdf &gt; report.md\ncat report.pdf | markitdown             # stdin works too</code></pre>\n<p>From Python:</p>\n<pre><code class=\"language-python\">from markitdown import MarkItDown\n\nmd = MarkItDown(enable_plugins=False)\nresult = md.convert_local(&quot;pricing.xlsx&quot;)   # local files only; see Security below\nprint(result.markdown)</code></pre>\n<p>On Office files the output was clean. A one-sheet workbook came back as a heading per sheet plus a Markdown table:</p>\n<pre><code class=\"language-markdown\">## Pricing\n| Plan | Price per month (USD) | Seats |\n| --- | --- | --- |\n| Free | 0 | 1 |\n| Team | 20 | 5 |</code></pre>\n<p>On the PDF it was fast (2.2 seconds) but shallow. The output had no Markdown headings. Words in justified lines often ran together (&quot;state-of-the-artspecializedAImodels&quot;), and the report&#39;s runtime table became loose lines of numbers. Pages that reproduce another paper&#39;s layout turned into wide pseudo-tables of letter fragments. That is what text-layer extraction gives you, and the README says as much: the output is meant for text-analysis tools, not high-fidelity conversion.</p>\n<p>For better PDF and scan handling, MarkItDown can hand conversion to Azure Document Intelligence (<code>-d -e &lt;endpoint&gt;</code>) or Azure Content Understanding (<code>--use-cu</code>). Both are billable Azure API calls. The <code>markitdown-ocr</code> plugin adds OCR of embedded images through an LLM vision client you pass in. If you pass no client, the plugin loads but silently skips OCR.</p>\n<h2>2. Docling</h2>\n<pre><code class=\"language-bash\">uv venv --python 3.12 .venv-docling &amp;&amp; source .venv-docling/bin/activate\nuv pip install docling      # on CPU-only Linux, add the PyTorch CPU index to skip CUDA wheels:\n# uv pip install docling --extra-index-url https://download.pytorch.org/whl/cpu --index-strategy unsafe-best-match\ndocling report.pdf --image-export-mode placeholder --output out/</code></pre>\n<p>Two defaults to know about:</p>\n<ul><li><strong>Images.</strong> With the CLI defaults, our Markdown file was 581 KB, because the figures were embedded as base64 data URIs. <code>--image-export-mode placeholder</code> replaces each one with <code>&lt;!-- image --&gt;</code> and brought the file down to 34 KB. Use <code>referenced</code> to write PNG files and link to them instead.</li><li><strong>First run.</strong> The first conversion downloads the layout and table models to the Hugging Face cache and the RapidOCR models into the package. That run took 60 seconds; later runs of the same file took about 35 seconds wall time, of which Docling reported 29 seconds converting.</li></ul>\n<p>The output had real structure: <code>## 1 Introduction</code>-style headings, proper paragraphs, and Table 1 as a Markdown table whose column headers are joined from the original&#39;s two header rows (<code>native backend - TTS</code>, <code>pypdfium backend - Mem</code>). Rows that share cells in the original were merged into one Markdown row, so check complex tables before you trust a number in them.</p>\n<p>OCR is on by default. <code>--no-ocr</code> cut the run to 8 seconds of conversion, but the Markdown was 33 lines shorter: it lost part of the reproduced DocLayNet paper pages at the end of the report, including that paper&#39;s Table 2. Only switch OCR off after you&#39;ve compared the output on your own documents.</p>\n<p>From Python:</p>\n<pre><code class=\"language-python\">from docling.document_converter import DocumentConverter\n\nresult = DocumentConverter().convert(&quot;report.pdf&quot;)   # a local path or a URL\nprint(result.document.export_to_markdown())</code></pre>\n<p>Docling also has a VLM pipeline (<code>docling --pipeline vlm --vlm-model granite_docling &lt;file&gt;</code>) and integrations with <a href=\"/entries/llamaindex/\">LlamaIndex</a>, <a href=\"/entries/haystack/\">Haystack</a>, LangChain and <a href=\"/entries/crewai/\">CrewAI</a>. To run it as a service, <a href=\"https://github.com/docling-project/docling-serve\">docling-serve</a> provides an HTTP API on port 5001 (<code>pip install docling-serve</code> then <code>docling-serve run</code>, or the <code>docling-serve-cpu</code> container image, listed at 4.4 GB).</p>\n<h2>3. Chunks for retrieval</h2>\n<p>If the text is going into a vector store, chunk it with Docling rather than splitting the Markdown yourself. Its chunkers work on the document structure, so each chunk carries its headings, captions and page numbers:</p>\n<pre><code class=\"language-bash\">docling report.pdf --to chunks --output chunks/</code></pre>\n<p>That wrote one JSONL file with 48 chunks for our PDF. Each line has <code>text</code> (the chunk with its heading prepended, ready to embed), <code>raw_text</code>, <code>num_tokens</code>, <code>headings</code>, <code>captions</code>, <code>page_numbers</code> and the origin file. The default is the <code>HybridChunker</code>, which starts from the document hierarchy, splits chunks that are too long for the tokenizer and merges small neighbours that share headings. When a table spans chunks, it repeats the table header by default. Set <code>--chunks-tokenizer</code> (a Hugging Face model id) and <code>--chunks-max-tokens</code> to match your embedding model. The first chunking run downloaded the <code>sentence-transformers/all-MiniLM-L6-v2</code> tokenizer as the default.</p>\n<p>Load those lines into any store from the <a href=\"/compare/vector-databases-for-agents/\">vector database comparison</a>, and keep <code>page_numbers</code> in the metadata so the agent can cite where an answer came from. <a href=\"/guides/give-an-agent-long-term-memory/\">Give an agent long-term memory</a> covers the memory side.</p>\n<h2>4. Give the converter to an agent over MCP</h2>\n<p>Both projects ship an MCP server, so an agent can convert files on demand instead of you running a pipeline first (background: <a href=\"/guides/connect-an-agent-to-a-remote-mcp-server/\">Connect an agent to a remote MCP server</a>).</p>\n<h3>markitdown-mcp</h3>\n<pre><code class=\"language-bash\">uv pip install markitdown-mcp\nmarkitdown-mcp                                      # stdio, the default\nmarkitdown-mcp --http --host 127.0.0.1 --port 3001  # Streamable HTTP at /mcp, SSE at /sse</code></pre>\n<pre><code class=\"language-json\">{\n  &quot;mcpServers&quot;: {\n    &quot;markitdown&quot;: { &quot;command&quot;: &quot;markitdown-mcp&quot; }\n  }\n}</code></pre>\n<p>It exposes one tool, <code>convert_to_markdown</code>, taking an <code>http:</code>, <code>https:</code>, <code>file:</code> or <code>data:</code> URI. Checked with the Inspector:</p>\n<pre><code class=\"language-bash\">npx @modelcontextprotocol/inspector --cli markitdown-mcp \\\n  --method tools/call --tool-name convert_to_markdown --tool-arg uri=file:///path/to/pricing.xlsx</code></pre>\n<p>It returned the same table as the CLI. The README recommends its Docker image for Claude Desktop, mounting only the directory you want converted (<code>-v /home/user/data:/workdir</code>).</p>\n<h3>docling-mcp</h3>\n<p>The client snippet in Docling&#39;s docs is <code>uvx --from=docling-mcp docling-mcp-server</code>. With docling-mcp 3.3.0 that is not enough, for two reasons we hit:</p>\n<ol><li><strong>Conversion mode defaults to <code>remote</code>.</strong> Without <code>DOCLING_MCP_SERVICE_URL</code>, every conversion failed with &quot;DOCLING_MCP_SERVICE_URL is not set but DOCLING_MCP_CONVERSION_MODE=remote&quot;. To convert on your own machine, install the <code>local</code> extra and set the mode.</li><li><strong>Transport defaults to <code>streamable-http</code></strong> on <code>localhost:8000</code>. A client that launches the command and expects stdio needs <code>--transport stdio</code>.</li></ol>\n<p>This configuration converted our PDF (the first call took about 40 seconds, including server start-up):</p>\n<pre><code class=\"language-json\">{\n  &quot;mcpServers&quot;: {\n    &quot;docling&quot;: {\n      &quot;command&quot;: &quot;uvx&quot;,\n      &quot;args&quot;: [&quot;--from&quot;, &quot;docling-mcp[local]&quot;, &quot;docling-mcp-server&quot;, &quot;--transport&quot;, &quot;stdio&quot;],\n      &quot;env&quot;: { &quot;DOCLING_MCP_CONVERSION_MODE&quot;: &quot;local&quot; }\n    }\n  }\n}</code></pre>\n<p>The server keeps converted documents in an in-memory cache. <code>convert_document_into_docling_document</code> returns a <code>document_key</code>, and the agent then calls <code>export_docling_document_to_markdown</code> (with an optional <code>max_size</code> in characters) or the anchor tools (<code>get_overview_of_document_anchors</code>, <code>search_for_text_in_document_anchors</code>, <code>get_text_of_document_item_at_anchor</code>) to read parts of a long document instead of all of it. The other tools create and edit documents. Load fewer tool groups with the positional argument (<code>conversion</code>, <code>generation</code>, <code>manipulation</code>, plus optional RAG groups for LlamaIndex and Llama Stack). For a shared deployment, run docling-serve once and point each docling-mcp at it in remote mode with <code>DOCLING_MCP_SERVICE_URL</code> and <code>DOCLING_MCP_SERVICE_API_KEY</code>.</p>\n<h2>5. Security</h2>\n<p>Both converters read whatever their process can read.</p>\n<ul><li>MarkItDown&#39;s README warns that <code>convert()</code> accepts local paths, remote URIs and streams, so untrusted input can make it read local files or fetch internal URLs. It recommends validating input, blocking private, loopback, link-local and metadata-service addresses, and calling the narrowest method you need (<code>convert_local()</code>, <code>convert_response()</code> or <code>convert_stream()</code>).</li><li>markitdown-mcp has no authentication and binds to localhost in HTTP mode. Its README says the tool can read any file the server&#39;s user can access. In our test, <code>convert_to_markdown</code> with <code>file:///etc/hostname</code> returned the file. Run it in a container or VM with only the documents mounted, and do not bind it to other interfaces.</li><li>docling-mcp takes local file paths and URLs too, so the same advice applies.</li><li>Converted text is untrusted input. A PDF can carry instructions aimed at the agent that reads it. Keep converted content separate from instructions, and keep risky tools behind approval. <a href=\"/guides/run-untrusted-code-in-a-sandbox/\">Run untrusted code in a sandbox</a> covers isolation.</li></ul>"
}
