For the complete documentation index, see llms.txt. Markdown versions of documentation pages are available by appending .md to the page URL.
メインナビゲーション

認証

プラグインの MCP サーバー向け認証パターン。

ユーザー認証

多くのプラグインの MCP サーバーは、読み取り専用の匿名モードで動作できます。ただし、顧客固有のデータや書き込み操作を提供する場合は、ユーザーを認証する必要があります。

公開したプラグインは、ChatGPT と Codex で実行できます。MCP の認可仕様は両方のプロダクトに適用されます。このガイドでは、コールバック、メタデータドキュメント、連携インターフェースが利用環境によって異なる場合に、ChatGPT 固有のクライアントの詳細を説明します。

既存のサーバー側アプリケーションに接続したり、ユーザー間でデータを共有したりする必要がある場合は、独自の認可サーバーと連携できます。

OAuth 2.1 によるカスタム認証

認証を必要とする MCP サーバーでは、MCP 認可仕様に準拠した OAuth 2.1 フローを実装する必要があります。

構成要素

  • リソースサーバー: ツールを公開し、リクエストごとにアクセストークンを検証する MCP サーバー
  • 認可サーバー: トークンを発行し、ディスカバリメタデータを公開する ID プロバイダー、または独自の実装
  • クライアント: ユーザーの代理として動作する ChatGPT や Codex などの OpenAI ホストです。 対応するクライアントは、Client ID Metadata Documents(CIMD)、動的クライアント登録(DCR)、 事前定義の OAuth クライアント、および PKCE を使用します。

MCP 認可仕様の要件

  • MCP サーバーでの保護対象リソースのメタデータの公開
  • 認可サーバーからの OAuth メタデータの公開
  • OAuth フロー全体での resource パラメーターの引き継ぎ
  • OpenAI ホストが OAuth クライアントを識別または登録する方法の選択:CIMD、DCR、または事前定義の OAuth クライアント
  • 認可サーバーが受け入れるトークンエンドポイント認証方式の公開

以下では、仕様で求められる内容をわかりやすく説明します。

MCP サーバーでの保護対象リソースのメタデータの公開

  • ChatGPT がメタデータの取得先を把握できるように、GET https://your-mcp.example.com/.well-known/oauth-protected-resource のような HTTPS エンドポイントを用意する必要があります。または、401 Unauthorized レスポンスの WWW-Authenticate ヘッダーで同じ URL を通知します。
  • このエンドポイントは、リソースサーバーと、そこで利用できる認可サーバーを記述した JSON ドキュメントを返します。
{
  "resource": "https://your-mcp.example.com",
  "authorization_servers": ["https://auth.yourcompany.com"],
  "scopes_supported": ["files:read", "files:write"],
  "resource_documentation": "https://yourcompany.com/docs/mcp"
}
  • 設定が必要な主なフィールドは次のとおりです。
    • resource:MCP サーバーの正規の HTTPS 識別子です。ChatGPT は OAuth フロー中に、この値をそのまま resource クエリパラメーターとして送信します。
    • authorization_servers:ID プロバイダーを指す、1 つ以上の発行者のベース URL です。ChatGPT は各 URL から OAuth メタデータの取得を試みます。
    • scopes_supported:ChatGPT がユーザーに求める権限を説明するための任意のリスト
    • RFC 9728 で定義された resource_documentationresource_policy_uriresource_tos_uri などの任意の追加フィールドを使うと、クライアントや管理者が構成を把握しやすくなります。

未認証であることを理由にリクエストをブロックする場合は、次のようなチャレンジを返します。

HTTP/1.1 401 Unauthorized
WWW-Authenticate: Bearer resource_metadata="https://your-mcp.example.com/.well-known/oauth-protected-resource",
                         scope="files:read"

このヘッダーだけで、ChatGPT はそれまで把握していなかったメタデータ URL も検出できます。

