0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

Codex の MCP サーバーを自作する前に、実行権限を決めておこう

0
Last updated at Posted at 2026-09-24

codex mcp-server がなくなったので、codex exec を呼ぶ MCP サーバーを自分で作ろうと思っている。
Claude Code などから仕事を渡し、Codex に実装やレビューをさせて、結果を受け取りたい。

そう考えている人に、先に共有しておきたいことがあります。

MCP ツールとして公開する前に、その Codex が「何を読めるか」「どこへ接続できるか」を決め、実際の起動方法で確かめてください。
プロンプトを渡して回答を返す処理だけでは、その範囲は決まりません。cwd を限定してもホーム配下を読めたり、古い起動オプションを残しただけで新しい制限が効かなくなったりします。どちらも私の環境で起きました。

この記事では、Codex CLI の権限制御を試した結果を、自作 MCP サーバーの設計にどう反映するかという順番でまとめます。
対象は、まず手元で動かす連携です。MCP サーバーの完成実装や、複数利用者へ公開するサービスの設計ではありません。
ここで示す MCP の入力や実行管理は、これから実装するための設計案です。

なくなったのは「Codex を MCP サーバーにする」機能

公式資料では、codex mcp-server コマンドと codex-mcp-server バイナリの削除が案内されています。
一方、Codex から外部の MCP サーバーを使う機能は残っていますcodex mcp はそちらを管理するコマンドです。公式の削除・移行案内

向き 今回の話との関係
Claude Code など → MCP → Codex codex mcp-server が担っていた側。この記事の対象
Codex → MCP → 外部ツール 現在もサポートされている側

公式の移行先は Codex App Server です。ただし、App Server は独自のプロトコルを使い、そのまま MCP クライアントへ登録できる置き換えではありません。調査時点では experimental で、本番用途はサポート対象外と案内されています。

呼び出し元に MCP の形を残したいなら、自作サーバーが Codex との間を仲介することになります。
以下では、単発の仕事を codex exec で実行する構成を考えます。App Server を使う場合も権限や結果の扱いは検討が必要ですが、ここでの CLI の設定・実測がそのまま当てはまるとは限りません。

MCP に包むと、どこまで自分が管理することになるか

構成を図にすると、こうなります。

自作サーバーは、起動する実行ファイル、引数、作業ディレクトリ、環境変数、出力の返し方を決めます。
呼び出し元のツール承認と、子として動く Codex の権限は別々に確認が必要です。親から継承される OS の制限がある場合も含め、実際のプロセス構成で何が強制されるかを見ます。

また、Codex の権限プロファイルが制限するのは、主に図の「サンドボックス内コマンド」です。
自作 MCP サーバー自身のファイル操作まで、その子で動く Codex のサンドボックスが制限してくれるわけではありません。
サーバーが入力されたパスを直接読んで結果へ添付するなら、その処理にも独自の制限が必要です。

Windows では、ACL の変更で SSH が使えなくなった

私が起動条件を気にするようになった理由には、Windows での体験もあります。
以前、ホームディレクトリで Codex を起動した後、~/.ssh 配下の ACL(アクセス制御リスト)に権限が追加され、OpenSSH が秘密鍵の利用を拒否するようになりました。
追加された権限を外して復旧しましたが、気づいたきっかけは Codex のエラーではなく、普段使っていた SSH が通らなくなったことでした。

当時の CLI の版とサンドボックス設定は記録していません。そのため、私の件の発生条件や、現在の版でも再現するかは未確認です。
ただし、よく似た問題は公式リポジトリにも記録されています。

出典 記録されていること
openai/codex PR #18443:ユーザープロファイル全体への ACL 付与を避ける修正 USERPROFILE 配下の SSH 設定や鍵にサンドボックス用の ACL が加わり、OpenSSH が拒否する問題を説明しています。2026 年 4 月 19 日にマージされた修正です
openai/codex Issue #31468:作業領域内への .ssh シンボリックリンクで起きる問題 .ssh の実体が dotfiles の作業領域内にある構成で、継承された ACL により Bad owner or permissions が発生したという利用者報告です。報告された CLI は 0.142.3 です

