md2gd client reference

Everything the md2gd command-line client does: install, sign in, link a Markdown file to a Google Doc, and keep them two-way synced.

Overview

md2gd is a single, dependency-free binary that keeps a local Markdown file and a Google Doc in sync, both directions, block by block. Your agents and tools write Markdown; your team reviews and edits in the real Google Doc; md2gd reconciles the two continuously.

Auth is brokered by the md2gd web app, so the client never opens its own Google screen. Document content flows directly between your machine and Google. It never passes through md2gd’s servers.

Two commands get you running: md2gd login, then md2gd add. add starts syncing for you; everything below is reference detail.

Install

One line. It detects your OS/arch, verifies the checksum, and installs the binary:

curl -fsSL https://md2gd.com/install.sh | sh

By default it lands in /usr/local/bin (falling back to ~/.local/bin); set MD2GD_INSTALL_DIR to choose. For a direct download or Windows, see the download page for per-platform binaries and PATH setup.

Confirm it’s installed:

md2gd version

Sign in

Connect your Google account. This opens your browser to md2gd.com; you approve once there and the client receives a device token. If you’re already signed in to md2gd.com, it’s instant.

md2gd login

The device token is saved to credentials.json (permissions 0600) under your config dir: ~/Library/Application Support/md2gd on macOS, ~/.config/md2gd on Linux. Additional machines run the same command and never see a Google screen.

Link a file to a Doc

md2gd add links one local Markdown file to one Google Doc and keeps them in sync, both directions. You either create a new Doc for the file or point md2gd at one you already have.

md2gd only ever accesses the single Doc you create or pick (the drive.file scope), never anything else in your Drive. That is also why there is no “link by URL”: a Doc md2gd didn’t create and you didn’t pick is one it can’t read or write, so use --pick to hand it an existing Doc.

Create a new Doc with --create

Makes a fresh Doc that the app owns. --title defaults to the file name:

md2gd add notes.md --create --title "Project notes"

Pick an existing Doc with --pick

Opens the Google Picker in your browser. The Doc you select is the only thing md2gd can touch, nothing else in your Drive:

md2gd add notes.md --pick

Optional: sync into a tab with --new-tab

By default the file syncs to the Doc body, which is all most setups need. Add --new-tab to sync into a new tab instead, leaving the rest of the Doc untouched, so several files can share one Doc, each in its own tab. It works with either option above, and --tab-title defaults to the file name:

md2gd add notes.md --create --new-tab --tab-title "Spec"
md2gd add notes.md --pick --new-tab

Start syncing

md2gd add already started syncing. The daemon runs in the background and auto-starts on login, so there’s nothing more to do. To turn it off or back on later, see Turn syncing on/off.

Turn syncing on/off

md2gd runs as a per-user background service (macOS LaunchAgent, Linux systemd user unit, Windows logon service) that auto-starts on login and restarts if it crashes. md2gd add enables it automatically and restarts it so new links are picked up right away.

To control it manually: md2gd disable stops and removes the service (your links and login are kept), md2gd enable reinstalls and starts it, and md2gd status reports whether it’s running.

Command reference

CommandDescription
md2gd login [--api url]Connect your Google account (browser) and save a device token.
md2gd logoutMark the device offline and clear the local token.
md2gd add <file.md> --create [--title T]Create a new Doc, link the file to it, and start syncing.
md2gd add <file.md> --pickChoose an existing Doc via the browser Google Picker, link, and start syncing.
md2gd add <file.md> --new-tab [--tab-title T]Add a new tab to the Doc and sync the file into it. Combine with --create or --pick.
md2gd lsList all linked files with their sync status and Doc URL.
md2gd rm <file.md | sync-id>Unlink a file. The local file and Google Doc are untouched.
md2gd resolve [file.md]Walk any set-aside conflicts (or list an unsupported file’s offenders). --keep-doc-all / --keep-mine-all skip the prompts.
md2gd enableInstall and start the background service; auto-start on login.
md2gd disableStop and remove the background service. Links and login are kept.
md2gd statusPrint whether the background service is running.
md2gd tokenVend a short-lived Google access token (diagnostic).
md2gd versionPrint the client version.

How sync works

  • Two-way, block by block. md2gd diffs at the block level (headings, paragraphs, list items, code blocks) so edits on either side merge without clobbering the whole document.
  • Local edits push instantly. The daemon watches your files; saving a linked .md reconciles it right away.
  • Doc edits pull on a periodic poll. Remote (Google-side) changes are picked up on the next poll, about every minute.
  • Conflicts: the Doc wins, nothing is lost. If the same block changed on both sides since the last sync, the Doc’s version goes live in your file and yours is set aside in an append-only <file>.conflicts.md beside it, written as a standard diff3 section any editor renders as a three-way merge. Sync keeps flowing (status clash_resolved); run md2gd resolve to walk each set-aside block and keep the Doc’s version, restore yours, or hand-merge.
  • Unsupported constructs stop that file. Content md2gd can’t round-trip losslessly (for example images or horizontal rules) halts that file’s sync with a clear status instead of corrupting the document. Remove the construct and sync resumes on its own.
  • Tokens refresh themselves. The client holds only short-lived (~1h) Google access tokens and re-vends them before expiry. On a transient Google error it retries with backoff (up to ~5 min) before reporting the link as errored; a 401 triggers an immediate token refresh and retry.
Drafting these docs with an AI agent? Add a short rule so it writes only the Markdown md2gd can sync, and never the constructs that freeze a doc. The download page has ready-made rule files for Claude, Cursor, Codex, Copilot, and more.

Config & environment

VariablePurpose
MD2GD_CONFIG_HOMEDirectory for credentials.json (defaults to your OS config dir).
MD2GD_INSTALL_DIRInstall location for the curl | sh installer.

Credentials live in credentials.json (0600): the device token, the API base, and a stable device ID that a re-login reuses so you don’t pile up duplicate devices.

Diagnostics

Check the service is up:

md2gd status

Sign out

Marks this device offline on the dashboard and clears the local token. Your links and the stable device ID are kept, so a later md2gd login reconnects the same device:

md2gd logout

Troubleshooting

“Unidentified developer” on first run (macOS)

The binary isn’t notarized yet. Clear the quarantine flag once:

xattr -d com.apple.quarantine $(which md2gd)

A link shows “auth expired”

Rare, since refresh tokens are long-lived. It happens only if you revoked access in Google settings, went ~6 months without syncing, or changed your Google password. Fix with md2gd login.

A link shows an error or “broken”

The Doc may have been deleted, or access to it removed. Re-link with md2gd add, or remove the link from the dashboard.

A file stopped syncing with “unsupported”

That file contains a construct md2gd won’t round-trip (for example an image or a horizontal rule). Run md2gd resolve <file.md> to list exactly what to change, on each side; remove or adjust it and the file resumes on the next reconcile.