3行でいうと

  • MCPは codex mcp add か、config.toml の [mcp_servers.<名前>] で足します。公式GitHubのソースには、add、list、get、remove、login、logout の6つのサブコマンドがあります。認証は、OAuth、環境変数のトークン、ヘッダーを作るコマンドの3通りが公式の設定にあります。
  • スキルは SKILL.md を置いたフォルダで、必須の項目は name と description の2つです。置き場所は .agents/skills(リポジトリ)と $HOME/.agents/skills(ユーザー)などで、作り始めは $skill-creator が公式の案内です。
  • フックは、既定でオンです。管理者が配ったものを除き、中身のハッシュを信頼するまで動かず、公式は、ツールのフックを「有用なガードレールであり、完全な強制の境界ではない」と書いています。プラグインは、これらをまとめて配る箱で、フックを含むものは公開ディレクトリに出せません。

この記事は、公式ドキュメントと公式GitHubを2026年10月24日に読んでまとめたもので、実際に使って確かめた記録ではありません。運営者はCodexを入れていますが、使い心地はこの記事には書いていません。コマンド、設定名、JSONやTOMLの例は公式の表記を写したもので、動作確認はしていません。公式の文章は要約の形で取得したため、全文を写せていない例があります。その場合は、どこまでが公式でどこからが僕らの組み立てかを、本文で分けて書きます。

先に結論

前提:用語をそろえる

4つの部品の違い:どれで何を足すのか

公式ドキュメントの説明から、役割と置き場所を並べます。最後の列は、権限や信頼の扱いの要点です。詳しくは各節と「権限との関係」の節に書きます。

部品 足すもの 主な置き場所(公式の記載) 権限や信頼の要点
MCPサーバー 外部サービスの道具 ~/.codex/config.toml、信頼したプロジェクトの .codex/config.toml ツールごとに承認の方式を決められる。破壊的と宣言したツールは、必ず承認を求める
スキル 手順書と資料 .agents/skills、$HOME/.agents/skills、/etc/codex/skills 多いと一覧に入りきらない。スクリプトの承認には skill_approval の項目がある
フック 動きの途中の割り込み hooks.json、config.toml の [hooks](~/.codex/ とプロジェクトの .codex/) 管理外のフックは、中身のハッシュを信頼するまで動かない。強制の境界ではない
プラグイン 上の3つの束 マーケットプレイスのファイルから導入 導入や有効化だけでは、フックは信頼されない

使い分けの目安は、公式の「Skills & Plugins」にあります。指示だけで足りるならスキル、外部サービスや他の道具も組み合わせたいならプラグイン、という整理です。

MCPサーバーを足す

コマンドで足す

公式のMCPのページに載っている形は、次のとおりです。

codex mcp add <server-name> --env VAR1=VALUE1 --env VAR2=VALUE2 -- <stdio server-command>
codex mcp add context7 -- npx -y @upstash/context7-mcp
codex mcp add example --url https://mcp.example.com --oauth-client-id my-client
codex mcp add example --url https://mcp.example.com --oauth-client-id my-client --oauth-client-secret "$MCP_CLIENT_SECRET"

-- は、Codex自身のフラグと、起動するサーバーのコマンドを分ける区切りです。--url を付けるとStreamable HTTPのサーバーになります。ページに書かれた説明は、次のとおりです。

6つのサブコマンド(公式GitHubのソースから)

公式のコマンド一覧のページは、読んだ範囲では、mcp について add、login、logout の説明しか文章になっていませんでした。フラグの表が画面の部品で、要約に出てこなかったためです。そこで、公式リポジトリ(openai/codex)の codex-rs/cli/src/mcp_cmd.rs の要約で補いました。ソースの要約なので、ドキュメントの記述ではなく、版によって変わりうる点に注意してください。

サブコマンド ソースから読めたこと
add <名前> --url か、-- のあとのコマンドのどちらか一方が必須。ほかに --bearer-token-env-var と --oauth-resource がある。同じ名前があると置き換える。追加したあとOAuthの対応を調べ、対応していればすぐログインを始める。分からなければ codex mcp login <名前> を案内する
list STDIOとHTTPの2つの表を出す。--json で、機械向けの出力もある(項目の中身は、読んだ範囲では確かめられていません)
get <名前> 設定を表示する。--json もある。HTTPヘッダーの値は ***** で隠れ、http_headers_helper は <redacted> と出る。消し方として codex mcp remove <名前> も表示する
remove <名前> 設定から消して保存する。OAuthの資格情報は消さない
login <名前> --no-browser(URLを表示し、コールバックURLを受け取る)、--scopes、--oauth-client-registration がある。Streamable HTTPのサーバーだけが対象
logout <名前> 保存済みの資格情報を消す。Streamable HTTPのサーバーだけが対象

