tsukurun-Lab

🔄 Notion 3.8のSkills APIで、Codexに入れたSkillを同期してみる

AIで記事の下書きを作っていると、「ここは自分の言い方とちょっと違うな」と直したくなることがあります。そんな気づきを、Notionの文章用Skill「Writing Voice」へルールとして書き足しています。SkillはAIに繰り返し守ってほしい指示をまとめたものです。

見出しの付け方も、好きなものを試しているときの言葉も、次の下書きに活かしたい。

でもNotion側を直しただけでは、以前Codexへ入れたファイルは古いままでした。Notionでルールを直したら、Codex側のファイルも更新する必要があります。

そこで気になったのがNotion 3.8で案内されたSkills APIです。APIはプログラムからデータの取得などを頼むための窓口。Notionで育てているルールを、そのままCodexでも使いやすくできたら。そんなところから試し始めました。

Writing Voiceを1つ取得するところから始めて、4件をまとめて取り込み、最後は同期の手順そのものもSkillにしました。Notionで変更したルールを、会話で頼んでCodexへ取り込めるところまで試しています。

Notionの情報やSkillの使い分けについては以前の記事にも書いています。

🧩 Notion SkillとMCP、結局どう使い分ける?実際にSkillを作って見えてきた役割

Notionで育てたSkillを、Codexでも使いたい

Notion 3.7 では、Skillをファイルとして持ち出す方法が案内されていました。自分もそれでCodexへ入れていたので、 3.8の「新しいSkills API」 を見たときは今度は自動で同期されるのかなと思っていました。

調べてみると、従来のAPIでも ページ本文を取得 し、Skillのファイルへ変換する仕組みは作れます。実際にWriting VoiceのページをこのAPIで取得すると、187個のブロックが2回に分かれて返ってきました。本文を読み、Skillファイルへ組み立てる処理は別に必要です。今回は取得まで試しましたが、変換処理を作ってSkills APIの出力と比べるところまでは行っていません。

今回の Skills API では、AIへの指示を書いた SKILL.md や共有対象の補助ファイルをアーカイブで取得できます。変更を比べるための version_id も返ってきます。ファイルを自分で組み立てずに済むところが便利でした。

ただ、APIが用意されただけで、Codex側のファイルまで自動で更新されるわけではありません。取得した内容をCodex向けに整え、保存先へ反映する処理は、こちらで用意しました。

Notion 3.8のリリース案内 ではSkills APIが新機能として紹介されています。ただ、APIが3.8以前に利用できたかは、手元での実験や公開資料からは確認できていません。コマンドに出てくる Notion-Version: 2026-03-11 はAPI仕様の日付で、リリース名の「3.8」とは別です。

Codexで使うには、形式と保存先の確認も必要

まずはWindowsのPowerShellからWriting Voiceを1件取得しました。取得コマンドは後半の「自分のSkillを1つ取得する手順」にまとめています。ファイルは取得できましたが、そのままではCodexに認識されませんでした。

Codexが読むには、説明欄も必要

取得した SKILL.md の冒頭には、Skillの名前や説明を書く欄があります。今回は説明の description が空で、Codexから読み込みエラーが返ってきました。

ここはCodex向けの名前と説明を付けて対応しました。NotionページのIDは補足情報を記録する metadata 欄へ移し、本文の文章ルールはそのまま残しています。

たとえば、冒頭は次のような形です。これは構造を示す例で、ファイル全体をこの数行で置き換えるものではありません。

---
name: my-writing-voice
description: 自分の文章ルールに沿って日本語の記事を書くときに使う。
metadata:
  notion_page_id: 自分のNotionページID
---
image block

Codex向けに冒頭の形式を整えたあとも、本文はNotionから取得した内容と一致しています。

Skillの置き場所は、環境ごとに確かめる

Skills APIから取得したファイルを、Codexが読む場所へ置く必要があります。今回はAPI取得後のファイルを以前のフォルダに置けば使えると思っていましたが、Codexはそこを読み込んでいませんでした。

Codexの公式説明 にあるユーザー用の保存先は $HOME/.agents/skills です。ただし $HOME が指す場所は実行環境によって異なります。自分の場合、CodexがSkillを読む保存先はWSL側の /home/cross/.agents/skills でした。

保存先はCodexに確認してもらいました。取得後に表示されたファイルのフルパスを添えて、次のように頼みました。

NotionのSkills APIから取得したSkillを、今使っているCodexへ取り込みたいです。
取得したSKILL.md:{ここをPowerShellで表示されたフルパスに置き換える}

