Authorization Code flowを使ってMCPサーバーをAmazon Bedrock AgentCore Gatewayに接続してみた
こんにちは、せーのです。
2026 年 4 月に AWS から Connecting MCP servers to Amazon Bedrock AgentCore Gateway using Authorization Code flow というブログが出ていました。AgentCore Gateway に OAuth で守られた MCP サーバー(例: GitHub)を Authorization Code(いわゆる 3LO)でつなぐ話で、Implicit sync と schema upfront の違い、tools/list と tools/call、-32042 の URL elicitation まで、公式のデモとして一気通貫で説明されています。
本稿では、awslabs/amazon-bedrock-agentcore-samples の github-mcp-server.ipynb と同じ筋に沿って、GitHub MCP サーバー を Target に載せるところから、Inbound(Cognito の JWT)と Outbound(GitHub OAuth)の違い、tools/list / tools/call、-32042 の挙動までを、手順とセットで整理します。
つまり、何をしているのか
今回、やろうとしていることをフローにすると、下記のようになります。

GitHub MCP で GitHub 上のリソースを触るには、そのユーザーの許可が必要です。
そのため Gateway ユーザーが tools/call などで実際に GitHub へ手を伸ばす場面では、まだトークンがない/足りないときに限り、ブラウザの認可フロー(後述の -32042)が挟まり、許可された範囲でだけ GitHub MCP が動きます。
一方、利用可能なツールの一覧を見るだけの tools/list のたびに、毎回 GitHub の認可画面が出るのは現実的ではありません。
そこで ターゲット登録(Implicit sync など)の段階で、管理者が一度 GitHub 側の OAuth を完了させ、ツール定義を Gateway にキャッシュしておきます。こうしておくと、一覧の段階では **「GitHub MCP のツールがこういう名前で存在する」**と Gateway が即返せるわけです。
違いを整理すると、**一覧(カタログ)**はキャッシュで足りるが、実実行はユーザーの GitHub への認可とトークンが要る、という二段に分かれている、というイメージです。
OAuth2による認証・認可のおさらい
OAuth2 や認証・認可の本を読むと長くなるので、この記事で出てくる範囲だけに絞って整理します。
- 認証は「本人確認」、認可は「何をしていいかの許可」です。どちらもこのデモでは OAuth 2.0 の仕組みが絡みます。
- ここでは ログインが二種類出てきます。混同しやすいので、名前を分けます。
- Inbound(手前): MCP クライアントが AgentCore Gateway を呼ぶときの身分証。今回のサンプルでは Amazon Cognito が発行する JWT を
Authorization: Bearerで送ります。Gateway のauthorizerTypeはCUSTOM_JWTで、この JWT が正当かを検証します。 - Outbound(奥): Gateway が GitHub の MCP サーバーにツール実行を依頼するとき、GitHub 側が求める アクセストークン。これは Authorization Code flow で、ユーザーがブラウザで GitHub にログインして一度許可を出すことで得ます。トークンの保管や更新のイメージは AgentCore Identity / Token Vault に寄せられます。
- Inbound(手前): MCP クライアントが AgentCore Gateway を呼ぶときの身分証。今回のサンプルでは Amazon Cognito が発行する JWT を
つまり、「Gateway に入るトークン」と「GitHub に行くトークン」は別物です。Gateway の設定を触る 管理者と、実際に Gateway を叩く Gateway ユーザーは役割が分かれますが、どちらの局面でも「ブラウザで一度許可する」場面が出ます。
Authorization Code flow(3LO) は、ざっくり 「一度ブラウザを開いて、サービス(ここでは GitHub)に『この連携を許可する』と言ってもらう」 流れです。機密をアプリに直書きせず、短期のコードを経由してアクセストークンに交換します。
URL session binding は、「認証用 URL を開き始めた人」と「最後に許可ボタンを押した人」が同じかを AgentCore Identity が確認する仕組みです。誤って URL を第三者に渡したときに、別人の許可でトークンが紐づくのを防ぎます。authorization URL と session 情報には有効期限(例: 10 分) もあります(詳細は開発者ガイド参照)。
違いを整理すると、手前は Cognito JWT で Gateway に入る、奥は GitHub OAuth でツール実行の権限を取る、という二段構えのイメージです。
補足として、Gateway 作成時は MCP プロトコルバージョンに 2025-11-25 以降を指定する必要があります。これより古い設定だと、Authorization code grant(3LO) まわりのオプションが選べない、という制約があります。
やってみた
以降は、awslabs/amazon-bedrock-agentcore-samples の github-mcp-server.ipynb と同じ筋の流れです(細部は Jupyter を正とするとよいです)。コードは 理解のための抜粋なので、コピペ運用はサンプルを参照してください。
前提:GitHub App と IAM、サンプル
GitHubでは GitHub App を作成し、Client ID / Client Secret を控えます。OAuth2 Credential Provider 作成後に返る callback URL を、GitHub App の Authorization callback URL に貼ります。Advanced Settings の Expire user authorization tokens は Disable にしておくと、AgentCore Identity 側のトークン更新の挙動と噛み合わせやすいです(GitHub App 作成手順でもこの設定が推奨されています)。
IAMは開発者ガイドの Gateway 用 IAM 権限に沿ってください。検証で広い権限を付ける場合でも、本番では最小権限に寄せるのが安全です。
サンプルリポジトリを clone し、チュートリアル配下で依存関係を入れます。
git clone https://github.com/awslabs/amazon-bedrock-agentcore-samples.git
cd amazon-bedrock-agentcore-samples/01-tutorials/02-AgentCore-gateway/05-mcp-server-as-a-target/03-authorization-code-flow
pip install -r requirements.txt
boto3 は bedrock-agentcore-control を認識できるバージョン(実測では 1.42 系以降)が必要です。古いとクライアント生成でつまずきます。AWS SSO で CLI ログインしている環境では、pip install "botocore[crt]" が必要、というケースもあります。
OAuth2 Credential Provider
bedrock-agentcore-control の create_oauth2_credential_provider で、GitHub 用ベンダ(GithubOauth2)を指定して作成します。応答の callbackUrl を GitHub App に登録し、credentialProviderArn は以降の Target と IAM で参照します。
# 概念イメージ(実際はサンプルのセルどおりに)
response = agentcore_client.create_oauth2_credential_provider(
name="github-mcp-server-provider",
credentialProviderVendor="GithubOauth2",
oauth2ProviderConfigInput={
"githubOauth2ProviderConfig": {
"clientId": "<GITHUB_CLIENT_ID>",
"clientSecret": "<GITHUB_CLIENT_SECRET>",
}
},
)
callback_url = response["callbackUrl"]