あわせて、add と remove は、名前に使える文字を、英数字、-、_、:、@、/、. に限っています(ソースの記述)。remove が資格情報を消さないことは、見落としやすい点です。サーバーをやめるときは、logout も使う、という手順になります。

会話の中では、/mcp で動いているサーバーを見られます。コマンド一覧のページには、/mcp verbose でサーバーの詳細、/mcp login <名前> でログインができる、とあります。

デスクトップアプリとIDE拡張には、設定画面からサーバーを足す入口があります。CLI、アプリ、IDE拡張は、同じホストの上では同じMCP設定を共有する、とMCPのページに書かれています。

設定ファイルで書く

設定は、~/.codex/config.toml、または信頼したプロジェクトの .codex/config.toml の、[mcp_servers.<名前>] に書きます。公式の例は次のとおりです。

[mcp_servers.context7]
command = "npx"
args = ["-y", "@upstash/context7-mcp"]
env_vars = ["LOCAL_TOKEN"]
[mcp_servers.figma]
url = "https://mcp.figma.com/mcp"
bearer_token_env_var = "FIGMA_OAUTH_TOKEN"

env_vars は、親の環境にある変数を、サーバーへ引き渡す指定です。env のように値を書かずに済むので、ファイルに秘密が残りません。

主な項目を、公式の表(MCPのページ、Sample Configuration、設定リファレンス)から表にまとめます。

項目 既定 公式の説明の要点
command、args、cwd なし STDIOサーバーの起動コマンド、引数、作業フォルダ。command が必須
url なし Streamable HTTPのサーバーのアドレス。必須
enabled Sample Configurationの注釈で「省略時はtrue」 false で、設定を消さずに無効にする
required 記載なし true なら、このサーバーが初期化できないと、起動や再開が失敗する
startup_timeout_sec 10秒 起動を待つ時間。ミリ秒の別名 startup_timeout_ms もある
tool_timeout_sec 60秒 ツール1回を待つ時間
enabled_tools、disabled_tools なし 許可リストと拒否リスト。拒否は、許可のあとに適用される
default_tools_approval_mode 記載なし auto、prompt、writes、approve のいずれか
tools.<ツール>.approval_mode サーバーの既定を引き継ぐ ツールごとの上書き

承認の方式のうち、writes は、「読み取り専用と印が付いていないツール」だけ確認する方式だと、MCPのページに説明があります。auto と approve が具体的に何をするかは、名前以上の説明を、読んだ範囲では見つけられませんでした。

ツールを絞る:許可リスト、拒否リスト、承認の方式

権限との関係で、いちばん手が届きやすいのが、サーバーが出す道具を絞る設定です。公式のMCPページの例(chrome_devtools)は、次のキーを使っています。ページの要約から、キーと値の断片を取り出したもので、TOMLの組み立てと、起動の設定を省いたことは僕らの整理です。

[mcp_servers.chrome_devtools]
# command や url の設定は省略
enabled_tools = ["open", "screenshot"]
disabled_tools = ["screenshot"]   # enabled_tools のあとに適用される
default_tools_approval_mode = "prompt"

[mcp_servers.chrome_devtools.tools.open]
approval_mode = "approve"
output_token_limit = 30000

読み方は、次のとおりです。enabled_tools に挙げたものだけが残り、そこから disabled_tools のものが引かれるので、この例の最終的な道具は open だけになります(公式の注釈どおり、拒否は許可のあとに適用されます)。サーバー全体の承認は prompt で、open だけ approve に上書きしています。output_token_limit は、道具1回の出力の予算で、公式は「標準の20%の余裕を足す前」の数と書いています。あわせて、mcp_optional_startup_grace_ms(既定1000ミリ秒)は、必須でないサーバーを、最初の道具一覧を作るときに待つ時間で、0 にすると、サーバーごとの startup_timeout_sec まで待ちます。OAuthの戻り先に独自のパスや外部のURLが要るときは、mcp_oauth_callback_url があります。

認証:3通りと、OAuthの細かい設定

設定リファレンスとMCPのページから、HTTPサーバーの認証を整理します。

方式 書き方 公式の記載
OAuth auth を省略(既定は oauth)。codex mcp login <名前> でログイン 保存されたOAuthの資格情報を使う。保存先は mcp_oauth_credentials_store(auto、file、keyring)
ChatGPTのセッション auth = "chatgpt" 信頼できる自社の提供元に限り、ChatGPTのセッションを使い、だめなら保存済みOAuthに戻る
固定のトークン bearer_token_env_var、http_headers、env_http_headers トークンそのものではなく、環境変数の名前を書く形が用意されている
ヘッダーを作るコマンド http_headers_helper ローカルのHTTP接続だけで使える。STDIOやリモート実行の接続では使えない