前者には修正が入り、後者にはリンク先が作業領域内にあるという具体的な条件があります。私の体験がどちらかと同じ原因だったとまでは特定できていません。
それでも、サンドボックスの準備に伴うホスト側の権限変更が、Codex の外で使うツールにも影響し得るという点は、自作サーバーの設計に関係します。

Windows も対象にするなら、作業ディレクトリの文字列だけでなく、その中のリンクがどこを指すかや、起動前後の ACL の変化も確認したいところです。
MCP サーバーが固定する起動場所・実行環境には、こうした影響も含まれます。後述する Linux の実測を、そのまま Windows での安全性の根拠にはしません。

出力は、情報が外へ渡る経路でもある

サンドボックス内のコマンドから直接通信できなくても、読み取った内容を標準出力へ出せば、Codex のツール結果としてモデルへ渡り得ます。
さらにその内容を MCP の結果として返すと、呼び出し元のモデルにも渡り得ます。

これは特別な送信処理を作らなくても起こります。DB を読み取り専用にしても、SELECT の結果を返せば同じです。
返却直前にマスクすればよい、とは限りません。その前に Codex 側のモデルへ渡っている可能性があるからです。

守るべき秘密は、実行するコードが読めない状態にします。ファイルだけでなく、継承する環境変数や DB で取得できるデータも対象です。
MCP・Apps・ブラウザーなど、Codex が利用する別のツールについても個別の制御が要ります。権限プロファイルの適用範囲

ツールの引数から、先に決める

自作すると、cwdsandboxconfig、追加の CLI 引数などを全部渡せる汎用ツールにしたくなります。
しかし、その形では呼び出し元が毎回、実行権限そのものを選ぶことになります。

私なら最初は、登録したプロジェクトに仕事を渡す、次の程度の入力に絞ります。

{
  "project": "sample-app",
  "task": "テスト用 DB に接続する処理を実装し、検証結果を報告してください"
}

これは設計例です。project を受け取ったサーバーが、事前登録した作業場所と設定へ対応付けます。
呼び出し元に公開する入力と、サーバー側で管理する値を次のように分けます。

項目 サーバー側での扱い
プロジェクト 登録済み ID から実パスへ解決する。入力をそのままパスとして使わない
実行ファイル・設定・権限プロファイル 検証した組み合わせを固定する
CLI 引数 任意の引数や -c 上書きを転送しない。未定義の入力項目を拒否する
環境変数 起動に必要なものを整理し、無関係な認証情報を継承しない
出力先・実行 ID サーバーが割り当てる。依頼ごとに分ける
実行時間・同時実行数 サーバー側で上限を持つ。同じ作業ツリーへの並行編集を管理する

プロンプトはシェル文字列へ埋め込まず、子プロセス用の標準入力や独立した引数として渡します。
ただし、シェルへの文字列挿入を避けても、Codex はプロンプトを解釈してコマンドを実行するエージェントです。引数の扱いと、実行できる範囲の制限は両方必要です。

「レビュー専用」というツール名や説明も、書き込みを禁止する設定の代わりにはなりません。
用途に応じた制限を実際に設定し、その範囲で依頼を受けます。

なぜ引数を固定したいのか:-s を足すと制限が消えた

私は従来、Codex を -s read-only または -s workspace-write で起動していました。
このとき、cwd を限定しても、ホーム配下のファイルを読めました。作業場所の指定を、読み取り範囲の指定だと思ってはいけませんでした。

そこで権限プロファイルによる読み取り制限を試すと、次の結果になりました。

起動時の指定 ワークスペース外のダミーファイル
-s read-only 読めた
-p db-probe 読めなかった
-p db-probe -s workspace-write 読めた

通常のユーザー設定では、-s / --sandbox や読み込み済みの sandbox_mode が、権限プロファイルを選ぶ default_permissions より優先されます。
私の試験でも、古い -s を残すと警告なしで読み取り制限が外れました。-p で読み込む設定全体が無視された、という意味ではありません。