認可サーバーからの OAuth メタデータの公開

  • ChatGPT が構成を読み取れるように、ID プロバイダーは次のいずれかの well-known ディスカバリドキュメントを公開する必要があります。
    • https://auth.yourcompany.com/.well-known/oauth-authorization-server にある OAuth 2.0 メタデータ
    • https://auth.yourcompany.com/.well-known/openid-configuration にある OpenID Connect メタデータ
  • 各ドキュメントは、OpenAI ホストが必要とする 3 つの主要な情報、つまりユーザーのリダイレクト先、コードの交換方法、ホスト自身の識別方法を提供します。一般的なレスポンスは次のようになります。
{
  "issuer": "https://auth.yourcompany.com",
  "authorization_response_iss_parameter_supported": true,
  "authorization_endpoint": "https://auth.yourcompany.com/oauth2/v1/authorize",
  "token_endpoint": "https://auth.yourcompany.com/oauth2/v1/token",
  "client_id_metadata_document_supported": true,
  "token_endpoint_auth_methods_supported": ["none", "private_key_jwt"],
  "registration_endpoint": "https://auth.yourcompany.com/oauth2/v1/register",
  "code_challenge_methods_supported": ["S256"],
  "scopes_supported": ["files:read", "files:write"]
}
  • 次のフィールドを正しく設定する必要があります。
    • issuer:認可サーバーの正規の識別子です。 保護対象リソースのメタデータの authorization_servers リストにも、まったく同じ値を使用します。
    • authorization_response_iss_parameter_supported:認可サーバーが、エラーレスポンスを含むすべての認可レスポンスで iss パラメーターを返す場合にのみ、 true に設定します。
    • authorization_endpointtoken_endpoint:ChatGPT が OAuth の認可コード + PKCE フローを最初から最後まで実行するために必要な URL
    • client_id_metadata_document_supported:ChatGPT のクライアント登録に CIMD を使用する場合は、true に設定します。ChatGPT は CIMD が利用可能な場合に優先して使用しますが、CIMD と DCR の両方が利用可能な場合は、プラグインの作成者が DCR を選択できます。
    • token_endpoint_auth_methods_supported:認可サーバーが受け入れるトークンエンドポイント認証方式を含めます。これは CIMD、DCR、事前定義の OAuth クライアントに適用されます。CIMD では、ChatGPT はパブリッククライアントのトークン交換用に none、署名付きクライアントアサーションを使ったトークン交換用に private_key_jwt をサポートしています。他の OAuth クライアントでは、一般に noneclient_secret_postclient_secret_basic が使われます。
    • registration_endpoint:動的クライアント登録(DCR)をサポートする場合に含めます。DCR により、ChatGPT は MCP サーバー接続専用の client_id を作成し、再利用できます。
    • code_challenge_methods_supportedS256 を含める必要があります。 認可サーバーのメタデータにこのフィールドがない場合や、 S256 への対応が明示されていない場合、その MCP サーバーはサポートされません。 これはMCP 認可仕様の要件です。
    • 任意のフィールドは RFC 8414 / OpenID Discovery に従います。管理者がポリシーを設定する際に役立つフィールドを含めてください。

OIDC スコープ

  • プロバイダーが .well-known/oauth-authorization-server または .well-known/openid-configuration ドキュメントの scopes_supported で OIDC スコープ(openidemailprofile など)への対応を明示している場合、ChatGPT は OAuth フロー中にデフォルトでそれらのスコープを要求します。
  • ID プロバイダーによっては、対応を明示している OIDC スコープがデフォルトでは有効になっていない場合があります。プロバイダーの構成設定を確認し、対応を明示しているすべてのスコープが OAuth クライアントで有効になっていることを確認してください。これは、CIMD を使用するクライアント、手動で作成したクライアント、DCR で作成したクライアントのいずれにも当てはまります。

ワークスペースのドメイン制限への対応

ChatGPT Enterprise ワークスペースでは、メールドメインの所有権を確認できます。OAuth で連携したプラグインがユーザーの確認済みメールアドレスを提供すると、ChatGPT はそのメールドメインを使い、その企業のユーザー ID で個人用ワークスペースや組織外の別のワークスペースにプラグインを連携することを防げます。

この保護に対応するには、認可サーバーを次のように構成します。

  • OpenID Connect のディスカバリメタデータを公開します。
  • openidemail のスコープへの対応を明示し、有効にします。
  • ユーザーの email クレームと email_verified: true を返す UserInfo エンドポイントをメタデータで公開します。

OAuth フロー中に、これらのクレームを ID トークンに含めて返すこともできますが、ワークスペースのドメイン制限には UserInfo エンドポイントが必要です。

エンタープライズのワークスペース側でも、ドメインの所有権を確認する必要があります。認可サーバーは、ChatGPT がワークスペースに設定された確認済みドメインと照合するためのユーザー ID 情報を提供します。ワークスペースがドメインを所有していることを確認するものではありません。

再認可時のログインコンテキストの保持

追加の OAuth スコープを要求する場合など、ChatGPT が既存の連携を再認可する際には、以前の OIDC ID トークンを標準の id_token_hint パラメーターとして認可リクエストに含めることがあります。ユーザーが最初からログインし直すことなく追加のスコープを付与できるようにするには、最初の OAuth フローで ID トークンを発行し、認可時に id_token_hint を利用するよう認可サーバーを構成します。

この最適化は任意です。ID トークンを利用できない場合や、認可サーバーがこのヒントを使用しない場合でも、再認可は機能します。

発行者識別によるコールバックの保護

OpenAI ホストは、RFC 9207 の 発行者識別を使用して、 認可サーバーの取り違え攻撃から OAuth コールバックを保護します。 ChatGPT と Codex が対象となる OAuth クライアントを作成する際に、固定のリダイレクト URI を使用できるようにするには、次の設定を行います。

  • 認可サーバーの メタデータauthorization_response_iss_parameter_supported: true を設定します。
  • メタデータの issuer フィールドと 保護対象リソースのメタデータの authorization_servers リストには、まったく同じ発行者識別子を使用します。
  • 成功時もエラー時も、すべての認可レスポンスで iss を返します。 その値は、メタデータの issuer と完全に一致する必要があります。 クライアントは文字列の完全一致で比較し、末尾のスラッシュ、パス、ポート、大文字と小文字の違いを正規化しません。