OAuthの登録方式は、CIMD(対応していれば既定)、DCR、設定した client_id を使う方法(登録を飛ばす)の3つです。ログインのときだけ --oauth-client-registration auto|cimd|dcr で変えられ、この指定は config.toml には保存されません。コールバックの既定は http://127.0.0.1/callback で、固定のポートが要るサーバー向けに、全体の mcp_oauth_callback_port と、サーバーごとの oauth.callback_port があります。サーバーごとの指定のほうが優先されます。

OAuthの落とし穴(公式の記載)
  • 発行者(iss)が一致しない応答は、必ず拒否されます。サーバーが発行者の確認に対応すると宣言しているのに iss がない応答も拒否で、コードの交換や別の方式への切り替えは起きません。
  • 戻り先がポートなしのループバックのとき、認可サーバー側は、ポートが変わる戻り先を受け付ける必要がある、と公式のMCPページが書いています(RFC 8252の7.3節を挙げています)。固定のポートが要るサーバーには、mcp_oauth_callback_port があります。別のホスト、パス、クエリを含むコールバックは、DCRか、設定した client_id が必要です。
  • 資格情報の取り出し元が1つも見つからないとき、Codexは認証なしで接続することがある、とMCPのページにあります。

入れる前に確かめたい、公式の注意

スキルを作る

SKILL.mdの形と、フォルダの構成

公式の「Build skills」にある最小の形は、次のとおりです。

---
name: skill-name
description: Explain exactly when this skill should and should not trigger.
---

Skill instructions for ChatGPT or Codex to follow.

name と description が必須で、読んだページには、ほかの項目の記載はありませんでした。フォルダの構成は、次のとおりです。

パス 必須か 役割
SKILL.md 必須 指示とメタデータ
scripts/ 任意 実行できるコード
references/ 任意 資料
assets/ 任意 テンプレートや素材
agents/openai.yaml 任意 見た目、呼び出しの方針、依存する道具の宣言

公式リポジトリ(openai/skills)の skill-creator は、より細かい作法を書いています。このリポジトリは「deprecated(非推奨)」と表示されていて、現在の例は openai/plugins を見るよう案内されています。そのため、次の内容は、旧リポジトリの中のスキルの要約として読んでください。

agents/openai.yaml の項目は、公式のページに次のように書かれています。

interface:
  display_name: "Optional user-facing name"
  short_description: "Optional user-facing description"
  icon_small: "./assets/small-logo.svg"
  icon_large: "./assets/large-logo.png"
  brand_color: "#3B82F6"
  default_prompt: "Optional surrounding prompt to use the skill with"

policy:
  allow_implicit_invocation: false

dependencies:
  tools:
    - type: "mcp"
      value: "openaiDeveloperDocs"
      description: "OpenAI Docs MCP server"
      transport: "streamable_http"
      url: "https://developers.openai.com/mcp"

agents/openai.yaml は、公式が「ChatGPTのデスクトップアプリでの表示(UIメタデータ)の設定」に使うと書いているファイルで、interface はその表示用の項目です。allow_implicit_invocation は既定が true で、false にすると、依頼の文章からは自動で選ばれなくなりますが、$スキル名 で自分から呼ぶことはできます。dependencies.tools は、スキルが必要とする道具(例ではMCPサーバー)の宣言です。必須の項目や、mcp 以外の type は、読んだページには書かれていませんでした。Sample Configurationには skill_mcp_dependency_install = true というコメント行の例がありますが、何をするかの説明や成熟度は、読んだ範囲では確認できていません。

置き場所と、見つけられ方

公式の表では、Codexは次の場所からスキルを読みます。リポジトリの中は、作業フォルダからリポジトリのルートまで、各階層の .agents/skills を調べます。

範囲 場所 想定する使い方(公式の説明)
リポジトリ $CWD/.agents/skills 作業フォルダ固有のスキル(例:マイクロサービス単位)
リポジトリ $CWD/../.agents/skills リポジトリ内の親フォルダで共有するスキル
リポジトリ $REPO_ROOT/.agents/skills リポジトリ全体のスキル
ユーザー $HOME/.agents/skills どのリポジトリでも使う個人のスキル
管理者 /etc/codex/skills マシンやコンテナの既定。SDKや自動化用
システム OpenAI同梱 skill-creatorなどの汎用スキル

作り方の3通りと、入れ方

方法 公式の説明
$skill-creator 用途、きっかけ、指示だけにするかスクリプトも入れるか(既定は指示のみ)を尋ねて、下書きを作る
手で作る フォルダを作り、SKILL.md を置く
Record & Replay 操作を一度見せると、記録して下書きを作る。macOSのみで、Computer Useが使える必要がある

Record & Replayのページには、保存先、プランの条件、データの扱いは書かれていませんでした。公式は、秘密や機微な情報を使わないよう書いています。

