Skill Tracer
Skills › Scientific Agent Skills › markitdown › mcp_and_plugins.md

mcp_and_plugins.md

243 lines · skills/markitdown/references/mcp_and_plugins.md at 5f2bc03

Open on GitHub

Most marks come from matching words in the file, so treat them as pointers.

1# MCP Server and Plugin System
2

MCP Server and Plugin System

Raw lines 1 to 2
Uses an outside tool MCP :1
3## Official MCP Package
4
5The Microsoft monorepo publishes `markitdown-mcp`. As of October 1, 2026, the package version is `0.0.1a7`; it depends on `markitdown[all]>=0.1.1,<0.2.0` and `mcp>=2.1.1,<3.0.0`. Runtime checks used MCP SDK 2.2.0. The 2.x server API replaces the older FastMCP import.
6
7Pin both packages to ensure the documented converter version:
8
9```bash
10uv pip install \
11 "markitdown==0.1.8" \
12 "markitdown-mcp==0.0.1a7"
13```
15The server exposes exactly one tool:
17```text
18convert_to_markdown(uri: str) -> str
19```
21Accepted URI schemes are `http:`, `https:`, `file:`, and `data:`.

Official MCP Package

The Microsoft monorepo publishes markitdown-mcp. As of October 1, 2026, the package version is 0.0.1a7; it depends on markitdown[all]>=0.1.1,<0.2.0 and mcp>=2.1.1,<3.0.0. Runtime checks used MCP SDK 2.2.0. The 2.x server API replaces the older FastMCP import.

Pin both packages to ensure the documented converter version:

uv pip install \
  "markitdown==0.1.8" \
  "markitdown-mcp==0.0.1a7"

The server exposes exactly one tool:

convert_to_markdown(uri: str) -> str

Accepted URI schemes are http:, https:, file:, and data:.

Raw lines 3 to 22
Uses an outside tool MCP :3
23## Transport Modes

Transport Modes

Raw lines 23 to 24
25### STDIO
27STDIO is the default and preferred local transport:
29```bash
30markitdown-mcp
31```
33Generic MCP client configuration:
35```json
37 "mcpServers": {
38 "markitdown": {
39 "command": "markitdown-mcp",
40 "args": []
41 }
42 }
44```
46The MCP client launches the server with the same filesystem and network permissions as the client process unless additional sandboxing is applied.

STDIO

STDIO is the default and preferred local transport:

markitdown-mcp

Generic MCP client configuration:

{
  "mcpServers": {
    "markitdown": {
      "command": "markitdown-mcp",
      "args": []
    }
  }
}

The MCP client launches the server with the same filesystem and network permissions as the client process unless additional sandboxing is applied.

Raw lines 25 to 47
Uses an outside tool MCP :33
48### Streamable HTTP and SSE
50```bash
51markitdown-mcp --http --host 127.0.0.1 --port 3001
52```
54Endpoints:
56- Streamable HTTP: `http://127.0.0.1:3001/mcp`
57- SSE: `http://127.0.0.1:3001/sse`
59`--sse` is a deprecated alias for `--http`. Streamable HTTP is stateless and returns JSON responses. The server starts both transports in the same app. Local in-process tests verified tool discovery, conversion, and `/mcp` initialization; no public server was deployed. Expected file/fetch/conversion failures become MCP `ToolError` messages with sensitive internal detail reduced.

Streamable HTTP and SSE

markitdown-mcp --http --host 127.0.0.1 --port 3001

Endpoints:

  • Streamable HTTP: http://127.0.0.1:3001/mcp
  • SSE: http://127.0.0.1:3001/sse

--sse is a deprecated alias for --http. Streamable HTTP is stateless and returns JSON responses. The server starts both transports in the same app. Local in-process tests verified tool discovery, conversion, and /mcp initialization; no public server was deployed. Expected file/fetch/conversion failures become MCP ToolError messages with sensitive internal detail reduced.

