GitHub Copilot CLIのBYOK機能でBedrock APIからOpus5を使ってみた
はじめに
AI事業本部の竹口です。
GitHub Copilot CLIを、Amazon Bedrock API経由でモデルを呼び出して使いたいと思ったことはありませんか?私はありません。
しかしながら、選択肢が多いのは良いことです。
会社のAWS課金枠・ガバナンスの中でエージェントCLIを使いたい、モデルを自分で選びたいといった需要も考えられます。
というわけで、GitHub Copilot CLI の BYOK 機能を活用して、Bedrock APIからOpus5を使ってみました。
最初に結論
GitHub Copilot CLI の BYOK 機能を使うことで、以下の状態からエージェントを動かすことができました。
- GitHubアカウントなし
- サブスクなし
- Bedrock APIキーのみ
しかしながら、Claude Opus 5 モデルを動かすには、CLI と Bedrock の通信間に proxy を立てて temperature パラメータを除去する必要がありました。
Copilot CLI の BYOK 機能とは
BYOK (Bring Your Own Key) とは、GitHub がホストするモデルルーティングの代わりに、ユーザーが指定したLLMプロバイダーを接続できる機能です。
モデルの要件として、以下が必須とされています。
- ツール呼び出しに対応している
- ストリーミングに対応している
また、以下が推奨とされています。
- 128k トークンのコンテキスト ウィンドウを持つ
サポートされているプロバイダー
公式ドキュメントでは、Azure OpenAI、Anthropic、または OpenAI 互換のエンドポイントが使用できる旨が記載されています。
| プロバイダーの種類 | 互換性のあるサービス |
|---|---|
| openai | OpenAI、Ollama、vLLM、Foundry Local、およびその他の OpenAI Chat Completions API 互換エンドポイント。 既定のプロバイダーの種類。 |
| azure | Azure OpenAI Service。 |
| anthropic | Anthropic ( Claude ) |
Bedrock は Anthropic API と互換性をもったエンドポイント(/anthropic/v1/messages)と API キー認証を提供いるため、 type=anthropic を指定することで Bedrock APIからOpus5を使用できます。
主な環境変数
Copilot CLIを開始する前に、以下の環境変数を設定する必要があります。
| 環境変数 | 要否 | 役割 |
|---|---|---|
COPILOT_PROVIDER_BASE_URL |
必須 | モデルプロバイダーの API エンドポイントのベースURL。設定すると BYOK モード発動 |
COPILOT_PROVIDER_TYPE |
任意 | openai(既定) / azure / anthropic |
COPILOT_PROVIDER_API_KEY |
任意 (認証を不要とするプロバイダーには不要) | API キー(anthropic タイプでは x-api-key として送信される) |
COPILOT_MODEL |
必須 | モデル名(内部設定用IDとワイヤ送信名を同時に設定する簡易オプション) |
COPILOT_PROVIDER_MODEL_ID / COPILOT_PROVIDER_WIRE_MODEL |
任意 | 内部設定用IDと実際に送るモデル名を分けたいとき |
やってみた
環境
- macOS (Tahoe v26.5.1)
- Amazon Bedrock (us-west-2)
- GitHub Copilot CLI (v1.0.80)
Step 1: API キーの払い出し
今回はコンソールからGUIベースで操作し、APIキーを発行します。
AWSのBedrockコンソールに移動し、サイドバー > 「モデルカタログ」 に移動

モデルカタログから目的のモデルをクリック ( 今回はClaude Opus5 )

「プレイグラウンドで開く」から会話をすることで、モデルを有効化します。


目的のモデルを有効化できたら、サイドバー > 「APIキー」に移動

短期/長期のAPIキーを生成します。
長期のAPIキーを発行する場合は、有効期限の設定を行いましょう。


