| name | notion-formatting |
| description | Notion page creation, updates, and rich formatting via MCP. Auto-load when user mentions Notion, "update page", "edit page", "create page", "Notion page", "search Notion", "format the page", "rich formatting", "callout", "columns", or any task involving reading, writing, or updating Notion content. Covers: Notion MCP commands, rich formatting style system (callouts, columns, tables, toggles, dividers), page structure blueprints, callout composition patterns, update vs replace semantics, notification spam prevention, and formatting best practices. |
Notion Page Management & Rich Formatting
A comprehensive skill for creating beautifully formatted Notion pages via the Notion MCP server.
Covers the full formatting system: callouts, columns, tables, toggles, dividers, colored backgrounds,
page structure blueprints, and all the gotchas you'll hit along the way.
Self-Learning Protocol
This skill evolves with you. When you give feedback on formatting, correct a pattern, or express a preference, append it here:
> **[Learned YYYY-MM-DD]:** <concise finding>
What to learn from:
- User says "I don't like this layout" or "make it more like X" --> record the preference
- A formatting pattern breaks or renders wrong --> record the gotcha
- User consistently prefers certain colors, icons, or structures --> record the pattern
- A new Notion MCP behavior is discovered --> record the finding
- User gives positive feedback ("this looks great") --> record what worked and why
How to apply learnings:
- Before creating any page, scan the Learnings section at the bottom
- User preferences override the default patterns in this skill
- If a learning contradicts a pattern above, the learning wins (it's newer)
MCP Connection
This skill uses the Notion MCP server. Connect it however your agent supports MCP:
Claude Desktop / Cursor / Windsurf (add to your MCP config):
{
"mcpServers": {
"notion": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.notion.com/mcp"]
}
}
}
Pi / MCPorter (add to ~/.mcporter/mcporter.json):
"notion": {
"command": "npx",
"args": ["-y", "mcp-remote", "https://mcp.notion.com/mcp"]
}
If auth expires: rm -rf ~/.mcp-auth then run any MCP command to re-auth.
Common MCP Workflows
All examples below use MCP tool calls. Your agent calls these directly via MCP. If you're using a CLI bridge like MCPorter, prefix with mcporter call 'notion.<tool>(...)'.
Search across Notion
notion-search(query: "my search term", query_type: "internal")
# query_type: "internal" = Notion pages only, "connected" = external sources, omit = both
Fetch a page or database
notion-fetch(id: "page-or-db-uuid")
Create pages
If calling via CLI/subprocess, always use Python. Bash shell escaping breaks with quotes, newlines, and special characters. If your agent calls MCP directly, just pass the parameters.
Parameters:
notion-create-pages(
pages: [{"properties": {"title": "🎯 My Page Title"}, "content": "<markdown content>"}],
parent: {"page_id": "parent-uuid-here"}
)
Correct format:
Wrong format (will error):
Update pages (preserving comments and discussions)
Rules before any update:
- Use
update_content, NOT replace_content on pages with inline discussions/comments. replace_content destroys all comment threads.
- Batch all changes into ONE API call. Every separate call re-triggers Notion notifications for every
<mention-user> in every touched block.
- Avoid matching text that contains
<mention-user> tags. If your old_str includes a mention, that person gets notified even if you're not changing the mention.
- Callout content on a NEW LINE. Never put content on the same line as the
<callout> tag.
Correct pattern (batched, preserves discussions):
notion-update-page(
page_id: "your-page-uuid",
command: "update_content",
content_updates: [
{"old_str": "old text 1", "new_str": "new text 1"},
{"old_str": "old text 2", "new_str": "new text 2"},
{"old_str": "old text 3", "new_str": "new text 3"}
]
)
Full page rewrite (ONLY for pages with zero discussions):
notion-update-page(
page_id: "uuid",
command: "replace_content",
new_str: "<full markdown content>"
)
# If it fails with "child pages would be deleted", show the list to user
# and ask before retrying with: allow_deleting_content: true
Comments
# Page-level comment
notion-create-comment(page_id: "uuid", rich_text: [{"text": {"content": "Comment"}}])
# Comment on specific text (~10 chars from start + end with ellipsis)
notion-create-comment(page_id: "uuid", selection_with_ellipsis: "# Meeting No...es heading", rich_text: [{"text": {"content": "Note"}}])
# Reply to a thread
notion-create-comment(page_id: "uuid", discussion_id: "discussion://pageId/blockId/discussionId", rich_text: [{"text": {"content": "Reply"}}])
# Get all comments (include_all_blocks for inline discussions)
notion-get-comments(page_id: "uuid", include_all_blocks: true)
Other tools
# Move pages
notion-move-pages(page_or_database_ids: ["uuid1"], new_parent: {"page_id": "parent-uuid"})
# Duplicate a page
notion-duplicate-page(page_id: "uuid")
# Create a database (SQL DDL syntax)
notion-create-database(
schema: "CREATE TABLE (\"Name\" TITLE, \"Status\" SELECT('Todo':red, 'Done':green))",
parent: {"page_id": "uuid"},
title: "My DB"
)
Safety rules
replace_content checks for child pages/databases. If any would be deleted, the call fails with an error. Never set allow_deleting_content: true without user confirmation.
update_content is always preferred over replace_content for existing pages.
- Always fetch before update. Use fetch output to find exact text for
old_str matching.
update_content old_str must match exactly. Whitespace, newlines, formatting must be identical to what fetch returns.
- Pass
page_id directly, not wrapped in data. The API takes page_id, command, and content_updates as top-level named parameters.
Rich Formatting Style System
Always use rich formatting when creating Notion pages. Never create a page that is just flat markdown paragraphs. Use callouts, columns, tables, toggles, dividers, and colored backgrounds.
Content Format: Notion's Extended Markdown
The Notion MCP accepts an extended markdown-like format with HTML-ish tags. This is NOT standard markdown.
Block Types and Exact Syntax
Headings:
## H2: Section Title
### H3: Subsection Title
Use H2 for major sections, H3 for subsections. H1 is the page title. Never skip heading levels.
Dividers:
---
Place between every major section (between H2 groups).
Callouts (the most important formatting block):
<callout icon="EMOJI" color="COLOR">
\tContent here. Use **bold** for emphasis. Use <br> for multi-line.
\t- Bullet points inside callouts work
\t- Second bullet
</callout>
CRITICAL: Callout content MUST be indented with a tab character (\t). Without the tab, content renders outside the callout.
Callout Color Semantics
| Color Value | Visual | Use For | Icon Examples |
|---|
red_bg | Red background | Critical issues, errors, blockers, danger | ⚡🚨❌🔴🚧 |
orange_bg | Orange background | Warnings, caution, needs attention | ⚠️🟠 |
yellow_bg | Yellow background | Action needed, medium priority, cost/budget | ⚠️📋💰🟡 |
green_bg | Green background | Success, completed, positive outcomes | ✅🟢🛡️🤖 |
blue_bg | Blue background | Information, context, tips, current state | 📌💡📍 |
purple_bg | Purple background | Key takeaways, TL;DR, quotes, definitions | 📌💬🔑📦 |
gray_bg | Gray background | Reference lists, instructions, neutral info | 📋✏️❓⚪🔐 |
pink_bg | Pink background | Milestones, deadlines, strategic emphasis | 🎯🗺️ |
Callout Icon Selection Guide
| Icon | Meaning |
|---|
| 📌 | Key point, TL;DR, takeaway |
| 💡 | Tips, insights, pro-tips |
| ⚠️ | Warnings, caution, gotchas |
| ✅ | Success, completed, positive |
| ❌ | Errors, blockers, critical issues |
| 📋 | Reference lists, checklists |
| 💬 | Quotes, stakeholder input |
| 🚨 | Security, incidents |
| 🔑 | Access, definitions, keys |
| 📍 | Current position, "where we are" |
| ⏭️ | Next steps, what's coming |
| 🔥 | Why it matters, urgency |
| 💰 | Cost, budget, financial |
| 🎯 | Deadlines, targets, goals |
| 🗺️ | Migration, roadmap, path |
| ❓ | Open questions, unknowns |
| 🏗️ | Building, in progress |
Columns (side-by-side layouts)
<columns>
\t<column>
\t\t<callout icon="📍" color="blue_bg">
\t\t\t**Left column content**
\t\t\t- Point A
\t\t\t- Point B
\t\t</callout>
\t</column>
\t<column>
\t\t<callout icon="⏭️" color="green_bg">
\t\t\t**Right column content**
\t\t\t- Point C
\t\t\t- Point D
\t\t</callout>
\t</column>
</columns>
CRITICAL: Column content MUST be indented with tabs. Each level gets one more tab. Two columns is standard; three works but gets narrow.
Tables
<table fit-page-width="true" header-row="true">
<tr>
<td>**Header 1**</td>
<td>**Header 2**</td>
<td>**Header 3**</td>
</tr>
<tr>
<td>Value 1</td>
<td>Value 2</td>
<td>Value 3</td>
</tr>
<tr color="green_bg">
<td>**Highlighted row**</td>
<td>Important value</td>
<td>Stands out</td>
</tr>
</table>
Table rules:
- Always use
header-row="true" for tables with headers
- Bold header cell text:
<td>**Header**</td>
- Each
<td> MUST be on its own line. Multiple <td> on one line causes Notion to merge columns
- Row-level coloring:
<tr color="green_bg"> highlights the entire row
- Row color semantics:
green_bg = success, blue_bg = totals, yellow_bg = warning, red_bg = critical
Toggle blocks (expandable)
<details>
<summary>Click to expand this section</summary>
\tHidden content here. Can contain any blocks: paragraphs, tables, callouts, lists.
</details>
Use toggles for deep-dive content, FAQ sections, or optional reference information.
Other blocks
> Quote text for editorial commentary or asides. Keep short (1-2 sentences).
- [ ] Unchecked task
- [x] Completed task

