メインコンテンツにスキップ
OpenAI

Codex ハーネスの解放:App Server を構築した方法

Celia Chen 氏、Technical Staff メンバー

読み込んでいます...

OpenAI のコーディングエージェント Codex は、ウェブアプリ(新しいウィンドウで開く)CLI(新しいウィンドウで開く)IDE 拡張機能(新しいウィンドウで開く)、および 新しい Codex macOS アプリなど、さまざまなプラットフォームで利用可能です。内部的には、それらはすべて同じ Codex ハーネス(すべての Codex 体験の基盤となるエージェントループとロジック)によって動作します。それらをつなぐ重要な要素とは何でしょうか?クライアントフレンドリーな双方向 JSON-RPC1 API である Codex App Server(新しいウィンドウで開く) です。

この記事では、Codex App Server を紹介し、これまでに得た知見を基に、Codex の機能を製品に組み込んでユーザーのワークフローを強化する最適な方法をご紹介します。App Server のアーキテクチャとプロトコル、さまざまな Codex サーフェスとの統合方法について説明し、Codex をコードレビュアー、SRE エージェント、またはコーディングアシスタントとして活用するためのヒントを紹介します。

App Server の起源

アーキテクチャの詳細に入る前に、App Server の背景を知っておくと役立ちます。当初、App Server は Codex ハーネスを製品間で再利用するための実用的な手段でしたが、徐々に進化して当社の標準プロトコルとなりました。

Codex CLI は TUI(ターミナルユーザーインターフェース)として始まりました。つまり、Codex はターミナル経由でアクセスされることを意味します。VS Code 拡張機能(Codex エージェントとやり取りするための、より IDE フレンドリーな方法)を開発した際、同じハーネスを使用して、再実装せずに IDE UI から同じエージェントループを駆動する方法が必要でした。これはリクエスト/レスポンスを超えた豊富なインタラクションパターン(ワークスペースの探索、エージェント推論中の進捗ストリーミング、差分出力など)のサポートを意味していました。最初に、Codex を MCP サーバーとして公開する(新しいウィンドウで開く)ことを試みましたが、VS Code にとって意味のある形で MCP のセマンティクスを維持するのは難しいことが分かりました。代わりに、TUI ループを反映した JSON-RPC プロトコルを導入し、これが非公式な初期バージョン(新しいウィンドウで開く)の App Server となりました。当時は、他のクライアントが App Server に依存することは想定されていなかったため、安定した API として設計されていませんでした。

その後数か月で Codex の採用が拡大するにつれ、社内チームや外部パートナーは、ユーザーのソフトウェア開発ワークフローを加速するために、同じハーネスを自社製品に組み込めるようにしたいと考えるようになりました。たとえば、JetBrains と Xcode は IDE グレードのエージェント体験を求めていましたが、Codex デスクトップアプリは多くの Codex エージェントを並行してオーケストレーションする必要がありました。こうした要求に応えるため、当社製品とパートナー統合の両方が長期にわたって安全に依存できるプラットフォームサーフェスを設計する必要が生じました。統合が容易で、下位互換性があり、既存のクライアントに支障をきたすことなくプロトコルを進化させることができる必要がありました。

次に、異なるクライアントが同じハーネスを使用できるように、アーキテクチャとプロトコルをどのように設計したかについて説明します。

Codex ハーネスの舞台裏

まず、Codex ハーネスの内部と、Codex App Server がそれをクライアントに公開する方法について詳しく見ていきましょう。前回の Codex ブログでは、ユーザー、モデル、ツールの間のインタラクションを調整するコアエージェントループについて詳しく解説しました。これは Codex ハーネスのコアロジックですが、完全なエージェント体験にはさらに多くの要素があります。

1. スレッドのライフサイクルと永続性。スレッドは、ユーザーとエージェントの間で行われる Codex の会話です。Codex はスレッドを作成、再開、フォーク、アーカイブし、イベント履歴を保持するため、クライアントは再接続して一貫したタイムラインをレンダリングできます。

2. 設定と認証。Codex は設定を読み込み、デフォルトを管理し、「ChatGPT でサインイン」などの認証フローを実行し、認証情報の状態を管理します。

3. ツールの実行と拡張。Codex はサンドボックス内で shell/file ツールを実行し、MCP サーバーやスキルなどのインテグレーションを接続して、一貫したポリシーモデルの下でエージェントループに参加できるようにします。

