~bigbes/confluence-md-utilities

Markdown to Confluence XML converter with bidirectional sync
3e8e97f0 — Eugene Blikh a month ago
fix: round-trip fidelity for inline TOC macros, nbsp emphasis, and strikethrough
3c0272ac — Eugene Blikh a month ago
fix: round-trip fidelity for heading comments, literal tags, and emphasis
f6c846a1 — Eugene Blikh a month ago
fix: preserve inline spacing in fmt and code-block round-trip

clone

read-only
https://git.srht.bigb.es/~bigbes/confluence-md-utilities
read/write
git@git.srht.bigb.es:~bigbes/confluence-md-utilities

You can also use your local clone with git send-email.

#mdcx

Markdown to Confluence XML converter with bidirectional sync for self-hosted Confluence Server/Data Center.

Converts Markdown to Confluence storage format XML and back. Pull pages from Confluence, edit locally as Markdown, push changes back. Template-aware embedding preserves metadata tables, changelogs, inline comment markers, user references, and attachment images through round-trips.

#Install

go install go.bigb.es/confluence-md-utilities/cmd/mdcx@latest

Or build locally with just:

just install

#Commands

#md2xml

Markdown to Confluence XML.

mdcx md2xml input.md -o output.xml
cat input.md | mdcx md2xml > output.xml

#xml2md

Confluence XML to Markdown.

mdcx xml2md input.xml -o output.md
cat input.xml | mdcx xml2md > output.md

#embed

Convert Markdown and insert between marker comments in an existing Confluence document, preserving everything outside the markers (metadata table, TOC, changelog, etc.).

mdcx embed input.md --template template.xml -o output.xml

Template must contain marker comments:

<!-- MD_CONTENT_START -->
<!-- MD_CONTENT_END -->

#extract

Extract content between markers from a Confluence XML document and convert back to Markdown.

mdcx extract input.xml -o output.md
mdcx extract input.xml --raw    # raw Confluence XML, no conversion

#pull

Fetch a page from Confluence and convert to Markdown.

mdcx pull "https://confluence.example.com/pages/viewpage.action?pageId=12345" -o page.md
mdcx pull "https://confluence.example.com/display/TEAM/Page+Title" -o page.md
mdcx pull "https://confluence.example.com/display/TEAM/Page+Title" --raw -o page.xml

# Also save inline + page comments to page.md.comments.json
mdcx pull "https://confluence.example.com/display/TEAM/Page+Title" -o page.md --with-comments

--with-comments writes a sidecar JSON file at <output>.comments.json with two arrays — inline_comments and page_comments — each containing nested replies. Inline comments correlate to the markdown's data-inline-comment="UUID" spans through the marker_ref field. Comment bodies stay as raw Confluence storage XML; pushing comments back is not supported. Requires --output (does not work with stdout).

#push

Convert local Markdown and update a Confluence page.

# Replace entire page body
mdcx push "https://confluence.example.com/display/TEAM/Page" page.md

# Template mode: only replace content between markers, keep everything else
mdcx push "https://confluence.example.com/display/TEAM/Page" page.md --template

# With version message
mdcx push "https://confluence.example.com/display/TEAM/Page" page.md -m "Updated intro"

#fmt

Pretty-print Confluence storage XML with syntax highlighting.

mdcx fmt page.xml
mdcx fmt page.xml --color=force     # force colors even when piped
mdcx fmt page.xml --color=disabled  # no colors
mdcx fmt page.xml -o formatted.xml  # write to file (colors auto-disabled)

Colors: tags in blue/magenta/cyan by namespace, attributes in yellow, values in green, CDATA and comments in gray. Enabled by default when output is a terminal.

Block elements get their own lines with indentation. Short <li>, <h1>-<h6>, <th>, <td> stay on one line when content fits. Lines longer than 120 characters are wrapped at word boundaries (UTF-8 aware). CDATA content is preserved as-is.

#Authentication

pull and push require a Personal Access Token:

# Via environment variable
export CONFLUENCE_TOKEN="your-token-here"
mdcx pull "https://confluence.example.com/display/TEAM/RFC-42" -o rfc.md

# Via flag
mdcx pull --token "your-token" "https://confluence.example.com/display/TEAM/RFC-42" -o rfc.md

#Typical workflow

export CONFLUENCE_TOKEN="..."

# Pull
mdcx pull "https://confluence.example.com/display/TEAM/RFC-42" -o rfc.md

# Edit
vim rfc.md

# Push back, preserving template structure
mdcx push "https://confluence.example.com/display/TEAM/RFC-42" rfc.md --template -m "Updated requirements"

#Shell completions

# Bash
source <(mdcx completion bash)

# Zsh (add to your .zshrc)
mdcx completion zsh > "${fpath[1]}/_mdcx"

# Fish
mdcx completion fish | source

#Supported elements

Markdown Confluence XML
# Heading <h1> ... <h6>
**bold** <strong>
*italic* <em>
~~strike~~ <del>
`code` <code>
Fenced code blocks <ac:structured-macro ac:name="code"> with CDATA
- item / 1. item <ul>/<ol> with <li>
Nested lists Nested <ul>/<ol> inside <li>
- [x] task <ac:task-list>/<ac:task>
[text](url) <a href="...">
![alt](url) <ac:image><ri:url .../>
> blockquote <ac:structured-macro ac:name="info"> (info panel)
--- <hr/>
GFM tables <table> with <th>/<td> wrapped in <p>

#Round-trip preservation

Confluence-specific elements that have no Markdown equivalent are preserved through round-trips using HTML spans:

Confluence element Markdown representation
<ac:inline-comment-marker ac:ref="UUID"> <span data-inline-comment="UUID">text</span>
<ac:link><ri:user ri:userkey="KEY"/></ac:link> <span data-user-key="KEY"/>
<ac:image><ri:attachment ri:filename="F"/></ac:image> <span data-attachment="F"/>

These are converted back to proper Confluence tags on push.

#License

MIT