x-notion-mcp
Notion MCP (Spec Placeholder)
This skill is a SPEC / placeholder for implementing a reliable workflow to upload local media (PNG/GIF/MP4/etc.) into Notion privately and embed it into a Notion page, even when the current Notion MCP toolset does not expose file upload APIs.
Problem Statement
We often generate local artifacts (screenshots, GIFs, clips) in ~/brain/obsidian/.../attachments/ and want them embedded in a Notion page. Notion’s API supports direct uploads, but our current MCP tools typically only support page CRUD via Notion-flavored markdown and do not expose:
- File Upload API (create/send/complete/retrieve)
- Block children append/update (JSON block payloads)
Therefore we need a hybrid approach that preserves privacy (no public hosting) and integrates smoothly with the existing MCP workflow.
Requirements
- R1: No public hosting: Do not upload to Imgur/S3/CDNs. Files must be Notion-hosted uploads.
- R2: Private workspace access: Uploaded media must be accessible only to users with access to the page/workspace.
- R3: Deterministic embedding: Embed media at a specific location in a page (e.g., under “Video Demo Analysis”), not only at end-of-page.
- R4: Idempotency: Re-runs should not duplicate media blocks. Detect existing media blocks / use a marker section.
- R5: Safe defaults: Never delete content or move pages/databases without explicit instruction.
- R6: Handle limits: Respect workspace upload limits (free plans may have small per-file caps).
Key Notion API Capabilities (Non-MCP)
Notion supports uploading local files using the File Upload APIs:
- Create:
POST /v1/file_uploads(docs) - Send:
POST /v1/file_uploads/{id}/sendasmultipart/form-data(docs) - File object types (including
file_upload) (docs) - Append blocks to a page:
PATCH /v1/blocks/{block_id}/children(docs)
Once a file upload reaches status="uploaded", it can be attached to media blocks using:
{
"type": "file_upload",
"file_upload": { "id": "<file_upload_uuid>" }
}
Proposed Architecture
Option A (Recommended): Add a local helper script in agent-instructions
Create a script (Python preferred) that:
- Uploads local files to Notion via File Upload API
- Appends image/video blocks to a target page at a chosen insertion point
- Optionally returns a “receipt” (uploaded IDs, block IDs, file sizes)
Then continue using MCP for everything else (creating/updating pages via markdown).
Option B: Extend the Notion MCP server
Add MCP tools (or enhance existing ones) to support:
notion-file-uploads-createnotion-file-uploads-sendnotion-file-uploads-completenotion-block-children-append
This is higher leverage but requires MCP server development and rollout.
Authentication Strategy
- Use a Notion integration token (
NOTION_API_KEY) with:- Access to the target page
- “insert content” capability (required for appending blocks)
- Keep secrets out of git. Read from environment or a local config (e.g.
~/.config/notion/profiles.json).
Insertion Strategy (Deterministic Placement)
Goal: Insert media under a specific section such as “Video Demo Analysis”.
Approach:
- Retrieve the page’s block children via the Notion API (not MCP) and find:
- A heading block matching a target title, OR
- A marker paragraph like
<!-- MEDIA: C-Infinity -->
- Append blocks with
position: { type: "after_block", after_block: { id: "<block_id>" } } - If marker/heading not found, fall back to appending at end.
Idempotency Strategy
Insert a marker block the first time:
- A paragraph block containing a stable identifier, e.g.
MEDIA_SECTION: c-infinity-autoassembler
On rerun:
- Find the marker
- Enumerate subsequent blocks until next heading of same/higher level
- If the expected images already exist (by caption or filename in adjacent text), skip
Media Mapping Rules
- PNG/JPG/WebP → image blocks
- GIF → image blocks (renders inline)
- MP4 → video blocks (or file blocks if video fails)
Captions should include:
- Original filename
- Source timestamp (if available)
- Short description (from analyzer output)
CLI Interface (Suggested)
poetry run python scripts/notion_media_push.py \
--page "https://www.notion.so/<page-id>" \
--section "Video Demo Analysis" \
--media ~/brain/obsidian/Timatron/attachments/yt-Bx3hMd7Fuew-*.png \
--media ~/brain/obsidian/Timatron/attachments/yt-Bx3hMd7Fuew-*.gif \
--dry-run
Validation / Pre-flight Checks
- Confirm page is accessible to token (fetch page).
- Confirm per-file size is under workspace max (see “working with files and media” guide).
- Confirm upload status transitions to
uploadedbefore attaching. - Confirm blocks were appended where expected.
Risks / Gotchas
- Notion-hosted file URLs are time-limited signed URLs when fetched (this is normal for Notion “file” objects). The files remain private, but URLs can be accessed while valid.
- Free workspaces may cap uploads to ~5 MiB/file (verify programmatically).
- Rate limits and block child append limits (max 100 blocks per request).
- Different Notion API versions; prefer latest (currently
2025-09-03per docs).
Implementation Checklist (Future Work)
- Add
scripts/notion_media_push.pyusingrequests(multipart/form-data for send) - Add config loading for token (env + optional profiles file)
- Add block retrieval + insertion point resolution
- Add idempotency marker strategy
- Add unit-ish “dry-run” mode to print planned actions without changes
- Add docs/examples for common workflows (e.g., pushing YouTube analyzer outputs)