3行でいうと

  • 公式の線引きは、CIや定期ジョブの自動化はSDK、認証・会話履歴・承認・ストリームまで自分のプロダクトの中に持ちたいときはapp-server、です。codex mcp-server は削除済みで、app-serverへの移行が案内されています。
  • TypeScriptのSDK(@openai/codex-sdk)は codex のCLIを子プロセスとして起動します。PythonのSDK(openai-codex)はローカルのapp-serverをJSON-RPCで制御します。どちらも「ローカルのCodexを動かす」もので、サーバーに置くなら、そこにCodex本体と作業フォルダが要ります。
  • app-serverのコマンドとWebSocket転送は、公式が「実験的で、本番向けには未対応」と書いています。さらに、app-serverの認証は商用やホスト型のサービスでは許されたことがない、という注意書きがあります。公式が勧めるSign in with ChatGPTは、読んだ範囲では選ばれた商用パートナー向けで、一般向けの手順は見つかりませんでした。
  • 実験的の印がついた機能は、initialize で capabilities.experimentalApi を true にしないと使えません。モデル名や使える範囲は変わりやすいので、公式の表示と日付をそのまま確かめてください。

この記事は、実際に使って確かめた記録ではありません。運営者はCodexを入れていますが、使い心地はこの記事には書いていません。この記事は、2026年10月24日に公式ドキュメント(learn.chatgpt.com、developers.openai.com)と公式リポジトリ(openai/codex)を読んでまとめたものです。コードとJSONは公式の例を写したか、公式の記述からこの記事で並べ直したもので、実行していません。取得は要約の形で返るため、読み切れていない箇所は「読めていない」と書きました。契約や法律の助言ではありません。

先に結論

前提:用語をそろえる

入口を選ぶ:どれを使うか

公式のSDKのページ、app-serverのページ、MCPサーバーのページ、モデルのページ、OpenAI APIのコード生成のガイドを並べ直しました。

入口 向いていること 公式が書いている状態 認証とホスト型の扱い 出典
codex exec 1回きりの画面なしの実行、CI 前のガイド(07)に詳しく書いた CODEX_API_KEY が対象(環境変数のページ)。ホスト型の可否は読んだ範囲では見つからなかった Non-interactive mode
TypeScript SDK(@openai/codex-sdk) サーバー側のNode.js(18以上)から、ローカルのCodexのスレッドを開始、継続、再開する 公式のSDKのページに、実験的や非推奨の印は見つからなかった CODEX_API_KEY が対象。既存のログインも使える。ホスト型の可否は読んだ範囲では見つからなかった Codex SDK
Python SDK(openai-codex) ローカルのapp-serverをJSON-RPCで制御する。Python 3.10以上 「stable release」と書かれ、先行版は pip install --pre で選ぶ 既存のログイン情報を自動で使い、login_chatgpt() などで始められる。ホスト型の可否は読んだ範囲では見つからなかった Codex SDK
app-server 認証、会話履歴、承認、ストリームのイベントまで含めた、自前のクライアントを作る コマンドとWebSocketは、実験的で本番向けには未対応と書かれている 認証は商用・ホスト型では許されたことがない(app-serverのページ)。ローカルとオープンソースは継続可 Codex app server
Responses API(OpenAI API) Codexのエージェントの動きを使わず、モデルを直接呼ぶ 通常のOpenAI API OpenAI APIのキーで呼ぶ。Codexの認証の話とは別 Code generation(OpenAI API)
codex mcp-server (MCPサーバーとしてCodexをホストする) 削除済み。app-serverへ移行 該当なし(削除済み) Codex MCP server

読み方のコツです。公式のapp-serverのページは、冒頭で「認証、会話履歴、承認、ストリームされるエージェントのイベントまで含む、プロダクトの中の深い統合」に向く、と書き、「ジョブの自動化やCIならSDKを使う」と書き分けています。SDKのページも、独自のクライアントを作るならapp-serverへ、と案内しています。また、PythonのSDKは中でapp-serverを使っているので、「SDKで足りるか、app-serverを直接叩くか」は、SDKの窓口で足りるかどうかの話です。

TypeScript SDK:最小の例