ほかの人のスキルは $skill-installer で入れます(例は $skill-installer linear)。入れたあとは、再起動して読み込ませます。公式は、スキルの導入をローカルでの試用向けとし、自作のスキルを配るときはプラグインにするよう案内しています。旧リポジトリ(openai/skills)の案内では、名前で入れるものは .curated、.experimental のものはフォルダを指定します。

無効にする、数が多いとき

[[skills.config]]
path = "/path/to/skill/SKILL.md"
enabled = false

これで、消さずに無効にできます。反映には再起動が必要です。

最初に見える一覧(名前、説明、ファイルの場所)には予算があります。既定は、コンテキストの2%、または大きさが分からないときは8,000文字です。多いと、まず説明が短くされ、それでも足りないと一部が一覧から外れて、警告が出ます。予算は skills.max_context_tokens で変えられ、上限は10,000トークンです。この予算は最初の一覧だけに使われ、スキルが選ばれたあとは SKILL.md の全文が読まれます。

フックを書く

どこに書くか、どう動くか

フックは、hooks.json、config.toml の [hooks]、有効なプラグインが持ってくる設定の3か所から読まれます。主な4か所は、~/.codex/hooks.json、~/.codex/config.toml、<リポジトリ>/.codex/hooks.json、<リポジトリ>/.codex/config.toml です。

構造は、イベント、一致の条件(matcher group)、処理(handler)の3段です。TOMLの例は、Sample Configurationにあります。

[hooks]
[[hooks.PreToolUse]]
matcher = "^Bash$"

[[hooks.PreToolUse.hooks]]
type = "command"
command = 'python3 "/absolute/path/to/pre_tool_use_policy.py"'
timeout = 30
statusMessage = "Checking Bash command"

JSONの形は、公式のHooksのページに完全な例がありますが、要約の取得では全文を写せませんでした。断片から読み取れた構造は、次のとおりです。トップに任意の description と hooks があり、その下に、イベント名をキーとした配列、各要素に任意の matcher と、処理の配列 hooks が入ります。公式の例の断片には、"matcher": "Bash" と、"command": "/usr/bin/python3 \"$(git rev-parse --show-toplevel)/.codex/hooks/pre_tool_use_policy.py\"" がありました。リポジトリのフックは、.codex/hooks/... のような相対パスではなく、Gitのルートから解決するパスで書くよう勧められています。公式の理由は、Codexがサブフォルダから起動されることがあるためです。

処理の種類は command と mcp_tool です。prompt と agent は読み込まれても、飛ばされます。timeout を省くと、ほとんどのフックは600秒です。SessionEnd と Interrupt は省略時1秒で、上限は3秒です。

イベントと、止められるもの

イベント 動く時点 matcherの対象
SessionStart 会話の開始(startup、resume、clear、compact、fork) 開始の種類
UserPromptSubmit プロンプトを送ったとき 使われない
PreToolUse ツールの実行前 ツール名
PermissionRequest 承認を求める直前 ツール名
PostToolUse ツールが出力を出したあと ツール名
PreCompact、PostCompact 文脈の圧縮の前後 manual か auto
SubagentStart、SubagentStop サブエージェントの開始と停止 サブエージェントの種類
Stop ターンの終わり 使われない
Interrupt 動いているターンを中断したとき 使われない
SessionEnd 会話の終わり 終了の理由(今は other のみ)

matcher は正規表現の文字列で、"*"、空、省略は、すべてに一致します。apply_patch(ファイル編集)は、Edit や Write でも指定できますが、フックに渡る tool_name は、いつも apply_patch です。

ツールの対象には限りがあります。シェルコマンド、apply_patch、MCPのツール(mcp__filesystem__read_file のような名前)、一部のローカルの関数ツールは対象です。WebSearch のようなホスト側のツールは対象外です。公式は、一部の特殊なツールの経路が、既定のフックの経路を通らないことがあるため、ツールのフックは「有用なガードレールであり、完全な強制の境界ではない」と書いています。

入力と出力:標準入力のJSON、終了コード、決定

どのコマンドのフックも、標準入力でJSONを1つ受け取ります。共通の項目は、session_id、transcript_path、cwd、hook_event_name、model で、多くのイベントに permission_mode(default、acceptEdits、plan、dontAsk、bypassPermissions)が付きます。PreToolUse には、さらに turn_id、tool_name、tool_use_id、tool_input が入ります。transcript_path の形式は、安定した仕様ではないと書かれています。

出力の約束事は、次のとおりです。

{
  "hookSpecificOutput": {
    "hookEventName": "PreToolUse",
    "permissionDecision": "deny",
    "permissionDecisionReason": "Destructive command blocked by hook."
  }
}

古い形の decision: "block" と reason も受け付けられます。公式ページの断片から、キーと文言は読めましたが、上の入れ子の形は、断片をつなげた僕らの書き方です。

