Authorization Code flowを使ってMCPサーバーをAmazon Bedrock AgentCore Gatewayに接続してみた

Authorization Code flowを使ってMCPサーバーをAmazon Bedrock AgentCore Gatewayに接続してみた

2026.04.07

こんにちは、せーのです。

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/listtools/call-32042 の URL elicitation まで、公式のデモとして一気通貫で説明されています。

本稿では、awslabs/amazon-bedrock-agentcore-samplesgithub-mcp-server.ipynb と同じ筋に沿って、GitHub MCP サーバー を Target に載せるところから、Inbound(Cognito の JWT)と Outbound(GitHub OAuth)の違いtools/list / tools/call-32042 の挙動までを、手順とセットで整理します。

つまり、何をしているのか

今回、やろうとしていることをフローにすると、下記のようになります。

bedrock_agent_core_oauth_mcp_2.png

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 が発行する JWTAuthorization: Bearer で送ります。Gateway の authorizerTypeCUSTOM_JWT で、この JWT が正当かを検証します。
    • Outbound(奥): Gateway が GitHub の MCP サーバーにツール実行を依頼するとき、GitHub 側が求める アクセストークン。これは Authorization Code flow で、ユーザーがブラウザで GitHub にログインして一度許可を出すことで得ます。トークンの保管や更新のイメージは AgentCore Identity / Token Vault に寄せられます。

つまり、「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-samplesgithub-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 tokensDisable にしておくと、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

boto3bedrock-agentcore-control を認識できるバージョン(実測では 1.42 系以降)が必要です。古いとクライアント生成でつまずきます。AWS SSO で CLI ログインしている環境では、pip install "botocore[crt]" が必要、というケースもあります。

OAuth2 Credential Provider

bedrock-agentcore-controlcreate_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"]

github-callback-url-update.png

Cognito、IAM ロール、Gateway 作成

Inbound 用に Cognito ユーザープールと M2M クライアント、リソースサーバー/スコープを用意します(サンプルの utils が User Pool やクライアントの作成・再利用を肩代わりします)。discovery URLhttps://cognito-idp.<region>.amazonaws.com/<userPoolId>/.well-known/openid-configuration 形式です。

Gateway 作成時は authorizerTypeCUSTOM_JWT を選び、customJWTAuthorizerCognito の discovery URL許可するクライアント ID を渡します。authorizerType は後から変更できないので、作り直しが発生します。プロトコルは MCPsupportedVersions2025-11-25 を含めます。

IAM ロールには、Identity や Secrets Manager、Gateway 自身の参照など、サンプルのヘルパーが付与するポリシーに、GetWorkloadAccessTokenForUserIdCompleteResourceTokenAuth など 3LO に必要な権限が含まれる想定です。ここでリージョンやポリシーの ARN がズレると、後段で Failed to obtain execution role credentials 系のエラーが出ることがあるので、Gateway を作るリージョンと IAM ポリシー内のリージョンを揃える、と覚えておくとよいです。

Method 1: Implicit sync(管理者が先に OAuth)

Implicit syncでは、CreateGatewayTarget 時に GitHub MCP のエンドポイントOAuth 系の Credential Providerreturn 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 bindingCompleteResourceTokenAuth で完了させます(サンプルの utils.complete_session_binding)。その後、同じ tools/call を再送すると、Token Vault にキャッシュされたトークンで GitHub MCP が呼ばれ、200 で結果が返ります。

キャッシュの再利用と Force Authentication

一度トークンがキャッシュされていれば、別のツールも追加のブラウザ操作なしで呼べる、という動きを確認できます。権限変更や再ログインが必要なときは、forceAuthentication をメタデータで指定して 意図的に OAuth をやり直す、という流れもサンプルにあります。

ハマりどころ

この手順を進めるときに心当たりになりやすかった点を、圧縮して列挙します。

症状・論点 心当たり
UnknownServiceErrorbedrock-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を使う方は参考にしてみてください。

参考資料

この記事をシェアする

関連記事