公式のSDKのページによると、TypeScriptの窓口は、アプリが「ローカルのCodexのスレッド」を開始、継続、再開できるライブラリです。サーバー側で使うこと、Node.js 18以上が必要なことが書かれています。

npm install @openai/codex-sdk

最小の使い方は、公式のページの例のとおり、次の形です。先頭のimportの行は、公式のREADMEにある1行を足しました。

import { Codex } from "@openai/codex-sdk";

const codex = new Codex();
const thread = codex.startThread();
const result = await thread.run(
  "Make a plan to diagnose and fix the CI failures"
);

console.log(result.finalResponse);

同じスレッドで続けるには、もう一度 run() を呼びます。昔のスレッドに戻るには、スレッドのIDを resumeThread に渡します。

// running the same thread
const result = await thread.run("Implement the plan");

console.log(result.finalResponse);

// resuming past thread
const threadId = "<thread-id>";
const thread2 = codex.resumeThread(threadId);
const result2 = await thread2.run("Pick up where you left off");

console.log(result2.finalResponse);

中で何が起きているか(公式のREADMEより)

公式のSDKのREADMEによると、TypeScriptのSDKは、@openai/codex の codex のCLIを包み、子プロセスとして起動します。CLIとのやり取りは、標準入出力のJSONLです。ここで言えるのは次のことです。

途中経過を読む runStreamed()、返答の形を決める outputSchema

公式のREADMEの例は次の形です(抜粋で、switch の中の分岐は公式の例では先頭の item.completed までしか読めていません)。

const { events } = await thread.runStreamed(
  "Diagnose the test failure and propose a fix"
);

for await (const event of events) {
  switch (event.type) {
    case "item.completed":
      // 完了した項目(メッセージ、コマンド、ファイル変更など)を読む
      break;
    case "turn.completed":
      // 使用量(event.usage)が入る
      break;
  }
}

outputSchema は、JSON Schemaを run() の2つ目の引数に渡します。READMEの例は次のとおりです。

const schema = {
  type: "object",
  properties: {
    summary: { type: "string" },
    status: { type: "string", enum: ["ok", "action_required"] },
  },
  required: ["summary", "status"],
  additionalProperties: false,
} as const;

const turn = await thread.run("Summarize repository status", {
  outputSchema: schema,
});
console.log(turn.finalResponse);

画像を渡す例も、READMEにあります。

const turn = await thread.run([
  { type: "text", text: "Describe these screenshots" },
  { type: "local_image", path: "./ui.png" },
  { type: "local_image", path: "./diagram.jpg" },
]);

(上の case 内のコメントは僕らの補足です。turn.completed の usage の細かい項目は読めていません。)

設定の渡し方と、よくつまずく点

オプション 渡す場所 公式の説明
workingDirectory startThread() Codexが動くフォルダ。既定は現在のフォルダ
skipGitRepoCheck startThread()(README) Codexは既定でGitリポジトリを求める(取り返しのつかない失敗を避けるため)。true で確認を飛ばす
env Codex のコンストラクタ 既定ではCLIがNode.jsの環境を継承する。env を渡すと、CLIが受け取る変数を細かく決められる。CODEX_API_KEY のような必要な変数は、渡した環境の上にSDKが足す
baseUrl Codex のコンストラクタ openai_base_url の設定として渡される
config Codex のコンストラクタ 設定のJSON。入れ子のキーは点でつないだ形にされ、値はTOMLの形で --config key=value として渡される
configOverrides Codex のコンストラクタ 点のつなぎでは書けないキー用に、TOMLの文字列をそのまま --config に渡す

優先順位は、弱い順に、config、configOverrides、SDKが決める設定(baseUrl やスレッドごとの設定)と書かれています。これらはCodexの設定ファイルを書き換えません。公式の例は次の形です。

const codex = new Codex({
  config: { default_permissions: "audit" },
  configOverrides: [
    'permissions.audit.filesystem={":root"="read","/path/to/project/.env"="deny"}',
  ],
});

これは、「audit という名前の権限プロファイルを選び、全体は読み取りだけ、.env は拒否」という設定を、SDKの呼び出しの側から与える例、と僕らは読みました。承認とサンドボックスの考え方は前のガイド(02)に書きました。