最小のフックを書いて、手で確かめる

公式のページには、PreToolUse の登録の例と、止めるときのJSONの形はありますが、読んだ範囲では、Pythonのスクリプトの全文は載っていませんでした。次のスクリプトは、公式の入出力の約束(標準入力のJSON、tool_input.command、止めるときのJSON、終了コード)に合わせてこの記事で組み立てた例で、動作確認はしていません。前に載せたTOMLの pre_tool_use_policy.py に当たるものです。

#!/usr/bin/env python3
import json, sys

event = json.load(sys.stdin)
command = event.get("tool_input", {}).get("command", "")

if "rm -rf" in command:
    print(json.dumps({
        "hookSpecificOutput": {
            "hookEventName": "PreToolUse",
            "permissionDecision": "deny",
            "permissionDecisionReason": "rm -rf is blocked by this hook."
        }
    }))
sys.exit(0)

Codexに登録する前に、手で試せます。実際の入力には session_id などの項目も入りますが、このスクリプトが読むのは tool_input.command だけです。

echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"rm -rf /tmp/x"}}' | python3 pre_tool_use_policy.py
echo '{"hook_event_name":"PreToolUse","tool_name":"Bash","tool_input":{"command":"ls"}}' | python3 pre_tool_use_policy.py; echo "exit=$?"

1行目はJSONが出て、2行目は何も出ずに exit=0 になるはずです。ここで気をつけたいのは、公式が、PreToolUse の通常の文字の出力(プレーンテキスト)は無視される、と書いている点です。止めたいときは、JSONを返すか、終了コード2で理由を標準エラーに出す必要があります。また、rm -rf の文字列を探すだけの検査は、書き方を変えれば抜けられます。これは仕組みを見るための例で、守りの本体ではありません。登録したあとは、CLIの /hooks で中身を確かめて、信頼してから動きます。

PreToolUse では、ツールの入力の書き換えもできます。permissionDecision: "allow" と updatedInput を組み合わせたときだけ有効で、Bash と apply_patch では、文字列の command を含める必要があります。

失敗として扱われる値と、使い分け(公式の記載)
  • PreToolUse が permissionDecision: "ask"、昔の decision: "approve"、continue: false、stopReason、suppressOutput を返すと、フックは失敗として報告され、ツール呼び出しは続行されます。
  • PostToolUse は実行後なので、完了したコマンドを取り消せません。decision: "block" や終了コード2は、ツールの結果をフックの文章に置き換えて、モデルがそこから続ける、という動きです。
  • Stop の decision: "block" は、ターンを拒否するのではなく、Codexを続けさせます。reason が新しいユーザープロンプトになります。入力の stop_hook_active が、続行中かどうかの手がかりです。続けて止められる回数の上限は、読んだ範囲では見つかりませんでした。
  • PermissionRequest は、複数のフックのうち1つでも deny なら拒否が優先され、次に allow があれば承認の画面を出さずに通します。updatedInput、updatedPermissions、interrupt は予約済みで、返すと安全側で失敗します。
  • async: true のフックは、バックグラウンドで動き、操作を止めたり書き換えたりできません。出力は、会話の次の安全な時点で渡されます。1セッションで同時に動くのは最大8つで、超えた分は待たされ、セッションが終わると、終わっていないものは取り消され、渡っていない出力は捨てられます。なお、モデルに見える各フックの出力は、asyncかどうかにかかわらず、既定でおよそ2,500トークンに制限されます。超えた分は、一時フォルダの hook_outputs/<セッションID>/<UUID>.txt に全文が保存され、モデルには前後の一部(プレビュー)が渡されます。秘密を出力しないよう、公式が注意しています。

信頼の仕組み

管理者が配ったもの以外のフックは、中身を確かめて信頼するまで動きません。信頼は、フックの現在のハッシュに結びつけて記録されます。そのため、新しいフックや、書き換えたフックは「要確認」の印が付き、信頼するまで飛ばされます。CLIの /hooks で、出どころの確認、新規と変更のレビュー、信頼、無効化ができます。レビューが要るフックがあると、起動時に警告が出ます。

システム、MDM、クラウド、requirements.toml から来る管理側のフックは、方針により信頼済みとして扱われ、利用者は無効にできません。外で確認済みの単発の自動化なら、--dangerously-bypass-hook-trust で、その呼び出しに限って、永続的な信頼なしにフックを動かせます。名前のとおり、公式が「信頼の確認を飛ばす」ものとして出している指定です。

フックが動かないとき:公式が挙げる理由の早見表

公式のHooksのページには、専用のデバッグの節はありません。ただ、動かない理由になりうる条件が、ページのあちこちに書かれています。それを1か所に集めました(並べ方は僕らの整理です)。