Raw lines 48 to 60
Uses an outside tool MCP :59
61## MCP Security Model
63The server:
65- Has no authentication
66- Runs with the current user's privileges
67- Can read files accessible to that user through `file:` URIs
68- Can fetch network resources accessible to that process
69- Accepts broad URI input with no built-in application allowlist
71Requirements:
731. Prefer STDIO.
742. Keep HTTP/SSE bound to `127.0.0.1` or `localhost`.
753. Never expose it on `0.0.0.0`, a LAN interface, or the public Internet without a separately designed authenticated gateway and strict input policy.
764. Run it in a container, VM, or sandbox when processing agent-controlled URIs.
775. Mount only the required input directory, preferably read-only.
786. Deny sensitive network ranges and metadata endpoints.
797. Do not give the server access to home-directory secrets, SSH keys, cloud credentials, or broad research storage.
81The tested MCP SDK enables Host/Origin checks for localhost transports; these checks are not credentials. Localhost is not authentication: other processes or users on the same machine may still reach the port.

MCP Security Model

The server:

  • Has no authentication
  • Runs with the current user's privileges
  • Can read files accessible to that user through file: URIs
  • Can fetch network resources accessible to that process
  • Accepts broad URI input with no built-in application allowlist

Requirements:

  1. Prefer STDIO.
  2. Keep HTTP/SSE bound to 127.0.0.1 or localhost.
  3. Never expose it on 0.0.0.0, a LAN interface, or the public Internet without a separately designed authenticated gateway and strict input policy.
  4. Run it in a container, VM, or sandbox when processing agent-controlled URIs.
  5. Mount only the required input directory, preferably read-only.
  6. Deny sensitive network ranges and metadata endpoints.
  7. Do not give the server access to home-directory secrets, SSH keys, cloud credentials, or broad research storage.

The tested MCP SDK enables Host/Origin checks for localhost transports; these checks are not credentials. Localhost is not authentication: other processes or users on the same machine may still reach the port.

Raw lines 61 to 82
Uses an outside tool MCP :61
83## MCP Plugins
85The MCP process disables MarkItDown plugins by default. It enables them only when the named variable is explicitly set to a truthy value:
87```bash
88MARKITDOWN_ENABLE_PLUGINS=true markitdown-mcp
89```
91Do this only for installed, reviewed plugins. Enabling plugins loads Python entry points into the server process, expanding both code-execution and file/network capabilities.

MCP Plugins

The MCP process disables MarkItDown plugins by default. It enables them only when the named variable is explicitly set to a truthy value:

MARKITDOWN_ENABLE_PLUGINS=true markitdown-mcp

Do this only for installed, reviewed plugins. Enabling plugins loads Python entry points into the server process, expanding both code-execution and file/network capabilities.

Raw lines 83 to 92
Uses an outside tool MCP :83
93## Container Isolation
95The official guide recommends Docker for desktop-agent use. A secure deployment should:
97- Build from a reviewed, pinned `v0.1.8` source checkout.
98- Run as a non-root user.
99- Mount a narrow input directory read-only.
100- Use a read-only root filesystem when practical.
101- Drop unnecessary Linux capabilities.
102- Restrict outbound networking.
103- Avoid mounting the Docker socket, home directory, or credential stores.
105Illustrative runtime shape after building a trusted image (container execution was not tested in this audit):
107```bash
108docker run --rm -i \
109 --read-only \
110 --cap-drop ALL \
111 -v "/absolute/path/to/documents:/workdir:ro" \
112 markitdown-mcp:0.1.8
113```
115The conversion URI inside the container would use a path under `/workdir`.

Container Isolation

The official guide recommends Docker for desktop-agent use. A secure deployment should:

  • Build from a reviewed, pinned v0.1.8 source checkout.
  • Run as a non-root user.
  • Mount a narrow input directory read-only.
  • Use a read-only root filesystem when practical.
  • Drop unnecessary Linux capabilities.
  • Restrict outbound networking.
  • Avoid mounting the Docker socket, home directory, or credential stores.