Cognito、IAM ロール、Gateway 作成
Inbound 用に Cognito ユーザープールと M2M クライアント、リソースサーバー/スコープを用意します(サンプルの utils が User Pool やクライアントの作成・再利用を肩代わりします)。discovery URL は https://cognito-idp.<region>.amazonaws.com/<userPoolId>/.well-known/openid-configuration 形式です。
Gateway 作成時は authorizerType に CUSTOM_JWT を選び、customJWTAuthorizer に Cognito の discovery URL と 許可するクライアント ID を渡します。authorizerType は後から変更できないので、作り直しが発生します。プロトコルは MCP、supportedVersions に 2025-11-25 を含めます。
IAM ロールには、Identity や Secrets Manager、Gateway 自身の参照など、サンプルのヘルパーが付与するポリシーに、GetWorkloadAccessTokenForUserId や CompleteResourceTokenAuth など 3LO に必要な権限が含まれる想定です。ここでリージョンやポリシーの ARN がズレると、後段で Failed to obtain execution role credentials 系のエラーが出ることがあるので、Gateway を作るリージョンと IAM ポリシー内のリージョンを揃える、と覚えておくとよいです。
Method 1: Implicit sync(管理者が先に OAuth)
Implicit syncでは、CreateGatewayTarget 時に GitHub MCP のエンドポイントと OAuth 系の Credential Provider、return URL などを指定します。ターゲットは **CREATE_PENDING_AUTH(Needs Authorization)**になり、authorization URL が返ります。管理者がブラウザで GitHub を許可すると、ツール定義が Gateway にキャッシュされ、状態が READY になります。
サンプルでは localhost の callback サーバー(FastAPI)を立て、CompleteResourceTokenAuth までを utils.start_callback_and_open_auth 系で実行します。コンソールからターゲットを作った場合は session binding が自動、ノートブックなどコード経由では CompleteResourceTokenAuth を自分で呼ぶ必要があります。
Method 2: Schema upfront(スキーマを先に渡す)
Schema upfrontでは、mcpToolSchema.inlinePayload にツール定義(JSON)を渡します。この場合、管理者の OAuth なしでターゲットを READY にできるので、CI/CD や IaC に向きます。一方で SynchronizeGatewayTargets は使えないなど、運用上のトレードオフがあります。Method 1 と 2 は ターゲット設定の更新で切り替え可能です。
違いを整理すると、Method 1 は「作るときに管理者が一度 GitHub を許可してツール一覧を取りに行く」、Method 2 は「ツール一覧はファイルで渡し、作るときの OAuth は省略」、というイメージです。
tools/list:カタログだけなら即返る
Gateway ユーザーが Cognito のトークンを付けて tools/list を送ると、キャッシュされたツール定義が返ります。ツール名は {target名}___{ツール名}(アンダースコア 3 つ)形式でプレフィックスが付き、複数 Target を載せたときの衝突を避けます。セマンティック検索を有効にしていると、Gateway 付属の検索ツールも混ざります。
tools/call と -32042:初回は「追加情報が要る」
tools/call は実際に GitHub 側のツールを叩くので、まだ有効な GitHub アクセストークンがないと、Gateway は OAuth フローを開始します。その結果、JSON-RPC としては エラー -32042("This request requires more information.")が返り、data.elicitations に ブラウザで開く URL が入ります。これが URL elicitation(追加のユーザー操作が必要だとクライアントに伝える仕組み)です。
{
"jsonrpc": "2.0",
"id": "invoke-tool",
"error": {
"code": -32042,
"message": "This request requires more information.",
"data": {
"elicitations": [
{
"mode": "url",
"elicitationId": "<ID>",
"url": "https://bedrock-agentcore.<region>.amazonaws.com/identities/oauth2/authorize?request_uri=...",
"message": "Please login to this URL for authorization."
}
]
}
}
}
ユーザーが URL を開き GitHub で許可すると、session binding を CompleteResourceTokenAuth で完了させます(サンプルの utils.complete_session_binding)。その後、同じ tools/call を再送すると、Token Vault にキャッシュされたトークンで GitHub MCP が呼ばれ、200 で結果が返ります。
キャッシュの再利用と Force Authentication
一度トークンがキャッシュされていれば、別のツールも追加のブラウザ操作なしで呼べる、という動きを確認できます。権限変更や再ログインが必要なときは、forceAuthentication をメタデータで指定して 意図的に OAuth をやり直す、という流れもサンプルにあります。
ハマりどころ
この手順を進めるときに心当たりになりやすかった点を、圧縮して列挙します。
| 症状・論点 | 心当たり |
|---|---|
UnknownServiceError(bedrock-agentcore-control) |
boto3 / botocore が古い。新しめのバージョンへ上げる |
| SSO ログイン環境で認証まわりが不安定 | botocore[crt] を入れる |
Failed to obtain execution role credentials |
Gateway のリージョンと IAM ロールのポリシー内リージョンがズレていないか(サンプルのロール生成がセッションのデフォルトリージョンを使う場合がある) |
| authorization URL が無効 | **有効期限(例: 10 分)**内に session binding を終える |
| ツールが呼べない/別 Target と混同 | ツール名の {target名}___{ツール名} 形式を確認 |
| Method 2 で同期したい | Schema upfront では SynchronizeGatewayTargets 非対応。設計を見直す |
まとめ
AgentCore Gateway と GitHub MCP を Authorization Code flow でつないでみて感じたのは次のような点です。
- MCP の入口を一本化しつつ、Inbound(Cognito JWT) と Outbound(GitHub OAuth) を分けて考えると、全体の筋が腹に落ちる
- Method 1 / Method 2は、「管理者が先に同期するか」「スキーマを先に渡すか」 のトレードオフで、運用に合わせて選べる
tools/listはキャッシュ閲覧 、tools/callで初めて GitHub 向けトークンが要る なら-32042+ URL が返る、という段階差がはっきりする- session binding と トークンキャッシュのおかげで、毎回ブラウザ、ではなく、必要なときだけブラウザ、という体験になる
MCPの認証だけでなく認可も細かく制御しつつ、管理が複数に散らばることがない、いい方法だと思います。組織でMCPを使う方は参考にしてみてください。
参考資料
- Connecting MCP servers to Amazon Bedrock AgentCore Gateway using authorization code flow(AWS ブログ)
- Amazon Bedrock AgentCore Gateway(開発者ガイド)
- Amazon Bedrock AgentCore Identity(開発者ガイド)
- OAuth 2.0 authorization URL session binding
- Gateway の IAM 権限
- IAM のポリシー例・権限(AgentCore 全般)
- CreateGatewayTarget API リファレンス
- amazon-bedrock-agentcore-samples / authorization-code-flow(GitHub)