<span color="red">**HIGH**</span> <!-- Inline colored text for status indicators -->
<empty-block/> <!-- Spacing -->
<mention-page url="https://www.notion.so/PAGE_ID"/>
<mention-user url="user://USER_UUID"/>
Page Structure Blueprint
Every well-formatted Notion page should follow this pattern:
[Page icon emoji in title]
├── callout (red_bg or blue_bg): THE headline stat or key framing point
│ Bold the most important number/fact. First thing readers see.
│
├── callout (blue_bg, 📌): Context/purpose/methodology
│ Explains scope in 2-3 sentences.
│
├── divider (---)
│
├── columns (2-col): Executive summary / key data
│ ├── column 1: callouts with current state / scope
│ └── column 2: callouts with next steps / comparison
│
├── divider (---)
│
├── H2: "First Major Section"
│ ├── paragraph: Context text
│ ├── callout: Supporting detail
│ ├── table or columns: Data
│ └── quote: Editorial aside
│
├── divider (---)
│
├── ... (repeat for each major section)
│
├── H2: "Open Questions"
│ └── callout (gray_bg, ❓): List of unknowns/TBDs
│
├── divider (---)
│
├── H2: "Next Steps"
│ ├── numbered list or to-do items
│ └── callout (yellow_bg, ⚠️): Warning/action needed
│
├── divider (---)
│
└── callout (green_bg, ✅ or purple_bg, 📌): Final summary/key takeaway
Page Types
| Page Type | Opening | Body | Closing |
|---|
| Status Brief | Priority callout + exec summary columns | Tables, quotes, architecture | ✅ summary callout |
| Gap Report | Headline stat (red_bg) + methodology (blue_bg) | Data sections with comparison columns | ❓ open questions + 🎯 next steps |
| RFC / Design Doc | TL;DR callout + scope callout | Problem, solution, alternatives with toggles | Next steps + decision callout |
| Dashboard | Purpose callout | Tables, status indicators, to-do lists | Ownership callout |
| Meeting Notes | Context callout | Key decisions as callouts, action items as to-dos | Summary callout |
Visual Rhythm Rules
- Never have more than 3 paragraphs in a row without a visual break (callout, divider, table, toggle, columns, or list)
- Alternate between text-heavy and visual blocks. After a paragraph, add a callout or table.
- Dividers between every H2 section. No exceptions.
- Start strong. First block after title should be a colored callout with the key takeaway.
- End strong. Last block should be a callout (green for summary, purple for takeaway).
- Group related callouts. Comparing two things? Use columns. Listing severity? Stack with descending colors.
- Tables for 3+ columns of structured data. For 2-column data, callouts in columns look better.
- Toggles to hide detail. If a section is > 5 blocks of skippable detail, wrap it in
<details>.
- Quote blocks for editorial voice. After data, add a
> quote to interpret it.
- Bold key terms on first mention.
Callout Composition Patterns
Pattern 1: Severity-ranked stack
<callout icon="🔴" color="red_bg">
\t**Critical issue** -- Category A<br>Metric: **291 / 310 (93.9%)**
</callout>
<callout icon="🟠" color="orange_bg">
\t**High priority** -- Category B<br>Metric: **18 / 310 (5.8%)**
</callout>
<callout icon="🟡" color="yellow_bg">
\t**Medium priority** -- Category C<br>Metric: **45 / 310 (14.5%)**
</callout>
<callout icon="⚪" color="gray_bg">
\t**Review queue** -- Category D<br>Metric: **295 / 310 (95.2%)**
</callout>
Pattern 2: Executive summary columns
<columns>
\t<column>
\t\t<callout icon="📍" color="blue_bg">
\t\t\t**Where we are**
\t\t\t- Working prototype **built and demoed**
\t\t\t- Integrated with **full UI**
\t\t\t- Runs on **test data**
\t\t</callout>
\t\t<callout icon="🔥" color="red_bg">
\t\t\t**Why it matters**
\t\t\t- Key pain point for **2+ years**
\t\t\t- Solution works **instantly, 24/7**
\t\t</callout>
\t</column>
\t<column>
\t\t<callout icon="⏭️" color="green_bg">
\t\t\t**What's next**
\t\t\t- Connect to **real backend**
\t\t\t- Run a **pilot**
\t\t\t- Measure results
\t\t</callout>
\t\t<callout icon="💰" color="yellow_bg">
\t\t\t**Cost estimate**
\t\t\t- Estimated ~$400-500/month
\t\t</callout>
\t</column>
</columns>
Pattern 3: Comparison columns
<columns>
\t<column>
\t\t<callout icon="📞" color="red_bg">
\t\t\t**Current (Expensive)**
\t\t\t- Manual process
\t\t\t- High error rate
\t\t</callout>
\t</column>
\t<column>
\t\t<callout icon="🤖" color="green_bg">
\t\t\t**New (Better)**
\t\t\t- Automated
\t\t\t- Near-zero errors
\t\t</callout>
\t</column>
</columns>
Pattern 4: Definition pair columns
<columns>
\t<column>
\t\t<callout icon="🔑" color="purple_bg">
\t\t\t**Primary method** -- the main intended path. Examples: smart lock, keypad/PIN.
\t\t</callout>
\t</column>
\t<column>
\t\t<callout icon="🔐" color="gray_bg">
\t\t\t**Backup method** -- the fallback if the main flow fails. Examples: physical key.
\t\t</callout>
\t</column>
</columns>
Pattern 5: Phase progression table with row colors
<table header-row="true">
<tr>
<td>**Phase**</td>
<td>**Period**</td>
<td>**What Happens**</td>
</tr>
<tr color="green_bg">
<td>**1 -- Pilot**</td>
<td>Month 1-2</td>
<td>Small-scale test with early adopters.</td>
</tr>
<tr color="yellow_bg">
<td>**2 -- Scaling**</td>
<td>Month 3-4</td>
<td>Expand to 50-100 users.</td>
</tr>
<tr color="orange_bg">
<td>**3 -- Full Migration**</td>
<td>Month 5-6</td>
<td>All users on the new system.</td>
</tr>
<tr color="blue_bg">
<td>**4 -- Legacy Off**</td>
<td>Month 7</td>
<td>Old system decommissioned.</td>
</tr>
</table>
Pattern 6: Tiered risk table
<table header-row="true">
<tr>
<td>**Risk**</td>
<td>**Examples**</td>
<td>**Mitigation**</td>
</tr>
<tr color="green_bg">
<td>**Low**</td>
<td>Public info, FAQs</td>
<td>No auth needed</td>
</tr>
<tr color="yellow_bg">
<td>**Medium**</td>
<td>Account details</td>
<td>Basic verification</td>
</tr>
<tr color="red_bg">
<td>**High**</td>
<td>Financial actions</td>
<td>Full MFA required</td>
</tr>
</table>
Pattern 7: Success callout with bullet list
<callout icon="✅" color="green_bg">
\tNot everything is broken. These areas are **mostly or fully complete**:
\t- **Feature A:** essentially 100% complete
\t- **Feature B:** 94% complete
\t- **Feature C:** 99% complete
</callout>
Pattern 8: Warning with nuance
<callout icon="⚠️" color="yellow_bg">
\t**Not every item needs this.** Some resources have it, some don't. Missing this is **not automatically a bug**. Missing the **primary** version is far more serious.
</callout>
Pattern 9: Stakeholder quotes (alternating colors)
<callout icon="💬" color="purple_bg">
\t**Alice** -- *"This looks great. Let's move forward."* Wants to expand scope.
</callout>
<callout icon="💬" color="yellow_bg">
\t**Bob** -- Asked about integration with existing tools. Key concern: *"How does this fit with what we already have?"*
</callout>
Pattern 10: Image + text side by side
<columns>
\t<column>
\t\t
\t</column>
\t<column>
\t\tExplanatory text next to the diagram. Highlight **key decision points** and explain the flow.
\t</column>
</columns>
Formatting Gotchas
- Tab indentation is mandatory for callout content, column content, and nested blocks. Use
\t in Python strings.
- Each
<td> on its own line. Never put multiple <td> on one line or Notion merges columns.
- Use
<br> for line breaks inside callouts, not newlines (newlines may create separate paragraphs).
- Always use Python subprocess for page creation/updates. Bash escaping breaks with complex content.
- Write content to a temp file first (
/tmp/content.md), then read it in Python.
- 100-block limit per API call. For pages > 100 blocks, use
replace_content (no practical limit).
- Callout content on a NEW LINE with tab. Never on the same line as the opening tag.
- Images work in
create-pages and full replace_content. For partial updates, use replace_content_range.
- Mermaid diagrams render natively. Use fenced
mermaid code blocks.
- To append content: Use
update_content with content_updates. Find the last block, match it as old_str, set new_str to that block + new content.
update_content requires content_updates (array of {old_str, new_str}). Don't pass new_str directly.
create-pages uses properties.title and content, NOT title and content_markdown.
Notification Spam Prevention
Every update_content call re-triggers Notion notifications for <mention-user> tags in modified blocks. To prevent spamming teammates:
- Batch updates aggressively. One API call with 5 updates = 1 notification pass. Five calls = 5 notification rounds.
- Avoid touching mention-containing blocks unless necessary. Match text that excludes the mention span.
- Never re-write existing mentions. Even if old_str and new_str both contain the same mention, Notion re-fires the notification.
- For to-do sections with mentions as headers: Match only the checkbox line, not the heading.
- For tables with mentions: Don't modify the table unless the content itself is changing.
Learnings
This section grows automatically as you use the skill. Your agent appends entries when it discovers new patterns, gotchas, or your preferences.
[Template]: Copy this format for new learnings:
> **[Learned YYYY-MM-DD]:** <what was discovered and why it matters>
A shareable Notion formatting skill for AI coding agents. Original by Mahir Isikli.