Illustrative runtime shape after building a trusted image (container execution was not tested in this audit):

docker run --rm -i \
  --read-only \
  --cap-drop ALL \
  -v "/absolute/path/to/documents:/workdir:ro" \
  markitdown-mcp:0.1.8

The conversion URI inside the container would use a path under /workdir.

Raw lines 93 to 116
117## Plugin Discovery
119Plugins are Python distributions registered under the `markitdown.plugin` entry-point group.
121List discovered plugins without enabling them:
123```bash
124markitdown --list-plugins
125python scripts/inspect_installation.py
126```
128Enable plugins for one CLI conversion:
130```bash
131markitdown --use-plugins input.rtf -o output.md
132```
134Enable in Python:
136```python
137from markitdown import MarkItDown
139converter = MarkItDown(enable_plugins=True)
140result = converter.convert_local("input.rtf")
141print(result.markdown)
142```

Plugin Discovery

Plugins are Python distributions registered under the markitdown.plugin entry-point group.

List discovered plugins without enabling them:

markitdown --list-plugins
python scripts/inspect_installation.py

Enable plugins for one CLI conversion:

markitdown --use-plugins input.rtf -o output.md

Enable in Python:

from markitdown import MarkItDown

converter = MarkItDown(enable_plugins=True)
result = converter.convert_local("input.rtf")
print(result.markdown)
Raw lines 117 to 143
Runs code python scripts/inspect_installation.py :125
144## Plugin Trust Checklist
146Before installing a plugin:
148- Confirm the exact package name; defend against typosquatting.
149- Verify the publisher and source repository.
150- Review `pyproject.toml`, entry points, dependencies, and install hooks.
151- Inspect converters for filesystem, subprocess, environment, and network access.
152- Pin an exact version and retain a lockfile/hash in production.
153- Test in an isolated environment with non-sensitive documents.
154- Re-run review after every update.
156Do not install arbitrary packages merely because they use the `#markitdown-plugin` tag.

Plugin Trust Checklist

Before installing a plugin:

  • Confirm the exact package name; defend against typosquatting.
  • Verify the publisher and source repository.
  • Review pyproject.toml, entry points, dependencies, and install hooks.
  • Inspect converters for filesystem, subprocess, environment, and network access.
  • Pin an exact version and retain a lockfile/hash in production.
  • Test in an isolated environment with non-sensitive documents.
  • Re-run review after every update.

Do not install arbitrary packages merely because they use the #markitdown-plugin tag.

Raw lines 144 to 157
158## Plugin Interface Version 1

Plugin Interface Version 1

Raw lines 158 to 159
160### Converter
162```python
163from typing import Any, BinaryIO
165from markitdown import (
166 DocumentConverter,
167 DocumentConverterResult,
168 StreamInfo,
172class ExampleConverter(DocumentConverter):
173 def accepts(
174 self,
175 file_stream: BinaryIO,
176 stream_info: StreamInfo,
177 **kwargs: Any,
178 ) -> bool:
179 return (stream_info.extension or "").lower() == ".example"
181 def convert(
182 self,
183 file_stream: BinaryIO,
184 stream_info: StreamInfo,
185 **kwargs: Any,
186 ) -> DocumentConverterResult:
187 payload = file_stream.read()
188 return DocumentConverterResult(
189 markdown=payload.decode("utf-8", errors="replace")
190 )
191```
193`accepts()` must restore the stream position if it reads any bytes.

Converter

from typing import Any, BinaryIO

from markitdown import (
    DocumentConverter,
    DocumentConverterResult,
    StreamInfo,
)