ラッパーに権限プロファイルを追加しても、古い起動オプションが残っていれば意図した制限になりません。
旧方式の sandbox_mode / sandbox_workspace_write とプロファイル方式は混在させず、実際に起動する引数を確認します。
管理者の allowed_permission_profiles には別の優先規則があります。公式の注意事項

この経験から、自作 MCP ではサーバーが検証済みの起動形を組み立てる設計にしたいと考えています。
失敗したときに制限を緩めて再実行する処理も入れません。実行できなかった理由を返し、設定の変更は別途検証します。

ファイルと接続先を絞る設定を試した

ここからは、MCP サーバーに組み込む前段として行った実測です。
「テスト用 DB へ接続するコードを動かしたい」という用途で、必要な接続先だけ開けられるかを調べました。

検証環境は Linux VM、Codex CLI 0.154.0 です。権限プロファイルは Beta、内蔵ネットワークプロキシは experimental です。
主な測定にはモデルを介さない codex sandbox を使っています。MCP 経由の一連の動作、exec との同一性、DB 認証・SQL 実行・TLS 証明書検証・実ビルドは未確認です。

プロキシの有効化まで含めて設定する

以下は検証用の db-probe.config.toml の全体例です。CODEX_HOME が既定なら、配置先は ~/.codex/db-probe.config.toml です。

default_permissions = "db-probe"
approval_policy = "never"
web_search = "disabled"

[features]
network_proxy = true

[permissions.db-probe]
extends = ":workspace"

[permissions.db-probe.filesystem]
":root" = "deny"
":minimal" = "read"
":tmpdir" = "deny"
":slash_tmp" = "deny"
# Codex 本体の配置先が :minimal に含まれない場合に追加する
# "/path/to/codex-install-root" = "read"

[permissions.db-probe.network]
enabled = true
enable_socks5 = true
allow_local_binding = false

[permissions.db-probe.network.domains]
"192.0.2.10" = "allow" # テスト用 DB
"192.0.2.20" = "allow" # 到達確認用 HTTPS サーバー

IP は説明用です。192.0.2.0/24RFC 5737 の文書用アドレスで、実測では RFC 1918 のプライベート IP を使いました。
使用時は自分の試験環境の値へ置き換えます。HTTPS サーバーは検証用に許可したもので、DB だけが必要な用途でも許可する、という意味ではありません。

ファイル側はワークスペースへの書き込みを継承し、ルートを原則拒否したうえで、:minimal などの例外を読み取り可能にしています。
ワークスペース内の秘密はこの設定では隠れません。:minimal の全パスや、実ビルドに必要な一時領域も、今回は確定できていません。

通信側で重要なのは、domains の allowlist はプロキシが有効でなければ強制されないことです。
私も最初は domains だけを書き、許可外の宛先に到達してしまいました。enabled = true で通信を許可することと、内蔵プロキシを起動することは別です。

approval_policy = "never" は、承認のやり取りをしない単発実行を想定した設定です。「何でも許可する」という意味ではありませんし、この指定だけでアクセス範囲が狭くなるわけでもありません。
web_search = "disabled" も Web 検索に対する指定であり、Codex が利用する全 MCP・Apps などをまとめて無効にはしません。

-p は独立した設定一式への切り替えではない

-p db-probe は、ベース設定に $CODEX_HOME/db-probe.config.toml を追加で読み込む指定です。
既存の設定や適用されるプロジェクト設定との合成を考える必要があります。

指定 役割
-p db-probe 読み込む設定ファイルを選ぶ
default_permissions = "db-probe" 通常の実行で使う権限プロファイルを選ぶ
codex sandbox -P db-probe 境界の検証に使う権限プロファイルを選ぶ

自作サーバーの設定は、依頼を受けるたびに利用者の普段の設定へ何となく足すのではなく、合成元も含めて管理します。
Codex が編集できる作業ツリーと、サーバーの起動設定を置く場所も区別します。

実測から分かった、MCP 側へ持ち込むべき条件

読めなかっただけでは、制限が効いたとは言えない

