Files
our-claude-skills/custom-skills/32-notion-writer/SKILL.md
Andrew Yim 6ac547e78f
Some checks failed
Verify Skills / verify-skills (push) Has been cancelled
refactor(skills): clean skill names (strip NN- prefix from name:) — convention change
Adopt: directory keeps its NN- ordering prefix; skill `name:` is the clean form
without it (dir 16-seo-schema-validator → name: seo-schema-validator). Nicer to
invoke, matches the original desktop/SKILL.md names, still globally unique.

- 71 root SKILL.md: name: NN-foo → name: foo (flat skills + reference-curator suite).
  Plugins (mac-optimizer/multi-agent-guide/dintel-bootstrap) already clean; 95 already clean.
- scripts/migrate_skill_root.py: derive name = dirname minus NN- prefix (skill_name()).
- CLAUDE.md + SKILL-MIGRATION-GUIDE.md: document the dir-prefix / clean-name convention.

verify_skills.py: 0 name collisions across all renamed skills. (The ~/.claude/skills
symlinks were re-pointed to the clean names separately — filesystem only.)

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-28 02:11:01 +09:00

5.1 KiB

name, description
name description
notion-writer Markdown to Notion page writer with database row creation support. Triggers: write to Notion, export to Notion, push content, create Notion page.

Notion Writer Skill

Push markdown content to Notion pages or databases via Claude Code.

Prerequisites

  • Python virtual environment at ~/Project/our-claude-skills/custom-skills/02-notion-writer/code/scripts/venv
  • Notion integration token (preferred: stored in 1Password — see Credential handling below)
  • Target pages/databases must be shared with the integration in Notion (Database/Page → ⋯ → Connections → add integration)

Quick Start

cd ~/Project/our-claude-skills/custom-skills/02-notion-writer/code/scripts
source venv/bin/activate

Credential handling

Do NOT store the Notion API key in a .env file. A long-lived plaintext secret on disk is unnecessary risk — Notion integration tokens grant write access to every page/database the integration is connected to.

Preferred: fetch from 1Password at runtime

Store the token once in 1Password (Item: Notion - Claude Agent, Vault: Development, Field: api-key), then fetch on each invocation. The token lives only in process memory — never on disk, never in shell history.

# One-shot push — token fetched + used + discarded in a single shell line
NOTION_API_KEY="$(op read 'op://Development/Notion - Claude Agent/api-key')" \
  python notion_writer.py --test

For repeated use in the same session, scope the variable to a subshell so it leaves no trace:

(
  export NOTION_API_KEY="$(op read 'op://Development/Notion - Claude Agent/api-key')"
  python notion_writer.py --database "$DB_URL" --title "Foo" --file foo.md
  python notion_writer.py --database "$DB_URL" --title "Bar" --file bar.md
)
# NOTION_API_KEY is gone from the parent shell

Field name conventions in 1Password

When you create the Notion integration item, use these field names:

Field Value
api-key (CONCEALED) The integration token (ntn_... for current API, secret_... for legacy)
username Integration display name (e.g. "Claude Agent")
hostname https://api.notion.com
notesPlain Vault for which workspace + which databases this integration is connected to

The script reads NOTION_API_KEY from the environment — it does not call op itself. This keeps the script free of op-specific dependencies and lets the same script work in CI/CD environments that inject the secret differently (GitHub Actions secrets, Vault, etc.).

Fallback: .env file (discouraged)

If you must use a file (e.g. running on a host without 1Password CLI), place it at ~/Project/our-claude-skills/custom-skills/32-notion-writer/code/.env with:

NOTION_API_KEY=ntn_xxxxxxxx_paste_your_token_here

Set the file to mode 600 (chmod 600 .env) and never commit it. Add .env to .gitignore if not already.

What NEVER to do

  • Echo the token to stdout: echo "$NOTION_API_KEY" (lands in terminal scrollback + shell history if the var was inline)
  • Pass the token as a CLI argument: --api-key "ntn_..." (visible in ps, history, telemetry)
  • Commit a .env file to git, even briefly (commit history is forever)
  • Paste the token into chat / issues / PRs — assume any pasted secret is compromised within minutes

Token rotation

If a token is exposed (or you reasonably suspect it might be):

  1. Go to https://www.notion.so/my-integrations → open the integration → Reset the secret
  2. Update the 1Password item's api-key field with the new token
  3. Verify with NOTION_API_KEY="$(op read ...)" python notion_writer.py --test
  4. The old token is invalidated immediately — any leftover scripts using it will fail loudly

Commands

Test Connection

python notion_writer.py --test

List Accessible Content

python notion_writer.py --list
python notion_writer.py --list --filter pages
python notion_writer.py --list --filter databases

Get Page/Database Info

python notion_writer.py -p PAGE_URL --info
python notion_writer.py -d DATABASE_URL --info

Write to Page

# Append content
python notion_writer.py -p PAGE_URL -f content.md

# Replace content
python notion_writer.py -p PAGE_URL -f content.md --replace

# From stdin
cat report.md | python notion_writer.py -p PAGE_URL --stdin

Create Database Row

python notion_writer.py -d DATABASE_URL -t "Entry Title" -f content.md

Supported Markdown

Markdown Notion Block
# Heading Heading 1
## Heading Heading 2
### Heading Heading 3
- item Bulleted list
1. item Numbered list
- [ ] task To-do (unchecked)
- [x] task To-do (checked)
> quote Quote
```code``` Code block
--- Divider
Paragraphs Paragraph

Workflow Example

Integrate with Jamie YouTube Manager to log video info:

# Check video and save to markdown
python jamie_youtube_api_test.py VIDEO_URL

# Write to Notion
python notion_writer.py -p LOG_PAGE_URL -f output/video_status.md