現在の同名Skillと、Codexが実際に読み込む保存先を確認してください。
今あるファイルとどこが違うかを確認し、変更内容を見せてください。
参照画像や補助ファイルは、更新後も使えるように残してください。
本文は変えず、必要なら名前や説明など冒頭の形式だけ整えてください。
更新前に元のファイルをバックアップし、更新後はCodexのSkill一覧に表示されるか確認してください。

ここまで確認して、ようやくWriting VoiceがCodexのSkill一覧に並びました。Notionで書いた文章ルールを、Codexでも使えるようになりました。

4つのSkillを、同じ手順で取り込めるようにする

Writing VoiceがCodexで使えるようになると、ほかの3つも同じように取り込みたくなりました。ひとつずつ取得して、形式や保存先を確かめるのは少し手間です。

取り込み方も次から使える形にしておきたい。そこでCodexに、APIからの取得や差分確認を行うスクリプトと、その使い方をまとめた同期用Skillを作ってもらいました。名前は notion-skills-sync です。

同期用Skillには「先に差分を見る」「更新後にもう一度確認する」といった進め方を書き、実際のファイル取得や保存はスクリプトが担当します。Codexに同期を頼むと、この手順に沿ってスクリプトを実行してもらう形です。

対象にしたのは次の4件です。

  • Writing Voice:文章の書き方や見出しの考え方
  • Blog Writer:記事を作るときの手順
  • FeaturedImage Maker:サムネイルを作るときのルール
  • Blog → X Repackager:BlogからXの投稿案を作るときのルール

同期の進め方も、Skillに残す

Notionでは4ページそれぞれに、読み取り用の接続 Codex Skills Sync を追加しました。接続の作り方は、後半の「自分のSkillを1つ取得する手順」に載せています。

同期用Skillとスクリプトは、次のように頼んで作ってもらいました。

NotionのSkills APIから、指定したSkillを同期するローカルSkill notion-skills-sync を作ってください。
同期する対象:{自分のSkill名とNotionページIDをここへ列挙}

まず、この環境のCodexが読み込む保存先と、既存Skillを確認してください。
各Skillの名前・説明・NotionページID・保存先を設定にまとめてください。
APIから取得した内容と手元のファイルを比べ、変更のあるファイルだけ更新してください。
本文は変えず、Codexで読むために必要な名前や説明など、冒頭の形式だけ整えてください。
参照画像や補助ファイルは、更新後も使えるように残してください。
上書きするファイルは先にバックアップしてください。
取得に失敗したり、同期したファイルが手元で編集されていたりしたら、更新を止めてください。
APIトークンはチャットやログへ出さず、この環境で安全に設定する手順を用意してください。
確認だけ行うcheckと、実際に更新するapplyを用意してください。
Skillには、check→変更内容の確認→apply→再度checkという手順を書いてください。
作成後は、notion-skills-sync がCodexのSkill一覧に表示されるか確認してください。

以下のファイルやコマンドは、この依頼で作ってもらった自分の環境用のものです。Codexの標準機能として用意されているわけではありません。

Windowsでは、作成した設定スクリプトをPowerShellから実行し、APIトークンを保存しました。Windowsの自分のユーザーだけが使える形で暗号化しています。

powershell.exe -NoProfile -ExecutionPolicy Bypass -File "$env:USERPROFILE\.codex\skills\notion-skills-sync\scripts\setup-token.ps1"

まず差分を見て、確認してから更新する

check はNotion側の版と手元のファイルを確認する操作、 apply は変更を反映する操作です。最初の取り込みでは、WSL側のターミナルからスクリプトを直接実行しました。パスは自分の配置場所です。

# 確認だけ
python3 /mnt/c/Users/cross/.codex/skills/notion-skills-sync/scripts/sync.py check

# 確認結果を見たうえで更新
python3 /mnt/c/Users/cross/.codex/skills/notion-skills-sync/scripts/sync.py apply

# 更新後にもう一度確認
python3 /mnt/c/Users/cross/.codex/skills/notion-skills-sync/scripts/sync.py check

4件ともCodexのSkill一覧に並び、再確認ではすべて「変更なし」でした。

image block

Windows/WSLで最初に4件を取り込んだときの確認結果です。再確認によるファイルの書き換えはありませんでした。

同期用Skillを登録したあとは、会話で次のように頼めます。

notion-skills-sync を使って、NotionのSkillを同期してください。
まずはcheckだけ実行して、変更内容を見せてください。

変更内容を見てから、続けて更新を頼みます。

確認した変更を反映してください。
更新後にもう一度checkし、差分が残っていないか確認してください。