Step 2: 環境変数の設定
Step 1で払い出したAPIキーを、シェル履歴に残さない方式で環境変数に設定します。
read -rs "?Bedrock APIキーを貼り付けてEnter: " BEDROCK_API_KEY && export BEDROCK_API_KEY
また、以下の環境変数を設定します。
export COPILOT_PROVIDER_TYPE=anthropic
export COPILOT_PROVIDER_BASE_URL="http://127.0.0.1:8788/anthropic" # 後述する proxy の URL
export COPILOT_PROVIDER_API_KEY="$BEDROCK_API_KEY"
export COPILOT_MODEL='<モデルID>'
※ 本検証では、モデルIDを global.anthropic.claude-opus-5 に設定しました。
Step 3: proxyを動かす
下記の proxy スクリプトを動かして、CLI ←→ Bedrock 間のリクエストを整形します。
import http from 'node:http'
import https from 'node:https'
import { URL } from 'node:url'
const REGION = process.env.REGION ?? 'us-west-2'
const DEFAULT_PORT = 8788
const TARGET = new URL(process.env.BEDROCK_TARGET ?? `https://bedrock-runtime.${REGION}.amazonaws.com`)
const PORT = Number(process.env.PROXY_PORT ?? DEFAULT_PORT)
const STRIP_KEYS = ['temperature', 'top_p', 'top_k']
// Messages API のリクエストは画像込みでも数MB程度。無制限に受けるとローカルDoSの穴になる
const MAX_BODY_BYTES = 32 * 1024 * 1024
// ホップバイホップヘッダーと、書き換えが必要なヘッダーは転送しない
const DROP_HEADERS = new Set(['host', 'connection', 'content-length', 'transfer-encoding'])
function buildForwardHeaders(reqHeaders) {
return Object.fromEntries(
Object.entries(reqHeaders).filter(([name]) => !DROP_HEADERS.has(name.toLowerCase()))
)
}
// JSON ボディから STRIP_KEYS を除いた新しいボディを返す(JSONでなければそのまま)
function stripBody(rawBody) {
if (rawBody.length === 0) return { body: rawBody, stripped: [] }
try {
const parsed = JSON.parse(rawBody.toString('utf8'))
if (parsed === null || typeof parsed !== 'object' || Array.isArray(parsed)) {
return { body: rawBody, stripped: [] }
}
const stripped = STRIP_KEYS.filter((key) => key in parsed)
if (stripped.length === 0) return { body: rawBody, stripped }
const cleaned = Object.fromEntries(
Object.entries(parsed).filter(([key]) => !STRIP_KEYS.includes(key))
)
return { body: Buffer.from(JSON.stringify(cleaned), 'utf8'), stripped }
} catch {
return { body: rawBody, stripped: [] }
}
}
const server = http.createServer((clientReq, clientRes) => {
const chunks = []
let receivedBytes = 0
let rejected = false
clientReq.on('data', (chunk) => {
receivedBytes += chunk.length
if (receivedBytes > MAX_BODY_BYTES) {
rejected = true
console.error(`[proxy] request body exceeded ${MAX_BODY_BYTES} bytes, rejected`)
clientRes.writeHead(413, { 'content-type': 'application/json' })
clientRes.end(JSON.stringify({ error: 'payload_too_large' }))
clientReq.destroy()
return
}
chunks.push(chunk)
})
clientReq.on('end', () => {
if (rejected) return
const { body, stripped } = stripBody(Buffer.concat(chunks))
const transport = TARGET.protocol === 'https:' ? https : http
const upstreamReq = transport.request(
{
protocol: TARGET.protocol,
hostname: TARGET.hostname,
port: TARGET.port || (TARGET.protocol === 'https:' ? 443 : 80),
path: clientReq.url,
method: clientReq.method,
headers: {
...buildForwardHeaders(clientReq.headers),
host: TARGET.host,
'content-length': Buffer.byteLength(body),
},
},
(upstreamRes) => {
const note = stripped.length > 0 ? ` (stripped: ${stripped.join(', ')})` : ''
console.error(
`[proxy] ${clientReq.method} ${clientReq.url} -> ${upstreamRes.statusCode}${note}`
)
clientRes.writeHead(upstreamRes.statusCode, upstreamRes.headers)
upstreamRes.pipe(clientRes) // SSE ストリーミングもそのまま素通し
}
)
upstreamReq.on('error', (error) => {
// 詳細はコンソールにのみ出す(レスポンスに載せると内部情報の露出になる)
console.error(`[proxy] upstream error: ${error.message}`)
if (clientRes.headersSent) {
clientRes.destroy()
return
}
clientRes.writeHead(502, { 'content-type': 'application/json' })
clientRes.end(JSON.stringify({ error: 'proxy_upstream_error' }))
})
upstreamReq.end(body)
})
})
server.listen(PORT, '127.0.0.1', () => {
console.error(`[proxy] listening on http://127.0.0.1:${PORT} -> ${TARGET.origin}`)
})
仮にこの proxy を挟まず直接 Base URL を設定した場合、下記のようなエラーが発生します。理由は後述。
400 `temperature` is deprecated for this model.
Step 4: copilot -p で起動
copilot -p "こんにちは。1+1は?"
すると、以下のような返答が戻ってきました。
2やで!✌️
やっほー、なんか作業あったら気軽に言うてな〜
私のGitHub Copilotの個人的な設定の影響で陽気な関西弁が戻ってきましたが、成功です。
今回の検証では -p オプションを使っていますが、対話モードでも問題なく動きます。

