Convert files, URLs and text to LLM-ready Markdown with paging, provenance and warnings.
Convert anything (documents, web pages, data files, code, archives, and more) to clean Markdown for AI tools. No silent loss. Apache-2.0.
ezmd turns an input into an intermediate representation and renders it as Markdown with YAML frontmatter under one of four output profiles, plus a JSON sidecar with block provenance and an explicit list of warnings. When something cannot be converted, ezmd says so instead of dropping it quietly.
Status: 0.1. Documents, web pages, data files, code, email, ebooks and archives convert today. OCR, audio and video come next; see the roadmap.
From PyPI (Python 3.12 or newer):
pip install ezmd # or: uv tool install ezmd
pip install "ezmd[docs]" # optional: Docling for PDF layout (large, pulls torch)
uvx ezmd convert https://example.com
uvx ezmd-mcp # the MCP server
On Linux, install CPU torch first or ezmd[docs] pulls about 6 GB of CUDA wheels, and the Docling extra needs its models downloaded
once; see docs/install.md.
From source (needs uv):
git clone https://github.com/larpey/ezmd && cd ezmd
uv sync --all-packages
uv sync --all-packages --extra docs # optional: Docling for PDF layout (large, pulls torch)
uv sync --all-packages --extra data # optional: pyarrow for Parquet
Check what is available on your machine (converters, extras, and system programs such as LibreOffice):
uv run ezmd capabilities
uv run ezmd doctor
The TypeScript client is on npm:
npm install @ezmd/sdk
uv run ezmd convert fixtures/office/docx-review/input.docx --profile compact
uv run ezmd convert fixtures/pdf/born-digital-report/input.pdf --profile rag
uv run ezmd convert fixtures/text/markdown-kitchen-sink/input.md --json
uv run ezmd detect fixtures/pdf/born-digital-report/input.pdf
uv run ezmd batch fixtures/text --recursive --out out/text
Warnings go to stderr as WARN [code] message. ezmd batch skips unchanged inputs when run again. URLs
work too; they are fetched through an SSRF guard that refuses private and loopback addresses:
uv run ezmd convert https://example.com --profile compact
Every command and option is in the CLI reference.
import ezmd
result = ezmd.convert(
"fixtures/office/docx-review/input.docx",
profile="compact",
on_progress=lambda p: print(p.stage, p.progress),
)
print(result.markdown)
for w in result.warnings:
print(w.severity, w.kind, w.message)
print(len(result.chunks(chunk_tokens=200)), "rag chunks") # other profiles render from the same conversion
result.render("rag") renders another profile without converting again. See docs/library.md.
From a source checkout, point your MCP client at uv run ezmd-mcp. Claude Code:
claude mcp add ezmd -- uvx ezmd-mcp
claude mcp add ezmd -- uv --directory /path/to/ezmd run ezmd-mcp # from a source checkout
Claude Desktop (claude_desktop_config.json), using the published package:
{
"mcpServers": {
"ezmd": {
"command": "uvx",
"args": ["ezmd-mcp", "--allowed-dirs", "/Users/you/Documents"]
}
}
}
From a source checkout, use "command": "uv" with "args": ["--directory", "/path/to/ezmd", "run", "ezmd-mcp", ...]. Other clients, tools, and the HTTP transport:
docs/mcp.md.
Run the API in-process (inline queue, no Redis), then convert with curl:
uv run ezmd serve --port 8080
curl -s -F file=@fixtures/text/plain-utf8/input.txt -F profile=compact "http://127.0.0.1:8080/v1/convert?wait=30&format=md"
The web UI is served at / once it is built (needs Node 22 and pnpm); then open http://127.0.0.1:8080:
pnpm install && pnpm --filter @ezmd/web build
API reference: docs/api.md and docs/api/openapi.json.
The Compose stack (Caddy, API, sandboxed workers, Redis) builds from this checkout:
bash deploy/bootstrap.sh --plain-http --build
Then open http://localhost:8080. Signed images for each release are on GHCR (ghcr.io/larpey/ezmd-api,
ezmd-worker, ezmd-fetch-node). Everything else
(TLS, API keys, backups, upgrades, every environment variable): docs/selfhost.md.
| Family | Formats |
|---|---|
| Documents | PDF (text layer by default; Docling layout with the docs extra), DOCX, PPTX, XLSX, ODF, RTF, legacy Office (.doc, .xls, .ppt) when LibreOffice is installed |
| Ebooks and notebooks | EPUB, Jupyter notebooks |
| Web | HTML pages and URLs (Trafilatura, rules-based, raw) |
| Data | CSV, TSV, JSON, JSON Lines, YAML, TOML, XML, SQLite, Parquet (data extra); database connection strings are refused without echoing credentials |
| Code | Source files; repository archives (zip or tar) packed with a secret scan, with --converter code.repo_pack or from a GitHub repository URL |
| Archives | ZIP, TAR, and gzip/bzip2/xz TAR with bomb limits; 7z with the 7z extra |
| EML, MBOX, Outlook MSG | |
| Specialized | SEC EDGAR filings |
| Text | Plain text, Markdown |
The full list, with engines, extras, status, fixture counts, and thresholds, is the generated
converter matrix. Local directories are not an input yet (zip the folder, or
use ezmd batch to convert the files in it one by one). Audio, video, OCR, chat exports, and social
platforms are on the roadmap.
full: fidelity first; full frontmatter, numbered headings, page markers, and a JSON sidecar.compact: for pasting into a chat; minimal frontmatter, link list at the end, a 16,000-token budget.rag: for indexing; chunk markers with breadcrumbs, and the chunks in the sidecar.agent: tool output for agents; the body sits inside an untrusted-content fence.Details: docs/output-format.md.
full, compact, rag, or agent.See CONTRIBUTING.md (setup, the council process, the fixture process), SECURITY.md, and CODE_OF_CONDUCT.md.
Apache-2.0 (see LICENSE and NOTICE). Optional extras (for example 7z and
nonfree) and some model weights carry their own licenses; they are listed in
docs/licenses.md.
Source-derived launch command. Check the maintainer’s required arguments and credentials before running:
uvx ezmd-mcpMerge this template into ~/Library/Application Support/Claude/claude_desktop_config.json. Keep existing servers. Add any arguments, credentials, and permissions required by the maintainer; this template has not been install-tested.
{
"mcpServers": {
"io-github-larpey-ezmd": {
"command": "uvx",
"args": [
"ezmd-mcp"
]
}
}
}Restart Claude Desktop completely for changes to take effect. Confirm the server appears connected in the client’s tool list, then try a read-only example from its documentation.
Claude Desktop setup referenceezmd works with any MCP-compatible client. Copy the config snippet from the Configuration section above and add it to the file shown for your client, then restart the application.
~/Library/Application Support/Claude/claude_desktop_config.jsonRestart Claude Desktop completely for changes to take effect.~/.cursor/mcp.jsonRestart Cursor for changes to take effect..vscode/mcp.jsonReload VS Code window for changes to take effect.~/.codeium/windsurf/mcp_config.jsonRestart Windsurf for changes to take effect..mcp.jsonSave at the project root, then start Claude Code in that project and review the MCP server approval prompt. Keep real credentials out of shared files.