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 服务器,负责提供工具并在每次请求时验证访问令牌。
  • 授权服务器: 您的身份提供方或自定义实现,负责签发 Token 并发布发现元数据。
  • 客户端: 代表用户执行操作的 OpenAI 主机,例如 ChatGPT 或 Codex。 受支持的客户端使用客户端 ID 元数据文档(CIMD)、动态客户端注册(DCR)、 预定义的 OAuth 客户端和 PKCE。

MCP 授权规范要求

  • 在您的 MCP 服务器上托管受保护资源元数据
  • 通过您的授权服务器发布 OAuth 元数据
  • 在整个 OAuth 流程中回传 resource 参数
  • 选择 OpenAI 主机标识或注册其 OAuth 客户端的方式:CIMD、DCR 或预定义的 OAuth 客户端
  • 发布您的授权服务器接受的 Token 端点身份验证方法

以下用通俗的语言说明规范的要求。

在您的 MCP 服务器上托管受保护资源元数据

  • 您需要提供一个 HTTPS 端点,例如 GET https://your-mcp.example.com/.well-known/oauth-protected-resource(或在 401 Unauthorized 响应的 WWW-Authenticate 标头中声明同一 URL),以便 ChatGPT 知道从哪里获取您的元数据。
  • 该端点返回一个 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:指向您的身份提供方的一个或多个签发者基础 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,即使此前从未见过该 URL。