ChatGPT と Codex は、ユーザーをリダイレクトする前に、選択したメタデータの issuer を記録し、 認可コードを交換する前に、返された iss を確認します。 サーバーが発行者識別への対応を明示しているにもかかわらず、iss を省略したり、 一致しない値を返したりした場合、ChatGPT と Codex はそのレスポンスを拒否します。 これらの要件は、MCP の認可レスポンスの 検証ルールに従っています。

リダイレクト URL

MCP サーバーの管理ページに表示される本番環境用のリダイレクト URI を、そのまま認可サーバーの許可リストにコピーしてください。

  • 認可サーバーが上記の発行者識別の要件を満たしていない場合、 ChatGPT はコールバック ID 固有のリダイレクト URI https://chatgpt.com/connector/oauth/{callback_id} を使用します。
  • 認可サーバーがこれらの要件を満たしている場合、 ChatGPT は固定のリダイレクト URI https://chatgpt.com/connector_platform_oauth_redirect を使用します。

ChatGPT がコールバック ID 固有のリダイレクトを導入する前に公開された MCP サーバーも、引き続き固定のリダイレクト URI を使用します。

OAuth フロー全体での resource パラメーターの引き継ぎ

  • ChatGPT は、認可リクエストとトークンリクエストの両方に resource=https%3A%2F%2Fyour-mcp.example.com を追加します。これにより、トークンが上記の保護対象リソースのメタデータに関連付けられます。
  • その値をアクセストークン(通常は aud クレーム)にコピーするように認可サーバーを設定してください。これにより、MCP サーバーは、トークンが他の対象ではなく自分自身に対してのみ発行されたことを検証できます。
  • 想定するオーディエンスやスコープを持たないトークンを受け取った場合は拒否し、WWW-Authenticate チャレンジを返して、ChatGPT に正しいパラメーターでの再認可を促してください。

認可コードフローへの対応

  • MCP クライアントとして動作する ChatGPT は、S256 コードチャレンジを使用した PKCE 付きの認可コードフローを実行します。これにより、傍受された認可コードを攻撃者が再利用できないようにします。
  • クライアントがメタデータから PKCE への対応を確認できるよう、認可サーバーは code_challenge_methods_supportedS256 を含めて公開する必要があります。

OAuth フロー

上記の MCP 認可仕様を実装していれば、OAuth フローは次のように進みます。

  1. ChatGPT が MCP サーバーに保護対象リソースのメタデータを問い合わせます。

  1. ChatGPT が自身を OAuth クライアントとして識別する情報を提示します。MCP サーバーが CIMD を使用する場合、ChatGPT は動的クライアント登録を省略し、CIMD ドキュメントの URL を client_id として送信します。上記の発行者識別の要件を満たす認可サーバーには固定の https://chatgpt.com/oauth/client.json を使用し、それ以外のサーバーにはコールバック ID 固有の https://chatgpt.com/oauth/{callback_id}/client.json を使用します。MCP サーバーの管理ページには、接続のコールバックモードに対応する正確なクライアントメタデータドキュメントとリダイレクト URI が表示されます。MCP サーバーが DCR を使用する場合、ChatGPT はその MCP サーバー接続に対して認可サーバーの registration_endpoint を一度呼び出し、生成された client_id を受け取って、その接続で同じクライアントを再利用します。

CIMD を使用する場合、クライアント登録の手順はありません。次の画面は DCR を使用する場合のものです。

  1. ユーザーが初めてツールを呼び出すと、ChatGPT クライアントが OAuth 認可コード + PKCE フローを開始します。ユーザーは認証を行い、要求されたスコープに同意します。

  1. ChatGPT が認可コードをアクセストークンと交換し、以降の MCP リクエストに付加します(Authorization: Bearer <token>)。

  1. サーバーはリクエストごとにトークンの発行者、オーディエンス、有効期限、スコープを検証してから、ツールを実行します。

クライアント登録

認可サーバーが対応しており、プラグイン開発者が選択する場合は、クライアント登録方式として Client ID Metadata Documents(CIMD)を優先して使用してください。CIMD では、ChatGPT は HTTPS で提供されるメタデータドキュメントの URL を client_id として使用します。認可サーバーはそのドキュメントを取得し、公開されたクライアントメタデータとリダイレクトリソース識別子を検証したうえで、その URL を ChatGPT の固定のクライアント識別情報として扱います。

CIMD に対応する場合は、認可サーバーのメタデータに client_id_metadata_document_supported: true を設定してください。これにより、ChatGPT は CIMD を選択した MCP サーバーに対して、単一の固定クライアント識別情報を使用できます。認可サーバーは、この識別情報をリダイレクト URI の許可リスト、レート制限、その他のポリシーに使用できます。

ChatGPT は、 MCP SEP-3149 で提案された CIMD の移行を進めています。 本番環境の CIMD ドキュメントでは、 token_endpoint_auth_methods_supported を、ChatGPT が使用できる方式の配列として公開しています。 配列内に優先順位はありません。移行期間中は、 従来の単数形フィールド token_endpoint_auth_method も、優先する方式を示すために公開します。

