# Claude CodeをCognito認証のMCPサーバーに接続する — 事前登録したクライアントIDとheadersHelperの2通り

> Amazon Cognitoは動的クライアント登録に対応していない。その背後にあるBedrock AgentCore RuntimeのMCPサーバーへ、Claude Codeから接続する2つの方法を解説する。

- Source: https://oharu121.com/ja/blog/claude-code-cognito-mcp-oauth-client-id-headershelper/
- Published: 2026-10-08T01:22:06+09:00
- Tags: Claude Code, MCP, AgentCore, Amazon Cognito

---
## はじめに

Bedrock AgentCore Runtime 上で MCP サーバーを動かし、インバウンド認証を Amazon Cognito に任せていました。これを Claude Code に登録して、`/mcp` → Authenticate からブラウザでサインインできるようにしたいと考えました。

オプションなしの `claude mcp add` は、ブラウザが開く前に `Incompatible auth server: does not support dynamic client registration` で止まります。Cognito のアクセストークンをヘッダーとして貼り付ければ接続はできます。ただしトークンの期限（デフォルトで 1 時間）が切れると、サーバーは `Token has expired` を返すようになります。

Claude Code には OAuth の経路が 2 つ、ヘッダーの経路が 2 つあり、**Cognito で使えるのはそのうちの 2 つ、事前登録したアプリクライアントの ID と `headersHelper` です**。事前登録したクライアント ID を使えばブラウザでサインインでき、トークンの更新は Claude Code が担います。`headersHelper` は接続のたびにスクリプトで新しいトークンを発行します。どちらも、トークンが切れるたびに登録し直す必要はありません。

## Claude Code がリモート MCP サーバーに認証する 4 つの方法

Claude Code の HTTP MCP サーバーの認証方法は 4 つあり、**誰がトークンを取得するか**で 2 つに分かれます。OAuth の 2 経路では、Claude Code 自身がブラウザのフローを実行し、トークンを保存します。ヘッダーの 2 経路では、Claude Code の外にある何かがトークンを用意し、Claude Code はそれを転送するだけです。

*Figure — AuthRoutes: 登録エンドポイントが必要なのはデフォルトの OAuth 経路だけで、Cognito に欠けているのはその 1 点です。*

| 経路 | 設定 | ブラウザでのサインイン | トークンの期限切れ時 | Cognito で使えるか |
| --- | --- | --- | --- | --- |
| OAuth、動的クライアント登録 | なし（デフォルト） | あり | Claude Code が更新する | いいえ |
| OAuth、事前登録したクライアント | `oauth.clientId`、`oauth.callbackPort` | あり | Claude Code が更新する | **はい** |
| 静的ヘッダー | `headers.Authorization` | なし | 差し替えるまで接続に失敗する | トークン 1 本の有効期間だけ使える |
| `headersHelper` | ヘッダーを出力するコマンド | なし | コマンドが再実行される | **はい** |

これらの経路は組み合わせられません。Claude Code のドキュメントには、`headers.Authorization` を設定すると OAuth にはフォールバックしない（「does not fall back to OAuth」）と書かれています。そのためヘッダーが拒否されると、サインインが必要なサーバーとしてではなく、接続に失敗したサーバーとして表示されます。

## デフォルトの OAuth フローが Cognito で止まる理由

**動的クライアント登録**（RFC 7591）は、OAuth クライアントが認可サーバーの `registration_endpoint` に POST して、自分のクライアント ID を作成する仕組みです。Claude Code はデフォルトでこれを使います。公開 MCP サーバーなら URL だけで接続できるのはそのためです。

ディスカバリー自体は AgentCore に対して問題なく動きます。トークンなしのリクエストには 401 が返り、その `WWW-Authenticate` ヘッダーが Runtime の RFC 9728 保護リソースメタデータを指しています。そのメタデータには、認可サーバーとして Cognito ユーザープールが記載されています。フローが止まるのは、その先で Cognito のメタデータを取得したときです。

```bash
curl -s https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX/.well-known/openid-configuration | jq '{registration_endpoint, code_challenge_methods_supported, scopes_supported}'
# {"registration_endpoint":null,"code_challenge_methods_supported":null,"scopes_supported":["openid","email","phone","profile"]}
```

`registration_endpoint` がないので、Claude Code にはクライアント ID を取得する手段がありません。そのため、ブラウザを開く前に処理を打ち切ります。

