Confluence doesn't speak markdown — it speaks ADF. So "publishing" rots into hand-repasting and watching the tables break. One command converts and replaces the page body instead.
| ITEM | SHIPPED |
|---|---|
| ADF push | 15 Jul 2026 |
| Date pills | 16 Jul 2026 |
See the spec for detail.
Your doc lives in markdown. Confluence stores its pages as ADF — Atlassian Document Format, a JSON tree. Nothing bridges those two by hand, so "publish it to the wiki" degenerates into pasting into the editor and watching tables, links and headings come out wrong. Every republish, again.
One command converts markdown → ADF and replaces the page body. The doc stays the source of truth on disk; Confluence becomes a render target. Point it at a page URL (or a raw page ID), name any rows to strip, push.
#…######, pipe tables (first row is the header, separator discarded), links and _italic_, single-line > blockquotes, --- rules. YAML frontmatter is stripped.YYYY-MM-DD inside a table cell becomes a real ADF date node — a native Confluence pill. Dates in narrative paragraphs stay plain text, which is what you actually want.--drop-row "NAME" strips field-table rows that only make sense locally — provenance, debug metadata — before anything is published.preserve-extensions.py lifts them off the current page, anchors each to its nearest preceding heading, and re-splices them into the new body.Fetch the current page (and keep it — the preserve step needs it), convert, splice, push:
# 1. getConfluencePage with contentFormat:"adf" → save to /tmp/current-page.json # 2. convert + preserve python3 <skill-dir>/md-to-adf.py <source> --drop-row "Source" > /tmp/adf-full-raw.json python3 <skill-dir>/preserve-extensions.py /tmp/current-page.json /tmp/adf-full-raw.json > /tmp/adf-full.json # 3. push from disk — never inline the body python3 <skill-dir>/mcp-http-call.py updateConfluencePage /tmp/update-args.json
The push goes through a direct Atlassian MCP HTTP client that reads its bearer token from the keychain and loads args from a file. Inline tool-call args flow through the model's output tokens — fragile above a few KB (character-level corruption, escaping bugs, hard size limits). The disk path is tested at ≥600KB of args.
**bold**. Hit one of those and you extend md-to-adf.py — you do not hand-edit the ADF output.python3, the Atlassian MCP tools loaded, and edit access to the target page.--drop-row flags and target page — not in this skill.