{
  "token_endpoint_auth_methods_supported": ["none", "private_key_jwt"],
  "token_endpoint_auth_method": "private_key_jwt"
}

複数形のフィールドは、2 つのドキュメントで異なる立場から方式を示します。認可サーバーのメタデータにはトークンエンドポイントが受け付ける方式を列挙し、ChatGPT の CIMD ドキュメントには ChatGPT が使用できる方式を列挙します。ChatGPT は、両方に共通する方式から 1 つを選択します。従来の単数形フィールドで優先されている方式が両方に含まれる場合、ChatGPT はその方式を使用します。これは、単数形フィールドの指定を依然として必須と扱う認可サーバーとの互換性を保つためです。それ以外の場合は、両方に共通する別の方式を使用できます。

CIMD の複数形フィールドを読み取る認可サーバーは、 ローカルのセキュリティポリシーでそのクライアントによる使用が禁止されていない限り、 両方に共通するどの方式も受け付けるべきです。共通しない方式は拒否する必要があります。client_id の URL は 固定のままで、方式ごとのドキュメントを選択するために クエリパラメーターを使用することはありません。

対応している方式は次のとおりです。

  • none:トークンエンドポイントが、クライアント認証なしで PKCE による認可コードの交換に対応している場合は、このパブリッククライアントフローを使用してください。ChatGPT はクライアントごとのシークレットを保存しません。
  • private_key_jwt:トークンエンドポイントがクライアント認証を必要とする場合は、この署名付きクライアントアサーションフローを使用してください。ChatGPT は CIMD メタデータに公開 JWKS の URL を掲載します。JWKS は、メタデータと同じオリジンの /oauth/jwks.json から配信されます。ChatGPT は、管理された秘密鍵と kid を使ってサーバー側でトークンリクエストに署名します。認可サーバーは、公開 JWKS を使ってアサーションを検証します。

DCR も引き続きサポートされています。registration_endpoint を含めると、プラグイン開発者が DCR を選択した場合や CIMD を利用できない場合に、ChatGPT が動的に登録できます。ChatGPT は MCP サーバー接続ごとに DCR を一度実行し、その後は登録した OAuth クライアントを保持して、その接続で再利用します。ただし、個別の接続が多数あると、DCR によって登録されるクライアントも多くなり得るため、大規模な運用では通常 CIMD の方が管理しやすくなります。

MCP サーバー接続を使用している間は、登録した OAuth クライアントと、クライアントシークレットがある場合はそのシークレットを有効に保ってください。認可サーバーがいずれかの認証情報を失効、削除、または置換すると、ユーザーやレビュー担当者の接続時に invalid_client エラーが発生する可能性があります。アクセストークンとリフレッシュトークンは、通常どおり有効期限を設けたり、ローテーションしたりできます。

クライアントの識別

MCP サーバーが、リクエストの送信元が本当に ChatGPT であることをどう確認できるか、という質問がよく寄せられます。ChatGPT は MCP サーバーへの接続時に OpenAI が管理するクライアント証明書を提示するため、mTLS を使ってトランスポート層でクライアントを検証できます。また、ChatGPT の公開されている送信元 IP アドレス範囲を許可リストに登録することもできます。ChatGPT は、クライアントクレデンシャル、サービスアカウント、JWT ベアラーアサーションなど、マシン間の OAuth グラントを サポートしていません 。また、カスタム API キーや、お客様が提供する mTLS 証明書を提示することもできません。

CIMD は、ChatGPT の識別情報を宣言する固定のドキュメントを HTTPS で認可サーバーに提供し、クライアントの識別をさらに強化します。private_key_jwt を使用する場合は、CIMD メタデータに掲載された公開 JWKS を使って、ChatGPT がトークンエンドポイントに送信するクライアントアサーションを検証してください。

相互 TLS(mTLS)

ChatGPT は、MCP サーバーとの TLS 接続を確立する際に、OpenAI が管理するクライアント証明書を提示するようになりました。アプリケーションでクライアント証明書を検証する場合は、以下の OpenAI 証明書チェーンを信頼するように設定してください。

MCP サーバーとの TLS 接続を確立する際にクライアント証明書を検証するには、次の手順に従ってください。

  • リーフ証明書が存在し、その証明書チェーンが OpenAI Connectors mTLS 中間 CA につながることを確認してください。
  • リーフ証明書がクライアント認証用として有効であることを確認してください。
  • リーフ証明書の SAN の dnsNamemtls.prod.connectors.openai.com であることを確認してください。
  • リーフ証明書のフィンガープリントを固定しないでください。OpenAI は、公開されている CA チェーンを維持したまま、リーフ証明書をローテーションする場合があります。

MCP クライアントとしての ChatGPT の認証には mTLS を使用してください。エンドユーザーの認証とツールへのアクセスの認可には、引き続き OAuth 2.1 を使用してください。

ID プロバイダーの選択

ほとんどの OAuth 2.1 ID プロバイダーは、ディスカバリードキュメントの公開、none または private_key_jwt を使った CIMD への対応、必要に応じた DCR への対応、発行するトークンへの resource パラメーターの引き継ぎを行えば、MCP の認可要件を満たせます。クライアント登録で CIMD に対応しているプロバイダーを優先してください。