症状 確かめること(公式の記載)
新しい、または書き換えたフックが動かない 信頼は、フックのハッシュに結びつく。変わると、確認するまで飛ばされる。CLIの /hooks で確認する
プロジェクトのフックだけが動かない そのプロジェクトの .codex/ の層を信頼しているときだけ読まれる
どのフックも動かない [features] の hooks = false になっていないか。管理者の requirements.toml で止められていないか
同じ層の設定が警告を出す hooks.json と [hooks] の両方があると、混ぜられて警告が出る。片方に統一する
UserPromptSubmit や Stop で matcher が効かない この2つは matcher を使わない
PermissionRequest が呼ばれない 承認が要らないコマンドでは動かない
PreToolUse が素通りになる エラー、タイムアウト、形の違う応答は、失敗として報告されるだけで、ツールは止まらないことがある。通常の文字の出力は無視される

プラグインを作って配る

構成とマニフェスト

プラグインは、スキル、MCPサーバー、フックなどを、1つのフォルダにまとめたものです。ここで、公式の資料に、ずれが見えました。

どちらが現在の標準かは、読んだ範囲では、断定できません。読んだとおりに両方を並べます。

.codex-plugin/ に置いてよいのは、plugin.json だけで、フックの設定、.app.json などは、プラグインのルートに置く、とあります。足せるもの($plugin-creator が作るもの)は、.mcp.json、.app.json、skills/、hooks/、scripts/、assets/ です。.mcp.json と .app.json は空で作られ、hooks/ は空のフォルダです。

ポータブル形式の項目は、$schema、name(ケバブケース、識別子と名前空間を兼ねる)、version、description、任意の author、homepage、repository、license、keywords です。OpenAI固有の項目は、extensions.com.openai の下に、apps、hooks、onboardingSkill、interface(displayName、shortDescription、longDescription、developerName、category、capabilities、websiteURL、privacyPolicyURL、termsOfServiceURL、defaultPrompt、brandColor、composerIcon、logo、screenshots など)を置きます。extensions.com.openai がオブジェクトとして存在すると、.codex-plugin/plugin.json の上書きを、混ぜずに丸ごと置き換える、と書かれています。パスは ./ で始め、プラグインのルートの中に収めます。

公式サンプルのfigmaのマニフェストから、主なキーだけを抜き出します(値の省略は僕らです。ほかに homepage、repository、license、keywords、各種のURLも入っています。バージョンは2026年10月24日に見えたものです)。

{
  "name": "figma",
  "version": "2.0.20",
  "description": "...",
  "author": { "name": "Figma", "url": "https://www.figma.com" },
  "skills": "./skills/",
  "apps": "./.app.json",
  "interface": {
    "displayName": "Figma",
    "shortDescription": "Figma design-to-code workflows",
    "category": "Creativity",
    "capabilities": ["Interactive", "Read", "Write"],
    "brandColor": "#1ABCFE",
    "composerIcon": "./assets/logo-padded.png",
    "logo": "./assets/logo-padded.png"
  }
}

MCPとフックをプラグインに入れる

プラグインの .mcp.json(ポータブル形式では mcp.json)は、$schema と、名前をつけた mcpServers の表で、各サーバーに type(通信方式)と url を書きます。足場が作る .mcp.json を、名前を変えるだけでポータブル形式にはできません。type が必要だからです。認証は、各サーバーの extensions["com.openai"].auth に書きます。

auth.type 意味(公式の記載)
none 認証なし。保護されたサーバーには付けない
oauth OAuth。クライアント登録は provided(clientId が必要)、dcr、cimd から選ぶ
mixed 公開の範囲とOAuthの併用。固定のAPIキーとは併用しない
api_key headerScheme は bearer、basic、custom_header

クライアントのシークレットは、マニフェストに入れず、接続の設定のときに入れます。Host は、独自ヘッダーの名前に使えません。

フックは、プラグインのルートの hooks/hooks.json が既定で見つかります。extensions.com.openai.hooks で、パス、パスの配列、インラインの設定を指定すると、既定の探索が置き換わります。フックのコマンドには、環境変数 PLUGIN_ROOT(導入先のルート)と PLUGIN_DATA(書き込めるデータの場所)が渡され、互換のため CLAUDE_PLUGIN_ROOT と CLAUDE_PLUGIN_DATA も設定されます。公式が書く条件は、次のとおりです。

マーケットプレイス:配る側のファイル

マーケットプレイスは、marketplace.json で書きます。置き場所は、リポジトリなら $REPO_ROOT/.agents/plugins/marketplace.json、個人用なら ~/.agents/plugins/marketplace.json です。互換のため、.claude-plugin/marketplace.json も読まれます。公式サンプル集の marketplace.json の最初の項目は、次のとおりです。

