You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
* Resolved an issue with linked images in docx [mammoth] (microsoft#1405)
* Fixed documentation typos in _base_converter.py (microsoft#1393)
* Ensure safe ExifTool usage: require >= 12.24 (microsoft#1399)
* feat: add version verification for ExifTool to ensure security compliance
* fix: improve ExifTool version verification
---------
* Bump actions/checkout from 4 to 5 (microsoft#1394)
Bumps [actions/checkout](https://github.com/actions/checkout) from 4 to 5.
* Add HTML support to DocumentIntelligenceConverter (microsoft#1352)
* fix: correctly pass custom llm prompt parameter (microsoft#1319)
* fix: correctly pass custom llm prompt parameter
* Update README.md (microsoft#1335)
Fix typo in README.md
* Update README.md (microsoft#1350)
ISSUE microsoft#1339
* Update README.md (microsoft#1191)
Fix: Subtle spelling mistake fixed.
* Adding support for data-src Attribute (microsoft#1226)
* supportfordata-src
* docs: correct minor typos (microsoft#1173)
* fix docx parse error(\n in alt) (microsoft#1163)
* Handle PPTX shapes where position is None (microsoft#1161)
* Handle shapes where position is None
* Fixed recursion error, and place no-coord shapes at front
* feat: add checkbox support to Markdown converter (microsoft#1208)
This change introduces functionality to convert HTML checkbox input elements
(<input type=checkbox>) into Markdown checkbox syntax ([ ] or [x]).
Co-authored-by: Meirna Kamal <meirna.kamal@vodafone.com>
* Test if mammoth resolves rlinks. (microsoft#1451)
* Upgrade mammoth to 1.11.0 (microsoft#1452)
* Bump versions of mammoth and pdfminer.six (microsoft#1492)
* Updated pyproject to require a minimum version of pdfminer.six to ensure CVE-2025-64512 is patched.
* [MS] Update PDF table extraction to support aligned Markdown (microsoft#1499)
* Added PDF table extraction feature with aligned Markdown (microsoft#1419)
* Add PDF test files and enhance extraction tests
- Added a medical report scan PDF for testing scanned PDF handling.
- Included a retail purchase receipt PDF to validate receipt extraction functionality.
- Introduced a multipage invoice PDF to test extraction of complex invoice structures.
- Added a borderless table PDF for testing inventory reconciliation report extraction.
- Implemented comprehensive tests for PDF table extraction, ensuring proper structure and data integrity.
- Enhanced existing tests to validate the order and presence of extracted content across various PDF types.
* fix: update dependencies for PDF processing and improve table extraction logic
* Bumped version of pdfminer.six
---------
Authored-by: Ashok <ashh010101@gmail.com>
* Fix: PDF parsing doesn't support partially numbered lists (microsoft#1525)
* Fix: PDF parsing doesn't support partially numbered lists
* Refactor: Move import of PARTIAL_NUMBERING_PATTERN to the top of the test file
* Refactor: Improve assertion formatting in partial numbering tests
* [MS] Extend table support for wide tables (microsoft#1552)
* feat: enhance PDF table extraction to support complex forms and add new test cases
* feat: enhance PDF table extraction with adaptive column clustering and add comprehensive test cases
* fix: correct formatting and improve assertions in PDF table tests
* Add text/markdown to Accept header (microsoft#1554)
* Remove onnxruntime<=1.20.1 Windows pin (microsoft#1551)
* Bump version for release. (microsoft#1564)
* [MS] Add OCR layer service for embedded images and PDF scans (microsoft#1541)
* Add OCR test data and implement tests for various document formats
- Created HTML file with multiple images for testing OCR extraction.
- Added several PDF files with different layouts and image placements to validate OCR functionality.
- Introduced PPTX files with complex layouts and images at various positions for comprehensive testing.
- Included XLSX files with multiple images and complex layouts to ensure accurate OCR extraction.
- Implemented a new test suite in `test_ocr.py` to validate OCR functionality across all document types, ensuring context preservation and accuracy.
* Enhance OCR functionality and validation in document converters
- Refactor image extraction and processing in PDF, PPTX, and XLSX converters for improved readability and consistency.
- Implement detailed validation for OCR text positioning relative to surrounding text in test cases.
- Introduce comprehensive tests for expected OCR results across various document types, ensuring no base64 images are present.
- Improve error handling and logging for better debugging during OCR extraction.
* Add support for scanned PDFs with full-page OCR fallback and implement tests
* Bump version to 0.1.6b1 in __about__.py
* Refactor OCR services to support LLM Vision, update README and tests accordingly
* Add OCR-enabled converters and ensure consistent OCR format across document types
* Refactor converters to improve import organization and enhance OCR functionality across DOCX, PDF, PPTX, and XLSX converters
* Refactor exception imports for consistency across converters and tests
* Fix OCR tests to match MockOCRService output and fix cross-platform file URI handling
* Bump version to 0.1.6b1 in __about__.py
* Skip DOCX/XLSX/PPTX OCR tests when optional dependencies are missing
* Add comprehensive OCR test suite for various document formats
- Introduced multiple test documents for PDF, DOCX, XLSX, and PPTX formats, covering scenarios with images at the start, middle, and end.
- Implemented tests for complex layouts, multi-page documents, and documents with multiple images.
- Created a new test script `test_ocr.py` to validate OCR functionality, ensuring context preservation and accurate text extraction.
- Added expected OCR results for validation against ground truth.
- Included tests for scanned documents to verify OCR fallback mechanisms.
* Remove obsolete HTML test files and refactor test cases for file URIs and OCR format consistency
- Deleted `html_image_start.html` and `html_multiple_images.html` as they are no longer needed.
- Updated `test_file_uris` in `test_module_misc.py` to simplify assertions by removing unnecessary `url2pathname` usage.
- Removed `test_ocr_format_consistency.py` as it is no longer relevant to the current testing framework.
* Refactor OCR processing in PdfConverterWithOCR and enhance unit tests for multipage PDFs
* Revert
* Revert
* Update REDMEs
* Refactor import statements for consistency and improve formatting in converter and test files
* Fix O(n) memory growth in PDF conversion by calling page.close() afte… (microsoft#1612)
* Fix O(n) memory growth in PDF conversion by calling page.close() after each page
* Refactor PDF memory optimization tests for improved readability and consistency
* Add memory benchmarking tests for PDF conversion with page.close() fix
* Remove unnecessary blank lines in PDF memory optimization tests for cleaner code
* Bump version to 0.1.6b2 in __about__.py
* Update PDF conversion tests to include mimetype in StreamInfo
* Updated warning about binding to non-local interfaces. (microsoft#1653)
* fix: handle deeply nested HTML that triggers RecursionError (microsoft#1644)
* fix: handle deeply nested HTML that triggers RecursionError (microsoft#1636)
Large HTML files with deep DOM nesting (e.g., SEC EDGAR filings) cause
markdownify's recursive DOM traversal to exceed Python's default
recursion limit (1000). Previously this RecursionError was caught by
the top-level _convert() dispatcher, which then fell through to
PlainTextConverter — silently returning the raw HTML as 'markdown'
with no warning.
This fix catches RecursionError in HtmlConverter.convert() and falls
back to BeautifulSoup's iterative get_text() method, which handles
arbitrary nesting depths. A warning is emitted so callers know the
output is plain text rather than full markdown.
Root cause chain:
1. HtmlConverter.convert() calls markdownify.convert_soup() (recursive)
2. Deeply nested HTML (>~400 levels) triggers RecursionError
3. _convert() catches all Exceptions, stores in failed_attempts
4. PlainTextConverter.accepts() matches text/html via 'text/' prefix
5. PlainTextConverter.convert() returns raw HTML bytes as text
6. Caller receives 'markdown' that is actually unconverted HTML
* refactor: address review feedback on RecursionError fallback
- Move 'import warnings' to module top level (was inside except block)
- Make test environment-independent by temporarily lowering
sys.setrecursionlimit(200) instead of relying on depth=500 being
sufficient on all platforms; original limit restored in finally block
- Add strict=True keyword argument to opt out of the plain-text
fallback and let RecursionError propagate to the caller
* test: use result.markdown instead of deprecated result.text_content
---------
Co-authored-by: jigangz <jigangz@github.com>
* Clarify security posture in READMEs (microsoft#1807)
* feat: Add Azure Content Understanding converter (microsoft#1865)
* inital version
* improve mime type detection
* prebuilt-image custom analzyer route to image
* enhance cu priority over di
* fix: apply black formatting
* update cache of known prebuilt name and README improvement
* add test cases, run black
* update readme and deriving content_type from the resolved file_type
* update readme
* Bump version to 0.1.6 (microsoft#1914)
---------
Co-authored-by: afourney <adamfo@microsoft.com>
Co-authored-by: JonahDelman <jonah.delman@gmail.com>
Co-authored-by: t3tra <admin@t3tra.net>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: safen0s <99965118+safen0s@users.noreply.github.com>
Co-authored-by: Stefan Rink <stefan-rink@users.noreply.github.com>
Co-authored-by: [W]DOS_ <40659600+W-DOS0@users.noreply.github.com>
Co-authored-by: Utkarsh kumar <m83610278@gmail.com>
Co-authored-by: Ebrahim Tayabali <47640402+ebrahimHakimuddin@users.noreply.github.com>
Co-authored-by: Noah Zhu <118643158+Noah-Zhuhaotian@users.noreply.github.com>
Co-authored-by: Dmitry <98899785+mdqst@users.noreply.github.com>
Co-authored-by: Yuzhong Zhang <141388234+BetterAndBetterII@users.noreply.github.com>
Co-authored-by: Richard Ye <33409792+richardye101@users.noreply.github.com>
Co-authored-by: Meirna <61427701+Meirna-kamal@users.noreply.github.com>
Co-authored-by: Meirna Kamal <meirna.kamal@vodafone.com>
Co-authored-by: lesyk <lesyk@users.noreply.github.com>
Co-authored-by: Bas Nijholt <basnijholt@gmail.com>
Co-authored-by: jigangz <115519042+jigangz@users.noreply.github.com>
Co-authored-by: jigangz <jigangz@github.com>
Co-authored-by: Chien Yuan Chang <ds.chienyuanchang@gmail.com>
[](https://github.com/microsoft/autogen)
6
6
7
-
> [!TIP]
8
-
> MarkItDown now offers an MCP (Model Context Protocol) server for integration with LLM applications like Claude Desktop. See [markitdown-mcp](https://github.com/microsoft/markitdown/tree/main/packages/markitdown-mcp) for more information.
9
-
10
7
> [!IMPORTANT]
11
-
> Breaking changes between 0.0.1 to 0.1.0:
12
-
> * Dependencies are now organized into optional feature-groups (further details below). Use `pip install 'markitdown[all]'` to have backward-compatible behavior.
13
-
> * convert\_stream() now requires a binary file-like object (e.g., a file opened in binary mode, or an io.BytesIO object). This is a breaking change from the previous version, where it previously also accepted text file-like objects, like io.StringIO.
14
-
> * The DocumentConverter class interface has changed to read from file-like streams rather than file paths. *No temporary files are created anymore*. If you are the maintainer of a plugin, or custom DocumentConverter, you likely need to update your code. Otherwise, if only using the MarkItDown class or CLI (as in these examples), you should not need to change anything.
8
+
> MarkItDown performs I/O with the privileges of the current process. Like open() or requests.get(), it will access resources that the process itself can access. Sanitize your inputs in untrusted environments, and call the narrowest `convert_*` function needed for your use case (e.g., `convert_stream()`, or `convert_local()`). See the [Security Considerations](#security-considerations) section of the documentation for more information.
15
9
16
10
MarkItDown is a lightweight Python utility for converting various files to Markdown for use with LLMs and related text analysis pipelines. To this end, it is most comparable to [textract](https://github.com/deanmalmgren/textract), but with a focus on preserving important document structure and content as Markdown (including: headings, lists, tables, links, etc.) While the output is often reasonably presentable and human-friendly, it is meant to be consumed by text analysis tools -- and may not be the best option for high-fidelity document conversions for human consumption.
17
11
@@ -113,6 +107,7 @@ At the moment, the following optional dependencies are available:
113
107
*`[pdf]` Installs dependencies for PDF files
114
108
*`[outlook]` Installs dependencies for Outlook messages
115
109
*`[az-doc-intel]` Installs dependencies for Azure Document Intelligence
110
+
*`[az-content-understanding]` Installs dependencies for Azure Content Understanding
116
111
*`[audio-transcription]` Installs dependencies for audio transcription of wav and mp3 files
117
112
*`[youtube-transcription]` Installs dependencies for fetching YouTube video transcription
To find available plugins, search GitHub for the hashtag `#markitdown-plugin`. To develop a plugin, see `packages/markitdown-sample-plugin`.
134
129
130
+
#### markitdown-ocr Plugin
131
+
132
+
The `markitdown-ocr` plugin adds OCR support to PDF, DOCX, PPTX, and XLSX converters, extracting text from embedded images using LLM Vision — the same `llm_client` / `llm_model` pattern that MarkItDown already uses for image descriptions. No new ML libraries or binary dependencies required.
133
+
134
+
**Installation:**
135
+
136
+
```bash
137
+
pip install markitdown-ocr
138
+
pip install openai # or any OpenAI-compatible client
139
+
```
140
+
141
+
**Usage:**
142
+
143
+
Pass the same `llm_client` and `llm_model` you would use for image descriptions:
144
+
145
+
```python
146
+
from markitdown import MarkItDown
147
+
from openai import OpenAI
148
+
149
+
md = MarkItDown(
150
+
enable_plugins=True,
151
+
llm_client=OpenAI(),
152
+
llm_model="gpt-4o",
153
+
)
154
+
result = md.convert("document_with_images.pdf")
155
+
print(result.text_content)
156
+
```
157
+
158
+
If no `llm_client` is provided the plugin still loads, but OCR is silently skipped and the standard built-in converter is used instead.
159
+
160
+
See [`packages/markitdown-ocr/README.md`](packages/markitdown-ocr/README.md) for detailed documentation.
161
+
162
+
### Azure Content Understanding
163
+
164
+
[Azure Content Understanding](https://learn.microsoft.com/azure/ai-services/content-understanding/) provides higher-quality conversion with structured field extraction (YAML front matter), multi-modal support (documents, images, audio, video), and configurable analyzers.
Content Understanding is ideal when you need capabilities beyond what built-in or Document Intelligence converters provide:
171
+
172
+
-**Audio and video files** — CU is the only option for video, and the higher-quality cloud option for audio. Built-in converters have no video support and only basic audio transcription.
173
+
-**Structured field extraction** — [Prebuilt](https://learn.microsoft.com/azure/ai-services/content-understanding/concepts/prebuilt-analyzers) or [custom-built](https://learn.microsoft.com/azure/ai-services/content-understanding/how-to/customize-analyzer-content-understanding-studio?tabs=portal) analyzers extract domain-specific fields (invoice amounts, receipt dates, contract clauses) serialized as YAML front matter. Neither built-in nor Doc Intel integration exposes fields.
174
+
-**Higher-quality document extraction** — Cloud-based layout analysis and OCR for scanned PDFs, complex tables, and multi-page documents.
175
+
-**Single API for all modalities** — One `cu_endpoint` handles documents, images, audio, and video with automatic analyzer routing.
result = md.convert("report.pdf") # documents → prebuilt-documentSearch
199
+
result = md.convert("meeting.mp4") # video → prebuilt-videoSearch
200
+
result = md.convert("call.wav") # audio → prebuilt-audioSearch
201
+
print(result.markdown)
202
+
```
203
+
204
+
**With a custom analyzer** (for domain-specific field extraction):
205
+
206
+
```python
207
+
md = MarkItDown(
208
+
cu_endpoint="<content_understanding_endpoint>",
209
+
cu_analyzer_id="my-invoice-analyzer",
210
+
)
211
+
result = md.convert("invoice.pdf")
212
+
print(result.markdown)
213
+
# Output includes YAML front matter with extracted fields:
214
+
# ---
215
+
# contentType: document
216
+
# fields:
217
+
# VendorName: CONTOSO LTD.
218
+
# InvoiceDate: '2019-11-15'
219
+
# ---
220
+
# <!-- page 1 -->
221
+
# ...
222
+
```
223
+
224
+
When `cu_analyzer_id` is set, the converter automatically scopes it to compatible file types based on the analyzer's modality. Incompatible types (e.g., audio files with a document analyzer) auto-route to default prebuilt analyzers.
225
+
226
+
**Cost note:** Each `convert()` call for a CU-routed format is a billable Azure API call. Use `cu_file_types` to restrict which formats route to CU:
227
+
228
+
```python
229
+
from markitdown.converters import ContentUnderstandingFileType
230
+
231
+
md = MarkItDown(
232
+
cu_endpoint="<content_understanding_endpoint>",
233
+
cu_file_types=[ContentUnderstandingFileType.PDF], # only PDFs use CU
234
+
)
235
+
```
236
+
237
+
More information about Azure Content Understanding can be found [here](https://learn.microsoft.com/azure/ai-services/content-understanding/).
238
+
135
239
### Azure Document Intelligence
136
240
137
241
To use Microsoft Document Intelligence for conversion:
@@ -164,14 +268,14 @@ result = md.convert("test.pdf")
164
268
print(result.text_content)
165
269
```
166
270
167
-
To use Large Language Models for image descriptions, provide `llm_client` and `llm_model`:
271
+
To use Large Language Models for image descriptions (currently only for pptx and image files), provide `llm_client` and `llm_model`:
@@ -199,7 +303,7 @@ contact [opencode@microsoft.com](mailto:opencode@microsoft.com) with any additio
199
303
200
304
### How to Contribute
201
305
202
-
You can help by looking at issues or helping review PRs. Any issue or PR is welcome, but we have also marked some as 'open for contribution' and 'open for reviewing' to help facilitate community contributions. These are ofcourse just suggestions and you are welcome to contribute in any way you like.
306
+
You can help by looking at issues or helping review PRs. Any issue or PR is welcome, but we have also marked some as 'open for contribution' and 'open for reviewing' to help facilitate community contributions. These are of course just suggestions and you are welcome to contribute in any way you like.
203
307
204
308
<divalign="center">
205
309
@@ -235,6 +339,14 @@ You can help by looking at issues or helping review PRs. Any issue or PR is welc
235
339
236
340
- Run pre-commit checks before submitting a PR: `pre-commit run --all-files`
237
341
342
+
### Security Considerations
343
+
344
+
MarkItDown performs I/O with the privileges of the current process. Like `open()` or `requests.get()`, it will access resources that the process itself can access.
345
+
346
+
**Sanitize your inputs:** Do not pass untrusted input directly to MarkItDown. If any part of the input may be controlled by an untrusted user or system, such as in hosted or server-side applications, it must be validated and restricted before calling MarkItDown. Depending on your environment, this may include restricting file paths, limiting URI schemes and network destinations, and blocking access to private, loopback, link-local, or metadata-service addresses.
347
+
348
+
**Call only the conversion method you need:** Prefer the narrowest conversion API that fits your use case. MarkItDown's `convert()` method is intentionally permissive and can handle local files, remote URIs, and byte streams. If your application only needs to read local files, call `convert_local()` instead. If you need more control over URI fetching, call `requests.get()` yourself and pass the response object to `convert_response()`. For maximum control, open a stream to the input you want converted and call `convert_stream()`.
349
+
238
350
### Contributing 3rd-party Plugins
239
351
240
352
You can also contribute by creating and sharing 3rd party plugins. See `packages/markitdown-sample-plugin` for more details.
Copy file name to clipboardExpand all lines: packages/markitdown-mcp/README.md
+10-7Lines changed: 10 additions & 7 deletions
Display the source diff
Display the rich diff
Original file line number
Diff line number
Diff line change
@@ -1,5 +1,9 @@
1
1
# MarkItDown-MCP
2
2
3
+
> [!IMPORTANT]
4
+
> The MarkItDown-MCP package is meant for **local use**, with local trusted agents. In particular, when running the MCP server with Streamable HTTP or SSE, it binds to `localhost` by default, and is not exposed to other machines on the network or Internet. In this configuration, it is meant to be a direct alternative to the STDIO transport, which may be more convenient in some cases. DO NOT bind the server to other interfaces unless you understand the [security implications](#security-considerations) of doing so.
@@ -54,7 +58,7 @@ Once mounted, all files under data will be accessible under `/workdir` in the co
54
58
55
59
It is recommended to use the Docker image when running the MCP server for Claude Desktop.
56
60
57
-
Follow [these instrutions](https://modelcontextprotocol.io/quickstart/user#for-claude-desktop-users) to access Claude's `claude_desktop_config.json` file.
61
+
Follow [these instructions](https://modelcontextprotocol.io/quickstart/user#for-claude-desktop-users) to access Claude's `claude_desktop_config.json` file.
58
62
59
63
Edit it to include the following JSON entry:
60
64
@@ -96,13 +100,13 @@ If you want to mount a directory, adjust it accordingly:
96
100
97
101
## Debugging
98
102
99
-
To debug the MCP server you can use the `mcpinspector` tool.
103
+
To debug the MCP server you can use the `MCP Inspector` tool.
100
104
101
105
```bash
102
106
npx @modelcontextprotocol/inspector
103
107
```
104
108
105
-
You can then connect to the insepctor through the specified host and port (e.g., `http://localhost:5173/`).
109
+
You can then connect to the inspector through the specified host and port (e.g., `http://localhost:5173/`).
106
110
107
111
If using STDIO:
108
112
* select `STDIO` as the transport type,
@@ -127,8 +131,7 @@ Finally:
127
131
128
132
## Security Considerations
129
133
130
-
The server does not support authentication, and runs with the privileges if the user running it. For this reason, when running in SSE or Streamable HTTP mode, it is recommended to run the server bound to `localhost` (default).
131
-
134
+
The server does not support authentication, and runs with the privileges of the user running it. For this reason, when running in SSE or Streamable HTTP mode, the server binds by default to `localhost`. Even still, it is important to recognize that the server can be accessed by any process or users on the same local machine, and that the `convert_to_markdown` tool can be used to read any file that the server's user has access to, or any data from the network. If you require additional security, consider running the server in a sandboxed environment, such as a virtual machine or container, and ensure that the user permissions are properly configured to limit access to sensitive files and network segments. Above all, DO NOT bind the server to other interfaces (non-localhost) unless you understand the security implications of doing so.
0 commit comments