claude-code-proxy-patterns について
Claude Code Proxy Patternsは、Claude Codeのリクエストをモデルに応じて異なるプロバイダーへ振り分けるローカルリバースプロキシのパターンを扱うスキルです。モデル別ルーティング、OAuth処理、リトライ、ヘルスチェックに加え、プロキシの設定とトラブルシューティングを説明します。
主な機能
- 01モデル名に基づくリクエスト振り分け
- 02Anthropic OAuthのパススルー
- 03Anthropic互換プロバイダーへの対応
- 04トークンのキャッシュと更新
- 05リトライと同時実行数の制限
- 06切り替えとヘルスチェックの手法
活用シーン
- 01Haikuを設定済みプロバイダーへ振り分け
- 02OAuth認証エラーの調査
- 03Anthropic互換プロバイダーの追加
- 04ローカルClaude Codeプロキシの運用
インストール
コマンドをコピーしてターミナルで実行します。
npx skills add https:/ / github.com/ terrylica/ cc-skills --skill claude-code-proxy-patternsインストール後、Claude Code で/claude-code-proxy-patternsを入力して読み込まれたか確認します。
同梱ファイル
SKILL.md と一緒にインストールされる参考資料やスクリプト。
references/anti-patterns.mdevolution-log.mdlaunchd-configuration.mdoauth-auto-refresh.mdoauth-internals.mdprovider-compatibility.mdtask-templates.md詳細
Claude Code プロキシパターン
Claude Code のモデル階層を異なるバックエンドに振り分けるマルチプロバイダープロキシです。Haiku は、より低コストで高速な Anthropic 互換プロバイダーに、Sonnet/Opus は Anthropic に振り分けます(ネイティブの OAuth パススルー)。可用性を高めるため、launchd による自動再起動機能を備えた Go バイナリプロキシと、フェイルオーバー用ラッパーが含まれています。
対象範囲:OAuth サブスクリプション(Max プラン)を利用する Claude Code 用のローカルリバースプロキシです。リクエスト本文のモデル名に基づいて振り分けます。
実装例:
- Go プロキシバイナリ:
/usr/local/bin/claude-proxy(ポート 8082)
自己進化型スキル:このスキルは使用を通じて改善されます。手順に誤りがある場合、パラメーターが変わった場合、または回避策が必要になった場合は、先送りせず直ちにこのファイルを修正してください。実際に再現可能な問題がある場合にのみ更新してください。
このスキルを使用する場面
- Claude Code のマルチプロバイダープロキシの構築やデバッグ
- OAuth サブスクリプションモードでの
ANTHROPIC_BASE_URLの設定 - Anthropic 互換プロバイダーとの統合
- プロキシ経由で発生する「OAuth not supported」や認証エラーの診断
- Claude Code における OAuth トークンの保存方法と送信方法の理解
使用しない場面:Claude API キーのみを使う構成(プロキシは不要)、MCP サーバーの開発、Claude Code のフック(API レベルではなくツールレベルで動作)、企業 HTTPS プロキシ経由の通信。
アーキテクチャ
Claude Code (OAuth/Max subscription)
|
| ANTHROPIC_BASE_URL=http://127.0.0.1:8082 (Go proxy)
| (unset ANTHROPIC_API_KEY to avoid auth conflict)
v
+----------------------------------+
| Go proxy (:8082) |
| launchd managed, auto-restart |
+----------------------------------+
|
| model =
| claude-haiku-
| 4-5-20251001
v
+-----------+
| 3rd-party |
| provider |
+-----------+
ポート設定:
:8082- Go プロキシ(エントリーポイント、launchd による管理、自動再起動)
Go プロキシは、組み込みの再試行ロジックに cenkalti/backoff/v4 を使用します。
プロキシは、各 /v1/messages リクエスト本文から model フィールドを読み取ります。設定済みの Haiku モデル ID と一致する場合、リクエストは設定済みのプロバイダーに送信されます。それ以外はすべて、OAuth パススルーを使って実際の Anthropic に送信されます。
動作パターン
WP-01:Keychain からの OAuth トークン読み取り
Claude Code がトークンを保存する macOS Keychain から OAuth トークンを読み取ります。
サービス:「Claude Code-credentials」(ハイフンの前にスペースがあることに注意) アカウント:getpass.getuser() で取得する現在のユーザー名
import subprocess, json, getpass
result = subprocess.run(
["security", "find-generic-password",
"-s", "Claude Code-credentials",
"-a", getpass.getuser(), "-w"],
capture_output=True, text=True, timeout=5, check=False,
)
if result.returncode == 0:
data = json.loads(result.stdout.strip())
oauth = data.get("claudeAiOauth")
詳しい説明は、references/oauth-internals.md を参照してください。
WP-02:トークンの JSON 構造
Keychain には、claudeAiOauth キーを含む JSON エンベロープが保存されます。
{
"claudeAiOauth": {
"accessToken": "eyJhbG...",
"refreshToken": "rt_...",
"expiresAt": 1740268800000,
"subscriptionType": "claude_pro_2025"
}
}
注:expiresAt はミリ秒単位(Unix エポック _ 1000)です。time.time() _ 1000 と比較するか、1000 で割って秒に変換してください。
WP-03:OAuth のベータヘッダー
OAuth トークン認証には、anthropic-beta: oauth-2025-04-20 ヘッダーが必須です。これがない場合、Anthropic は Bearer トークンを拒否します。
重要:既存のベータヘッダーを置き換えず、追加してください。
# proxy.py:304-308
existing_beta = original_headers.get("anthropic-beta", "")
beta_parts = [b.strip() for b in existing_beta.split(",") if b.strip()] if existing_beta else []
if "oauth-2025-04-20" not in beta_parts:
beta_parts.append("oauth-2025-04-20")
target_headers["anthropic-beta"] = ",".join(beta_parts)
WP-04:ANTHROPIC_API_KEY=proxy-managed
OAuth モードでは、ANTHROPIC_BASE_URL を設定するだけでは不十分です。ANTHROPIC_BASE_URL が適用される API キーモードに切り替えるには、Claude Code から ANTHROPIC_API_KEY も設定済みとして認識される必要があります。
# In .zshenv (managed by proxy-toggle)
export ANTHROPIC_BASE_URL="http://127.0.0.1:8082"
export ANTHROPIC_API_KEY="proxy-managed"
"proxy-managed" はダミーのセンチネル値です。プロキシはこの値をインターセプトし(324 行目)、プロバイダーには転送しません。
WP-05:TTL 付き OAuth トークンキャッシュ
Keychain のサブプロセス呼び出しを繰り返さないよう、トークンを 5 分間キャッシュします。
# proxy.py:117-118
_oauth_cache: dict = {"token": None, "expires_at": 0.0, "fetched_at": 0.0}
_OAUTH_CACHE_TTL = 300 # Re-read from Keychain every 5 minutes
キャッシュの無効化条件:
- TTL の期限切れ(前回の取得から 5 分経過)
- トークンの
expiresAtの経過 - プロキシの再起動
WP-06:認証の優先順位
Anthropic 宛てのリクエストでは、プロキシは複数の認証情報源を順番に試します。
1. REAL_ANTHROPIC_API_KEY env var -> x-api-key header (explicit config)
2. Keychain OAuth token -> Authorization: Bearer + anthropic-beta
3. ~/.claude/.credentials.json -> Authorization: Bearer (plaintext fallback)
4. Forward client Authorization -> Pass through whatever Claude Code sent
5. No auth -> Will 401 (expected)
実装については proxy.py:293-314 を参照してください。
WP-07:count_tokens エンドポイントの認証
/v1/messages/count_tokens エンドポイントには、/v1/messages と同じ認証が必要です。Claude Code は、事前のトークン数カウントにこのエンドポイントを呼び出します。ここで認証情報が欠けていると、エラーが通知されずに失敗します。このエンドポイントを実装していない Anthropic 以外のプロバイダーには、501 を返します。
WP-08:Anthropic 互換プロバイダーの URL
Anthropic の /v1/messages API 形式に対応するサードパーティプロバイダーです。
| プロバイダー | ベース URL | 備考 |
|---|---|---|
| (使用するプロバイダー) | https://<provider-host>/anthropic | 確認項目:モデルのエコー、追加のコンテンツブロック、cache_control、count_tokens |
全体の一覧は、references/provider-compatibility.md をご覧ください。
WP-09:並行処理セマフォ
プロバイダーごとのレート制限により、サードパーティ API への過剰なリクエストを防ぎます。Anthropic は独自にレート制限を処理するため、セマフォはありません。
# proxy.py:207-209
MAX_CONCURRENT_REQUESTS = int(os.getenv("MAX_CONCURRENT_REQUESTS", "5"))
haiku_semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
opus_semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
sonnet_semaphore = asyncio.Semaphore(MAX_CONCURRENT_REQUESTS)
WP-10:proxy-toggle の有効化/無効化
proxy-toggle スクリプトは、.zshenv のエントリーとフラグファイルをアトミックに管理します。
~/.claude/bin/proxy-toggle enable # Adds env vars, creates flag file, checks health
~/.claude/bin/proxy-toggle disable # Removes env vars, removes flag file
~/.claude/bin/proxy-toggle status # Shows routing flag, proxy process, .zshenv state
重要:切り替え後は Claude Code を再起動する必要があります。ANTHROPIC_BASE_URL は起動時に読み込まれるためです。
WP-11:ヘルスエンドポイント
/health エンドポイントは、監視用にプロバイダーの設定状態を返します。
curl -s http://127.0.0.1:8082/health | jq .
WP-12:リトライ機能付き Go プロキシ
cenkalti/backoff/v4 を使用したリトライ機能を内蔵する Go プロキシです(指数バックオフ:500ms → 1s → 2s、経過時間の上限は 5s)。
import "github.com/cenkalti/backoff/v4"
backoffConfig := backoff.NewExponentialBackOff(
backoff.WithInitialInterval(500 * time.Millisecond),
backoff.WithMultiplier(2),
backoff.WithMaxInterval(2 * time.Second),
backoff.WithMaxElapsedTime(5 * time.Second),
)
err := backoff.Retry(operation, backoffConfig)
場所:/usr/local/bin/claude-proxy | 環境:.zshenv 内の ANTHROPIC_BASE_URL=http://127.0.0.1:8082
WP-13:Launchd サービスの設定
Go プロキシは macOS の launchd デーモンとして実行され、クラッシュ時の自動再起動と起動時の永続化に対応します。
Plist:/Library/LaunchDaemons/com.example.claude-proxy.plist
Plist の設定全体、コマンド、検証チェックリスト、デバッグについては、references/launchd-configuration.md をご覧ください。
WP-14:OAuth トークンの自動更新
バックグラウンドの goroutine が 30 分ごとに、トークンの有効期限が切れる 5 分前に OAuth トークンを更新します。API による更新に失敗した場合は、Keychain にフォールバックします。
実装全体と更新ロジックについては、references/oauth-auto-refresh.md をご覧ください。
アンチパターンの概要
コード例を含む詳細については、references/anti-patterns.md を参照してください。
| ID | 重大度 | 問題点 | 対処方法 |
|---|---|---|---|
| CCP-01 | HIGH | ANTHROPIC_BASE_URL のみで、ANTHROPIC_API_KEY が設定されていない | ANTHROPIC_API_KEY=proxy-managed を設定する |
| CCP-02 | HIGH | anthropic-beta: oauth-2025-04-20 ヘッダーがない | 既存のベータヘッダーに追加する |
| CCP-03 | MEDIUM | /api/oauth/claude_cli/create_api_key エンドポイントを使用している | org:create_api_key スコープが必要(ユーザーが持つのは user:inference のみ) |
| CCP-04 | HIGH | キーチェーンのサービス名に小文字の "claude-code-credentials" を使用している | 実際の名前にはスペースが含まれます:"Claude Code-credentials" |
| CCP-05 | MEDIUM | ~/.claude/.credentials.json をプライマリとして読み込んでいる | キーチェーンが唯一の信頼できる情報源(SSoT)です。認証情報ファイルは古いフォールバックです |
| CCP-06 | HIGH | OAuth トークンをハードコードしている | トークンは期限切れになるため、キャッシュを使って動的に読み込む |
| CCP-07 | HIGH | プロキシ/フックで gh auth token を使用している | プロセスストーム(再帰的な起動)が発生する |
| CCP-08 | HIGH | OAuth トークンがある状態で、環境変数に ANTHROPIC_API_KEY が設定されている | Claude Code で認証の競合に関する警告が表示されます。設定を解除してください |
| CCP-09 | MEDIUM | 対応していないプロバイダーに cache_control パラメーターを送信している | プロバイダーに拒否されるため、allowedParams から削除する |
| CCP-10 | MEDIUM | プロキシの実行中に ANTHROPIC_API_KEY に実際のキーを設定している | プロキシがすべてのプロバイダーに転送するため、キーが漏えいする |
| CCP-11 | MEDIUM | /v1/messages/count_tokens を処理していない | 事前リクエストで認証エラーが発生する |
| CCP-12 | LOW | プロキシを 0.0.0.0 で実行している | セキュリティのため、127.0.0.1 にバインドする |
TodoWrite タスクテンプレート
セットアップ、プロバイダーの追加、診断、無効化のテンプレートについては、references/task-templates.md を参照してください。
リファレンス実装
稼働中の本番デプロイ(Go プロキシがプライマリ):
| ファイル | 用途 |
|---|---|
/usr/local/bin/claude-proxy | Go プロキシのバイナリ(約960行) |
~/.claude/tools/claude-code-proxy-go/main.go | Go プロキシのソース |
~/.claude/tools/claude-code-proxy-go/oauth_refresh.go | OAuth の自動更新(80行) |
~/.claude/tools/claude-code-proxy-go/.env | プロバイダー設定(chmod 600) |
/Library/LaunchDaemons/com.example.claude-proxy.plist | launchd の設定 |
~/.zshenv | 環境変数(ANTHROPIC_BASE_URL) |
変更後のチェックリスト
このスキルを変更した後、次を確認してください。
- アンチパターンの表が references/anti-patterns.md と一致している
- 動作するパターンを proxy.py のソースと照合して検証している
- 例に OAuth トークンがハードコードされていない
- ベータヘッダーのバージョンが最新である(
oauth-2025-04-20) - すべての内部リンクが相対パス(
./references/...)を使用している - リンク検証ツールに合格している
- スキル検証ツールに合格している
- 変更内容を references/evolution-log.md に追記している
トラブルシューティング
| 問題 | 原因 | 解決策 |
|---|---|---|
| Claude Code が ANTHROPIC_BASE_URL を無視する | ANTHROPIC_API_KEY が設定されていない(CCP-01) | .zshenv で ANTHROPIC_API_KEY=proxy-managed を設定する |
| Anthropic から 401 Unauthorized が返される | anthropic-beta ヘッダーがない(CCP-02) | プロキシが oauth-2025-04-20 を追加することを確認する |
| Keychain の読み取り結果が空になる | サービス名が誤っている(CCP-04) | "Claude Code-credentials"(スペースあり)を使用する |
| プロキシが実際の API キーを転送する | ANTHROPIC_API_KEY に実際のキーが設定されている(CCP-10) | proxy-managed のセンチネル値を使用する |
| count_tokens の認証に失敗する | エンドポイントハンドラーがない(CCP-11) | プロキシが /v1/messages/count_tokens を処理する必要があります |
| ネットワークからプロキシにアクセスできる | 0.0.0.0 にバインドされている(CCP-12) | 127.0.0.1 のみにバインドする |
| 有効化時にプロセスが大量に起動する | フック内の gh auth トークン(CCP-07) | フック/認証情報ヘルパーから gh CLI を呼び出さない |
| 5 分後にトークンの有効期限が切れる | キャッシュ TTL(WP-05) | 通常の動作です。プロキシが Keychain から再読み込みします |
| Claude Code で認証の競合に関する警告が表示される | ANTHROPIC_API_KEY が設定されている(CCP-08) | .zshenv で ANTHROPIC_API_KEY の設定を解除する |
| cache_control.ephemeral.scope エラー | プロバイダーが cache_control に対応していない(CCP-09) | allowedParams から cache_control を削除する |
実行後の振り返り
このスキルの実行後、終了する前に以下を確認してください。
- コマンドは成功しましたか? — 成功しなかった場合は、失敗の原因となった指示またはエラーテーブルを修正します。
- パラメーターや出力に変更はありましたか? — 基盤となるツールのインターフェイスに変更があった場合は、それに合わせて使用例とパラメーターテーブルを更新します。
- 回避策が必要でしたか? — 工夫が必要だった場合(異なるフラグや追加手順など)は、次回の実行で同じ回避策が不要になるよう、この SKILL.md を更新します。
実際に再現可能な問題がある場合に限り、更新してください。推測で更新しないでください。