class ExampleConverter(DocumentConverter):
    def accepts(
        self,
        file_stream: BinaryIO,
        stream_info: StreamInfo,
        **kwargs: Any,
    ) -> bool:
        return (stream_info.extension or "").lower() == ".example"

    def convert(
        self,
        file_stream: BinaryIO,
        stream_info: StreamInfo,
        **kwargs: Any,
    ) -> DocumentConverterResult:
        payload = file_stream.read()
        return DocumentConverterResult(
            markdown=payload.decode("utf-8", errors="replace")
        )

accepts() must restore the stream position if it reads any bytes.

Raw lines 160 to 194
195### Module registration
197```python
198from markitdown import MarkItDown
200__plugin_interface_version__ = 1
203def register_converters(markitdown: MarkItDown, **kwargs) -> None:
204 markitdown.register_converter(ExampleConverter())
205```

Module registration

from markitdown import MarkItDown

__plugin_interface_version__ = 1


def register_converters(markitdown: MarkItDown, **kwargs) -> None:
    markitdown.register_converter(ExampleConverter())
Raw lines 195 to 206
207### `pyproject.toml`
209```toml
210[project.entry-points."markitdown.plugin"]
211example = "example_markitdown_plugin"
212```
214MarkItDown calls `register_converters()` when a plugin-enabled instance is constructed and forwards the constructor keywords.

pyproject.toml

[project.entry-points."markitdown.plugin"]
example = "example_markitdown_plugin"

MarkItDown calls register_converters() when a plugin-enabled instance is constructed and forwards the constructor keywords.

Raw lines 207 to 215
216## Converter Priority
218Lower values run first:
220- Official OCR plugin: `-1.0`
221- Specific built-in formats: `0.0`
222- Generic text/HTML/ZIP converters: `10.0`
224Registering a converter before built-ins can change the parser selected for existing formats. Treat priority as part of the plugin's security and compatibility review.

Converter Priority

Lower values run first:

  • Official OCR plugin: -1.0
  • Specific built-in formats: 0.0
  • Generic text/HTML/ZIP converters: 10.0

Registering a converter before built-ins can change the parser selected for existing formats. Treat priority as part of the plugin's security and compatibility review.

Raw lines 216 to 225
226## Official OCR Plugin
228`markitdown-ocr==0.1.1` requires core >=0.1.8 and is an official plugin from the Microsoft monorepo:
230```bash
231uv pip install \
232 "markitdown==0.1.8" \
233 "markitdown-ocr==0.1.1" \
234 "openai==3.22.1"
235```
237It sends document images/pages to the configured OpenAI-compatible vision provider. Configuration and disclosure requirements are in `cloud_and_ocr.md`.

Official OCR Plugin

markitdown-ocr==0.1.1 requires core >=0.1.8 and is an official plugin from the Microsoft monorepo:

uv pip install \
  "markitdown==0.1.8" \
  "markitdown-ocr==0.1.1" \
  "openai==3.22.1"

It sends document images/pages to the configured OpenAI-compatible vision provider. Configuration and disclosure requirements are in cloud_and_ocr.md.

Raw lines 226 to 238
239## Sources
241- MCP guide at v0.1.8: https://github.com/microsoft/markitdown/tree/v0.1.8/packages/markitdown-mcp
242- MCP server implementation: https://github.com/microsoft/markitdown/blob/v0.1.8/packages/markitdown-mcp/src/markitdown_mcp/__main__.py
243- Sample plugin at v0.1.8: https://github.com/microsoft/markitdown/tree/v0.1.8/packages/markitdown-sample-plugin

Sources

  • MCP guide at v0.1.8: https://github.com/microsoft/markitdown/tree/v0.1.8/packages/markitdown-mcp
  • MCP server implementation: https://github.com/microsoft/markitdown/blob/v0.1.8/packages/markitdown-mcp/src/markitdown_mcp/__main__.py
  • Sample plugin at v0.1.8: https://github.com/microsoft/markitdown/tree/v0.1.8/packages/markitdown-sample-plugin
Raw lines 239 to 243
Uses an outside tool MCP :241