もうひとつ。env を指定すると、親のプロセスの環境変数は、指定したものだけになると読めます(READMEは「fully control(完全に決められる)」と書くだけで、「継承の代わり」とまでは明言していません)。公式の例は env: { PATH: "/usr/local/bin" } の形で、READMEはElectronアプリのようなサンドボックス化されたホストに便利、と添えています。PATHやHOMEを入れ忘れるとCLIが動かない、という事故がありうるので、最初は親の環境をコピーして必要なものを足す形から試すと安全に見えます(これは僕らの見立てです)。

認証

公式の環境変数のページによると、CODEX_API_KEY は、非対話の実行用のAPIキーで、exec、review、TypeScriptのSDK、遠隔の exec-server が対象です。信用できないリポジトリのコードを動かすときは、ジョブ全体ではなくコマンドごとに渡す、と書かれています。APIキーでは使えない機能(クラウド、GitHubのレビュー、Slackなど)があることは、前のガイド(06)に書いたとおりです。

Python SDK:最小の例

公式のPython SDKのREADMEとgetting-startedによると、インストールは次のとおりです。

pip install openai-codex

必要なのはPython 3.10以上です。SDKは、対応する openai-codex-cli-bin を依存として自動で入れます。「安定版のSDKは、対応する安定版のCodex CLIに合わせる」と書かれています。特定のローカルの実行ファイルを使いたいときだけ、CodexConfig(codex_bin=...) を渡します。先行版を試すなら、次のとおりです。

pip install --pre openai-codex

最小の例は、公式のREADMEのとおり次の形です。既存のCodexのログイン情報があれば、自動で使われます。

from openai_codex import Codex

with Codex() as codex:
    thread = codex.thread_start()
    result = thread.run("Explain this repository in three bullets.")
    print(result.final_response)

thread.run(...) は TurnResult を返し、最終の返答、集めた項目、トークンの使用量が入る、と書かれています。サンドボックスは3つの既定の型で選びます。

型 公式の説明
Sandbox.read_only ファイルを読めるが書けない
Sandbox.workspace_write 読めて、ワークスペースと設定した書き込み可能な場所には書ける
Sandbox.full_access ファイルシステムの制限なし

sandbox= を省くと、app-serverの設定の既定になります。run(...) や turn(...) に渡したサンドボックスは、そのターンと、同じスレッドの以後のターンに効く、と書かれています。

公式の例は、thread_start(sandbox=Sandbox.workspace_write) で始め、thread.run("Review the diff only.", sandbox=Sandbox.read_only) のように、レビューのターンだけ読み取りにする形です。

続きの会話は、同じ thread で run を重ねます。保存済みのスレッドに戻るには codex.thread_resume("thr_123") です。非同期のアプリには、AsyncCodex(async with AsyncCodex() as codex:、await codex.thread_start(...))が用意されています。

ログインをコードから始める

PythonのSDKは、既存のログイン情報が使える場合はそれを使い、明示的にログインを始めることもできます。公式の例は3つです。

# ChatGPTのブラウザログイン
with Codex() as codex:
    login = codex.login_chatgpt()
    print(login.auth_url)
    print(login.wait().success)

# ChatGPTのデバイスコード
with Codex() as codex:
    login = codex.login_chatgpt_device_code()
    print(login.verification_url, login.user_code)
    login.wait()

# APIキー
with Codex() as codex:
    codex.login_api_key("sk-...")

他のエージェントや道具の出力をSDKに渡すときは、ExternalMessage として渡す、と公式のgetting-startedにあります。道具としての権限は保つが、ユーザーが許可・承認した扱いにはならない、という説明です。細かい仕様はAPIリファレンス側にあり、読めていません。

公式のモデル名の例が、ページによって違う

SDKのページ(Pythonの例)とapp-serverのページの例は gpt-6.1-sol で、SDKのページは「そのアカウントで使えなければ model を省いて既定を使う」と書いています。app-serverのGitHubのREADMEの例は gpt-5.1-codex で、ページごとに例の更新時期が違うようです。公式のモデルのページ(確認日2026-10-24)は、Codexのモデルとして gpt-6-astra、gpt-6.1-sol、gpt-6-luna を挙げ、旧モデルの退役日も載せています(たとえば gpt-5.5 は2026年10月14日の退役と書かれ、ただしそれはChatGPTサインインのCodexの話で、APIキーとOpenAI APIは影響を受けない、とあります)。さらに、OpenAI APIのコード生成のガイドはCodexに合うモデルとして gpt-6-sol を挙げ(後の節)、モデルのページは gpt-6.1-sol を挙げていて、同じ公式の中でも名前がずれています。例のモデル名はそのままコピーせず、自分の環境で使える名前に置き換えてください。