この会話からの更新も、後述のCloud環境で実際に試しました。

APIに含まれない見本画像は、手元に残す

FeaturedImage Makerには、指示のほかに見本画像や画像サイズを整えるスクリプトもあります。Windows/WSLで最初に同期したとき、APIから取得したのはSkill本文で、手元にあった見本画像3枚と補助ファイルはそのまま残しました。

Skills APIで補助ファイルも取得できることと、今回のSkillのアーカイブに含まれていることは別です。今回確認した4件はいずれも SKILL.md だけだったので、画像や補助ファイルまでAPIで同期できたわけではありません。

Cloudでも、使う環境にSkillを用意する

続けてCodex Cloudでも同じ4件を取り込みました。Windows/WSLに置いたファイルがそのまま使えるわけではなく、Cloud側にも同期スクリプトとSkillを用意する必要がありました。

トークンの設定方法もWindowsとは違います。Cloudでは読み取り用トークンをNetwork secretに登録し、接続先に api.notion.com を指定しました。秘密の値をスクリプトへ書かずにAPIへ接続できます。

最初に開いたCloudのタスクでは、前のチャットで使ったファイルが見つからず、同期スクリプトを用意し直しました。その後、この環境では共有フォルダにあるスクリプトを再利用できましたが、同期用Skillの定義は別途追加しています。環境の設定を公開しただけで必要なファイルもそろう、とは考えない方がよさそうです。

Cloud側の保存先へ4件を取り込むと、CodexのSkill一覧にも並びました。FeaturedImage Makerの見本画像3枚はNotionの記事から別途取得し、画像サイズを整える補助スクリプトは作り直しています。

Notionで直したルールを、同期用Skillで取り込む

初回の取り込みができたので、今度はNotion側のSkillを更新してから、同期用Skillで更新を頼んでみました。Cloud環境で notion-skills-sync が有効になっていることを確かめ、先ほどの会話と同じく、差分確認から進めています。

最初の check で差分が見つかったのは3件でした。

  • Writing Voice:文章の実例を追加
  • Blog Writer:サムネイルのモチーフを1〜2要素に絞る指示を追加
  • FeaturedImage Maker:構図や見本画像、外枠を流用するときのルールを更新

Blog → X Repackagerは変更なしです。差分を確認してから3件を apply で更新し、もう一度 check すると4件すべて差分なしになりました。同期用Skillと対象の4件は、CodexのSkill一覧でも有効です。更新前のSkill本文はバックアップされ、手元の見本画像3枚と補助スクリプトも残っています。

Notionでルールを直し、Codexには同期を頼む。毎回ファイルの取得方法や保存先を説明し直さずに、更新できるところまで確認できました。最初の準備は必要ですが、次から同じ手順で頼めるのは便利です。

今回は、必要なときに会話で呼び出すところまでです。定期実行は設定していません。Notionが公開している GitHub同期サンプル も、READMEとワークフローは確認しましたが、実行はしていません。これはNotionからGitHubのPlugin marketplaceへ毎時同期する仕組みで、今回のCodexの保存先へ取り込む方法とは用途が異なります。別の環境で、記事の依頼文だけから同期用Skillを作れるかも未確認です。

Notionで直したルールをCodexでも使いやすくするために、同期の手順もSkillにまとめました。これからは文章や画像のルールを更新したときに、このSkillでCodex側にも取り込んでいこうと思います。

補足:自分のSkillを1つ取得する手順

ここからは、最初にWriting Voiceを1件取得したときの詳しい手順です。同期用Skillをまだ用意していない状態で、APIからファイルを取得する部分だけ試せます。WindowsのPowerShellを使い、新しい確認用フォルダへ保存します。

Notionに読み取り用の接続を追加する

Notionの公式手順 に沿ってDeveloper Portalで「Internal connection」を作ります。自分は Codex Skills Sync という名前で、次のように設定しました。

  1. 接続を作り、内容の読み取り(Read content)を許可する。取得だけなら内容の追加・更新権限は付けない。
  2. 接続の設定画面でAPIトークンを確認する。これはパスワードのように扱う認証用の値です。
  3. 取得したいSkillのページを開き、右上の「…」→接続(Connections)から作った接続を追加する。

接続の作成にはワークスペースのオーナー権限が必要です。

image block

接続の作成画面。最初は「Writing Voice sync test」という名前で、あとから「Codex Skills Sync」に変更しました。

PowerShellからSkillを取得する

Windowsのスタートメニューから「PowerShell」を開きます。管理者としての起動は不要です。

