CodeVetter
← All unpacks
CodeVetter · Repo Unpack evidence

microsoft/markitdown

Scanned commit b8f79c57eb dated 2026-09-21 — last upstream commit. Generated by codevetter 1.12.0 on 2026-09-22.

github.com/microsoft/markitdown ↗ · 176 files · 3.1 MB

MarkItDown [!IMPORTANT] MarkItDown performs I/O with the privileges of the current process.

Deep analysis by swe-2 via the devin CLI on 2026-09-22 (runtime not recorded) — every claim below links to its cited source files.

Overview

MarkItDown is a Microsoft-maintained Python monorepo whose core package converts files, URLs, and byte streams into Markdown optimized for LLM consumption. It dispatches input through a priority-ordered registry of pluggable DocumentConverter classes after sniffing content with Google's magika model and charset-normalizer. The repo also ships an MCP (Model Context Protocol) server package exposing conversion as a tool, an official OCR plugin that uses LLM vision models, and a sample plugin demonstrating the entry-point-based plugin API.

System map

The repo is a uv/pip-installable Python monorepo under packages/ with four distributions: markitdown (the library + `markitdown` CLI), markitdown-mcp (an MCP server wrapping the library), markitdown-ocr (a first-party plugin adding LLM-vision OCR), and markitdown-sample-plugin (an RTF reference plugin). The core engine is the MarkItDown class in _markitdown.py, which owns a requests.Session, a magika.Magika identifier, and a priority-sorted list of ConverterRegistration entries. All format logic lives in ~25 DocumentConverter subclasses under converters/ plus shared helpers in converter_utils/. Build is hatchling-based; tests run via `hatch test` or pytest in CI across Python 3.10-3.14 on Linux, Windows, and Windows ARM64.

Feature catalog

User-facing surface is three-fold: the `markitdown` CLI, the MarkItDown Python API (convert/convert_local/convert_stream/convert_uri/convert_response), and the `markitdown-mcp` server tool. Format coverage spans office docs (docx/xlsx/xls/pptx), PDF, images, audio, HTML plus URL-specialized converters (Wikipedia, YouTube, Bing SERP), EPUB, Outlook .msg, Jupyter notebooks, CSV, ZIP archives, and plain text. Cloud integrations offer Azure Document Intelligence and Azure Content Understanding as opt-in converters, and LLM-based image captions via a caller-supplied OpenAI-compatible client.

All 16 feature catalog claims →

Data flow

Data enters through convert()/convert_local()/convert_stream()/convert_uri()/convert_response(), is normalized to a seekable BinaryIO plus a list of StreamInfo guesses, then fanned out to priority-sorted converters. Type detection is layered: caller hints and headers produce a base StreamInfo, mimetypes fills gaps, then magika identifies content from bytes and charset-normalizer guesses text encoding; incompatible guesses produce two candidate StreamInfos tried in order. The winning converter returns a DocumentConverterResult whose markdown is normalized (trailing whitespace stripped, 3+ newlines collapsed) before returning.

Behavior traces

Three load-bearing flows were traced end-to-end. (1) CLI file conversion: argparse -> MarkItDown() -> convert -> convert_local -> _get_stream_info_guesses -> _convert -> _handle_output. (2) HTTP/MCP conversion: convert_uri -> requests GET -> convert_response -> _convert, with the MCP tool wrapping it in sanitized ToolErrors. (3) Plugin registration + nested zip/docx conversion showing the recursive and registry-driven design.

Testing signals

Tests are pytest-based and live in packages/*/tests. The core package has ~35 test modules anchored by a declarative FileTestVector harness (_test_vectors.py) asserting must_include/must_not_include substrings across 20+ real fixture files in tests/test_files. Coverage spans stream-info guessing, CLI vectors, per-converter edge cases (PDF tables/memory, docx math/images/styles, pptx images/SVG/notes, xlsx images, RSS titles, Outlook ANSI codepages, charset edge cases) and URI handling; remote-URL tests are skipped under GITHUB_ACTIONS. MCP tests drive the real stdio server subprocess and an in-process Starlette app via httpx.

Risk map

The dominant risk theme is I/O breadth: the library deliberately performs filesystem, network, and subprocess I/O with the caller's privileges, and the README explicitly warns callers to sanitize inputs. Specific hotspots: unrestricted HTTP fetching (SSRF) via convert_uri, file:// URI local file reads, unbounded in-memory buffering of remote/stream content, subprocess invocation of exiftool, plugin code execution via entry points, and an unauthenticated MCP HTTP mode. Defusedxml and an exiftool version check show some hardening is present.

Extension points

MarkItDown is explicitly designed for extension at two levels: in-process registration via MarkItDown.register_converter(converter, priority=...) and distribution-level plugins via the 'markitdown.plugin' entry-point group. Converters subclass DocumentConverter and implement accepts()/convert(); several built-ins expose finer hooks (DocxConverter._image_to_html, style_map, llm_* kwargs). The markitdown-ocr package is the canonical example of a plugin that overrides built-ins by registering with negative priority.

Contributor handoff

Edit surface is small and regular: to add/change a format, touch one file in converters/ plus tests. The invariants that matter: accepts() must leave the stream position unchanged; converters must raise MissingDependencyException (not ImportError) for optional deps; registration order is reversed within a priority. Run tests per-package (hatch test or pytest). Avoid touching _markitdown.py's _convert loop casually — many subtle contracts (position asserts, guess iteration, legacy kwargs) live there.

Languages

LanguageFilesShare of source
Python10696%
Markdown15docs/config
TOML44%
YAML4docs/config
HTML3docs/config
JSON2docs/config

Start reading here

Probable source entrypoint: packages/ — inferred from the directory tree and manifest, not the upstream docs.

Source roots

packages/ (161 files)

Source layout — packages/

PathFiles
packages/markitdown/105
packages/markitdown-mcp/11
packages/markitdown-ocr/36
packages/markitdown-sample-plugin/9

Repository structure

Top-level pathFiles
packages/161
.github/4
.devcontainer/1

Docs found: README.md

Package

packages/markitdown-mcp/pyproject.toml — markitdown-mcp, 0 declared dependencies.

CI & config entrypoints

Structural health signals

Average score 7.7/10 across 52 analyzed source files; 5 hotspots surfaced.

These are deterministic review leads — size, churn, and structural signals — not proof of a bug in this project.

Recent history