認証を一から自分で実装するのではなく、実績のある既存の ID プロバイダーを使用することを 強く お勧めします。

代表的な ID プロバイダーの手順を以下に紹介します。

Auth0

Auth0 は、メタデータの検出、CIMD 登録、API セキュリティ、自社およびサードパーティのツール呼び出し用のトークン交換を提供し、MCP クライアントから MCP サーバーへの安全な接続を可能にします。

ホスト型プロバイダーの例

トークン検証の実装

OAuth フローが完了すると、ChatGPT は受け取ったアクセストークンを、そのまま以降の MCP リクエストに付加します(Authorization: Bearer …)。MCP サーバーでリクエストを受信したら、そのトークンを信頼できないものとして扱い、リソースサーバーに必要なすべてのチェックを自分で行う必要があります。具体的には、署名の検証、発行者とオーディエンスの照合、有効期限の確認、リプレイ攻撃への対処、スコープに基づくアクセス制御です。この責任を負うのは ChatGPT ではなく、サーバーの実装者です。

具体的には、次の処理を行ってください。

  • 認可サーバーが公開する署名鍵を取得し(通常は JWKS 経由)、トークンの署名と iss を検証してください。
  • 有効期限切れのトークン、またはまだ有効になっていないトークンを拒否してください(exp/nbf)。
  • トークンが自分のサーバー向けに発行されていること(aud または resource クレーム)と、必須として指定したスコープが含まれていることを確認してください。
  • サーバー固有のポリシーチェックを実行したうえで、特定した識別情報をリクエストコンテキストに付加するか、WWW-Authenticate チャレンジを伴う 401 を返してください。

検証に失敗した場合は、401 Unauthorized と、保護対象リソースのメタデータを指す WWW-Authenticate ヘッダーを返してください。これにより、クライアントに OAuth フローの再実行を促します。

SDK のトークン検証用基本機能

Python と TypeScript の MCP SDK にはどちらもヘルパーが用意されているため、トークン検証を一から実装する必要はありません。

複数アカウントへの対応

複数アカウント機能では、個人用と仕事用など、複数のアカウントを同じプラグインに接続できます。OpenAI は、選択された接続の認証済み資格情報を使って、各ツール呼び出しをルーティングします。プロファイルツールがなくても、複数のアカウントを接続できます。接続を区別しやすくし、再接続後も同じプロファイルだと認識できるようにするには、安定した ID とわかりやすい表示用メタデータを返す、認証を必要とするプロファイルツールを提供してください。

ユーザーから見た複数アカウントの仕組み

ユーザーはプラグインの設定ページからアカウントを追加で接続できます。モデルは接続済みのすべてのアカウントを利用でき、ユーザーのリクエストに応じて、ツール呼び出し時に適切なアカウントを 1 つ以上選択します。各ツール呼び出しには、選択されたアカウントの資格情報と権限が使われます。

アカウントの識別精度の向上

OpenAI が接続済みプロファイルを認識し、わかりやすいラベルを表示できるようにするには、次のようにします。

  • リクエストの資格情報に対応するプロファイルを一意かつ安定して識別する不透明な ID を返す、認証を必要とするプロファイルツールを提供してください。これにより、OpenAI は再接続後も同じプロファイルを認識し、他のプロファイルと区別できます。id という名前のフィールドがこの目的に役立つのは、その値がこれらの要件を満たす場合に限られます。
  • 認証済みプロファイルの情報を取得するためにどのツールを呼び出せばよいかを OpenAI が検出できるよう、MCP メタデータでプロファイルツールを指定してください。

プロファイル情報が必要になると、OpenAI は指定されたツールを実行時に検出し、接続の資格情報を使って呼び出します。その後、レスポンスを検証してからプロファイルデータを使用します。プロファイルツールがなくてもアカウントは接続できますが、アカウントのラベル表示、認識、重複検出の信頼性が低下する可能性があります。プロファイルツールを宣言する場合は、有効な識別情報を返してください。無効なレスポンスによってアカウントを接続できなくなる場合があります。

安定したプロファイル識別情報の定義

プロファイルは、リクエストの認証済み資格情報がどの主体を表すかを識別します。どのプロファイルを個別に接続できるかは、サービス側で定義します。この仕様は、サービスの組織構造や認可モデルを規定するものではありません。

アプリ内で一意の不透明なプロファイル ID を返してください。同じプロファイルには、トークンの更新や再接続後も同じ ID を保持する必要があります。異なるプロファイルには、異なる ID が必要です。OpenAI は、これらの ID の内容を解釈せずに比較します。

既存の不変かつ不透明なプロバイダー ID でプロファイル全体を識別できる場合は、その ID を使ってください。そうでない場合は、不透明な ID を一度だけ割り当て、プロファイルとの対応付けを永続化し、以降のリクエストでも同じ ID を取得してください。内部的な関連付けはサービス内で管理し、返す ID に名前、メールアドレス、組織上の関係を埋め込まないでください。