```text
✘ Failed to connect — Incompatible auth server: does not support dynamic client registration
```

*Figure — DiscoveryFlow: Cognito のメタデータを読むまでは、どの手順も成功します。止まるのは、POST 先の登録エンドポイントがそこにないためです。*

また、Cognito は RFC 8414 のパス `/.well-known/oauth-authorization-server` には 400 を返し、提供しているのは OpenID Connect の `openid-configuration` ドキュメントだけです。これは後述する事前登録の経路で効いてきます。

いちばん手早い回避策は静的ヘッダーです。接続はできますが、保てるのはトークン 1 本の期限が切れるまでで、期限が切れると次のように表示されます。

```text
Status: ✘ Failed to connect
Issue: Server rejected the configured Authorization header (HTTP 401). … Error detail: {"jsonrpc":"2.0","error":{"code":-32001,"message":"Token has expired"}}
```

## 事前登録した Cognito アプリクライアントでブラウザからサインインする

**MCP の認可仕様は、事前登録を最優先に位置づけています**。認可サーバー用のクライアント ID をすでに持っているクライアントは、ほかの方法を試す前にそれを使うべきだ、という規定です。Claude Code は `oauth.clientId` でこれに対応しています。登録のステップを丸ごと飛ばすので、Cognito にない機能は一切要求しません。

Cognito 側では、アプリクライアントに次の設定が必要です。

1. 認可コードグラントを有効にし、クライアントシークレットは発行しないこと。Claude Code はパブリッククライアントで、PKCE は自動で送ります。
2. コールバック URL を `http://localhost:<port>/callback` にすること。Cognito はコールバック URL を完全一致で照合するので、ポートは固定する必要があります。
3. そのクライアント ID を AgentCore Runtime の `allowedClients` に登録すること。登録しないと、発行されたトークンは 401 で拒否されます。この 401 は、不正なトークンのときと見分けがつきません。

そのうえで、同じポートを指定し、メタデータ URL も明示してサーバーを登録します。

```bash
claude mcp add-json my-mcp-server '{
  "type": "http",
  "url": "https://bedrock-agentcore.ap-northeast-1.amazonaws.com/runtimes/<URL-encoded runtime ARN>/invocations?qualifier=DEFAULT",
  "oauth": {
    "clientId": "YOUR_APP_CLIENT_ID",
    "callbackPort": 8765,
    "authServerMetadataUrl": "https://cognito-idp.ap-northeast-1.amazonaws.com/ap-northeast-1_XXXXXXXXX/.well-known/openid-configuration",
    "scopes": "openid email profile"
  }
}'
```

`callbackPort` を指定すると、Claude Code はランダムなポートではなく、アプリクライアントがすでに信頼しているポートで待ち受けます。`authServerMetadataUrl` は、Cognito が実際に提供しているドキュメントを直接指します。

これでサーバーは失敗ではなく認証待ちとして表示され、Authenticate を選ぶと Cognito Hosted UI が開きます。

*Image: Claude Code の /mcp のサーバー一覧。Local MCPs のグループが赤枠で囲まれ、5 つのツールで接続済みのサーバーと、「needs authentication」と表示された -oauth サーバーが並んでいる*

*Image: Claude Code の /mcp でサーバーの詳細を表示した画面。ステータスは「needs authentication」、認証は「not authenticated」で、Authenticate の選択肢がある*

*Image: ユーザー名とパスワードの入力欄がある Amazon Cognito Hosted UI のサインインフォーム*

サインインすると、ブラウザがコールバックポート経由で Claude Code に認可コードを返します。

*Image: /mcp 実行後の Claude Code の出力。「Authentication successful. Connected to」と表示され、続くサーバー名はぼかしてある*

接続したサーバーのツールを呼び出すと、結果が返ってきました。Claude Code がトークンを保存しただけでなく、AgentCore がそのトークンを受け入れたことがこれで確認できます。

### スコープを固定するまで出る `invalid_scope`

**`oauth.scopes` を指定しないと、ブラウザでのサインインは成功しません**。Cognito からのエラー付きで、Claude Code のコールバックページに戻されます。

*Image: Claude Code のブラウザ用コールバックページ。「Authentication failed」と表示され、エラーは「invalid_request: invalid_scope」*

`authServerMetadataUrl` を設定すると、Claude Code は要求するスコープを `openid-configuration` の `scopes_supported` から取ります。このドキュメントは、OpenID Connect の標準に沿ってユーザープールが公開しているもので、プール全体が対応するスコープ `openid email phone profile` が並んでいます。