app-server:自前のクライアントを作る

何であって、何でないか

公式のapp-serverのページは、これを「VS Code拡張のようなリッチなクライアントを動かすためのインターフェース」と説明し、オープンソースだ、と書いています。公式のオープンソースのページは、app-serverのソースを openai/codex の codex-rs/app-server にあると示しています。また、MCPのページは、app-serverは「MCPサーバーではなく、MCPクライアントの置き換えにもならない」と書いています。使っていた人は、MCPの道具の呼び出しを、app-serverのプロトコルの呼び出しに置き換える必要があります。

起動と転送方式

転送 起動の書き方 公式の説明
stdio(既定) --listen stdio://(--stdio) 改行区切りのJSON(JSONL)
WebSocket --listen ws://IP:PORT 1つのテキストフレームに1つのJSON-RPCメッセージ。実験的で未対応
Unixソケット --listen unix:// または --listen unix://PATH WebSocketを、既定か指定のソケットの上で
なし --listen off ローカルの転送を作らない

WebSocketで待ち受けると、GET /readyz(待ち受けが始まれば 200 OK)と GET /healthz が使えます。Origin ヘッダーつきの要求は 403 で断られます。公式がWebSocketについて強く書いていることは、次のとおりです。

別の場所のapp-serverにCLIの画面をつなぐ例も、公式にあります。遠隔のときは、トークンを環境変数(CODEX_REMOTE_TOKEN)に置き、--remote-auth-token-env で渡す形が勧められています。

codex app-server --listen ws://127.0.0.1:4500
codex --remote ws://127.0.0.1:4500

流れ:initialize から turn/completed まで

公式の流れは、次のとおりです。

  1. app-serverを起動する(codex app-server、またはオプションつき)。
  2. 接続して、initialize を送り、そのあと initialized という通知を送る。この手順の前にほかの要求を送ると、Not initialized のエラーになります。2回目の initialize は Already initialized になります。
  3. スレッドを始め(thread/start)、続きなら thread/resume、枝分かれなら thread/fork。
  4. turn/start でターンを始め、turn/steer で途中に入力を足し、turn/interrupt で中断する。
  5. 届く通知を読み、turn/completed で最終の状態を受け取る。

公式の initialize の例(VS Code拡張のもの)は次の形です。

{
  "method": "initialize",
  "id": 0,
  "params": {
    "clientInfo": {
      "name": "codex_vscode",
      "title": "Codex VS Code Extension",
      "version": "0.1.0"
    }
  }
}

公式は、clientInfo.name が、OpenAIのコンプライアンスログ基盤で、どのクライアントかを識別するために使われる、と書いています。企業のインテグレーションは、既知のクライアントの一覧に載せてもらうため、OpenAIに連絡する、とも書かれています。

thread/start の公式の例は次の形です(app-serverのページのもの。approvalPolicy の never は、承認の確認を出さない設定のはずです。前のガイド02で意味を確かめてから使ってください)。

{ "method": "thread/start", "id": 10, "params": {
  "model": "gpt-6.1-sol",
  "cwd": "/Users/me/project",
  "approvalPolicy": "never",
  "sandbox": "workspaceWrite",
  "serviceName": "my_app_server_client"
} }

応答には thread.id などが入り、そのあと thread/started の通知が届きます。続けて turn/start の公式の例は次の形です。なお、この例の approvalPolicy: "unlessTrusted" は、app-serverのプロトコル側の値です。前のガイド02が書くCLI側の値(untrusted は廃止)とは別の面の表記なので、自分のCodexの版のスキーマで確かめてください。

{ "method": "turn/start", "id": 30, "params": {
  "threadId": "thr_123",
  "input": [ { "type": "text", "text": "Run tests" } ],
  "cwd": "/Users/me/project",
  "approvalPolicy": "unlessTrusted",
  "sandboxPolicy": {
    "type": "workspaceWrite",
    "writableRoots": ["/Users/me/project"],
    "networkAccess": true
  },
  "model": "gpt-6.1-sol",
  "effort": "medium",
  "summary": "concise"
} }