id は、次の要件を満たす必要があります。

  • 空文字列でも空白のみでもない文字列であること。数値のプロバイダー ID は文字列としてシリアライズすること
  • 同じプロファイルでは、トークンの更新、再接続、スコープの拡張後も同じ値を保持すること
  • アプリ経由で接続できる異なるプロファイルには、それぞれ異なる値を使うこと
  • プロファイルのメールアドレス、名前、表示ラベルが変わっても値を変えないこと
  • プロファイルの削除後も、別のプロファイルに再割り当てしないこと

ログイン、トークン、セッション、ツール呼び出しごとに新しい ID を生成しないでください。メールアドレスや変更可能な名前は、表示用メタデータに含めてください。変更や再割り当てが可能なメールアドレスは、安定したプロファイル ID として使用できません。Google OIDC では、メールアドレスのクレームではなく、安定した sub を使用してください。Google のドキュメントでは、メールアドレスは変更される可能性がある一方で、sub は変わらず、再利用されることもないと説明されています。Google の ID に関するドキュメントを参照してください。

連携機能を更新するときは、既存のプロファイル ID を保持してください。表示名の変更、新しいトークン、新しい接続によって、新たなプロファイルとして識別されることがあってはなりません。

プロファイルツールの実装と宣言

空の引数オブジェクトを受け取り、現在のプロファイルを返す、認証を必要とする読み取り専用ツールを公開してください。ツールの名前は get_profilewhoami、または別の名前でも構いません。実行時に検出できるよう、メタデータでプロファイルツールであることを示します。OpenAI がレスポンスを正しく利用できるよう、レスポンスは以下の識別情報の要件を満たす必要があります。

  • リクエストの検証済み資格情報から、対応する主体を特定してください。
  • 操作を読み取り専用にし、通常の接続権限で利用できるようにしてください。
  • 現在のリクエストの資格情報に対応するプロファイルを、必ず 1 つだけ返してください。
  • 呼び出し元に、ユーザー ID、メールアドレス、アカウント選択用の値の指定を要求しないでください。
  • 認証に失敗した場合は、仮の ID や別のアカウントのプロファイルではなく、適切な認証エラーを返してください。

プロファイルのレスポンスは、次の JSON Schema に準拠する必要があります。

{
  "$schema": "https://json-schema.org/draft/2020-12/schema",
  "type": "object",
  "properties": {
    "id": {
      "type": "string",
      "minLength": 1,
      "pattern": "\\S",
      "description": "Opaque profile identifier, unique within this app and unchanged across token refresh, reconnection, and display-metadata changes. Never reassigned to another profile."
    },
    "name": {
      "type": "string",
      "description": "Display name for the authenticated profile."
    },
    "email": {
      "type": "string",
      "description": "Email address for display; not used as the profile identity."
    },
    "nickname": {
      "type": "string",
      "description": "A useful label that helps users distinguish connected profiles."
    }
  },
  "required": ["id"],
  "additionalProperties": false
}

レスポンスには、空文字列でも空白のみでもない文字列の id を含める必要があります。表示用フィールドは任意です。ツールのメタデータは、プロファイル情報の取得先を OpenAI に伝えます。レスポンスは、現在の資格情報に対応するプロファイルを識別します。

スキーマ検証では、プロファイルの処理に必要な構造とフィールドの型がレスポンスに備わっているかを確認します。サービス側では、ID の一意性と安定性に加え、資格情報に対応する範囲が正しく適用されることも保証する必要があります。メタデータの指定やスキーマ検証の成功だけでは、こうした動作特性は証明されません。

ユーザーがプロファイルを区別できるよう、nameemailnickname のうち取得可能なものを含めてください。取得できない任意の値は省略し、架空の値や無関係な個人データを追加しないでください。人が読んで理解できる有用な補足情報は、ID ではなく nickname に含めてください。

ツールに _meta["openai/profile"]: true を設定し、プロファイルのレスポンススキーマをそのツールの outputSchema として公開してください。このマーカーは、プロファイル情報を提供するツールを OpenAI に伝えるものであり、機能を有効にしたり、利用資格を付与したりするものではありません。マーカーが存在しないか、値が false の場合、この仕組みではそのツールがプロファイル情報の取得元として指定されていないことを意味します。文字列、数値、null は、マーカーの値として無効です。

{
  "name": "get_profile",
  "description": "Return the profile represented by this request's authenticated credentials. The opaque id is unique within this app and remains unchanged across token refresh, reconnection, and display-metadata changes.",
  "inputSchema": {
    "type": "object",
    "properties": {},
    "additionalProperties": false
  },
  "outputSchema": {
    "$schema": "https://json-schema.org/draft/2020-12/schema",
    "type": "object",
    "properties": {
      "id": {
        "type": "string",
        "minLength": 1,
        "pattern": "\\S",
        "description": "Opaque profile identifier, unique within this app and unchanged across token refresh, reconnection, and display-metadata changes. Never reassigned to another profile."
      },
      "name": {
        "type": "string",
        "description": "Display name for the authenticated profile."
      },
      "email": {
        "type": "string",
        "description": "Email address for display; not used as the profile identity."
      },
      "nickname": {
        "type": "string",
        "description": "A useful label that helps users distinguish connected profiles."
      }
    },
    "required": ["id"],
    "additionalProperties": false
  },
  "annotations": {
    "readOnlyHint": true,
    "destructiveHint": false,
    "openWorldHint": false
  },
  "securitySchemes": [
    {
      "type": "oauth2",
      "scopes": []
    }
  ],
  "_meta": {
    "openai/profile": true
  }
}

