# Contributor handoff — microsoft/markitdown

> Canonical page: https://codevetter.com/unpack/markitdown/agent-handoff

# Contributor handoff — microsoft/markitdown

Scanned commit b8f79c57ebc0044be41323d89b2a45d3fda8460e (2026-09-21). Generated by codevetter codevetter 1.12.0 on 2026-09-22.

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.
- To add a format: create converters/_foo_converter.py with ACCEPTED_MIME_TYPE_PREFIXES/ACCEPTED_FILE_EXTENSIONS constants and a DocumentConverter subclass, export it in converters/__init__.py, register it in MarkItDown.enable_builtins at the right spot, and add the extra to pyproject [project.optional-dependencies]. — [packages/markitdown/src/markitdown/converters/__init__.py#L5-54](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/converters/__init__.py#L5-L54) [packages/markitdown/src/markitdown/_markitdown.py#L229-253](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L229-L253) [packages/markitdown/pyproject.toml#L36-64](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/pyproject.toml#L36-L64)
- Optional-dependency convention: wrap the import in try/except ImportError storing sys.exc_info() in a module-level _dependency_exc_info, then raise MissingDependencyException(MISSING_DEPENDENCY_MESSAGE.format(converter=..., extension=..., feature=...)) inside convert(). — [packages/markitdown/src/markitdown/converters/_docx_converter.py#L14-22](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/converters/_docx_converter.py#L14-L22) [packages/markitdown/src/markitdown/converters/_docx_converter.py#L76-88](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/converters/_docx_converter.py#L76-L88) [packages/markitdown/src/markitdown/_exceptions.py#L3-8](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_exceptions.py#L3-L8)
- Hard invariant from _base_converter docstring and _convert asserts: if accepts() reads from file_stream it MUST seek back; a converter that violates this triggers AssertionError inside _convert. — [packages/markitdown/src/markitdown/_base_converter.py#L62-70](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_base_converter.py#L62-L70) [packages/markitdown/src/markitdown/_markitdown.py#L666-669](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L666-L669)
- Registration order is counterintuitive: register_converter inserts at index 0, so LATER registrations are tried FIRST among equal priority — the comment in enable_builtins warns 'the most specific converters should appear below the most generic converters' in source order. — [packages/markitdown/src/markitdown/_markitdown.py#L226-228](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L226-L228) [packages/markitdown/src/markitdown/_markitdown.py#L737-739](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L737-L739)
- Test workflow: cd packages/markitdown && pip install hatch && hatch test (hatch env auto-installs [all] + openai); or run pytest directly after installing 'markitdown[all]' editable. CI also runs per-package pytest for markitdown-ocr and markitdown-mcp. — [packages/markitdown/pyproject.toml#L77-94](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/pyproject.toml#L77-L94) [.github/workflows/tests.yml](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/.github/workflows/tests.yml) [README.md#L369-392](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/README.md#L369-L392)
- New test-vector files go in tests/test_files plus a FileTestVector entry in tests/_test_vectors.py; expected-output assertions are substring-based (must_include/must_not_include), not snapshot diffs. — [packages/markitdown/tests/_test_vectors.py#L5-33](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/tests/_test_vectors.py#L5-L33) [packages/markitdown/tests/test_module_vectors.py#L59-70](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/tests/test_module_vectors.py#L59-L70)
- Deprecated kwargs file_extension/url still flow through convert_local/convert_stream/convert_response and _convert injects them from stream_info — preserve them when refactoring signatures or tests like test_zip_kwargs.py will break. — [packages/markitdown/src/markitdown/_markitdown.py#L368-403](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L368-L403) [packages/markitdown/src/markitdown/_markitdown.py#L651-657](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py#L651-L657) [packages/markitdown/tests/test_zip_kwargs.py](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/tests/test_zip_kwargs.py)
- MCP error contract: do not let raw exceptions leak — convert_to_markdown deliberately maps each exception class to a sanitized ToolError; new exception types from the library need explicit handling there. — [packages/markitdown-mcp/src/markitdown_mcp/__main__.py#L26-47](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown-mcp/src/markitdown_mcp/__main__.py#L26-L47) [packages/markitdown-mcp/tests/test_tool_errors.py](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown-mcp/tests/test_tool_errors.py)
- Read first: _markitdown.py (engine), _base_converter.py + _stream_info.py (contracts), one representative converter (_docx_converter.py for the optional-dep + HTML-intermediate pattern), and _markdownify.py for output shaping; for plugin work, _plugin.py in both markitdown-ocr and markitdown-sample-plugin. (inferred) — [packages/markitdown/src/markitdown/_markitdown.py](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_markitdown.py) [packages/markitdown/src/markitdown/_base_converter.py](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/_base_converter.py) [packages/markitdown/src/markitdown/converters/_docx_converter.py](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/converters/_docx_converter.py)
- Known trap: tests/test_module_vectors.py sets skip_remote only on GITHUB_ACTIONS — local runs may hit raw.githubusercontent.com for some vectors, so sandboxed agents should set GITHUB_ACTIONS=1 or expect network tests. — [packages/markitdown/tests/test_module_vectors.py#L21-26](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/tests/test_module_vectors.py#L21-L26)
- Version bumps happen per package in each src/<pkg>/__about__.py via hatch dynamic versioning — there is no root-level version file. — [packages/markitdown/pyproject.toml#L71-72](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/pyproject.toml#L71-L72) [packages/markitdown/src/markitdown/__about__.py](https://github.com/microsoft/markitdown/blob/b8f79c57ebc0044be41323d89b2a45d3fda8460e/packages/markitdown/src/markitdown/__about__.py)

Every claim links to its cited files at the pinned commit. Citations identify source evidence; they do not establish runtime correctness. Independent CodeVetter scan — not reviewed by upstream maintainers.

## Public product links

- [CodeVetter](https://codevetter.com/)
- [Download](https://codevetter.com/download)
- [Documentation](https://codevetter.com/docs/)
- [Source](https://github.com/Codevetter/codevetter)