アプリクライアントはその中から許可するスコープを個別に持っており、このアプリクライアントが許可していたのは `openid email profile` だけでした。リクエストに `phone` が含まれていたため、Cognito がそれを拒否したわけです。

アプリクライアントの許可スコープに合わせて `"scopes": "openid email profile"` を固定すると解決しました。アプリクライアントが許可しているスコープだけで組み合わせれば、どれでも動きます。失敗するのは、許可されていないスコープを 1 つでも要求したときです。

## `headersHelper` で自動更新される静的ヘッダー

`headersHelper` に指定したコマンド（以下、ヘルパー）は接続のたびに実行され、Claude Code はその標準出力をリクエストヘッダーとして使います。ツール呼び出しが 401 か 403 で返ってきた場合は、Claude Code がこれを再実行して再接続し、1 回だけリトライします。**新しい Cognito トークンを発行するヘルパーにすれば、期限切れは表に出なくなります**。

```bash title="scripts/mcp_headers.sh"
#!/usr/bin/env bash
set -euo pipefail
cd "$(dirname "$0")/.."
source .cognito.env   # COGNITO_CLIENT_ID, COGNITO_USERNAME, COGNITO_PASSWORD, AWS_REGION

TOKEN=$(aws cognito-idp initiate-auth --no-sign-request \
  --client-id "$COGNITO_CLIENT_ID" \
  --auth-flow USER_PASSWORD_AUTH \
  --auth-parameters "USERNAME=${COGNITO_USERNAME},PASSWORD=${COGNITO_PASSWORD}" \
  --region "$AWS_REGION" \
  --query 'AuthenticationResult.AccessToken' --output text)

printf '{"Authorization": "Bearer %s"}\n' "$TOKEN"
```

`InitiateAuth` は AWS の署名が不要な Cognito API なので、`--no-sign-request` を付ければ AWS の認証情報がまったくなくてもスクリプトが動きます。付けない場合、スクリプトは認証情報ブローカー経由で AWS の認証情報を取得する必要があり、ブローカーが MFA プロンプトを出して人の応答を待つことがあります。ヘルパーのタイムアウトは 10 秒なので、時間内に誰も応答しなければ接続は失敗します。

ほかにも条件が 2 つあります。

- アプリクライアントで `USER_PASSWORD_AUTH` を有効にしておくこと。
- AgentCore は**アクセストークン**を要求します。トークンの `client_id` クレームを `allowedClients` と照合するためです。

```bash
claude mcp add-json my-mcp-server '{
  "type": "http",
  "url": "https://bedrock-agentcore.ap-northeast-1.amazonaws.com/runtimes/<URL-encoded runtime ARN>/invocations?qualifier=DEFAULT",
  "headersHelper": "/absolute/path/to/scripts/mcp_headers.sh"
}'
```

最初に登録したときは、接続されずに次の警告が表示されました。

```text
MCP server 'my-mcp-server': headersHelper not run — this workspace has no persisted trust; accept the trust dialog here once interactively
```

**ヘルパーは Claude Code が実行するコマンドなので、信頼したワークスペースでしか動きません**。Claude Code のドキュメントは、これを任意のシェルコマンド（「an arbitrary shell command」）と説明しています。そのディレクトリで一度 `claude` を対話モードで起動し、信頼ダイアログを承認すれば十分でした。次の起動でサーバーが接続されました。

事前登録したクライアントとのトレードオフは、認証情報をどこに置くかです。ヘルパーには、ディスク上のパスワードと、パスワード認証を有効にしたアプリクライアントが必要です。一方、OAuth の経路で手元に残るのは、Claude Code が保存するリフレッシュトークンだけです。そのかわりヘルパーは、ヘッドレス環境やスクリプトのように、ブラウザでサインインする人がいない場面でも動きます。

## 本当に動的クライアント登録が必要な場合

事前登録では、ユーザーごとの Claude Code にあらかじめクライアント ID を設定しておくことになります。1 つのチームで使うなら問題ありませんが、URL を知っていれば誰でも追加できるサーバーにするには不便です。

