MarkItDown Workbench is a local document-to-Markdown tool built on top of Microsoft's MIT-licensed MarkItDown project. It keeps the original converter library, adds a FastAPI browser UI, and gives you a quick way to test uploads, URLs, and bundled sample files.
The goal is simple: drop in a PDF, Office document, HTML page, image, audio file, ZIP, EPUB, JSON, CSV, XML, or plain text file and get clean Markdown back for LLM workflows, notes, indexing, or analysis.
- The upstream
markitdownPython package underpackages/markitdown. - Built-in converters for PDF, DOCX, XLSX, PPTX, HTML, images, audio, ZIP, EPUB, CSV, JSON, XML, RSS, YouTube transcripts, and more.
- Optional plugin support through the MarkItDown plugin entry point system.
- A local FastAPI UI at
examples/quick_ui.py. - Separate UI templates and static assets in:
examples/quick_ui_templates/examples/quick_ui_static/
- A Markdown download endpoint for converted output.
- Environment-key visibility for optional LLM, OCR, Azure, and metadata features.
This project should run in a virtual environment. Do not install into the system Python on Debian, Parrot, Kali, Ubuntu, or other PEP 668-managed systems.
Recommended setup:
cd markitdown
./start.sh installThe launcher creates .venv automatically. If uv is available it uses uv venv and uv pip; otherwise it falls back to python3 -m venv and pip.
If port 8765 is already in use:
fuser -k 8765/tcpOr run the UI on another port:
uv run examples/quick_ui.py --port 8766Recommended launcher:
./start.sh runSee all launcher commands:
./start.sh --helpCheck or update optional environment variables:
./start.sh env status
./start.sh env editDirect command:
uv run examples/quick_ui.pyOpen:
http://127.0.0.1:8765/
The UI lets you:
- Upload a local file.
- Convert a remote URL.
- Pick a bundled test sample.
- Let MarkItDown automatically choose the converter.
- Enable installed plugins.
- Preview Markdown in the browser.
- Download the converted
.mdfile.
Architecture diagrams are in mermaid.md.
Build the local UI image:
docker build -t markitdown-workbench:local .Run it:
docker run --rm -p 8765:8765 --env-file .env markitdown-workbench:localOr use Compose:
cp .env.example .env
docker compose up --buildOpen:
http://127.0.0.1:8765/
Smaller image options:
# Keep common document support but skip broad [all] extras.
docker build \
--build-arg MARKITDOWN_EXTRAS=pdf,docx,pptx,xlsx \
-t markitdown-workbench:docs .
# Skip ffmpeg/exiftool OS packages.
docker build \
--build-arg INSTALL_MEDIA_TOOLS=false \
-t markitdown-workbench:small .
# Core package only, smallest but fewer formats.
docker build \
--build-arg MARKITDOWN_EXTRAS= \
--build-arg INSTALL_MEDIA_TOOLS=false \
-t markitdown-workbench:core .The Dockerfile uses python:3.13-slim-bookworm, not Alpine. Alpine is small, but MarkItDown's Magika/ONNX and several document dependencies rely on glibc/manylinux wheels. Alpine often forces source builds or fails on musl-based images, which can make the build slower, larger, or less reliable.
To pin Debian package versions, pass exact versions available from the selected Debian repository:
docker build \
--build-arg 'APT_MEDIA_PACKAGES=ffmpeg=7:5.1.6-0+deb12u1 libimage-exiftool-perl=12.57+dfsg-1' \
-t markitdown-workbench:pinned .Only pin versions that exist in the base image repository at build time.
Do not bake secrets into the image. Keep provider keys, Azure credentials, and plugin flags in the hosting provider's secret manager or pass them per request through the app's Advanced options.
Typical flow:
docker build -t your-registry/markitdown-workbench:latest .
docker push your-registry/markitdown-workbench:latestThen configure only the values you actually need in the hosting provider dashboard or secret manager. The app does not require provider keys for local conversion. Users can also enter API credentials in Advanced options for a single conversion request.
For public deployments, add authentication in front of the app before exposing it. MarkItDown can read uploaded files, fetch URLs, and process data with the permissions of the container, so do not expose an unauthenticated instance to the internet.
Convert a local file:
uv run markitdown path/to/document.pdf -o document.mdPrint output to the terminal:
uv run markitdown path/to/document.docxUse stdin:
cat path/to/document.pdf | uv run markitdown -x pdf > document.mdList installed plugins:
uv run markitdown --list-pluginsfrom markitdown import MarkItDown
converter = MarkItDown()
result = converter.convert("document.pdf")
print(result.markdown)For server-side or untrusted input, prefer the narrowest method that fits:
converter.convert_local("document.pdf")
converter.convert_stream(file_obj)
converter.convert_response(response)Avoid passing untrusted user input directly to the broad convert() method unless you have validated file paths, URL schemes, and network destinations.
The basic local converters do not need API keys.
Optional features such as vision/OCR plugins or Azure cloud conversion may need provider credentials. In the hosted UI, users can enter these in Advanced options for the current conversion. If a user selects a cloud feature without the needed credential, the app shows a custom error and local conversion remains available.
packages/markitdown Main converter library and CLI
packages/markitdown-mcp MCP server wrapper
packages/markitdown-ocr Optional OCR plugin using LLM vision
packages/markitdown-sample-plugin Example converter plugin
examples/quick_ui.py FastAPI browser UI
uv run python -m py_compile examples/quick_ui.py
uv run python -m pytest packages/markitdown/tests/test_module_misc.py::test_plain_text_falls_back_from_incorrect_ascii_hint
uv run python -m pytest packages/markitdown/tests/test_module_vectors.py::test_convert_localThis work is based on Microsoft MarkItDown, which is licensed under the MIT License. The original license and third-party notices remain in this repository:
LICENSEpackages/markitdown/ThirdPartyNotices.md
MIT licensing allows you to modify, rename, distribute, and build on the project, but you should keep the original copyright and license notices.