ワークスペースの内外にダミーファイルを置き、内側は読めて外側は読めないことを確認しました。
今回の Linux 環境では、拒否側は No such file or directory になりました。

ただし最初は、内側も外側も読めませんでした。理由は、ルートを拒否した結果、Codex 本体まで見えなくなり、bubblewrap が起動に失敗していたことです。
Codex の配置先に読み取り許可を加えると動きました。

MCP のヘルスチェックでも「拒否されるべきファイルが読めなかった」だけでは足りません。
許可されるべき操作が成功することも、同じ起動条件で確認します。

接続先を絞れても、ポートは別だった

内蔵プロキシを有効にした状態で、次を確認しました。ホスト名も説明用に置き換えています。

対象・経路 結果
許可 IP への HTTPS、SOCKS5 経由 HTTP 200
許可外ホスト名への HTTPS、SOCKS5 経由 応答コード 2 で拒否
許可 IP への直接 TCP 接続 Network is unreachable
DB の 5432 番、SOCKS5 経由 PostgreSQL SSLRequest に S が返った
同じ DB ホストの 22 番、SOCKS5 経由 到達した

HTTPS は curl -k で証明書検証を切った到達試験です。PostgreSQL の S も、TLS 開始を受け入れる応答までで、相手の認証や SQL 実行の確認ではありません。PostgreSQL の SSLRequest

ホストを許可しても、DB のポートだけに制限したことにはなりません。ホスト規則に :5432 を書いても、ポート部分は正規化で除去される仕様です。
ポートまで絞るなら別のネットワーク制御が必要で、対象はプロキシから DB への接続経路です。サンドボックス内のクライアントだけを制限して終わりにはできません。

ホスト名の扱いにも条件があった

プライベート IP に解決されるホスト名は、allowlist に書いても既定の allow_local_binding = false では拒否されました。同じ宛先の IP 指定は通ります。
内蔵プロキシには、宛先規則とは別にプライベート宛先への防御があるためです。

既存の値を true に変更すると、許可ホスト名は通り、許可外ホスト名は引き続き拒否されました。
これは追加の防御を緩める設定であり、ホスト名の解決先を特定 IP に固定するものではありません。

SOCKS5 応答コード 2 はルールセットによる拒否を表しますが、それだけではどの規則が拒否したかまでは分かりません。timeout や名前解決エラーとも区別して記録します。RFC 1928

接続できるコードの形も確認が要る

今回、SOCKS5 を使ったのは curl と socat です。ALL_PROXY が渡されれば、任意の DB ドライバーが自動的に使ってくれるわけではありません。
MCP ツールに「DB テスト可能」と書く前に、実際のドライバーや必要な中継を含めて動かす必要があります。

プロキシのポートも今回の環境では呼び出し間で変わりました。前回の値をサーバー側で記憶して使い回さず、実行時に提供される値を使います。

MCP と Codex の入出力を混ぜない

exec をバックエンドにする場合の起動形は、例えば次です。

codex exec -p db-probe -C /path/to/test-repository --json -

これはバックエンドの起動例で、MCP サーバーの完成コードではありません。
サーバーは実行ファイルと引数を分けて子プロセスを起動し、子専用の標準入力へ依頼文を書き込み、入力を閉じます。MCP サーバー自身の標準入力をそのまま引き継がせません。

--json の標準出力は Codex の JSONL イベントです。MCP の応答ではないので、内容を解析して必要な結果を取り出します。
stdio 方式の MCP サーバーでは、stdout に流してよいのは MCP メッセージです。Codex の出力や進捗ログをそのまま流すと、MCP の通信に混ざります。MCP の stdio 仕様

結果の扱いは、最初から次を区別しておくと確認しやすくなります。

観測するもの 用途
子プロセスの終了状態 起動失敗、異常終了、時間切れなどを検出する
Codex のイベント・最終回答 実行内容とモデルの報告を取得する
実際の差分・テスト結果 依頼した作業が達成されたか検収する

プロセスの終了コードが 0 でも、依頼したテストに合格したとは限りません。
逆に、試験で意図したアクセス拒否は、コマンドの非 0 終了として現れます。サーバー側で全部を「成功」か「失敗」の一言に丸めないようにします。