ここで言及したすべてのエージェントロジック(コアエージェントループを含む)は、Codex CLI コードベースの「Codex core(新しいウィンドウで開く)」という部分にあります。Codex core は、すべてのエージェントコードが格納されているライブラリであり、エージェントループを実行し、1つの Codex スレッド(会話)の永続性を管理するために起動できるランタイムでもあります。

有用であるためには、Codex ハーネスはクライアントからアクセス可能である必要があります。ここで App Server の出番です。

「App server のプロセス フロー」というタイトルの図。クライアントは JSON-RPC メッセージを stdio リーダーに送信し、リーダーはリクエストを Codex メッセージプロセッサに転送します。プロセッサは、ルックアップスレッド、スレッドハンドル、送信されたリクエスト、およびイベント/更新を介してスレッドマネージャーおよびコアスレッドと対話し、クライアントに応答を返します。

App Server は、クライアントとサーバー間の JSON-RPC プロトコルであると同時に、Codex のコアスレッドをホストする長期プロセスでもあります。上の図からわかるように、App Server プロセスには、stdio リーダー、Codex メッセージプロセッサ、スレッドマネージャー、コアスレッドという4つの主要コンポーネントがあります。スレッドマネージャーは各スレッドに対して1つのコアセッションを起動し、Codex メッセージプロセッサは各コアセッションと直接通信してクライアントのリクエストを送信し、更新を受信します。

1つのクライアントリクエストによって多数のイベント更新が発生する可能性があり、これらの詳細なイベントにより、App Server 上に豊富な UI を構築できるようになります。さらに、stdio リーダーと Codex メッセージプロセッサは、クライアントと Codex コアスレッド間のトランスレーション層として機能します。クライアントの JSON-RPC リクエストを Codex コアの操作に変換し、Codex コアの内部イベントストリームを監視し、それらの低レベルイベントを少数の安定した UI 対応の JSON-RPC 通知に変換します。

クライアントと App Server 間の JSON-RPC プロトコルは完全に双方向です。一般的なスレッドには、クライアントからのリクエストと多くのサーバー通知があります。さらに、エージェントが承認などの入力を必要とする場合、サーバーはリクエストを開始し、クライアントが応答するまで処理を一時停止できます。

会話プリミティブ

次に、App Server プロトコルの基礎となる会話プリミティブについて詳しく説明します。ユーザーとエージェントのやり取りが単純な要求/応答ではないため、エージェントループ用の API を設計するのは難しいです。1つのユーザーリクエストは、クライアントが忠実に表現する必要がある構造化された一連のアクションへと展開することがあります。具体的には、ユーザーの入力、エージェントの段階的な進捗、その過程で生成される成果物(例:差分)などです。このインタラクションストリームを UI 間で容易に統合可能かつレジリエンとなものとするため、明確な境界とライフサイクルを持つ3つのコアプリミティブを採用しました。