ハマったこと: 400 temperature is deprecated
前述した通り、proxy を挟まず Bedrock へ直接 Base URL を設定した場合、下記のようなエラーが発生しました。
400 `temperature` is deprecated for this model.
接続・認証・モデル解決は通っているものの、モデル呼び出しだけ 400 Error が発生している状態です。
結論から言うと、原因は以下の3つにありました。
- Copilot CLI は temperature:0 を送る仕様
- Opus 4.7 以降の Claude モデルは、temperature オプションが廃止されている
- CLI に temperature:0 を止める設定がない
1. Copilot CLI は temperature:0 を送る仕様
proxy なしで copilot を呼び出す際に、 --log-level debug --log-dir コマンドを使用して Debug ログを取得しました。
copilot -p "こんにちは。1+1は?" \
--log-level debug --log-dir ./verify-logs
ログを確認すると request の中に "temperature": 0 が含まれていました。
2026-08-25T11:36:04.405Z [DEBUG] Wire request: {
"model": "global.anthropic.claude-opus-5",
...
"temperature": 0
}
この問題については Copilot SDK リポジトリでも Issue として起票されていますが、Open のままです( 2026/8/25 現在 )。
2. Opus 4.7 以降の Claude モデルは、temperature オプションが廃止されている
Opus 4.7 モデルカード原文に記載されている通り、Opus 4.7 以降の Claude モデルでは temperature パラメータが廃止されています。
Starting with Claude Opus 4.7, temperature, top_p, and top_k parameters are no longer supported. The recommended migration path is to omit these parameters entirely from your requests and to use prompting to guide the model's behavior. If you previously relied on temperature = 0 for deterministic outputs, note that this setting did not guarantee identical responses across invocations.
3. CLI に temperature:0 を止める設定がない
そして現状、 GitHub Copilot CLI 側に temperature 送信を止める設定・環境変数は存在しません。
これらの理由が重なることで、 Claude Opus 5 を Bedrock API から呼び出す際には、間に JSON ボディから temperature / top_p / top_k を削除する proxy が必要になっていました。
まとめ
BYOK 機能とプロキシを活用することで、Amazon Bedrock から払い出した API を用いて GitHub Copilot CLI からエージェントを動かすことができました。
今回は Claude モデルの呼び出しを試みたため、間に余計なパラメータを除去する proxy が必要になりましたが、 DeepSeek など OpenAI 互換のエンドポイントを持つモデルであればもっと楽に呼び出しができそうです。
- GitHub Copilot CLI の BYOK 機能と Bedrock の Anthropic 互換エンドポイントによって、API キーだけで動かすことができた
- temperature 問題はプロキシで回避する必要があった
ローカルプロキシなんて一々立てたくないのでCopilot SDK には Issue 対応頑張ってほしい
この記事がどなたかの役に立ちましたら幸いです。
参考情報ソース