時間切れやキャンセル時には、返答を打ち切るだけでなく、その実行に属する子プロセスが残っていないかも管理します。
-o で最終回答をファイルへ出す設計なら、固定ファイル名の使い回しによる、並行実行や前回結果との取り違えを避けます。

接続確認は、MCP の入口まで通して行う

まずは codex sandbox で、モデルの判断を挟まずに境界を調べられます。
次の WOUTSIDE は、秘密を含まない試験用 Git リポジトリと、その外に用意したダミーファイルへ置き換えます。事前にサンドボックス外では両方読めることを確認します。

W='/path/to/test-repository'
OUTSIDE='/path/to/outside/fake-credential.txt'

sandbox() {
  codex sandbox -p db-probe -P db-probe -C "$W" -- "$@"
}

sandbox cat ./workspace-file.txt
sandbox cat "$OUTSIDE"

私の主な実測はここまでの入口を使っています。codex sandboxcodex exec が同一のポリシー経路を通るかは未確認です。
さらに、自作 MCP サーバーを挟むと引数・環境変数・起動場所の組み立てが増えます。単体で動いた設定を、MCP 経由でも同じだとは扱えません。

次の順番で同じ確認用スクリプトを動かし、結果を比較するのが次の段階です。

段階 確かめること
codex sandbox 内外のファイル、許可・拒否宛先、直接接続の挙動
サーバーが使う起動形の codex exec 同じスクリプトが実行され、同じ許可・拒否になること
MCP クライアントからの呼び出し 入力の検証、実行条件、結果の対応付け、時間切れまで含めて意図どおりであること

Windows で提供する場合は、この試験に起動前後の対象ディレクトリの ACL と、既存の SSH 利用への影響の確認も加えます。これは今後の確認項目であり、今回の Linux の検証では実施していません。

検証コマンドが失敗を隠していないことも必要です。curl -s はエラー表示を抑え、curl ...; echo rc=$? は表示後にシェル全体の終了コードを 0 にします。
printf ... | socat ... | od ... のようなパイプラインも、通常は最後のコマンドの終了コードしか返しません。エラー表示と終了コードを残し、必要なら Bash の pipefail を使います。

私自身、/etc/hosts の出力を head -1 で切ったことを忘れ、先頭行だけで「名前解決が壊れている」と誤報しました。
MCP の結果を読みやすくするために省略する場合も、何を省略したかを残し、切った先を確認せずに合格判定しないことが必要です。

管理者設定と設定の合成も、試験条件に含める

元の sandbox 試験では --include-managed-config を付けていません。管理者要件を含む比較では、このフラグなどを使い条件をそろえる必要があります。
ローカルの /etc/codex/requirements.toml が無いことは確認しましたが、それだけではクラウド配信を含む管理者要件全体の不在は言えません。管理者要件の配信元

codex doctor --json も補助にはなりますが、0.154.0 では -p を受け付けませんでした。
実際の exec -p db-probe と同じ条件で、合成後の規則全体を実行前に読み戻す方法は、今回確認した範囲では見つかっていません。

設定のハッシュを確認するなら、プロファイルのファイルだけでなく、ベース設定、適用されるプロジェクト設定、管理者要件、CLI の版、起動条件も対象にします。
ハッシュで分かるのは検証した入力からの変化であり、安全性そのものではありません。変更時には、MCP の入口からの対照試験まで戻ります。

最初の自作版で目指すところ

私なら、まず用途を一つ、対象プロジェクトを一つに絞り、必要なファイルと接続先だけを許可します。
DB を使うなら、モデルへ渡ってよいデータだけを含むテスト DB から始めます。認証、TLS、ドライバー経由の接続、実際のテストまで通ってから、その用途を MCP ツールとして提供します。

「Codex を呼び出せた」に加えて、許可した仕事ができ、許可していない操作はできず、その結果を呼び出し元へ正しく返せることを、自作版の最初の到達点にしたいと思います。

参考資料

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?