1. アイテム:アイテムは Codex における入出力の最小単位です。アイテムにはタイプ(例:ユーザーメッセージ、エージェントメッセージ、ツール実行、承認リクエスト、差分)があり、それぞれに明確なライフサイクルがあります。

  • item/started アイテムの開始時
  • オプションの item/*/delta コンテンツストリームとしてのイベント(ストリーミングアイテムタイプの場合)
  • アイテムが最終ペイロードで完了する際の item/completed

このライフサイクルにより、クライアントは started で直ちにレンダリングを開始し、delta で増分更新をストリーミングし、completed で処理を完了できます。

2. ターン:ターンとは、ユーザー入力によって開始されるエージェント作業の1単位です。クライアントが入力(例:「テストを実行して失敗を要約して」)を送信した時点で始まり、エージェントがその入力に対する出力の生成を完了した時点で終了します。ターンには、途中で生成される中間ステップと出力を表す一連のアイテムが含まれます。

3. スレッド:スレッドとは、ユーザーとエージェント間の継続的な Codex セッションを保持する永続的なコンテナで、複数のターンを含みます。スレッドは作成、再開、フォーク、アーカイブが可能です。スレッド履歴は永続化されるため、クライアントは再接続して一貫したタイムラインを表示できます。

さて、クライアントとエージェントの間の簡略化された会話を見てみましょう。この会話はプリミティブで表現されています。

「クライアント-サーバープロトコルメッセージフロー: 初期化ハンドシェイク」というラベルの付いた図。クライアントは clientInfo を含む初期化リクエストをサーバーに送信します。サーバーは、userAgent 文字列「my_client/1.0」を含む結果イベントで応答します。

会話の冒頭で、クライアントとサーバーは initialize ハンドシェイクを確立する必要があります。クライアントは、他のメソッドの前に単一の initialize リクエストを送信する必要があり、サーバーは応答でそれを確認します。これによりサーバーは機能を通知する機会を得られ、実際の処理開始前に双方がプロトコルのバージョン管理、機能フラグ、デフォルト設定について合意できます。以下は、OpenAI の VS Code 拡張機能のペイロードの例です。

JSON

1
{
2
"method": "initialize",
3
"id": 0,
4
"params": {
5
"clientInfo": {
6
"name": "codex_vscode",
7
"title": "Codex VS Code Extension",
8
"version": "0.1.0"
9
}
10
}
11
}

サーバーが返す内容は次のようになります。

JSON

1
{
2
"id": 0,
3
"result": {
4
"userAgent": "codex_vscode/0.94.0-alpha.7 (Mac OS 26.2.0; arm64) vscode/2.4.22 (codex_vscode; 0.1.0)"
5
}
6
}
「クライアント-サーバープロトコルメッセージフロー: スレッドとターンライフサイクル」というタイトルの図。クライアントは thread/start および turn/start のリクエストをサーバーに送信します。サーバーは、thread/started、turn/started、item/started、item/completed というイベントを発行し、ユーザーメッセージが “run tests and summarize failures.” であるターンを示します。

クライアントが新しいリクエストを行うと、まずスレッドを作成し、その後ターンを作成します。サーバーは進捗に関する通知(thread/startedturn/started)を送信します。また、ここにあるユーザーメッセージのように、アイテムとして登録された入力も返送されます。

「クライアント/サーバープロトコルのメッセージフロー:オプションの承認を伴うツールの実行」というタイトルの図。ツール呼び出し中、サーバーは item/started を送出し、その後、理由「run tests」とともに item/commandExecution/requestApproval を送出します。クライアントは承認イベント(許可/拒否)を返します。その後、サーバーは「pnpm test」のコマンド実行を示す item/completed を送信します。

ツール呼び出しもアイテムとしてクライアントに送り返されます。また、サーバーはアクションを実行する前に、サーバーリクエストを送信してクライアントの承認を求める場合があります。承認プロセスでは、クライアントが「許可」または「拒否」で応答するまでターンが一時停止します。VS Code 拡張機能における承認フローは以下の通りです。

ダークテーマのインターフェースで表示される許可プロンプト:「このワークスペースで pnpm test を実行することを許可しますか?」選択肢は以下の通り:1) はい 2) はい(pnpm test で始まるコマンドについては今後確認しない)3) いいえ(下部に送信ボタンがあります)
「クライアントサーバープロトコルメッセージフロー:ストリーミングエージェントメッセージフロー」というタイトルの図。サーバーはアシスタントメッセージを部分ごとにストリーミングします:item/started、2つの agentMessage/delta イベント(“ran 3 tests.”, “all passed”)、その後、item/completed。ターンは turn/completed で終了します。

最終的に、サーバーはエージェントメッセージを送信し、その後 turn/completed でターンを終了します。エージェントメッセージのデルタイベントは、メッセージが item/completed で確定するまで、メッセージの一部をストリームとして送信します。

図中のメッセージは読みやすさのために簡略化されています。完全なターンの JSON を確認するには、Codex CLI リポジトリからテストクライアントを実行できます。

Bash

1
codex debug app-server send-message-v2 "run tests and summarize failures"

クライアントとの統合

ここで、さまざまなクライアントサーフェスが App Server 経由で Codex を埋め込む方法を見てみましょう。ローカルアプリと IDE、Codex Web ランタイム、TUI の3つのパターンについて説明します。

「Codex ハーネスをアプリサーバー経由で統合した Codex クライアント」というタイトルの図。ファーストパーティクライアント(Codex Desktop App、TUI/CLI、Web Runtime)とサードパーティ統合(JetBrains IDE、VS Code、Xcode)は、JSON-RPC 呼び出しを通じて Codex ハーネスと通信します。

これら3つすべてにおいて、トランスポートは JSON-RPC over stdio (JSONL) です。JSON-RPC を使用すると、任意の言語でクライアントバインディングを簡単に構築できます。Codex のサーフェスおよびパートナー統合は、Go、Python、TypeScript、Swift、Kotlin などの言語で App Server クライアントを実装しています。TypeScript の場合、次のコマンドを実行することで、Rust プロトコルから直接定義を生成できます。

Bash

1
codex app-server generate-ts

他の言語の場合は、次のコマンドを実行して JSON スキーマ バンドルを生成し、好みのコードジェネレーターに渡すことができます。

Bash

1
codex app-server generate-json-schema

ローカルアプリと IDE

Codex 拡張機能が実行されている VS Code のスクリーンショット。Rust テストファイルが開かれており、その下の Codex パネルには fmt と cargo test -p codex-app-server だけが実行されていることが示され、最終的な合格/不合格の結果を待機しながらフォーマットとテストが進行中であることが報告されています。

ローカルクライアントは通常、プラットフォーム固有の App Server バイナリをバンドルまたはフェッチし、それを長時間実行される子プロセスとして起動し、JSON-RPC 用の双方向 stdio チャネルを開いたままにします。例えば、当社の VS Code 拡張機能やデスクトップアプリでは、配布されるアーティファクトにプラットフォーム固有の Codex バイナリが含まれており、テスト済みのバージョンに固定されているため、クライアントは常に検証済みの正確なビットを実行します。

すべての統合が頻繁にクライアントのアップデートを提供できるわけではありません。Xcode などの一部のパートナーは、クライアントを安定した状態に保ち、必要に応じて新しい App Server バイナリを指すことができるようにすることで、リリースサイクルを分離しています。これにより、サーバー側の改善(例:Codex コアの自動コンパクション機能の強化や新規サポートされた設定キー)を採用し、クライアントのリリースを待たずにバグ修正を展開できます。App Server の JSON-RPC インターフェースは下位互換性を保つように設計されているため、古いクライアントも新しいサーバーと安全に通信できます。

Codex Web

「ログイン成功メッセージの更新」というタイトルの更新が表示されている Codex Web インターフェースのスクリーンショット。左側のパネルには変更、テスト、修正されたファイルの概要が表示され、右側のパネルには login.rs のコード差分が表示され、ログイン成功メッセージの表現が更新されています。

Codex Web は Codex ハーネスを使用しますが、コンテナ環境で実行します。ワーカーは、チェックアウトされたワークスペースでコンテナをプロビジョニングし、その中で App Server バイナリを起動し、stdio2 チャネルを介して長期間有効な JSON-RPC を維持します。ユーザーのブラウザタブで実行されるウェブアプリは、HTTP と SSE を介して Codex バックエンドと通信し、ワーカーが生成したタスクイベントをストリーミングします。これにより、ブラウザ側の UI を軽量に保ちながら、デスクトップとウェブの両方で一貫したランタイムを維持できます。

ウェブセッションは一時的であるため(タブが閉じたり、ネットワークが切断されたりする)、ウェブアプリは長時間実行されるタスクの信頼できる情報源にはなりません。状態と進行状況をサーバーに保持することで、タブが閉じられても処理を継続できます。ストリーミングプロトコルと保存されたスレッドセッションにより、新しいセッションは簡単に再接続し、中断したところからスムーズに再開し、クライアント側で状態を再構築することなく追いつくことができます。

TUI/Codex CLI

Codex CLI を実行中のターミナルのスクリーンショット。OpenAI Codex のバナー(モデル:gpt-5.2-codex medium)、ユーザーコマンド「explain app server to me」、および「Working」ステータスが表示されています。下部にはショートカットオプションと共に、「write tests for @filename」という提案が表示されています。

歴史的に、TUI は「ネイティブ」クライアントであり、エージェントループと同じプロセスで実行され、app-server プロトコルではなく Rust のコアタイプと直接通信していました。それにより初期の反復は速くなりましたが、同時に TUI は特殊なケースのサーフェスになりました。

現在、App Server が存在するため、 TUI をリファクタリング(新しいウィンドウで開く)して他のクライアントと同様の動作を実現する予定です。具体的には、App Server の子プロセスを起動し、stdio 経由で JSON-RPC を通信し、同じストリーミングイベントと承認処理をレンダリングします。これにより、TUI がリモートマシン上で稼働する Codex サーバーに接続し、エージェントを計算リソースの近くに配置したまま、ノートパソコンがスリープしたり切断されたりしても作業を継続し、ローカルでライブ更新と操作を提供し続けるワークフローが可能になります。

適切なプロトコルの選択

Codex App Server は、今後当社が維持していく第一級の統合方法となりますが、機能がより制限された他の方法もあります。通常、クライアントには Codex との統合に Codex App Server を使用することを推奨しますが、さまざまな統合方法を検討し、それぞれの長所と短所を理解しておくことも重要です。以下は、Codex を活用する最も一般的な方法と、それぞれの方法が適している場合です。

JSON-RPC プロトコル

MCP サーバーとしての Codex

codex mcp-server(新しいウィンドウで開く) を実行し、stdio サーバーをサポートする任意の MCP クライアント(例:OpenAI Agents SDK(新しいウィンドウで開く))から接続します。これは、MCP ベースのワークフローがすでにあり、呼び出し可能なツールとして Codex を呼び出す場合に適しています。欠点は、MCP が公開しているものしか利用できないことです。そのため、より豊富なセッションセマンティクス(例:差分更新)に依存する Codex 固有のやり取りは、MCP エンドポイントを通じてはうまく対応できない可能性があります。

クロスプロバイダーエージェントハーネスプロトコル

一部のエコシステムは、複数のモデルプロバイダーやランタイムに対応できるポータブルなインターフェースを提供しています。これは、複数のエージェントを調整する単一の抽象化を望む場合に適しています。その代償として、これらのプロトコルは機能の共通部分集合に収束する傾向があり、特にプロバイダー固有のツールやセッションのセマンティクスが重要となる場合、より豊かなインタラクションの表現が困難になる可能性があります。この分野は急速に進化しており、現実世界のエージェントワークフローを表現する最適なプリミティブ(スキル(新しいウィンドウで開く)はその好例です)が明らかになるにつれ、より多くの一般的な標準が登場すると予想されます。

Codex App Server

Codex の完全なハーネスを安定した UI フレンドリーなイベントストリームとして公開したい場合は、App Server を選択してください。エージェントループの全機能と、ChatGPT によるサインイン、モデルの検出、構成管理などのその他のサポート機能の両方を利用できます。主なコストは統合作業です。なぜなら、ご自身の言語でクライアントサイドの JSON-RPC バインディングを構築する必要があるからです。しかし実際には、JSON スキーマとドキュメントを提供すれば、Codex が多くの面倒な作業を引き受けてくれます。私たちが一緒に仕事をした多くのチームは、Codex を使用してすぐに実用的な統合を実現できました。

Codex を埋め込む他の方法

単発のタスクや CI 実行向けの軽量でスクリプト可能な CLI モードです。非対話的に単一コマンドを完了まで実行し、構造化された出力をログとしてストリーム出力し、明確な成功/失敗シグナルで終了する自動化やパイプラインに理想的です。

独自のアプリケーション内からローカルの Codex エージェントをプログラムで制御するための TypeScript ライブラリ。別途 JSON-RPC クライアントを構築せずに、サーバーサイドのツールやワークフロー向けにネイティブのライブラリインターフェースが必要な場合に最適です。App Server よりも早く出荷されたため、現時点では対応言語数と対応範囲が少なくなっています。開発者の関心があれば、JSON-RPC バインディングを記述することなくチームがハーネスの範囲をより広くカバーできるように、App Server プロトコルをラップする追加の SDK を提供することを検討します。

今後の展開

本記事では、エージェントとの対話のための新たな標準設計へのアプローチと、Codex ハーネスを安定したクライアントフレンドリーなプロトコルに変える方法を紹介しました。また、App Server が Codex コアを公開し、クライアントが完全なエージェントループを駆動できるようにし、TUI、ローカル IDE 統合、Web ランタイムなどの幅広いサーフェスを強化する方法について説明しました。

これをきっかけに Codex を自分のワークフローに統合するアイデアが浮かんだなら、App Server を試してみる価値があります。すべてのソースコードは Codex CLI のオープンソースリポジトリ(新しいウィンドウで開く)にあります。フィードバックや機能のリクエストがあれば、お気軽にお寄せください。皆さまからのご意見をお待ちしております。また、エージェントをより多くの方にとって利用しやすくするため、今後も改善を続けてまいります。

著者

Celia Chen

謝辞

この記事への貢献者、Michael Bolin、Owen Lin、Eric Traut、Rasmus Rygaard、そして App Server に取り組んだ Codex チームの全員に心より感謝します。

脚注

  1. 1

    「JSON-RPC lite」のバリアントを使用しています。これはリクエスト、レスポンス、通知の形式を維持しつつ、"jsonrpc": "2.0" ヘッダーを省略し、厳密な JSON-RPC 2.0 ではなく stdio 上の JSONL としてフレーム化されたものです。

  2. 2

    「stdio」は、コンテナ内の app-serverのstdin/stdout を指します。ホストされたセットアップでは、これらのストリームは、多くの場合、永続的なネットワーク接続(WebSocket のような接続など)を介してコンテナランタイムにトンネリングされるため、文字通りのローカルパイプではない場合でも、stdio のように動作します。