APIMゲートウェイ経由でClaude Code・Codex・Continueを接続する
社内のAzure API Management(APIM)や企業向けLLMゲートウェイを経由してAIコーディングツールを使いたいとき、「どの設定ファイルをどう書けばいいか」が意外とわかりにくい。Claude Code、OpenAI Codex、Continue.devはそれぞれ要求するAPI形式が異なるため、同じゲートウェイでも設定方法が変わってくる。
この記事では、APIゲートウェイとの疎通確認から始まり、3つのツールそれぞれの設定方法までを順番に説明する。
Azure API Managementとは
そもそも Azure API Management(APIM)は何をするものか、を最初に整理しておく。
APIMはMicrosoftが提供するAPIゲートウェイサービスだ。クライアントからのリクエストを直接バックエンドのAIモデルに届けるのではなく、その間に入って認証・流量制御・ルーティングを担う。
[クライアント(Claude Code等)]
↓
[Azure API Management] ← ここで subscription key 検証・quota管理・routing
↓
[バックエンドモデル(Azure OpenAI / Anthropic互換等)]
企業がAPIMを挟む理由は主に以下だ。
- APIキーの一元管理: 開発者ごとに個別のsubscription keyを発行し、バックエンドのキーを直接渡さなくて済む
- Quota / Rate Limiting: ユーザーやProductごとにトークン消費・リクエスト数を制限できる
- ルーティング: 1つのAPIMホストの下に複数のバックエンド(Anthropic互換・OpenAI互換等)を束ねられる
- Policy: ヘッダ変換・キャッシュ・ログ記録などをAPIM側で管理できる
今回のように「社内APIMを経由してAIツールを使う」という構成では、APIMがキーの入り口になる。バックエンドモデルへの直接アクセスは開発者には渡されず、APIMのsubscription keyだけが配布される。
前提と全体像
今回扱う構成は以下のようなものだ。
[Claude Code / Codex / Continue]
↓ 環境変数 / 設定ファイル
[Azure API Management(APIM)または社内LLMゲートウェイ]
├─ /anthropic-compatible/v1/messages ← Claude Code用
└─ /openai/v1/responses ← Codex用
↓
[バックエンドモデル]
ひとつ理解しておくべき重要な事実がある。Claude Code・Codex・Continueは要求するAPI形式が異なるため、同じゲートウェイホストを使っていても、ツールごとに別のエンドポイントルートが必要になる。
| ツール | 要求API形式 | 設定ファイル |
|---|---|---|
| Claude Code | Anthropic Messages API | ~/.claude/settings.json |
| OpenAI Codex | OpenAI Responses API | ~/.codex/config.toml |
| Continue.dev | OpenAI Chat Completions互換(推奨) | ~/.continue/config.yaml |
「同じゲートウェイだから同じ設定で動くはず」という思い込みが、最初のつまずきになりやすい。
Step 1: ゲートウェイへの疎通確認
ツールの設定を触る前に、まずcurlでゲートウェイに直接疎通できるか確認する。この段階で問題を切り分けておくと、後の作業がかなり楽になる。
GETではなくPOSTで叩く
Anthropic Messages APIの /v1/messages はPOSTエンドポイントだ。最初にGETで叩くと404が返ってくるが、これはAPIが存在しないのではなく、メソッドが違うだけのことが多い。
$env:APIM_KEY = "<your-key>"
curl.exe -v `
-X POST "https://<apim-host>/<base-path>/v1/messages" `
-H "Content-Type: application/json" `
-H "anthropic-version: 2023-06-01" `
-H "x-api-key: $env:APIM_KEY" `
--json '{
"model": "<model-name>",
"max_tokens": 64,
"messages": [{ "role": "user", "content": "Say hello" }]
}'
認証ヘッダ名の確認方法
APIMで401が返ってきたとき、どのヘッダ名が必要かを探すには WWW-Authenticate レスポンスヘッダを見る。これはあまり知られていないが、APIMはこのヘッダに正しいキー名を明示してくれる。
HTTP/1.1 401 Access Denied
WWW-Authenticate: AzureApiManagementKey realm="...", name="x-api-key", type="header"
name="x-api-key" がそれだ。APIMのsubscription keyヘッダ名は既定では Ocp-Apim-Subscription-Key だが、API単位で変更できる。上の例では x-api-key に変更されている。ドキュメントを読んで既定値を送っても通らないのはこのためだ。
さらに注意が必要なのは、同じAPIMホストでもAPIルートごとにヘッダ名が異なる場合がある点だ。たとえば実際の検証では以下のような違いがあった。
| APIルート | 認証ヘッダ名 |
|---|---|
| Anthropic互換(Claude Code用) | x-api-key |
| Azure OpenAI Responses互換(Codex用) | api-key |
どちらのルートを叩くかによって送るヘッダ名が変わる。疎通確認は必ずルートごとに行うこと。
レスポンスコード別の対処
| HTTPステータス | 意味 | 対処 |
|---|---|---|
200 | 全て通過 | 次のステップへ |
401 | 認証失敗 | WWW-Authenticate のヘッダ名を確認 |
403 Access Denied | Productへの紐付けがない等 | APIM Product / Subscription の設定を確認 |
403 Quota Exceeded | クォータ超過(設定ミスではない) | Retry-After ヘッダの秒数待ってリトライ |
404 | パスが存在しない | APIM側のAPI route・operationを確認 |
400 | リクエスト不正 | JSONの形式・model名・BOM問題を確認 |
400 DeploymentNotFound | model名がバックエンドと不一致 | APIMに登録されているdeployment名を確認 |
502/500 | バックエンドエラー | APIMのtrace機能で確認 |
403 Quota Exceeded は認証や設定の失敗ではない。APIMのquota policyによってリクエスト上限に達した状態で、しばらく待てば解消する。Retry-After ヘッダに待機秒数が入っている。Codexは再接続を複数回試みるため、curl単発より消費が早い点に注意。
DeploymentNotFound は認証は通っているがリクエストbodyの model フィールドがバックエンドに登録されたdeployment名と一致していない場合に返る。モデル名の形式(gpt-5・gpt-5.2・gpt-5.2-codex 等)はAPIMの設定次第なので、管理者に確認するか curl で候補を総当たりする。
PowerShell 5.1 のBOM問題
Windows PowerShell 5.1 では Set-Content がBOM付きUTF-8で書き出す。JSONの先頭が { ではなく不可視の EF BB BF になり、バックエンドが unexpected character at char 0 で400を返す。
回避策は以下の通り。
[System.IO.File]::WriteAllText(
"$PWD\body.json",
$jsonString,
[System.Text.UTF8Encoding]::new($false) # $false = BOMなし
)
PowerShell 7(pwsh)を使っている場合はデフォルトでBOMなしUTF-8になるため、この問題は発生しない。
以降はCase A〜Cで構成されている。 使うツールに対応するCaseだけを参照すればよい。すべてを順番に実施する必要はない。
Case A: Claude Codeの設定
Claude Code をAPIM経由で使う設定。curlで200が確認できたら、以下のパラメータを渡す。
設定ファイルの場所
| 用途 | パス |
|---|---|
| CLI・VS Code拡張共通 | %USERPROFILE%\.claude\settings.json |
| VS Code拡張が読まない場合 | %APPDATA%\Code\User\settings.json |
settings.json の書き方
{
"env": {
"ANTHROPIC_API_KEY": "dummy",
"ANTHROPIC_BASE_URL": "https://<apim-host>/<base-path>",
"ANTHROPIC_MODEL": "<model-name>",
"ANTHROPIC_DEFAULT_OPUS_MODEL": "<model-name>",
"ANTHROPIC_DEFAULT_SONNET_MODEL": "<model-name>",
"CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS": "1",
"DISABLE_PROMPT_CACHING": "1"
}
}
ANTHROPIC_API_KEY に "dummy" を入れているのは、値が空だとClaude CodeがAPIキー未設定として起動を止めてしまうためだ。実際の認証はゲートウェイのカスタムヘッダが担う。
認証ヘッダは環境変数で管理する
ANTHROPIC_CUSTOM_HEADERS にAPIキーの値が入るため、settings.jsonには書かない。Windowsのユーザー環境変数として設定する。
[Environment]::SetEnvironmentVariable(
"ANTHROPIC_CUSTOM_HEADERS",
"x-api-key: <your-key>",
"User"
)
BASE_URLに /v1/messages を含めない
ここは間違えやすい。Claude Codeは内部で /v1/messages を付加するため、BASE_URLに含めると二重パスになる。
# 正しい
ANTHROPIC_BASE_URL = "https://<apim-host>/anthropicxapi"
# 誤り(/anthropicxapi/v1/messages/v1/messages になる)
ANTHROPIC_BASE_URL = "https://<apim-host>/anthropicxapi/v1/messages"
VS Code拡張版について
VS Code拡張版は ~/.claude/settings.json の env を読まず、Anthropicログインを要求してくるという報告がある(GitHub issue #11784)。この場合は %APPDATA%\Code\User\settings.json の claudeCode.environmentVariables に同じ変数を設定することで回避できる場合がある。
Microsoft Foundryを使う環境であれば、CLAUDE_CODE_USE_FOUNDRY=1 を使う公式手順の方が安定する。
動作確認
claude -p "Say hello"
Case B: OpenAI Codexの設定
OpenAI Codex をAPIM経由で使う設定。CodexはClaude Codeとは別物で、Anthropic Messages APIを解釈できない。APIM側にOpenAI Responses API互換のエンドポイントルートが別途必要になる。
設定ファイルの場所
%USERPROFILE%\.codex\config.toml
config.toml の書き方
Codexには認証ヘッダの渡し方が2種類ある。用途に応じて使い分ける。
パターンA: http_headers(シェル展開が使える環境)
model = "<model-name>"
model_provider = "apim"
model_reasoning_effort = "medium"
[model_providers.apim]
name = “APIM OpenAI Compatible” base_url = “https://<apim-host>/<openai-path>/v1” wire_api = “responses” http_headers = { “api-key” = “${APIM_KEY}” }
${APIM_KEY} はCodexがシェル経由で環境変数展開する。CLIで直接起動する場合に使う。
パターンB: env_http_headers(VS Code拡張など、シェル展開が使えない環境)
[model_providers.apim]
env_http_headers = { "api-key" = "APIM_KEY" }
重要: env_http_headers の値には $ なし・クォートなしの環境変数名そのものを書く。ランタイムがプロセスの環境変数から参照して展開する。"${APIM_KEY}" や "$APIM_KEY" と書くと展開されず文字列がそのまま送られるため注意。
いくつかの注意点がある。
base_url に /v1 を含める: CodexはBASE_URLに /v1 を含めることを前提としている。Claude Codeとは逆の挙動なので混同しやすい。
wire_api = "responses" の適用条件: CodexはデフォルトでOpenAI Responses API(/v1/responses)を使う。APIMのバックエンドがResponses APIに対応していない場合は404や400が返る。バックエンドがChat Completions APIのみ対応している場合は wire_api = "openai" を試す。Azure OpenAI ServiceのResponses API対応状況はバージョンによって異なる。
環境変数の設定
$env:APIM_KEY = "<your-key>"
Codex IDE extensionのprofilesについて
Codex CLIは config.toml に複数の [model_providers.*] を定義してプロファイルを切り替えられる。しかしCodex IDE extension(VS Code拡張)は現時点でprofilesに対応していない。拡張はtop-levelの設定(model と model_provider)を固定で読む。
複数環境を切り替えたい場合は、config.toml ごと差し替えるスクリプト方式を使う(後述)。
Case C: Continue.devの設定
Continue.dev をAPIM経由で使う設定。Continueは3つの中で最も設定の自由度が高い。複数のモデルを1つのconfig.yamlに並べてUIで切り替えられるため、「社内APIMモデルとローカルOllamaを混在させる」といった構成が作りやすい。
設定ファイルの場所
%USERPROFILE%\.continue\config.yaml
config.yaml の書き方(APIM + Ollama混在)
name: Mixed Config
version: 1.0.0
schema: v1
models:
- name: APIM GPT Chat
provider: openai
model: <model-name>
apiBase: https://<apim-host>/<openai-path>
apiKey: ${env:APIM_KEY} # Continue内部の認証フロー用(providerが参照)
requestOptions:
headers:
api-key: ${env:APIM_KEY} # APIMへの実際のHTTPヘッダとして付与
query:
api-version: 2025-03-01-preview
roles:
- chat
- edit
- apply
- name: Local Ollama Autocomplete
provider: ollama
model: qwen2.5-coder:7b
apiBase: http://<ollama-host>:11434/
roles:
- autocomplete
apiKey と requestOptions.headers の両方にキーを書く理由:apiKey はContinueの内部認証フローでproviderが参照する値であり、requestOptions.headers はAPIMへのHTTPリクエストに実際に追加されるヘッダだ。APIMのsubscription keyは後者のヘッダ名(api-key 等)で送る必要があるため、両方の設定が必要になる。
${env:VARNAME} 構文について
config.yaml で ${env:APIM_KEY} と書くと、ContinueがそのOS環境変数の値に展開してくれる。PowerShellの $env:APIM_KEY とは別物で、config.yaml 内の専用構文だ。
# これが Continue の環境変数参照構文
apiKey: ${env:APIM_KEY}
# PowerShell変数(config.yamlには書かない)
$env:APIM_KEY = "..."
APIキーをconfig.yamlにハードコードしたくない場合は、事前にOS環境変数として設定しておいてこの構文で参照する。ユーザー永続環境変数として設定する場合は以下。
[Environment]::SetEnvironmentVariable("APIM_KEY", "<your-key>", "User")
Responses APIのみのエンドポイントについて
APIMがResponses API専用のエンドポイントしか持っていない場合、ContinueがそのAPIを直接扱えるかはバージョン依存になる。Chat Completions互換のルートをAPIM側に用意しておく方が、Continueとの相性は確実によくなる。
VS Code拡張固有のコツ
ターミナルのCLIで動いても、VS Code拡張では別の話になることが多い。それぞれ注意点がある。
Case A: Claude Code拡張
~/.claude/settings.json の env は拡張では読まれないことがある
CLIは ~/.claude/settings.json の env ブロックを参照するが、VS Code拡張は拡張自身の設定ファイルを見に行く場合がある。その場合は %APPDATA%\Code\User\settings.json に claudeCode.environmentVariables を追加する。
"claudeCode.environmentVariables": [
{ "name": "ANTHROPIC_API_KEY", "value": "dummy" },
{ "name": "ANTHROPIC_BASE_URL", "value": "https://<apim-host>/<base-path>" },
{ "name": "ANTHROPIC_CUSTOM_HEADERS", "value": "x-api-key: <your-key>" },
{ "name": "ANTHROPIC_MODEL", "value": "<model-name>" },
{ "name": "ANTHROPIC_DEFAULT_OPUS_MODEL", "value": "<model-name>" },
{ "name": "ANTHROPIC_DEFAULT_SONNET_MODEL", "value": "<model-name>" },
{ "name": "CLAUDE_CODE_DISABLE_EXPERIMENTAL_BETAS", "value": "1" },
{ "name": "DISABLE_PROMPT_CACHING", "value": "1" }
]
設定後は VS Code を完全に再起動する
設定ファイルを保存しただけでは拡張に反映されないことがある。VS Codeをいったん終了して起動し直す。
VS Codeターミナルの環境変数と settings.json の env は別物
VS Codeターミナル内で $env:ANTHROPIC_BASE_URL が見えていても、それはそのPowerShellプロセスの変数であり、~/.claude/settings.json の env とは異なる。CLIが通るのに拡張だけ失敗する場合はここを疑う。なお、Windowsのユーザー環境変数として登録済みの変数([Environment]::SetEnvironmentVariable(..., "User") で設定したもの)はVS Code拡張プロセスでも参照できる。確認コマンド:
Get-ChildItem Env:ANTHROPIC* | Format-Table -AutoSize
Case B: Codex拡張
profilesは使えない → top-levelに直書きする
Codex CLIはprofilesで接続先を切り替えられるが、VS Code拡張はprofilesに対応していない。拡張は ~/.codex/config.toml のtop-levelにある model と model_provider を固定で読む。複数環境を切り替えたい場合は config.toml ファイルごと差し替える方式にする。
# 拡張向けにtop-levelに置く(profilesブロックは拡張では無視される)
model = "gpt-5.2"
model_provider = "apim"
[model_providers.apim]
base_url = “https://<apim-host>/<openai-path>” wire_api = “responses” query_params = { “api-version” = “2025-03-01-preview” } env_http_headers = { “api-key” = “APIM_KEY” } request_max_retries = 1
拡張のモデルドロップダウンを触らない
拡張UIのモデル選択ドロップダウンに表示される GPT-5.5・GPT-5.4 などはAPIMのdeployment名と一致しない可能性が高い。config.tomlで指定したモデルが使われるよう、ドロップダウンは変更しない。
gpt-5-codex は拡張で使わない(gpt-5.2 推奨)
2026年5月時点の挙動として、gpt-5-codex を拡張で使うと image_generation ツールが自動的に混入してクラッシュすることがある。CLI(codex exec)では gpt-5.2-codex が安定しているが、VS Code拡張では gpt-5.2 か gpt-5 の方が安全。APIまたはCLIのバージョンアップにより変わる可能性がある。
MCPサーバーを最小化する
Codex拡張の初期動作確認中は、MCPサーバーを最小限にする。不要なMCPがあるとタイムアウトやクォータ消費が増える。
# 確認が取れるまでコメントアウト推奨
# [mcp_servers.drawio]
# command = "npx"
# args = ["-y", "@drawio/mcp"]
request_max_retries = 1 でクォータ消費を抑える
Codexはデフォルトで複数回リトライする。APIMのquota policyにかかりやすい環境では request_max_retries = 1 を設定しておくと安全。
web_search = "disabled" で不要なAPI呼び出しを止める
web_search = "disabled"
[features].web_search = false は旧形式で非推奨。top-levelまたはprofile配下に上記の形式で置く。
設定のまとめ
3ツールの違いをまとめると以下のようになる。
| 項目 | Claude Code | Codex CLI | Codex拡張 | Continue.dev |
|---|---|---|---|---|
| 設定ファイル | ~/.claude/settings.json | ~/.codex/config.toml | ~/.codex/config.toml(top-level) | ~/.continue/config.yaml |
| API形式 | Anthropic Messages | OpenAI Responses | OpenAI Responses | Chat Completions互換 |
BASE_URLに /v1 | 含めない | 含める | 含める | providerによる |
| 認証ヘッダ設定 | ANTHROPIC_CUSTOM_HEADERS 環境変数 | http_headers(${VAR} 展開) | env_http_headers(変数名を値に) | requestOptions.headers |
| 複数モデル管理 | config.jsonコピー方式 | CLIはprofiles対応あり | profiles非対応(top-level固定) | config.yamlに並べてUI選択 |
セキュリティ上の注意
APIキーをコマンド引数やチャットに貼らない。APIMのログやチャット履歴にそのまま残る。ターミナルで確認する際は環境変数経由で渡す。また curl.exe -v はリクエストヘッダ(キー含む)をターミナルに平文出力するため、そのターミナルのスクリーンショットや出力を共有しないこと。
# NG: キーが直接残る
curl.exe -H "x-api-key: abc123def456..."
# OK: 環境変数経由
$env:APIM_KEY = "<key>"
curl.exe -H "x-api-key: $env:APIM_KEY"
もしキーがログに出てしまった場合は、即座にAPIM側でローテーションする。
settings.jsonやconfig.tomlをGit管理ディレクトリに置かない。キーを含む設定ファイルが誤ってリポジトリに入るリスクを避けるため、ホームディレクトリ(~/)に置くのが基本だ。特に claudeCode.environmentVariables にキー値を直書きした場合、その settings.json をGit管理すると即座にキーが漏洩する。キーは [Environment]::SetEnvironmentVariable でOS環境変数に登録する方が安全。
動作確認コマンド
# Claude Code CLI
claude -p "Say hello"
# Codex CLI(CLIバージョン)
codex "say hello in one sentence"
# Continue.dev: VS Code上のContinueパネルからチャット欄に "Say hello" と送信して応答を確認
まとめ
社内APIゲートウェイ経由でAIコーディングツールを動かすとき、つまずきやすいポイントを粒度別に整理する。
大きな前提として押さえておくこと:
- APIMは認証・quota管理・ルーティングを担うゲートウェイ。バックエンドへの直接アクセスは渡されず、subscription keyだけが窓口になる
- Claude Code・Codex・ContinueはAPI形式が異なる。同じAPIMホストでも、ツールごとに別のエンドポイントルートが必要になる
細かく気にしないといけないポイント:
- 認証ヘッダ名はAPIルートごとに違う — 401が出たら
WWW-Authenticateのname=を読む。同一ホストでも Claude Code用とCodex用でヘッダ名が違うことがある ANTHROPIC_BASE_URLには/v1/messagesを含めない — Claude Code が内部で付加するため二重パスになる。Codex のbase_urlは逆に/v1を含める必要がある403 Quota Exceededは設定ミスではない — APIキーや認証の問題ではなくquota超過。Retry-Afterの秒数だけ待つDeploymentNotFoundはmodel名の問題 — 認証は通っているが body のmodelフィールドがAPIMのdeployment名と一致していない- Continueの
${env:VAR}はOS環境変数を参照するconfig.yaml専用構文 — PowerShellの$env:とは別物 - Codex IDE extensionはprofiles非対応 — CLIではprofileで切り替えられるが、VS Code拡張はtop-levelの設定を固定で読む
curlで疎通確認(Step 1)を先に行い、その後は使うツールのCase(A/B/C)だけを参照する。複数ツールを検証する場合は Claude Code → Codex → Continue の順で進めると問題の切り分けがしやすい。
コメント