Putting MarkItDown behind a real interface

MarkItDown's conversion capability changes meaning when it moves from a local script to a command-line process or an agent-facing server. This chapter diagnoses that change: the same normalization layer can read a file or fetch a URI, but the surrounding interface determines who can request the read, which privileges it uses, and how failures are contained. The result is a practical boundary for deployment—what the caller must validate or isolate, and what the repository's tests can and cannot establish about quality.

The interface changes the trust boundary

In a local script, the person or program calling MarkItDown may already control the input. The Python API still offers a permissive convert() method, but it also provides narrower methods for local paths, already-fetched responses, and explicitly opened streams. That distinction lets the embedding application decide how much of the source-access problem belongs inside the conversion layer. A script processing its own reports may reasonably use the convenient path. A service accepting a locator from another party should not assume that convenience and control are the same thing.

The command-line interface exposes the same choices in a process that inherits the user's permissions. It can read a named file, receive bytes through standard input, write Markdown to standard output or a file, and accept extension, MIME-type, and character-set hints. Piping a stream is useful because it allows another program to control what bytes reach MarkItDown. It is not automatically a sandbox: the process still has whatever filesystem and network access its operating environment grants, and a filename or URI passed to the command can ask it to open a resource.

Docker changes the packaging and filesystem context, not the underlying conversion contract. A container can make it easier to provide a reproducible environment or mount only a selected directory, but the security outcome depends on the permissions, mounts, network access, credentials, and resource limits supplied at runtime. MarkItDown's design does not turn a container into an authorization policy. Isolation has to be configured around the process.

The MCP package makes the distinction especially clear. It exposes one tool, convert_to_markdown(uri), and delegates the URI to MarkItDown.convert_uri(). The URI may describe an HTTP(S) resource, a local file, or data embedded in the request. The interface is convenient for a trusted local agent because the agent can ask for conversion without knowing the library's Python API. It is also a resource-reading capability: the server can access files and network destinations available to the user running it.

The package documentation therefore describes the MCP server as intended for local use with trusted agents. STDIO is the default transport. Streamable HTTP and SSE are available, but the server binds to 127.0.0.1 by default and has no authentication. The implementation prints a warning when asked to bind to a non-localhost interface, explaining that other machines could then reach a server running with the user's privileges. The warning is valuable operational information, but it is not an access-control mechanism. A reachable, unauthenticated endpoint remains reachable by any process or user that can access that interface.