(公式の例には、ほかに personality と outputSchema も入っています。)

応答は、{ "id": 30, "result": { "turn": { "id": "turn_456", "status": "inProgress", "items": [], "error": null } } } の形です。input に入れられるのは、text、image、localImage(パス)などです。image は、learn.chatgpt.com のapp-serverのページでは { "type": "image", "url": "https://.../design.png" } の例ですが、GitHubのREADMEは、インラインのデータURLが要り、リモートのHTTP(S)の画像URLは拒否される、と書いていました(公式の2つの資料で食い違っています)。自分のCodexの版のスキーマと動きで確かめてください。モデル、effort、cwd、サンドボックスの方針、summary の上書きは、同じスレッドの以後のターンの既定になります。outputSchema だけは、そのターンだけです。

標準入出力で話す最小の組み立て(公式の例をこの記事で並べ直したもので、動作は確かめていません)は、次の順に1行ずつ送る形です。

{"method":"initialize","id":0,"params":{"clientInfo":{"name":"my_app","title":"My App","version":"0.1.0"}}}
{"method":"initialized","params":{}}
{"method":"thread/start","id":1,"params":{"cwd":"/path/to/project"}}
(応答の thread.id を使って)
{"method":"turn/start","id":2,"params":{"threadId":"<thread.id>","input":[{"type":"text","text":"Summarize this repo."}],"sandboxPolicy":{"type":"readOnly"}}}

initialized は、公式のNode.jsの例に send({ method: "initialized", params: {} }); とあるので、params を空で付けました。sandboxPolicy の readOnly は、公式のページに値として挙がっています。公式のNode.jsの例は、codex app-server を子プロセスで起動し、標準出力を1行ずつJSONとして読んで、この順で送る構成だと、取得した要約に書かれています。公式のNode.jsの例は、readline を取り込む行がなく、そのままでは動かない、と取得結果にありました。これを補って、承認の要求には必ず decline を返す骨組みにしたものが次です(この記事で公式の例と、承認の決定値の記述から並べ直したもので、この記事では動作を確かめていません)。

import { spawn } from "node:child_process";
import readline from "node:readline";

const proc = spawn("codex", ["app-server"], {
  stdio: ["pipe", "pipe", "inherit"],
});
const rl = readline.createInterface({ input: proc.stdout });
const send = (message: unknown) => {
  proc.stdin.write(`${JSON.stringify(message)}\n`);
};

rl.on("line", (line) => {
  const msg = JSON.parse(line);
  console.log(msg);

  if (msg.id === 1 && msg.result?.thread?.id) {
    send({
      method: "turn/start",
      id: 2,
      params: {
        threadId: msg.result.thread.id,
        input: [{ type: "text", text: "Summarize this repo." }],
      },
    });
  }

  // サーバーからの「要求」: method と id の両方がある
  if (msg.method?.endsWith("/requestApproval") && msg.id !== undefined) {
    // result の中身の形は、公式のページでは確かめられていない
    send({ id: msg.id, result: { decision: "decline" } });
  }
});

send({ method: "initialize", id: 0, params: {
  clientInfo: { name: "my_product", title: "My Product", version: "0.1.0" },
} });
send({ method: "initialized", params: {} });
send({ method: "thread/start", id: 1, params: { cwd: process.cwd() } });

最初の3つの send と、id が 0、1、2 と進む流れは、公式の例のとおりです。thread/start に付けた cwd は、公式の thread/start の例にある項目です。一方、承認への返事の形(result の中に decision を入れる書き方)は、公式のページに「決定値の payload で答える」とあるだけで、封筒の形は載っていませんでした。僕らの推測なので、次の generate-json-schema で作ったスキーマで必ず確かめてください。

版に合ったスキーマは、次のコマンドで作れます。

codex app-server generate-ts --out ./schemas
codex app-server generate-json-schema --out ./schemas

公式は、これらの出力は、生成したCodexの版に固有、と書いています。つまり、クライアントを書くときは、自分が動かすCodexの版からスキーマを作るのが、一番ずれにくいやり方です(これは僕らの見立てです)。