プロファイルへのアクセスに OAuth スコープが必要な場合は、連携機能で実際に使用するスコープを指定してください。この宣言だけでは認証は実装されません。サーバー側で資格情報を検証し、権限に基づくアクセス制御を行う必要があります。トークン検証の実装ツールリファレンスを参照してください。

outputSchema に照らして検証できるよう、プロファイルを structuredContent に含めて返してください。互換性のため、同じプロファイルを JSON にシリアライズしたものもテキストコンテンツ項目に含めてください。

{
  "content": [
    {
      "type": "text",
      "text": "{\"id\":\"prf_8d7e4b19\",\"name\":\"Alex Chen\",\"email\":\"alex@example.com\",\"nickname\":\"Alex — Moonwaffle work\"}"
    }
  ],
  "structuredContent": {
    "id": "prf_8d7e4b19",
    "name": "Alex Chen",
    "email": "alex@example.com",
    "nickname": "Alex — Moonwaffle work"
  },
  "isError": false
}

プロファイルのフィールドを最上位に配置した、単一の JSON オブジェクトを使ってください。

すでにプロファイルツールがある場合 は、名前を維持したままプロファイルのメタデータ宣言を追加し、標準のプロファイルレスポンスを返してください。既存のレスポンス形式が異なる場合は、サーバー側で変換するか、スキーマに準拠する小さなラッパーツールを公開してください。標準の連携方法では、すべてのアプリで同じ宣言とレスポンス形式を使用します。

具体例:Moonwaffle の永続的なプロファイル

架空のサービス Moonwaffle で、Alex が 2 つのプロファイルを個別に接続できるとします。Moonwaffle は、プロファイルごとに異なる不透明な ID を保存します。リクエストの資格情報から、保存済みのプロファイルのいずれかが特定され、プロファイルツールはその既存の ID を返します。

保存済みプロファイルの例。 ラベルは変更できますが、識別子は変わりません。

Alex — Moonwaffle personal: prf_42a9c6e0
Alex — Moonwaffle work:     prf_8d7e4b19

これらの ID の例には、プロファイルのラベルや内部的な関連付けは埋め込まれていません。プロファイルごとに一度だけ永続化され、再接続、トークンの更新、メールアドレスや表示名の変更後も同じ ID が使われます。

認証済みプロファイルからレスポンスを構築します。 この JavaScript の例は、MCP SDK に組み込めるハンドラーロジックを示しています。loadAuthenticatedProfile は、アプリケーション側で実装する連携コードです。リクエストの資格情報を検証し、その権限に基づくアクセス制御を行い、対応するプロファイルの永続化済み ID と表示用メタデータを取得します。requestContext はサーバーのリクエスト処理から渡されるものであり、モデルが指定するツール引数ではありません。

async function getProfile(requestContext) {
  // Your auth/provider integration validates credentials and loads
  // the existing profile. Auth failures use normal MCP auth handling.
  const account = await loadAuthenticatedProfile(requestContext);
  const id = account.profileId;

  if (typeof id !== "string" || id.trim().length === 0) {
    return {
      isError: true,
      content: [{ type: "text", text: "Profile identity unavailable." }],
    };
  }

  // Return the persisted ID unchanged; do not generate an ID per call.
  const profile = {
    id,
    ...(typeof account.name === "string" ? { name: account.name } : {}),
    ...(typeof account.email === "string" ? { email: account.email } : {}),
    ...(typeof account.nickname === "string"
      ? { nickname: account.nickname }
      : {}),
  };

  return {
    isError: false,
    structuredContent: profile,
    content: [{ type: "text", text: JSON.stringify(profile) }],
  };
}

上記のメタデータ宣言と入出力スキーマを使って、このハンドラーを登録してください。loadAuthenticatedProfile は、同等の資格情報が使われた場合や再接続後も、同じ保存済みプロファイルを特定する必要があります。OAuth の認可付与やセッションごとに新しいプロファイル ID を作成してはいけません。他のすべてのツールも、リクエストの資格情報を使い、同じプロファイルの権限に基づくアクセス制御を行う必要があります。

識別情報の動作確認:

テスト期待される結果
Moonwaffle の仕事用プロファイルを繰り返し呼び出す毎回 prf_8d7e4b19
トークンの更新、再接続、スコープの拡張後の同じプロファイルprf_8d7e4b19
メールアドレスや表示ラベルの変更後の同じプロファイルprf_8d7e4b19。ラベルは変更可能
Moonwaffle の個人用プロファイルprf_42a9c6e0。仕事用プロファイルとは異なる値
永続化済みのプロファイル ID が存在しない、または空白エラー結果。識別情報の捏造や別のプロファイルへのフォールバックは行わない

