{
  "markdown": "# notion-markdown-sync\n\nTwo-way markdown sync for Notion. Push local markdown files to Notion pages, pull Notion pages to local markdown, and keep them in sync — all through the [Notion Markdown API](https://developers.notion.com/guides/data-apis/working-with-markdown-content).\n\nBuilt as a [Claude Code plugin](https://code.claude.com/docs/en/plugins) — works with Claude Code, Claude.ai, and Claude Cowork.\n\n## Why This Plugin\n\nWe love Notion for organizing everything. We love writing in markdown — it's fast, portable, and lives in Git. And we love how AI agents like Claude Code work with markdown natively — reading, writing, and transforming text is what they're built for.\n\nThe problem? Getting markdown in and out of Notion has always been painful.\n\n**The existing approach** — tools like [Notion MCP](https://developers.notion.com/guides/mcp/mcp) and the blocks API — works at the block level. Reading a page means paginating through blocks 50 at a time, fetching nested children separately, then reconstructing the content. Writing means batching blocks 100 at a time with nesting limitations. For a complex page, that adds up to dozens of API calls.\n\n**This plugin uses Notion's Markdown API** — the entire page in a single call. A full push or pull takes **2-4 API calls** regardless of whether your page is 10 lines or 10,000 lines.\n\n| Approach | Read a page | Write it back | Nested content |\n|---|---|---|---|\n| Blocks API / Notion MCP | Paginated (50 blocks/req) + separate child fetches | Batched (100 blocks/req) + nesting limits | Extra calls per level |\n| **This plugin (Markdown API)** | **1 call** | **1 call** | **Included** |\n\nClaude Code + markdown is a natural fit. Instead of fighting with block-level APIs and complex SDK setups, this plugin lets you just say *\"push this doc to Notion\"* — and Claude handles the rest.\n\nWe built this at [VegaStack](https://vegastack.com) because we sync dozens of docs between our repos and Notion daily. We open-sourced it because everyone deserves a better way to bridge markdown and Notion.\n\n## Use Cases\n\n### Publish docs from your repo to Notion\n\nKeep your documentation in Git (where developers work) and push it to Notion (where everyone else reads it). PRDs, technical specs, architecture docs, runbooks, API references, onboarding guides — write in your editor, push to Notion.\n\n```\nPush docs/architecture.md to our \"Engineering Wiki\" database in Notion\n```\n```\nPush the PRD at docs/prd-user-auth.md to Notion under the Product Specs page\n```\n```\nPush all markdown files in docs/runbooks/ to the \"Runbooks\" database\n```\n\n### Pull Notion pages to local markdown\n\nDownload any Notion page as a clean markdown file. Edit in VS Code or Cursor, diff changes in Git, feed into Claude Code for analysis, or use as context for AI-assisted development.\n\n```\nPull https://notion.so/My-Wiki-abc123 to local markdown at wiki/\n```\n```\nPull our API reference page from Notion to docs/api-reference.md\n```\n```\nPull the \"Product Roadmap\" database — save each page as a separate markdown file in docs/roadmap/\n```\n\n### Keep PRDs and specs in sync\n\nWrite a PRD, technical spec, or RFC locally, push to Notion for team review. Product and engineering collaborate in Notion, developers pull changes back to their repo. The sync metadata tracks what changed and where.\n\n```\nSync docs/prd-user-auth.md with Notion\n```\n```\nSync docs/rfc-new-api.md — check if Notion version has changes from the team\n```\n\n### Convert GitHub-Flavored Markdown for Notion\n\nHave existing docs from GitHub repos, Claude Code plans, or other markdown tools with pipe tables, `->` arrows, `> [!NOTE]` callouts? The plugin auto-detects and converts them to Notion's format — no manual reformatting.\n\n```\nPush README.md to Notion — it has GFM tables and callouts\n```\n```\nPush my Claude Code plan at docs/plans/migration-plan.md to Notion for team review\n```\n\n### Manage Notion database pages from your editor\n\nPush project docs to a Notion database — each file becomes a page with properties like Status, Owner, Sprint, and Priority managed from YAML frontmatter.\n\n```\nPush docs/features/ to the \"Product Roadmap\" database in Notion\n```\n```\nUpdate the Status to \"Done\" and add tag \"v2.1\" for docs/features/dark-mode.md and push to Notion\n```\n\n### Bonus: automated Notion backups\n\nSince this plugin produces clean markdown files, you can combine it with Git and CI/CD for automated Notion backups. Pair with a nightly GitHub Action that pulls your critical Notion databases to a `notion-backup/` branch — instant version history for your entire workspace. No more worrying about accidental deletions or workspace issues.\n\n## Features\n\n- **Push** — Create or update Notion pages from local markdown files\n- **Pull** — Download Notion pages as clean local markdown with YAML frontmatter\n- **Two-way sync** — Detect changes on both sides, resolve conflicts\n- **GFM auto-preprocessing** — Automatically converts GitHub-Flavored Markdown (pipe tables, arrows, callouts) to Notion's enhanced format\n- **Media handling** — Download Notion media locally on pull, upload local images on push\n- **Database pages** — Full support for Notion database properties (select, multi-select, date, people, relations, etc.)\n- **Icon replication** — Preserves page icons (emoji, external URLs, custom emoji) on push and pull\n- **Validation** — Check markdown files for Notion compatibility before pushing\n- **Round-trip fidelity** — 100% content preservation on pull-push-pull cycles\n\n## Quickstart\n\n### 1. Install the plugin\n\n**From the plugin marketplace:**\n```bash\n# In Claude Code, open the plugin manager\n/plugin marketplace add vegastack/oss-notion-markdown-sync\n/plugin install notion-markdown-sync@vegastack-oss-notion-markdown-sync\n```\n\n**Manual install:**\n```bash\ngit clone https://github.com/vegastack/oss-notion-markdown-sync.git\nclaude --plugin-dir ./notion-markdown-sync\n```\n\n### 2. Set up your Notion API key\n\nCreate an internal integration at [notion.so/profile/integrations](https://www.notion.so/profile/integrations/internal), then add the token to your shell:\n\n```bash\n# Add to ~/.zshrc or ~/.bashrc\nexport NOTION_API_KEY=\"ntn_your_token_here\"\n```\n\nGrant the integration access to your pages: open the page in Notion > `...` menu > `Connections` > add your integration.\n\n### 3. Start syncing\n\nJust ask Claude in plain English:\n\n```\nPush docs/roadmap.md to Notion under page <page-id>\n```\n```\nPull this Notion page to local: https://notion.so/My-Page-abc123\n```\n```\nSync docs/guide.md with Notion\n```\n\n## How Each Operation Works\n\n### Push (local to Notion)\n\n```\nPush /path/to/document.md to Notion as a new page under <parent-page-id>\n```\n\nWhat happens:\n1. Auto-detects if the file is standard markdown or Notion-enhanced format\n2. Converts GFM syntax if needed (tables, arrows, callouts, code blocks)\n3. Creates or updates the page via the Markdown API\n4. Saves sync metadata (page ID, URL, timestamp) to the file's YAML frontmatter\n\n### Pull (Notion to local)\n\n```\nPull Notion page https://notion.so/My-Page-abc123 to docs/my-page.md\n```\n\nWhat happens:\n1. Fetches page content and properties via the Markdown API\n2. Downloads embedded images and files to a local `assets/` directory\n3. Writes the markdown file with YAML frontmatter containing sync metadata\n\n### Push to a database\n\n```\nPush docs/meeting-notes.md to the \"Meeting Notes\" database <database-id>\n```\n\nDatabase properties are stored in YAML frontmatter and synced both ways:\n\n```yaml\n---\nnotion_page_id: \"abc123...\"\nnotion_parent_type: \"database\"\ntitle: \"Q1 Planning Meeting\"\nnotion_properties:\n  Status: \"In Progress\"\n  Priority: \"High\"\n  Tags: [\"Planning\", \"Q1\"]\n  Due date: \"2026-03-20\"\n  Assignee:\n    - name: \"Ada Lovelace\"\n      id: \"user-uuid\"\n---\n\nYour meeting notes content here...\n```\n\n### Validate\n\n```\nValidate docs/guide.md for Notion compatibility\n```\n\nReports issues like unsupported heading levels (H5/H6), incorrect indentation, multi-line quote formatting, and unsupported block types — before you push.\n\n### Sync status\n\n```\nCheck Notion sync status for all markdown files in docs/\n```\n\nScans for files with sync metadata and reports which are synced, modified, or untracked.\n\n## GFM Auto-Preprocessing\n\nStandard GitHub-Flavored Markdown is automatically converted to Notion's enhanced format before pushing. The preprocessor auto-detects the format — files already in Notion format pass through unchanged.\n\n| GFM syntax | Converted to | Why |\n|---|---|---|\n| `->`, `<-`, `=>` | `→`, `←`, `⇒` | Notion escapes `>` and `<` to `\\>` and `\\<` |\n| `--` (standalone) | `—` (em dash) | Proper typography |\n| `~10` (approx) | `\\~10` | Prevents strikethrough interpretation |\n| Pipe tables | `<table>` HTML | Avoids content corruption in table cells |\n| Unlabeled ` ``` ` code blocks | ` ```plain text ` | Prevents Notion from guessing wrong language |\n| `> [!NOTE]` callouts | `<callout>` tags | GFM callout syntax not supported by Notion |\n| Multi-line `> ` quotes | Single `> ` with `<br>` | Multiple `>` lines create separate quote blocks |\n| Bare `file.sh` names | `` `file.sh` `` | Prevents Notion from auto-linking as domains |\n\n## Authentication and Security\n\nThis plugin **never stores, logs, or transmits your credentials**. It's a set of instructions — not a service. Your Notion API token stays in your local environment and goes directly from your machine to Notion's API over HTTPS. Nothing passes through any intermediary. See [PRIVACY.md](PRIVACY.md) for full details.\n\n### Setup\n\n1. Create an internal integration at [notion.so/profile/integrations](https://www.notion.so/profile/integrations/internal)\n2. Store the token using one of these methods:\n\n**Shell profile** (recommended for most users):\n```bash\n# Add to ~/.zshrc or ~/.bashrc\nexport NOTION_API_KEY=\"ntn_your_token_here\"\n```\n\n**`.env` file** (for project-specific tokens):\n```bash\n# Make sure .env is in your .gitignore!\nNOTION_API_KEY=ntn_your_token_here\n```\n\n**macOS Keychain** (most secure):\n```bash\n# Store the token\nsecurity add-generic-password -a \"$USER\" -s \"notion-api-key\" -w \"ntn_your_token_here\"\n\n# Retrieve it automatically (add to ~/.zshrc)\nexport NOTION_API_KEY=$(security find-generic-password -a \"$USER\" -s \"notion-api-key\" -w)\n```\n\n3. Grant the integration access: open the Notion page/database > `...` menu > `Connections` > add your integration\n\n## How It Works Under the Hood\n\nBuilt on Notion's [Markdown API](https://developers.notion.com/guides/data-apis/working-with-markdown-content) (v2026-03-11).\n\n**Push flow:**\n```\nLocal .md file\n  → Auto-preprocess (converts GFM to Notion format if needed)\n  → Strip YAML frontmatter\n  → Strip leading H1 (title is set via page properties)\n  → POST or PATCH via Markdown API\n  → Update local frontmatter with sync metadata (page ID, URL, timestamp)\n```\n\n**Pull flow:**\n```\nGET /v1/pages/:id/markdown (fetch content)\n  → GET /v1/pages/:id (fetch title, icon, properties)\n  → Prepend # H1 title to markdown\n  → Add YAML frontmatter with sync metadata\n  → Download media to local assets/ directory\n  → Write .md file\n```\n\n**Sync flow:**\n```\nPull current Notion content → Compare with local file (ignoring frontmatter)\n  → If only local changed: push to Notion\n  → If only Notion changed: update local file\n  → If both changed: show diff, ask user which to keep\n```\n\n## Plugin Structure\n\n```\nnotion-markdown-sync/\n├── .claude-plugin/\n│   ├── plugin.json                          # Plugin manifest\n│   └── marketplace.json                     # Marketplace catalog\n├── skills/\n│   └── notion-markdown-sync/\n│       ├── SKILL.md                         # Core skill instructions\n│       ├── scripts/\n│       │   ├── notion_media.py              # Media upload/download handler\n│       │   └── notion_preprocess.py         # GFM auto-detection + preprocessor\n│       ├── references/\n│       │   ├── enhanced-markdown-format.md  # Notion enhanced markdown spec\n│       │   ├── working-with-markdown-content.md  # Markdown API guide\n│       │   ├── notion-page-property-types.md     # Database property types\n│       │   └── sample-page-test-results.md       # H1/title behavior tests\n│       └── evals/\n│           └── evals.json                   # 5 eval scenarios, 34 assertions\n├── PRIVACY.md\n├── README.md\n├── LICENSE\n└── .gitignore\n```\n\n## Known Limitations\n\n| Limitation | Details |\n|---|---|\n| **Mermaid display mode** | Newly created mermaid blocks default to code view. The preview toggle is a Notion UI-only setting, not accessible via API. |\n| **Blank lines** | Notion strips blank lines between blocks. Use `<empty-block/>` in Notion-format files to preserve intentional spacing. |\n| **Bold + inline code** | `**\\`code\\`**` renders with extra `****` markers in Notion (platform behavior). |\n| **Media URL expiry** | Notion media URLs expire after ~1 hour. The media handler downloads files locally on pull to preserve them permanently. |\n\n## Requirements\n\n- [Claude Code](https://claude.com/claude-code) v1.0.33+ (or Claude.ai / Claude Cowork with plugin support)\n- A Notion internal integration token ([create here](https://www.notion.so/profile/integrations/internal))\n- Python 3.9+ (for media handling and GFM preprocessing scripts)\n\n## Contributing\n\nContributions are welcome! To get started:\n\n1. Fork the repo\n2. Create a feature branch\n3. Test your changes with the included evals (`skills/notion-markdown-sync/evals/evals.json`)\n4. Submit a pull request\n\n## License\n\n[MIT](LICENSE)\n\n---\n\n*This plugin is not affiliated with, endorsed by, or sponsored by Notion Labs, Inc. \"Notion\" is a trademark of Notion Labs, Inc. This plugin uses the public [Notion API](https://developers.notion.com/).*\n",
  "bytes": 13642,
  "sha": "925e5c985be5365b3817be6decd063b7694a6f0aa6183d6d7afc7ee7daeb7968",
  "repo_slug": "vegastack/oss-notion-markdown-sync",
  "fonte": "repo",
  "truncated": false,
  "api": "https://agentalog.com/api/listings/plg_vegastack_oss_notion_markdown_sync_notio_1895dd49/readme"
}