通知を読む:イベントの流れ

公式の「Event streaming」によると、スレッドを始めたあと、クライアントは thread/started、thread/status/changed、turn/*、item/*、serverRequest/resolved などの通知を読み続けます。

失敗したターンは、error のイベントのあと turn/completed(status: "failed")で終わります。codexErrorInfo には ContextWindowExceeded、UsageLimitExceeded、Unauthorized、SandboxError などの値が入り、取得できた場合は上流のHTTPステータスが httpStatusCode に入ります。

承認:サーバーからの「要求」に、クライアントが答える

app-serverでは、コマンドの実行、ファイルの変更、権限の追加の許可が、サーバーからクライアントへの要求として届きます。クライアントが答えないと、ターンは先へ進みません。(これは僕らの見立てです。公式は、承認の応答がないときの挙動を、読めた範囲では書いていません。)

種類 届く要求 答えの選択肢 公式の補足
コマンドの実行 item/commandExecution/requestApproval(itemId、threadId、turnId、任意で reason、command、cwd、availableDecisions) accept、acceptForSession、decline、cancel、または acceptWithExecpolicyAmendment 最後に item/completed で completed、failed、declined のどれか
ファイルの変更 item/fileChange/requestApproval(reason、grantRoot など) accept、acceptForSession、decline、cancel 同上
権限の追加 item/permissions/requestApproval(要求する権限のネットワークやファイルを含む) 許可した部分だけを permissions として返す scope を "session" にすると以後のターンも有効。省略か "turn" なら今のターンだけ。許可しなかった要求は無視される

どの要求も、答えたあとに serverRequest/resolved が届きます。道具やアプリの副作用のある呼び出しは承認が要ることがあり、破壊的と注釈された呼び出しは、必ず承認が要る、と書かれています。承認の画面を自分で作るとは、この表の3種類に、自分のUIで人の判断を返すことです。

危険な窓口と、取り消せない操作

公式のページが、特に注意を書いているものです。

実験的の印と、そのスイッチ

initialize の capabilities.experimentalApi を省くか false にすると、安定した範囲だけです。true にすると実験的な項目が使え、使わずに呼ぶと「requires experimentalApi capability」のエラーになります。公式が実験的とするものの例は、process/*、environment/info、dynamicTools、historyMode、chatgptAuthTokens、tool/requestUserInput などです。permissions は、experimentalApi で開く「Beta」のクライアント向けと書かれています。plugin/* は「開発中で、本番のクライアント向けではない」と書かれています。experimentalFeature/list は、機能ごとの段階(beta、underDevelopment、stable、deprecated、removed)を返します。

認証:3つの層を分けて考える

層1:SDKやapp-serverが動かすCodexは、誰としてOpenAIに入るか

app-serverのページのアカウントの節(account/*)によると、認証のモードは、APIキー(apikey)、ChatGPT管理(chatgpt)、外部のChatGPTトークン(chatgptAuthTokens、実験的)です。Amazon Bedrockの認証にも触れた箇所がありましたが、細部は読めていません。

方法 account/login/start の type 公式の説明
APIキー apiKey apiKey を渡す
ChatGPTのブラウザログイン chatgpt loginId と、ユーザーが開く authUrl が返る
ChatGPTのデバイスコード chatgptDeviceCode verificationUrl と userCode が返り、クライアントが表示する
外部のChatGPTトークン chatgptAuthTokens ホストが accessToken、chatgptAccountId、任意で chatgptPlanType を渡す。initialize で experimentalApi が必要

ほかに、account/read(現在の認証状態)、account/login/completed の通知、account/login/cancel、account/logout、account/updated の通知、account/rateLimits/read(使用量の窓と、リセットの時刻)、account/usage/read が、メソッド一覧に挙がっています。外部トークンの場合、サーバーが401を受けると account/chatgptAuthTokens/refresh を要求し、ホストが新しいトークンを返す流れで、要求は約10秒でタイムアウトする、と書かれています。

使いすぎを見張りたいときは、account/rateLimits/read(使用量の窓と、リセットの時刻)を定期的に読み、残りが少ないときに自分のアプリ側で新しいターンを止める、という使い方が考えられます(これは僕らの見立てです)。なお、UsageLimitExceeded はターンの失敗の codexErrorInfo にも現れるので、失敗の通知側でも拾えます。APIキーとChatGPTのプランで、使える機能と数え方が違う点は、前のガイド(06)の表を先に見てください。

Pythonでは、同じことを login_chatgpt()、login_chatgpt_device_code()、login_api_key() として呼べます。SDKは、既存のログイン情報を自動で使います。ログイン情報の保存場所と扱い(auth.json はパスワードと同じ)は、前のガイド(06)にまとめました。

層2:自分のクライアントと、app-serverの間の認証

stdioなら親子のプロセスの間なので、問題になりません。WebSocketで外に出すときだけ、上の --ws-auth と Authorization: Bearer の話です。

層3:商用・ホスト型で、利用者に何を使わせるか

ここが、組み込みの一番大きな分かれ目です。公式のapp-serverのページの注意書きは、次の趣旨です。

Sign in with ChatGPTは、公式のヘルプ側では、参加しているアプリにChatGPTのアカウントでサインインする仕組みで、対象のPlusとPro利用者は、自分のプランをAIの要求に使うことも選べる、と書かれています。アプリの利用は既存のプランの上限に数えられ、ユーザーは設定でアプリごとの週の上限を決められます。

開発者向けのページ(developers.openai.com/siwc)は、ウェブサイトへのサインイン、ChatGPTのプラグイン、オープンソースのアプリへの組み込みを挙げています。「クライアントIDの申請」のページは、現時点では「選ばれた商用パートナー」への提供で、待ちリストに入るには関心のフォームを出す、と書いています。つまり、一般の開発者が今日すぐ使える手順は、読んだ範囲では見つかりませんでした。 有料やリモートでホストするアプリは、オープンソース向けのドキュメントではなく関心のフォームへ、と案内されています。

オープンソースのアプリの場合の流れ(公式のSIWCのページの要約)は、次のとおりです。

  1. クライアントの準備:ext_agent_host_id(ホストごとに選んで保存する識別子)を用意し、ローカルの待ち受けとstate、nonce、PKCEを作る。
  2. ブラウザでOAuthに進み、ループバックにコードが返る。
  3. stateを確かめ、コードをPKCEつきで交換し、IDトークンと許可されたスコープを検証する。
  4. ベアラートークンで、Responses APIに store: false、stream: true で要求する。

ext_agent_host_id は、JWKサムプリントのURI(推奨)、urn:uuid:、did:key のいずれかの形式で、認証の資格情報としては扱えず、識別できる値(メールなど)も入れない、と公式は書いています。公開鍵由来のIDについて、秘密鍵の所持を、OpenAIは現時点では検証していない、とも書かれています。

この方式でapp-serverを使う場合、公式は、トークンを子プロセスの環境変数(ACCESS_TOKEN)に入れ、model_provider="openai_chatgpt_plan"、base_url を https://api.openai.com/v1、wire_api="responses"、requires_openai_auth=false、supports_websockets=false などの設定をつけて codex app-server --listen stdio:// を起動する、と書いています。トークンの更新はアプリが行い、更新したトークンでapp-serverを起動し直し、thread/resume で会話を続けます。model/list は、この設定では同梱のカタログを返すことがあり、「権利の確認ではなく一覧」として扱い、推論のターンが成功してはじめて使えると確かめられる、とあります。

この方式の制限(公式の「Preview limitations」)

ページ名は「Preview limitations」で、本文にも「preview」という語(「Preview behavior and limitations」)が使われています。「beta」とは書かれていません。

APIを直接使う:Codexを使わずにモデルを呼ぶ

「Codexをアプリに入れたい」の中には、実は「コードを書くモデルを自分のアプリから呼びたい」だけの場合があります。その場合は、Codexのエージェントではなく、OpenAI APIのResponses APIが入口です。公式のコード生成のガイドの例は、次の形です(抜粋で、全文は取得できませんでした)。

curl https://api.openai.com/v1/responses \
  -H "Authorization: Bearer $OPENAI_API_KEY" \
  -d '{ "model": "gpt-6-astra", "reasoning": { "effort": "high" } }'

ガイドは、API経由のコード生成には gpt-6-astra を既定として勧め、Codex向けのモデルとして gpt-5.3-codex などがあるが、最新の汎用モデルのほうが普通は良い選択、と書いています。Codex自体は gpt-6-sol が最も合う、とも書かれています(確認日2026-10-24。モデルの名前と推奨は変わります)。上の例は抜粋で、入力の行が見えていません。完全な例は公式のページで確かめてください。

Codexを会社のLLMゲートウェイにつなぐ設定も、公式にあります(ゲートウェイのページ)。~/.codex/config.toml に model_provider = "enterprise-gateway" を決め、[model_providers.enterprise-gateway] の表に base_url(例:https://gateway.example.com/v1)、wire_api = "responses"、env_key = "CODEX_GATEWAY_API_KEY" を書く形です。ただし、このページが対象にしているのはCLIとmacOS・Windowsのアプリで、SDKとapp-serverがこの設定に従うかは、ページに書かれていません。自分のアプリで使うなら、実際に動かして確かめる必要があります。

非推奨・実験的・削除済みの区別

対象 公式の書き方 出典
codex mcp-server、codex-mcp-server 削除済み。app-serverへ。アップグレード前に移行する Codex MCP server、Codex SDK
外部のMCPサーバーにCodexがつなぐ(codex mcp) 引き続き対応 Codex MCP server
app-serverのコマンド 実験的。本番向けには未対応 Codex app server、Codex MCP server
app-serverのWebSocket転送 実験的で未対応。ループバック以外は既定で認証なしの接続を許す(展開期間中) Codex app server
capabilities.experimentalApi で開くもの process/*、動的ツール、chatgptAuthTokens など Codex app server
plugin/*(list、read、install、uninstall) 開発中。本番のクライアント向けではない Codex app server
thread/rollback 削除済み Codex app server
review/start の delivery: "detached"、thread/compacted、item/fileChange/outputDelta、thread/resume と thread/fork のフル履歴の応答(ページ分けされたスレッド) 非推奨 Codex app server
app-serverの認証 商用・ホスト型では許されたことがない Codex app server
Python SDK 安定版(stable release)。先行版は --pre Codex SDK
Sign in with ChatGPT(開発者向け) 選ばれた商用パートナーへの提供、待ちリスト siwc / request-client-id

最初の10分でやること

公式に書かれている範囲で、読んだ人がすぐ動ける手順です。順番は僕らの整理です。

A. 自動化のため(SDK)

  1. 自分のパソコンのGitで管理している小さなフォルダを用意する(TypeScriptのSDKはGitリポジトリを求めるため)。
  2. TypeScriptなら npm install @openai/codex-sdk(Node.js 18以上)、Pythonなら pip install openai-codex(Python 3.10以上)。
  3. 上の最小の例を貼って、「このリポジトリを3行で説明して」のような読み取りだけの依頼を送る。Pythonなら sandbox=Sandbox.read_only を付ける。
  4. 認証は、すでにCodexにログインしていればそれが使われる。していなければ、前のガイド(06)を見て、ログインか、CODEX_API_KEY を1回の実行にだけ渡す。

B. 自前のクライアントのため(app-server)

  1. codex app-server generate-json-schema --out ./schemas で、自分のCodexの版のスキーマを作る。
  2. codex app-server(stdio)を、自分のコードから子プロセスで起動する。
  3. initialize、initialized、thread/start、turn/start を送り、item/agentMessage/delta と turn/completed を読む。読み取りだけにしたいときは、公式が値を挙げている turn/start の sandboxPolicy({"type":"readOnly"})を使う形から始める(thread/start の sandbox に readOnly を使えるかは、公式の例では確かめられませんでした)。
  4. 承認の要求(requestApproval)が来たら、最初は必ず decline を返すクライアントにして、流れを確かめてから accept を作る(これは僕らの見立てです)。

C. 自分のサービスの利用者に使わせたい

app-serverの認証が商用・ホスト型で許されていないことを読み直し、Sign in with ChatGPTの開発者向けページ(developers.openai.com/siwc)と関心のフォームを確かめます。提供の条件は、OpenAIの規約と担当者に確かめてください。この記事は助言ではありません。

僕らの見立て

ここからは僕らの見立てです。公式が書いていることと分けて読んでください。

気をつけること

出典