識別情報に関する保証は、すべてのプロファイルと、連携機能の将来の変更にわたって維持する必要があります。表示用メタデータ、トークンの内容、接続のライフサイクルイベントに左右されないよう、この保証を維持してください。

テストとロールアウト

  • ローカルテスト: 短いサイクルで検証と改善を繰り返せるよう、有効期間の短いトークンを発行する開発用テナントから始めます。
  • 内部テスト: 認証が動作するようになったら、広く展開する前にアクセスを信頼できるテスターに限定します。特定のツール、または MCP サーバー全体でアカウント連携を必須にできます。
  • ローテーション: トークンの失効、更新、スコープの変更に備えます。トークンがない場合や古くなっている場合、サーバーは未認証として扱い、対処に役立つエラーメッセージを返すようにします。
  • OAuth のデバッグ: MCP Inspector の Auth 設定で OAuth の各ステップを確認し、リリース前にフローが失敗する箇所を特定します。

認証を導入すると、ユーザー固有のデータや書き込み操作を ChatGPT と Codex のユーザーに提供できます。

認証 UI の表示

ChatGPT が OAuth のアカウント連携 UI を表示するのは、MCP サーバーが OAuth を利用可能または必要と示した場合に限られます。

ツール単位の OAuth フローを開始するには、メタデータ(securitySchemes とリソースメタデータドキュメント) および _meta["mcp/www_authenticate"] を含む実行時エラーの両方が必要です。両方が揃っていない場合、ChatGPT はそのツールのアカウント連携 UI を表示しません。

  1. リソースメタデータを公開します。 MCP サーバーは、https://your-mcp.example.com/.well-known/oauth-protected-resource のような well-known URL で OAuth 構成を公開する必要があります。

  2. securitySchemes で各ツールの認証ポリシーを記述します。 ツールごとに securitySchemes を宣言すると、OAuth が必要なツールと匿名で実行できるツールを ChatGPT に伝えられます。サーバー全体で同じポリシーを使用する場合も、ツールごとに宣言してください。サーバー単位のデフォルト設定に依存すると、後から個々のツールを変更しにくくなります。

    現在利用できるスキームは次の 2 種類です。複数を指定すると、認証が任意であることを示せます。

    • noauth:ツールを匿名で呼び出せるため、ChatGPT はすぐに実行できます。
    • oauth2:ツールには OAuth 2.0 アクセストークンが必要です。同意画面に正確な情報が表示されるよう、リクエストするスコープを含めてください。

    配列をまったく指定しない場合、ツールはサーバーが公開するデフォルト設定を継承します。noauthoauth2 の両方を宣言すると、匿名での呼び出しから始められ、アカウントを連携すると権限が必要な操作も利用できることを ChatGPT に伝えられます。クライアントへの通知内容にかかわらず、サーバーは呼び出しのたびにトークン、スコープ、オーディエンスを検証する必要があります。

    例(公開アクセス + 任意の認証):TypeScript SDK

    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { z } from "zod";
    
    declare const server: McpServer;
    
    server.registerTool(
      "search",
      {
        title: "Public Search",
        description: "Search public documents.",
        inputSchema: {
          q: z.string(),
        },
        outputSchema: {},
        securitySchemes: [
          { type: "noauth" },
          { type: "oauth2", scopes: ["search.read"] },
        ],
      },
      async ({ q }) => {
        return {
          content: [{ type: "text", text: `Results for ${q}` }],
          structuredContent: {},
        };
      }
    );

    例(認証必須):TypeScript SDK

    import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
    import { z } from "zod";
    
    declare const server: McpServer;
    
    server.registerTool(
      "create_doc",
      {
        title: "Create Document",
        description: "Make a new doc in your account.",
        inputSchema: {
          title: z.string(),
        },
        outputSchema: {},
        securitySchemes: [{ type: "oauth2", scopes: ["docs.write"] }],
      },
      async ({ title }) => {
        return {
          content: [{ type: "text", text: `Created doc: ${title}` }],
          structuredContent: {},
        };
      }
    );
  3. ChatGPT に認証 UI を表示させるには、ツールハンドラー内でトークンを確認し、_meta["mcp/www_authenticate"] を出力します 。トークンを調べ、発行者、オーディエンス、有効期限、スコープを検証します。有効なトークンがない場合は、_meta["mcp/www_authenticate"] を含むエラー結果を返し、その値に errorerror_description の両パラメーターを必ず含めてください。ステップ 1 と 2 を実装したうえで、この WWW-Authenticate ペイロードが実際にツール単位の OAuth UI の表示を引き起こします。チャレンジによって再認可が求められた場合、プロバイダーはそのフロー中にユーザーの既存のログインコンテキストを保持できます。

    {
      "jsonrpc": "2.0",
      "id": 4,
      "result": {
        "content": [
          {
            "type": "text",
            "text": "Authentication required: no access token provided."
          }
        ],
        "_meta": {
          "mcp/www_authenticate": [
            "'Bearer resource_metadata=\"https://your-mcp.example.com/.well-known/oauth-protected-resource\", error=\"insufficient_scope\", error_description=\"You need to login to continue\"'"
          ]
        },
        "isError": true
      }
    }