MCP 连接器的 OpenID 配置修复方案

这周我花了太多时间来修复一个远程 MCP 连接器。

该连接器一直无法找到我的 OAuth 服务器。服务器运行正常。问题在于缺少了一个路由和一个重定向。

当你在 MCP 中使用 OAuth 时,你会期望发现文档(discovery documents)能够正常工作。大多数工具会寻找以下两个路径:

  • /.well-known/oauth-authorization-server
  • /.well-known/oauth-protected-resource

这些路径告知客户端在哪里可以找到授权和令牌端点。

问题在于,许多客户端并不会寻找这些特定的路径。相反,它们会寻找 /.well-known/openid-configuration。

这是一个 OpenID Connect 路径。虽然它是不同的规范,但它位于相同的位置。我的包没有注册这个路径,因为它遵循的是 OAuth 规范,而不是 OIDC 规范。

客户端敲响了一扇不存在的门。它收到了 404 错误并退出了。

我有两个选择:

  1. 在 Nginx 中使用反向代理重定向。这是一种偷懒的修复方法。它将逻辑从代码中移到了基础设施中。这种方式难以测试,且在部署过程中容易出错。

  2. 在应用程序内部进行修复。这是更好的方法。

我选择让应用程序来响应探测。我创建了一个别名,将 OpenID 路径重定向到 OAuth 授权路径。

我使用了 308 永久重定向(Permanent Redirect)。

302 重定向可能会将 POST 请求更改为 GET 请求。而 308 重定向则非常严格。它告诉客户端跳转到新 URL,并保持相同的请求方法和请求体。这是处理永久迁移的正确方式。

我还将此功能放在了一个配置标志(configuration flag)之后。这允许用户在运行自己的 OIDC 发现服务时将其关闭。

通过在代码中实现这一点,我可以编写测试:

  • 一个测试用于检查重定向是否正确发生。
  • 一个测试用于跟随重定向,以确保元数据(metadata)是有效的。

这确保了如果元数据结构发生变化,我的测试会立即失败。我可以在流水线(pipeline)中发现错误,而不是等到用户无法连接时才发现。

在实践中,规范往往存在差异。即使两个标准的目标相似,客户端也会选择不同的路径。作为服务器开发者,你应该同时响应这两扇“门”。

将它们指向同一个房间,使用正确的重定向状态码,并用测试来保障。

让 MCP 连接器即插即用的 OpenID 配置别名

如果你正在构建 Model Context Protocol (MCP) 连接器,你可能已经遇到过这样一个问题:如何让你的连接器能够轻松地与各种身份提供商(Identity Providers, IdPs)集成,而无需手动配置每一个端点(endpoint)?

答案就在一个非常古老但极其强大的标准中:OpenID Connect (OIDC) 的发现机制(Discovery mechanism)。

问题所在

当你创建一个 MCP 连接器时,你通常需要与某个身份提供商进行交互。为了完成身份验证,你需要知道以下信息:

  • 授权端点 (Authorization Endpoint)
  • 令牌端点 (Token Endpoint)
  • 用户信息端点 (UserInfo Endpoint)
  • JWKS 端点 (JWKS Endpoint)

如果你的连接器要求用户手动输入这些 URL,用户体验会非常糟糕。更糟糕的是,如果身份提供商更改了这些端点,你的连接器就会失效。

解决方案:.well-known/openid-configuration

这就是 OpenID Connect 发现机制发挥作用的地方。

几乎所有的现代身份提供商(如 Google, Microsoft, Auth0, Okta 等)都遵循一个标准,即在特定的路径下提供一个 JSON 文档。这个路径通常是:

https://<your-issuer-url>/.well-known/openid-configuration

这个文档包含了该身份提供商所需的所有端点信息。

为什么这对 MCP 至关重要?

MCP 连接器的目标是实现高度的互操作性和易用性。通过利用这个“别名”(alias)或发现端点,你可以实现以下目标:

  1. 简化配置:用户只需要提供一个 issuer URL(例如 https://accounts.google.com),你的 MCP 连接器就可以自动推导出所有必要的端点。
  2. 提高鲁棒性:如果身份提供商更新了其端点,只要它们更新了发现文档,你的连接器就能自动适应,无需重新配置。
  3. 降低摩擦:减少了用户在配置过程中需要处理的技术细节。

如何实现

在你的 MCP 连接器代码中,实现逻辑非常简单:

  1. 获取用户提供的 issuer URL。
  2. 在该 URL 后拼接 /.well-known/openid-configuration
  3. 发送一个 GET 请求获取该 JSON 文档。
  4. 从返回的 JSON 中提取所需的端点。
// 伪代码示例
const issuerUrl = "https://example.com";
const discoveryUrl = `${issuerUrl.replace(/\/$/, "")}/.well-known/openid-configuration`;

const response = await fetch(discoveryUrl);
const config = await response.json();

const authEndpoint = config.authorization_endpoint;
const tokenEndpoint = config.token_endpoint;

结论

虽然 .well-known/openid-configuration 并不是专门为 MCP 设计的,但它是一个极其强大的工具。通过利用这个现有的标准,我们可以让 MCP 连接器的集成变得更加简单、可靠且用户友好。

如果你正在开发 MCP 生态系统中的工具,请务必考虑集成 OIDC 发现机制。