From 8aef7e4022be3d86cad0b014deb0c31041233c36 Mon Sep 17 00:00:00 2001 From: Sylvester Kaczmarek <16242628+sylvesterkaczmarek@users.noreply.github.com> Date: Thu, 10 Sep 2026 18:39:01 +0100 Subject: [PATCH] Make optional converters opt-in for MarkItDown MCP --- .github/workflows/tests.yml | 3 +- packages/markitdown-mcp/Dockerfile | 2 +- packages/markitdown-mcp/README.md | 28 ++++++++++- packages/markitdown-mcp/pyproject.toml | 5 +- .../markitdown-mcp/tests/test_dependencies.py | 48 +++++++++++++++++++ 5 files changed, 82 insertions(+), 4 deletions(-) create mode 100644 packages/markitdown-mcp/tests/test_dependencies.py diff --git a/.github/workflows/tests.yml b/.github/workflows/tests.yml index 1e725ec1b..55e953273 100644 --- a/.github/workflows/tests.yml +++ b/.github/workflows/tests.yml @@ -84,8 +84,9 @@ jobs: with: python-version: ${{ matrix.python-version }} + # Keep this environment free of optional converters to exercise the base install. - name: Install packages - run: pip install "./packages/markitdown[all]" ./packages/markitdown-mcp pytest + run: pip install ./packages/markitdown ./packages/markitdown-mcp pytest - name: Run tests working-directory: packages/markitdown-mcp diff --git a/packages/markitdown-mcp/Dockerfile b/packages/markitdown-mcp/Dockerfile index 18f78a30f..4206324a7 100644 --- a/packages/markitdown-mcp/Dockerfile +++ b/packages/markitdown-mcp/Dockerfile @@ -15,7 +15,7 @@ RUN apt-get update \ COPY . /app -RUN pip install --no-cache-dir /app +RUN pip install --no-cache-dir "/app[all]" WORKDIR /workdir diff --git a/packages/markitdown-mcp/README.md b/packages/markitdown-mcp/README.md index 508e7b598..8aeb16977 100644 --- a/packages/markitdown-mcp/README.md +++ b/packages/markitdown-mcp/README.md @@ -14,12 +14,38 @@ It exposes one tool: `convert_to_markdown(uri)`, where uri can be any `http:`, ` ## Installation -To install the package, use pip: +The base installation includes the MCP server and MarkItDown's core converters, +such as text and HTML. Optional document and media converters are installed +separately. This avoids requiring platform-specific dependencies for formats +you do not use. + +To install the base package, use pip: ```bash pip install markitdown-mcp ``` +To install every optional converter (the previous default), use: + +```bash +pip install "markitdown-mcp[all]" +``` + +For selected formats, install the corresponding MarkItDown extras alongside the +server. For example, to add PDF support: + +```bash +pip install markitdown-mcp "markitdown[pdf]" +``` + +With `uvx`, select the extra explicitly when full converter support is needed: + +```bash +uvx --from "markitdown-mcp[all]" markitdown-mcp +``` + +The provided Docker image continues to include all optional converters. + ## Usage To run the MCP server, using STDIO (default), use the following command: diff --git a/packages/markitdown-mcp/pyproject.toml b/packages/markitdown-mcp/pyproject.toml index b72fc5829..3995e0ba6 100644 --- a/packages/markitdown-mcp/pyproject.toml +++ b/packages/markitdown-mcp/pyproject.toml @@ -25,10 +25,13 @@ classifiers = [ ] dependencies = [ "mcp>=2.1.1,<3.0.0", - "markitdown[all]>=0.1.1,<0.2.0", + "markitdown>=0.1.1,<0.2.0", "requests>=2.0.0,<3.0.0", ] +[project.optional-dependencies] +all = ["markitdown[all]>=0.1.1,<0.2.0"] + [project.urls] Documentation = "https://github.com/microsoft/markitdown#readme" Issues = "https://github.com/microsoft/markitdown/issues" diff --git a/packages/markitdown-mcp/tests/test_dependencies.py b/packages/markitdown-mcp/tests/test_dependencies.py new file mode 100644 index 000000000..30ae86236 --- /dev/null +++ b/packages/markitdown-mcp/tests/test_dependencies.py @@ -0,0 +1,48 @@ +# Copyright (c) Microsoft Corporation. All rights reserved. +# Licensed under the MIT License. + +import asyncio +from importlib.metadata import requires + +from packaging.requirements import Requirement + + +def markitdown_requirements(): + return [ + requirement + for value in requires("markitdown-mcp") or [] + if (requirement := Requirement(value)).name == "markitdown" + ] + + +def test_base_install_does_not_require_optional_converters(): + requirements = [ + requirement + for requirement in markitdown_requirements() + if requirement.marker is None or requirement.marker.evaluate({"extra": ""}) + ] + assert len(requirements) == 1 + assert requirements[0].extras == set() + + +def test_all_extra_enables_converters_with_the_same_version_range(): + requirements = markitdown_requirements() + base = next( + requirement for requirement in requirements if requirement.marker is None + ) + optional = [ + requirement + for requirement in requirements + if requirement.marker is not None + and requirement.marker.evaluate({"extra": "all"}) + ] + assert len(optional) == 1 + assert optional[0].extras == {"all"} + assert optional[0].specifier == base.specifier + + +def test_mcp_tool_converts_html_without_optional_converters(): + from markitdown_mcp.__main__ import convert_to_markdown + + result = asyncio.run(convert_to_markdown("data:text/html,%3Ch1%3EHello%3C%2Fh1%3E")) + assert result.strip() == "# Hello"