下のコードは 同じPowerShellのウィンドウ で上から順に実行します。 PS C:\Users\... のような表示は入力せず、コード部分だけをコピーします。

最初の枠を実行し、入力欄にNotionのAPIトークンを貼り付けます。入力は画面に表示されません。トークンをコードやチャットに直接書かずに済みます。

$ErrorActionPreference = "Stop"
$secure = Read-Host "NotionのAPIトークン" -AsSecureString
$ptr = [Runtime.InteropServices.Marshal]::SecureStringToBSTR($secure)
try {
    $headers = @{
        Authorization = "Bearer $([Runtime.InteropServices.Marshal]::PtrToStringBSTR($ptr))"
        "Notion-Version" = "2026-03-11"
    }
} finally {
    [Runtime.InteropServices.Marshal]::ZeroFreeBSTR($ptr)
    $secure.Dispose()
}

この手順では認証情報をファイルに保存せず、開いているPowerShell内で使います。

次に取得したいSkillのNotionページIDを入力します。ページのリンクに含まれる32桁のID、またはハイフン付きのIDを使います。URL全体は入力しません。

たとえば今回のWriting Voiceなら 3e0d5238-b58c-818e-a7ed-d323cc31d9ff でした。ここは自分のSkillのIDに置き換えます。

$skill = $null
$skillId = ([guid](Read-Host "取得したいSkillのNotionページID")).ToString()
$skill = Invoke-RestMethod -Method Get -Uri "https://api.notion.com/v1/ai/skills/$skillId" -Headers $headers

[PSCustomObject]@{
    id = $skill.id
    version_id = $skill.version_id
    download_url_received = [bool]$skill.url
} | Format-List

成功すると、 id 、 version_id 、 download_url_received : True が表示されます。 version_id は同じ版のSkillかを比べるための値です。

取得できたら、次の枠を実行します。毎回新しい確認用フォルダを作り、ダウンロードした圧縮ファイルを展開します。

if (-not $skill.url) { throw "先にSkillの取得を成功させてください。" }

$testRoot = Join-Path $env:TEMP ("notion-skill-test-" + [guid]::NewGuid().ToString("N"))
$exportDir = Join-Path $testRoot "api-export"
New-Item -ItemType Directory -Path $exportDir -Force | Out-Null
$archive = Join-Path $testRoot "skill.tar.gz"

Invoke-WebRequest -UseBasicParsing -Uri $skill.url -OutFile $archive
$tarArgs = '-xzf "{0}" -C "{1}"' -f $archive, $exportDir
Start-Process -FilePath "tar.exe" -ArgumentList $tarArgs -Wait -NoNewWindow

$apiFiles = @(Get-ChildItem $exportDir -Recurse -Filter SKILL.md)
if ($apiFiles.Count -eq 0) { throw "SKILL.mdを確認できません。展開結果を確認してください。" }
$apiFiles | ForEach-Object { $_.FullName }

最後に表示される SKILL.md のフルパスを控えます。記事前半の「Skillの置き場所は、環境ごとに確かめる」にある依頼文へ渡すパスです。参照画像や補助ファイルが展開された場合は、それらも一緒に残します。

tar.exe は圧縮ファイルを展開するコマンドです。「見つからない」と出たら、先へ進む前にWindows側で使える展開方法を確認します。

ダウンロード後は次の枠で認証情報と一時URLを変数から外し、PowerShellを閉じられます。

$headers.Clear()
Remove-Variable headers, skill, secure, ptr -ErrorAction SilentlyContinue

これはNotion側のトークンを失効させる操作ではありません。パソコン内の確認用ファイルも残ります。

404なら、ページIDと接続設定を確かめる

自分は最初、個別SkillのページIDではなく、Plugin一覧にあった notion-skills-updater のIDを使ってしまいました。その結果、404エラーになっています。

Pluginは、Skillなどをまとめて配るための単位です。個別Skill用とPlugin用では、APIの取得先が違います。

  • Skillを1つ取得する: /v1/ai/skills/{SkillのページID}
  • Pluginを取得する: /v1/ai/plugins/{PluginのID}

今回の手順は上の個別Skill用です。404だけで原因が1つに決まるわけではないので、ページIDの取り違えとそのページに接続を追加したかの両方を確認します。401なら、入力したトークンが有効かを確認します。

なお、名前が似ていた notion-skills-updater 自体も、今回の取り込みには使いませんでした。 Plugin用のAPI で中身を確認すると、編集した内容をCodexからNotionの元ページへ書き戻す手順だったためです。書き戻しは実行していません。

同じカテゴリの記事

Blog一覧へ戻る