通过您的授权服务器发布 OAuth 元数据

  • 您的身份提供方必须提供以下某一种标准发现文档,以便 ChatGPT 读取其配置:
    • 位于 https://auth.yourcompany.com/.well-known/oauth-authorization-server 的 OAuth 2.0 元数据
    • 位于 https://auth.yourcompany.com/.well-known/openid-configuration 的 OpenID Connect 元数据
  • 每份文档都为 OpenAI 主机解答三个关键问题:将用户引导到哪里、如何兑换授权码,以及如何标识自身。典型的响应如下:
{
  "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:列出您的授权服务器接受的 Token 端点身份验证方法。这适用于 CIMD、DCR 和预定义的 OAuth 客户端。对于 CIMD,ChatGPT 支持使用 none 进行公共客户端 Token 兑换,以及使用 private_key_jwt 通过签名的客户端断言进行 Token 兑换。其他 OAuth 客户端通常使用 noneclient_secret_postclient_secret_basic
    • registration_endpoint:如果您支持动态客户端注册(DCR),请包含此字段。DCR 让 ChatGPT 能够为 MCP 服务器连接创建并复用专用的 client_id
    • code_challenge_methods_supported:必须包含 S256。如果 MCP 服务器的 授权服务器元数据省略此字段,或 未按 MCP 授权规范的要求声明支持 S256,则该 MCP 服务器不受支持。
    • 可选字段遵循 RFC 8414 / OpenID Discovery;请包含有助于管理员配置策略的字段。

OIDC 作用域

  • 如果您的身份提供方在其 .well-known/oauth-authorization-server.well-known/openid-configuration 文档的 scopes_supported 中声明了 OIDC 作用域(例如 openidemailprofile),ChatGPT 会在 OAuth 流程中默认请求这些作用域。
  • 某些身份提供方可能不会默认启用已声明的 OIDC 作用域。请检查您的身份提供方的配置设置,确保为 OAuth 客户端启用了每个已声明的作用域,无论该客户端使用 CIMD、由手动创建,还是通过 DCR 创建。

支持工作空间域名限制

ChatGPT Enterprise 工作空间可以验证电子邮件域名的所有权。当通过 OAuth 关联的插件提供用户已验证的电子邮件地址时,ChatGPT 可以根据其域名,阻止用户使用该企业身份在个人工作空间或组织外的其他工作空间中关联此插件。

要支持这项保护措施,请配置您的授权服务器,使其能够:

  • 发布 OpenID Connect 发现元数据。
  • 声明并启用 openidemail 作用域。
  • 声明一个 UserInfo 端点,用于返回用户的 email 声明和 email_verified: true

您也可以在 OAuth 流程中通过 ID Token 返回这些声明,但工作空间域名限制必须使用 UserInfo 端点。

企业工作空间也必须验证其域名。您的授权服务器负责提供用户身份,ChatGPT 会将该身份与工作空间配置的已验证域名进行比较;授权服务器不会验证工作空间对域名的所有权。

在重新授权期间保留登录上下文

当 ChatGPT 对现有关联重新授权(包括请求额外的 OAuth 作用域)时,可能会将先前的 OIDC ID Token 作为标准 id_token_hint 参数包含在授权请求中。要让用户无需重新开始登录即可授予额外的作用域,请将您的授权服务器配置为在首次 OAuth 流程中签发 ID Token,并在授权期间使用 id_token_hint 提供的信息。

这项优化是可选的。即使 ID Token 不可用,或您的授权服务器不使用该提示信息,重新授权仍可正常进行。

通过签发者标识保护回调

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 会在授权请求和 Token 请求中都附加 resource=https%3A%2F%2Fyour-mcp.example.com。这会将 Token 与上文所示的受保护资源元数据关联起来。
  • 请配置您的授权服务器,将该值复制到访问 Token 中(通常放在 aud 声明中),以便您的 MCP 服务器验证该 Token 是专门为其签发的,而非用于其他服务器。
  • 如果收到的 Token 不包含预期的受众或权限范围,请拒绝该 Token,并通过 WWW-Authenticate 质询提示 ChatGPT 使用正确的参数重新授权。

支持授权码流程

  • ChatGPT 作为 MCP 客户端,使用 S256 码质询执行带有 PKCE 的授权码流程,防止攻击者重放截获的授权码。
  • 您的授权服务器必须发布 code_challenge_methods_supported,并在其中包含 S256,以便客户端通过元数据确认服务器支持 PKCE。

OAuth 流程

如果您已实现上述 MCP 授权规范,OAuth 流程将如下所示:

  1. ChatGPT 向您的 MCP 服务器查询受保护资源元数据。

  1. ChatGPT 标识自身的 OAuth 客户端身份。当 MCP 服务器使用 CIMD 时,ChatGPT 会跳过动态客户端注册,并将 CIMD 文档 URL 作为 client_id 发送。对于满足上述签发者标识要求的授权服务器,ChatGPT 使用固定的 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 将授权码交换为访问 Token,并将其附加到后续的 MCP 请求中(Authorization: Bearer <token>)。

  1. 您的服务器会在每次请求中验证 Token(签发者、受众、到期时间、权限范围),然后再执行工具。

客户端注册

如果您的授权服务器支持,且插件构建者选择使用,请将客户端 ID 元数据文档(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"
}

复数形式字段在这两种文档中代表的含义不同:授权服务器元数据列出 Token 端点接受的方法,而 ChatGPT 的 CIMD 文档列出 ChatGPT 可使用的方法。ChatGPT 会从这两个集合的交集中选择一种方法。如果旧版单数形式字段指定的首选方法在交集中,ChatGPT 就会使用该方法,以兼容仍将单数形式字段视为强制要求的授权服务器。否则,ChatGPT 可以使用交集中的其他方法。

读取 CIMD 复数形式字段的授权服务器应接受 交集中的任意方法,除非本地安全策略禁止该 客户端使用某种方法。服务器必须拒绝交集之外的方法。client_id URL 保持固定,不会通过查询参数来选择特定方法对应的 文档。

支持的方法如下:

  • none:当您的 Token 端点支持基于 PKCE 的授权码交换且无需客户端身份验证时,请使用此公共客户端流程。ChatGPT 不会为每个客户端存储单独的密钥。
  • private_key_jwt:当您的 Token 端点要求客户端身份验证时,请使用此签名客户端断言流程。ChatGPT 会在其 CIMD 元数据中发布一个公开的 JWKS URL。JWKS 由元数据同源地址下的 /oauth/jwks.json 提供。ChatGPT 在服务端使用托管私钥和 kid 对 Token 请求进行签名;您的授权服务器则使用公开的 JWKS 验证该断言。

DCR 仍受支持。如果您包含 registration_endpoint,当插件构建者选择 DCR 或 CIMD 不可用时,ChatGPT 可以进行动态注册。ChatGPT 会为每个 MCP 服务器连接执行一次 DCR,然后保留并复用为该连接注册的 OAuth 客户端。大量独立连接仍可能通过 DCR 创建大量注册客户端,因此在大规模使用时,CIMD 通常更易于管理。

在 MCP 服务器连接使用期间,请确保已注册的 OAuth 客户端以及任何客户端密钥持续有效。如果您的授权服务器使其中任一凭据过期、将其删除或替换,用户和审查者在连接时可能会收到 invalid_client 错误。访问 Token 和刷新 Token 仍可正常过期或轮换。

客户端身份识别

一个常见问题是:您的 MCP 服务器如何确认请求确实来自 ChatGPT。ChatGPT 在连接 MCP 服务器时会出示由 OpenAI 管理的客户端证书,因此您可以在传输层通过 mTLS 验证客户端。您也可以将 ChatGPT 公布的出站 IP 范围加入允许列表。ChatGPT 支持客户端凭据、服务账户或 JWT 持有者断言等机器对机器 OAuth 授权方式,也无法出示自定义 API 密钥或客户提供的 mTLS 证书。

CIMD 向您的授权服务器提供一份固定的、通过 HTTPS 托管的 ChatGPT 身份声明,进一步增强客户端身份识别。使用 private_key_jwt 时,请根据 CIMD 元数据中发布的公开 JWKS,验证 ChatGPT 在 Token 端点提交的客户端断言。

双向 TLS(mTLS)

ChatGPT 现在会在与 MCP 服务器建立 TLS 连接时出示由 OpenAI 管理的客户端证书。如果您的应用会验证客户端证书,请将其配置为信任下面的 OpenAI 证书链。

在与您的 MCP 服务器建立 TLS 连接时,请按以下步骤验证客户端证书:

  • 验证叶证书是否存在,以及其证书链是否可追溯至 OpenAI 连接器 mTLS 中间 CA。
  • 验证叶证书是否可有效用于客户端身份验证。
  • 验证叶证书的 SAN dnsName 是否为 mtls.prod.connectors.openai.com
  • 避免固定叶证书指纹;OpenAI 可能会轮换叶证书,同时仍将其保留在已发布的 CA 证书链之下。

使用 mTLS 验证 ChatGPT 作为 MCP 客户端的身份。继续使用 OAuth 2.1 验证最终用户身份,并授权工具访问。

选择身份提供商

大多数 OAuth 2.1 身份提供商只要提供发现文档、支持采用 noneprivate_key_jwt 的 CIMD、在需要时支持 DCR,并将 resource 参数回传到签发的 Token 中,就能满足 MCP 授权要求。请优先选择支持通过 CIMD 注册客户端的提供商。

我们 强烈 建议您使用现有的成熟身份提供商,而不是自行从零实现身份验证。

以下是一些常用身份提供商的配置指南。

Auth0

Auth0 提供元数据发现、CIMD 注册、API 安全以及用于第一方和第三方工具调用的 Token 交换,让 MCP 客户端能够安全地连接到 MCP 服务器。

托管身份提供商示例

实现 Token 验证

OAuth 流程完成后,ChatGPT 会直接将收到的访问 Token 附加到后续 MCP 请求中(Authorization: Bearer …)。请求到达您的 MCP 服务器后,您必须将 Token 视为不受信任的数据,并自行执行完整的资源服务器检查,包括签名验证、签发者和受众匹配、到期时间检查、重放风险考量以及权限范围强制检查。这项责任由您承担,而非 ChatGPT。

在实际实现中,您应当:

  • 获取您的授权服务器发布的签名密钥(通常通过 JWKS),并验证 Token 的签名和 iss
  • 拒绝已过期或尚未生效的 Token(exp/nbf)。
  • 确认 Token 是为您的服务器签发的(audresource 声明),并且包含您标记为必需的权限范围。
  • 执行服务器特定的策略检查,然后将解析出的身份附加到请求上下文,或返回带有 WWW-Authenticate 质询的 401 响应。

如果验证失败,请返回 401 Unauthorized,并附带指向您的受保护资源元数据的 WWW-Authenticate 响应头。这会告知客户端重新运行 OAuth 流程。

SDK Token 验证基础功能

Python 和 TypeScript MCP 软件开发工具包均提供辅助功能,因此您无需从头实现这套验证逻辑。

支持多个账户

多账户功能允许用户将多个账户连接到同一个插件,例如个人账户和工作账户。OpenAI 使用所选连接中已通过身份验证的凭据来路由每次工具调用。即使没有账户资料工具,用户也可以连接多个账户。为帮助用户区分连接,并在重新连接后识别同一账户资料,请提供一个需要身份验证的账户资料工具,返回稳定的 ID 和有用的显示元数据。

用户如何使用多账户功能

用户可以在插件的设置页面连接更多账户。模型可以使用所有已连接的账户,并在调用工具时根据用户请求选择相关的一个或多个账户。每次工具调用都使用所选账户的凭据和权限。

改进账户识别

为帮助 OpenAI 识别已连接的账户资料并显示有用的标签:

  • 提供一个需要身份验证的账户资料工具,返回一个不透明 ID,用于唯一且稳定地标识请求凭据所代表的账户资料。这样,OpenAI 就能在重新连接后识别同一账户资料,并将其与其他账户资料区分开。名为 id 的字段只有在其值满足这些保证时,才能用于此目的。
  • 在 MCP 元数据中指定账户资料工具,让 OpenAI 能够发现应调用哪个工具来获取经过身份验证的账户资料信息。

需要账户资料信息时,OpenAI 会在运行时发现指定的工具,使用连接的凭据调用该工具,并在使用账户资料数据前验证响应。即使没有账户资料工具,用户仍然可以连接账户,但账户标签、账户识别或重复账户检测的可靠性可能会降低。如果您声明了账户资料工具,请返回有效的身份信息;无效响应可能导致账户无法连接。

定义稳定的账户资料身份标识

账户资料用于标识请求中已通过身份验证的凭据所代表的身份。哪些账户资料可以独立连接,由您的服务定义;此约定不规定您的服务的组织模型或授权模型。

返回一个在您的应用中唯一的不透明账户资料 ID。同一账户资料的 ID 必须在 Token 刷新和重新连接后保持不变;不同账户资料必须具有不同的 ID。OpenAI 会比较这些 ID,但不会解读其内容。

如果提供方现有的不可变、不透明 ID 能够完整标识账户资料,请使用该 ID。否则,请为账户资料分配一次不透明 ID,持久化保存两者的关联,并在后续请求中检索同一个 ID。所有内部关系都应保留在您的服务中;不要将姓名、电子邮件地址或组织关系编码到返回的 ID 中。

您的 id 必须:

  • 是非空且不全由空白字符组成的字符串。将提供方的数字 ID 序列化为字符串。
  • 对于同一账户资料,在 Token 刷新、重新连接和权限范围升级后保持不变。
  • 对于可通过应用连接的不同账户资料,具有不同的值。
  • 在账户资料的电子邮件地址、名称或显示标签发生变化时保持不变。
  • 在账户资料删除后,永不重新分配给其他账户资料。

不要为每次登录、每个 Token、每个会话或每次工具调用生成新的 ID。电子邮件地址和可编辑的名称应放在显示元数据中:可能变更或被重新分配的电子邮件地址不能用作稳定的账户资料 ID。对于 Google OIDC,请使用稳定的 sub,而非电子邮件声明;Google 文档说明,电子邮件地址可能会变更,而 sub 保持不变且永不重复使用。请参阅 Google 身份文档

更新集成时,请保留现有的账户资料 ID。显示名称变更、新 Token 或新连接都不得创建新的账户资料身份标识。

实现并声明您的账户资料工具

提供一个需要身份验证的只读工具,接受空参数对象并返回当前账户资料。该工具可以命名为 get_profilewhoami 或其他名称;其元数据会将其标识为账户资料工具,以便在运行时发现。响应必须满足下述身份要求,以便 OpenAI 正确使用它。

  • 根据请求中已验证的凭据确定身份。
  • 确保该操作为只读,并且使用常规连接的权限即可执行。
  • 仅返回一份账户资料,即当前请求的凭据所代表的账户资料。
  • 不要要求调用方提供用户 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 的唯一性、稳定性,以及凭据与账户资料的正确对应关系;无论是元数据还是通过模式检查,都不能证明这些行为层面的属性。

如果有 nameemail 和/或 nickname,请将其包含在响应中,以便用户区分账户资料。省略无法获取的可选值;不要编造这些值,也不要添加无关的个人数据。将有用且便于人阅读的上下文信息放在 nickname 中,而不是 ID 中。

使用 _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 权限范围,请使用您的集成实际所需的权限范围。该声明不会实现身份验证;服务器必须验证凭据并执行权限检查。请参阅实现 Token 验证工具参考资料

structuredContent 中返回账户资料,以便根据 outputSchema 对其进行验证。为确保兼容性,还应在一个文本内容项中包含序列化为 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 独立连接两份账户资料。Moonwaffle 为每份账户资料存储不同的不透明 ID。请求凭据会对应到其中一份已存储的账户资料,账户资料工具则返回其现有 ID。

已存储的账户资料示例。 标签可以变化,但标识符保持不变:

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

这些示例 ID 不编码账户资料标签或内部关系。每份账户资料的 ID 只需持久化保存一次,并在重新连接、Token 刷新以及电子邮件地址或显示名称变更后继续使用。

根据已通过身份验证的账户资料构建响应。 此 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
Token 刷新、重新连接或权限范围升级后的同一账户资料prf_8d7e4b19
电子邮件地址或显示标签变更后的同一账户资料prf_8d7e4b19;标签可以变化
Moonwaffle 个人账户资料prf_42a9c6e0,与工作账户资料不同
持久化的账户资料 ID 缺失或为空白返回错误结果;不编造身份,也不回退到其他账户资料

身份标识保证必须适用于所有账户资料,并在集成今后的变更中持续成立。该保证不应受显示元数据、Token 内容或连接生命周期事件的影响。

测试与上线

  • 本地测试: 先使用签发短期有效 Token 的开发租户,以便快速迭代。
  • 内部试用: 身份验证正常运行后,先仅向可信的测试人员开放访问,再大范围上线。您可以要求用户先关联账户,才能使用特定工具或整个 MCP 服务器。
  • 轮换: 提前规划 Token 撤销、刷新和权限范围变更的处理方式。您的服务器应将 Token 缺失或失效的请求视为未通过身份验证,并返回有助于解决问题的错误消息。
  • OAuth 调试: 使用 MCP Inspector 的身份验证设置逐步检查 OAuth 流程,在发布前准确定位流程出错的环节。

配置好身份验证后,您就可以向 ChatGPT 和 Codex 用户提供用户专属数据和写入操作。

触发身份验证界面

只有当您的 MCP 服务器表明 OAuth 可用或必需时,ChatGPT 才会显示 OAuth 账户关联界面。

触发工具级 OAuth 流程需要元数据(securitySchemes 和资源元数据文档) 以及 携带 _meta["mcp/www_authenticate"] 的运行时错误。两者缺一不可,否则 ChatGPT 不会显示该工具的账户关联界面。

  1. 发布资源元数据。 MCP 服务器必须通过 https://your-mcp.example.com/.well-known/oauth-protected-resource 等约定的发现 URL 提供其 OAuth 配置。

  2. 使用 securitySchemes 描述每个工具的身份验证策略。 为每个工具声明 securitySchemes,可以告知 ChatGPT 哪些工具需要 OAuth,哪些可以匿名运行。即使整个服务器使用相同的策略,也应坚持逐工具声明;服务器级默认设置会使后续单独调整某个工具变得困难。

    目前有两种方案类型可用,您可以同时列出多种类型,表示身份验证是可选的:

    • noauth:该工具可匿名调用;ChatGPT 可以立即运行它。
    • oauth2:该工具需要 OAuth 2.0 访问 Token;请列出您将请求的权限范围,以确保授权同意界面显示准确。

    如果完全省略该数组,工具将继承服务器公布的默认设置。同时声明 noauthoauth2,可以告知 ChatGPT 先匿名调用工具,关联账户后即可使用需要相应权限的功能。无论您向客户端声明什么,服务器仍必须在每次调用时验证 Token、权限范围和受众。

    示例(公开访问 + 可选身份验证):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 触发身份验证界面时,请在工具处理函数中检查 Token,并返回 _meta["mcp/www_authenticate"] 。检查 Token,验证其签发者、受众、有效期和权限范围。如果没有有效的 Token,请返回包含 _meta["mcp/www_authenticate"] 的错误结果,并确保该值同时包含 errorerror_description 参数。完成步骤 1 和 2 后,真正触发工具级 OAuth 界面的就是这个 WWW-Authenticate 载荷。当质询要求重新授权时,您的提供商可以在此流程中保留用户现有的登录上下文

    示例

    {
      "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
      }
    }