{
  "name": "linear",
  "source": {
    "source": "local",
    "path": "./plugins/linear"
  },
  "policy": {
    "installation": "AVAILABLE",
    "authentication": "ON_INSTALL"
  },
  "category": "Productivity"
}

トップレベルのキーは、name、interface(displayName を持つ)、plugins です。source の形は、local(path は ./ で始め、マーケットプレイスのルートの中に収める)、url、git-subdir(url、path、ref か sha)、npm(package、version、registry)です。policy.installation は、AVAILABLE、INSTALLED_BY_DEFAULT、NOT_AVAILABLE が挙がっています。authentication は、サンプルの中で ON_INSTALL と ON_USE を見ましたが、全部の値の一覧は、読んだ範囲にはありません。

Gitの ref や sha で固定できます。解決できない項目は飛ばされ、マーケットプレイス全体は失敗しません。npmのソースは、ライフサイクルスクリプトを動かさずに取得し、npm のコマンドが必要です。

コマンドと、ローカルでの試し方

コマンド 公式の記載
codex plugin marketplace add <ソース> owner/repo、owner/repo@ref、HTTP(S)かSSHのGit URL、ローカルのルートを指定できる。--ref で固定、--sparse PATH(繰り返し可、Gitのみ)で一部だけ取得する
codex plugin marketplace list 対象のマーケットプレイスの名前とルートを出す
codex plugin marketplace upgrade [名前] 更新する
codex plugin marketplace remove <名前> 取り除く
codex plugin(add、list、remove) マーケットプレイスから、導入、一覧、削除をする。--json の出力の項目は載っているが、引数の形は読めていない

CLIでは、/plugins でプラグインのブラウザが開きます。マーケットプレイスごとのタブがあり、詳細、導入と削除、有効と無効(導入済みのプラグインの上でスペースキー)を操作します。導入後は、新しいセッションで使い始めます。IDE拡張は、プラグインに対応していません(Pluginsのページ)。

ローカルで試す手順は、次のように書かれています。

  1. プラグインのフォルダを、リポジトリの plugins/ などへ置く。
  2. マーケットプレイスのファイルを作り、source.path をそのフォルダに向ける。
  3. アプリを再起動して、表示されるか確かめる。
  4. 有効と無効は、リポジトリの .codex/config.toml の [plugins."プラグイン名@マーケットプレイス名"] で切り替える。enabled = false でも、ファイルは取得される。

導入したプラグインは、~/.codex/plugins/cache/<マーケットプレイス名>/<プラグイン名>/<バージョン>/ にキャッシュされます。ローカルのプラグインのバージョンは local です。変えたときは、マーケットプレイスが指すフォルダを更新し、アプリを再起動します。更新の判定や、キャッシュの無効化の仕組みの説明は、読んだページには見つかりませんでした。

配り方の段階

段階 手順(公式の記載) 条件や注意
リポジトリ、個人 .agents/plugins/marketplace.json をコミット。個人用は ~/.agents/plugins/marketplace.json 設定は信頼したプロジェクトでだけ読まれる
GitHubなどのソース codex plugin marketplace add owner/repo --ref で固定できる
ワークスペース 管理者が公開する 公開ディレクトリとは別物。features.plugin_sharing = false で止められる
公開ディレクトリ ZIPを提出して審査を受ける 下の節。フックを含むものは出せない

管理者は、システムの config.toml や、クラウドの管理設定で、マーケットプレイスを定義することもできます。

公開ディレクトリに出す場合

「Submit plugins」のページには、次のことが書かれています。審査の期間や、承認までの目安は、読んだ範囲では見つかりませんでした。

ChatGPTのWeb側には、「Plugin Creator」を @ で呼んで、会話だけでプラグインを作る別の入口もあります。ワークスペースの権限(「Use plugins」など)が要り、新しく作ったものは最初は非公開です。共有とインストールは別の操作です。

権限との関係:部品ごとに、何が効いて、何が効かないか

前のガイド(02)で書いたサンドボックスと承認が、今回の4つの部品にどう関わるかを、公式の記載から整理します。読めなかった所は、そう書きます。

部品 効くもの(公式の記載) 効かない、または読めていないもの
MCPのツール default_tools_approval_mode とツールごとの approval_mode。破壊的と宣言したツールは必ず承認。granularの mcp_elicitations コマンドのネットワークプロキシは、MCPへの接続を絞らない。MCPサーバーがサンドボックスの中で動くかは、読んだページに書かれていない
スキル granularの skill_approval(スキルのスクリプトの承認)。.agents は、書き込める範囲の中でも読み取り専用に守られるパスに入る(Agent approvals & securityの記載) スキルのスクリプトが、どの囲いの下で動くかの、直接の説明は読めていない
フック PermissionRequest のフックで、承認の画面を出さずに通す、または拒否できる。入力に permission_mode が入る ツールのフックは、強制の境界ではない。フックが失敗すると、ツールが続行されることがある
プラグイン CodexのアプリとCLIでは、ホストのサンドボックスと承認の方針が適用される。外部サービスは、そのサービスの認証とアクセス制御に従う プラグインのフックの信頼は、導入や有効化だけでは与えられない