事前の関係がないクライアントに対して、現行の MCP 認可仕様は **Client ID Metadata Documents**（CIMD）を推奨し、動的クライアント登録を後方互換のための選択肢に格下げしています。CIMD では、クライアント ID はクライアントを記述した JSON ドキュメントの HTTPS URL で、認可サーバーがそれを取得します。Claude Code はすでにこの方式に対応しており、サーバーが `client_id_metadata_document_supported` を公開していれば使います。ただし **Cognito はこのフラグを公開していないので、ここでは CIMD は使えません**。

残るのは次の 3 つの回避策です。**いずれもここでは試していません**。ほかの Cognito ユーザーや AWS の AgentCore ガイドが挙げている方法で、Cognito と AgentCore のインバウンド JWT 検証の両方を残せるのは `/register` エンドポイントだけです。

1. **Cognito の前段に置く `/register` エンドポイント**。小さなサービスが RFC 7591 のリクエストを受け付けて `CreateUserPoolClient` を呼び、新しいクライアント ID を返します。あわせて、`registration_endpoint` を追加したメタデータドキュメントも提供します。代償は、管理系 API につながる公開エンドポイントを抱えることです。レート制限とリダイレクト URI の許可リストが必要になり、ユーザープールにはアプリクライアント数のクォータがあるので、不要になったクライアントの削除も欠かせません。新しいクライアントは、それぞれ AgentCore の `allowedClients` にも反映しなければなりません。
2. **OAuth プロキシ**。FastMCP の Cognito 連携は、動的クライアント登録に自分で応答し、すべてを実在する 1 つの Cognito アプリクライアントに転送します。そのため Cognito には、クライアントごとの登録が一切届きません。AgentCore のインバウンド JWT 検証ではなく、FastMCP 自身の認証レイヤーで作ったサーバーに向いています。
3. **動的クライアント登録に対応した ID プロバイダ**。AWS の AgentCore Runtime MCP ガイドは、Cognito ではなく Auth0 で自動 OAuth を実現する例を示しています。プロバイダをまだ選べるなら、これが最短の経路です。

## まとめ

- Claude Code のデフォルトの OAuth 経路は動的クライアント登録を前提としています。Cognito は `registration_endpoint` を公開していないので、`Incompatible auth server` で失敗します。
- **事前登録した Cognito アプリクライアントを使えば、ブラウザでサインインできます**。`oauth.clientId`、アプリクライアントのコールバック URL と一致する `callbackPort`、`openid-configuration` を指す `authServerMetadataUrl`、アプリクライアントが許可する範囲に絞った `oauth.scopes` を設定します。
- **`headersHelper` を使えば、自動で更新されるヘッダーになります**。`initiate-auth --no-sign-request` を呼んでアクセストークンを出力するスクリプトを、信頼したワークスペースで実行します。
- 静的な `Authorization` ヘッダーを設定すると OAuth へのフォールバックが無効になり、接続もトークン 1 本の有効期間しか持ちません。
- 事前の設定なしに登録させたい場合、MCP 認可仕様は現在 CIMD を推奨していますが、Cognito は対応していません。Cognito と AgentCore を組み合わせた構成でそのまま使えるのは `/register` のような登録用の中継サービスだけで、それ以外なら別の ID プロバイダに切り替えることになります。

## 参考リンク

- [MCP 仕様 2025-11-25: Authorization（クライアント登録の優先順位と Client ID Metadata Documents を含む）](https://modelcontextprotocol.io/specification/2025-11-25/basic/authorization)
- [Claude Code ドキュメント: Connect Claude Code to tools via MCP（事前設定した OAuth 認証情報と headersHelper を含む）](https://code.claude.com/docs/en/mcp)
- [RFC 7591: OAuth 2.0 Dynamic Client Registration Protocol](https://datatracker.ietf.org/doc/html/rfc7591)
- [RFC 9728: OAuth 2.0 Protected Resource Metadata](https://datatracker.ietf.org/doc/html/rfc9728)
- [OpenID Connect Discovery 1.0: `openid-configuration` ドキュメントの形式](https://openid.net/specs/openid-connect-discovery-1_0.html)
- [OAuth Client ID Metadata Document のドラフト](https://datatracker.ietf.org/doc/html/draft-ietf-oauth-client-id-metadata-document-00)
- [FastMCP: OAuth プロキシを使った AWS Cognito の OAuth 連携](https://gofastmcp.com/integrations/aws-cognito)
- [Amazon Bedrock AgentCore: Authenticate and authorize with Inbound Auth and Outbound Auth](https://docs.aws.amazon.com/bedrock-agentcore/latest/devguide/runtime-oauth.html)
