Convert a markdown article into paste-ready rich text for Medium, Substack, and Ghost. These editors ignore pasted markdown but honor the text/html clipboard flavor, so mdpaste converts markdown to HTML (with per-platform quirk fixes) and puts it on the clipboard. Paste into the editor; formatting survives.
Note: Medium's API is closed to new integrations (since 2025), so fully automated publishing isn't possible.
mdpasteowns the format step; you paste (or use a platform's "import from URL" against timbeach.com).
mdpaste article.md # copy Medium-flavored HTML to clipboard
mdpaste -t substack article.md # Substack profile (see Substack section)
mdpaste -t ghost article.md # Ghost profile
cat article.md | mdpaste # read stdin
mdpaste --stdout article.md # print HTML instead of copying
mdpaste -b https://example.com article.md # rewrite relative images to a different host
Relative image paths (e.g. pix/a.png, ../pix/a.png) are rewritten to <base-url>/pix/a.png. Absolute http(s):// images are left untouched. Lone links to embed hosts (YouTube, gist, etc.) become bare URLs so the editor auto-embeds them.
The --base-url default is https://timbeach.com (the author's site). If your images are hosted elsewhere, pass your own host with -b/--base-url (a configurable default is on the roadmap).
mdpaste -t substack applies extra quirk fixes for Substack's editor:
- Tables become aligned monospace text inside a code block (Substack has
no table block; pasted
<table>HTML flattens unpredictably and renders differently in email). A table cell containing a link keeps its text and drops the URL, with a warning. - Footnotes (
[^1]syntax) become plain[n]markers with a numbered endnotes list at the bottom, separated by a rule. (This flattening applies to every target: no editor's paste sanitizer keeps HTML footnote anchors. Substack's native footnotes can only be created by hand in its editor.) - A leading H1 is removed from the body and printed as
title: ...on stderr, ready to paste into Substack's separate title field. - Task list items are converted to
[ ]and[x]text markers, because Substack's paste sanitizer strips the checkbox<input>elements and the state would otherwise be lost. - A lone embed URL (YouTube etc.) auto-expands into a full embed card on paste; no manual step needed.
Known manual steps after pasting:
- A code fence's language tag does not produce syntax highlighting in Substack's editor on paste. The block pastes correctly as a code block, it just arrives unhighlighted; pick a language in Substack's own UI if you want highlighting.
For a quick-and-dirty conversion without quirk handling, add to your zsh config:
mdclip() { pandoc -f gfm -t html "${1:-/dev/stdin}" | xclip -selection clipboard -t text/html; }xclip(X11) orwl-clipboard(Wayland) for clipboard access.
cargo build --release
cp target/release/mdpaste ~/.local/bin/- Raw inline HTML in your markdown is dropped (comrak's safe default emits
<!-- raw HTML omitted -->). An opt-in--allow-htmlflag is on the roadmap. - URL scheme/host matching is case-sensitive (markdown URLs are virtually always lowercase, so this is rarely an issue).
- User-configurable image-path mapping (
~/.config/mdpaste/config.toml) with multiplelocal-path -> public-URLrules, so any user can map their own hosting layout (v1 ships only--base-url). --allow-htmlto pass raw inline HTML through untouched.
MIT, see LICENSE.