プロジェクトの信頼も関わります。信頼していないプロジェクトでは、プロジェクトの .codex/config.toml は読まれません。ユーザーの設定に trust_level = "untrusted" を書くと、コマンドの承認を求め、プロジェクトの設定も読まない、と「Agent approvals & security」にあります。信頼していない場所で、プロジェクトのMCP、フック、スキルが効かないのは、この仕組みのためです。

クラウドとの関係も、公式が書いています。クラウドの管理(orchestration)では、コマンドのフック、ローカルの設定やプラグイン由来のフックは、使えません。管理者が定めたリモートのMCPフックだけが、対象になりえます。これらのMCPフックは、Compliance APIの完全な監査の記録にはならない、とも書かれています。

最初の10分でやること

公式に書かれた手順の範囲で、手を動かす順番を並べます。順番の組み立ては僕らの整理で、公式の推奨そのものではありません。

  1. MCPを1つ足す(3分)。 公式の例のとおり、codex mcp add context7 -- npx -y @upstash/context7-mcp を実行し、codex mcp list で状態を見ます。会話の中では /mcp で、動いているサーバーを確かめます。入れたあとに消すなら、codex mcp remove context7 です。ログインが要るサーバーなら、あとで codex mcp logout も使います。
  2. スキルを1つ作る(4分)。 Codexで $skill-creator を呼び、毎回頼んでいる作業を、用途、きっかけ、指示のみ、の順に答えて下書きにします。置き場所は、そのリポジトリだけなら .agents/skills、どこでも使うなら $HOME/.agents/skills です。反映されないときは、再起動します。
  3. フックは見るだけにする(3分)。 最初は新しいフックを書かずに、CLIの /hooks を開いて、読み込まれているフックと、信頼の状態を見ます。止める系のフックは、config.toml に書く前に、スクリプトを単体で動かして、標準入力のJSONを流し込み、終了コードと出力を確かめる、という順が安全です。

プラグインは、この10分には入れていません。まずMCP、スキル、フックの単体で動くことを確かめてから、まとめる段階に進むほうが、問題の切り分けが楽になると考えるためです。

次の30分:最小のプラグインを作って、ローカルで試す

10分の次の段階です。公式の「ローカルで試す」手順(Build plugins)に出てくる形を、そのまま順に並べます。手順の区切りと所要時間はこの記事の整理で、動作確認はしていません。

  1. フォルダと入口を作る。 木の形は次のとおりです。スキルは、ルートの skills/ に置くと、自動で見つかり、マニフェストに skills の項目は要りません。
my-first-plugin/
├── plugin.json
└── skills/
    └── hello/
        └── SKILL.md
{
  "$schema": "https://agent-plugins.org/schemas/1.0.0/plugin.schema.json",
  "name": "my-first-plugin",
  "version": "1.0.0",
  "description": "Reusable greeting workflow"
}
  1. スキルを書く。 skills/hello/SKILL.md に、前に見た name と description と、本文の指示を書きます。
  2. 置いて、マーケットプレイスに登録する。 共有したいなら、フォルダを $REPO_ROOT/plugins/my-plugin に置き、$REPO_ROOT/.agents/plugins/marketplace.json を作ります。個人用なら、~/.codex/plugins/my-plugin に置き、~/.agents/plugins/marketplace.json を作ります。公式の例のファイルは次のとおりです(フォルダ名は、公式の例では my-plugin です。上の木の名前と合わせてください)。
{
  "name": "local-repo",
  "plugins": [
    {
      "name": "my-plugin",
      "source": { "source": "local", "path": "./plugins/my-plugin" },
      "policy": { "installation": "AVAILABLE", "authentication": "ON_INSTALL" },
      "category": "Productivity"
    }
  ]
}
  1. 再起動して、確かめる。 公式の手順は、ChatGPTのデスクトップアプリを再起動して、プラグインの一覧に出ることを確かめる、です。CLIでは /plugins が入口です。中身を変えたら、フォルダを更新して、また再起動します。
  2. 任意で、有効と無効を設定に書く。 リポジトリの .codex/config.toml に、[plugins."my-plugin@local-repo"] と enabled = true を書きます。enabled = false なら、入れたまま、そのプロジェクトだけ止められます。

source.path は、マーケットプレイスのルートから解決され、.agents/plugins/ からではない、と公式にあります。ここでつまずきやすいので、先に見ておくと楽です。

僕らの見立て

ここからは見立てです。公式の整理から組み立てたもので、公式の推奨ではありません。

気をつけること

出典