MCP-OAuth 服务器
为创建支持可流式传输的HTTP和SSE传输并带有OAuth授权的MCP服务器提供了一个参考实现,使开发人员能够以最少的配置构建带有OAuth授权的MCP服务器。
服务介绍
🌊 HTTP + SSE MCP 服务器与 OAuth
简介
此仓库提供了一个参考实现,用于创建支持可流式传输的 HTTP 和 SSE 传输协议的远程 MCP 服务器,并基于 MCP 规范使用 OAuth 进行授权。
请注意,此仓库中的 MCP 服务器在逻辑上是独立于处理报告 SSE + HTTP 传输的应用程序以及 OAuth 的。
因此,您可以轻松地 fork 此仓库,并插入您自己的 MCP 服务器和 OAuth 凭证,以实现具有您自己功能的 SSE/HTTP + OAuth MCP 服务器。
但是,为什么?
非常好的问题!MCP 规范在 2025 年 3 月 25 日增加了基于 OAuth 的授权规范。截至 2025 年 5 月 1 日:
- TypeScript SDK 包含了许多构建 OAuth 授权的 MCP 服务器所需的组件,但没有相关文档或教程说明如何构建这样的服务器
- Python SDK 既不包含可流式传输的 HTTP 传输实现,也不包含 TypeScript SDK 中存在的 OAuth 组件
- 可流式传输的 HTTP 传输在诸如 Cursor 和 Claude 桌面等 MCP 主机应用程序中广泛不受支持,尽管可以使用 JS/TS SDK 中的
StreamableHttpClientTransport类直接集成到用 JavaScript 编写的代理中
在 Naptha AI,我们非常希望构建一个基于可流式传输的 HTTP 传输的 OAuth 授权 MCP 服务器,但找不到任何参考实现,所以我们决定自己构建一个!
依赖项
Bun 是一个快速的一体化 JavaScript 运行时,推荐作为此仓库的运行时和包管理器。已对 npm + tsc 进行了有限的兼容性测试。
概述
此仓库提供了以下内容:
- 一个 MCP 服务器,您可以轻松替换为您自己的服务器
- 一个 express.js 应用程序,该应用程序同时管理 SSE 和可流式传输的 HTTP 传输以及 OAuth 授权。
您可以将您的凭证和 MCP 服务器插入到这个 express 应用程序中。
请注意,虽然此 express 应用程序实现了所需的 OAuth 端点(包括 /authorize 和授权服务器元数据端点 (RFC8414)),但它并没有实现 OAuth 授权服务器!
此示例将 OAuth 代理到支持动态客户端注册 (RFC7591) 的上游 OAuth 服务器。要使用此示例,您需要自带授权服务器。我们建议使用 Auth0;请参阅下面的 "设置 OAuth" 部分。
配置您的服务器
关于 OAuth 和动态客户端注册的注意事项
要使用此示例,您需要一个 OAuth 授权服务器。不要自己实现! 在创建我们的演示时,我们使用了 Auth0 —— 这是一个很好的选择,当然还有很多其他选择。MCP 规范要求支持一种不常见的 OAuth 功能,即 RFC7591,动态客户端注册。MCP 规范规定 MCP 客户端和服务器应支持动态客户端注册协议,以便 MCP 客户端(无论您的客户端传输位于何处)可以在没有用户注册的情况下获取客户端 ID。这允许新的客户端(代理、应用程序等)自动向新服务器注册。更多详细信息请参见 MCP 规范的授权部分,但这意味着您不能简单地直接代理到像 Google 或 GitHub 这样的提供商,因为它们不支持动态客户端注册(它们要求您在他们的 UI 中注册客户端)。
这为您留下了两个选项:
- 选择一个上游 OAuth 提供商,如 Auth0,它允许您使用 OIDC 身份提供商(如 Google 和 GitHub)进行身份验证,并且 确实 支持动态客户端注册;或者
- 在应用程序中自行实现动态客户端注册(即,Express 应用程序不仅是一个简单的 OAuth 代理,而是一个完整的或部分完整的 OAuth 服务器)。Cloudflare 为他们的 Workers OAuth MCP 服务器实现了类似的功能,我们可能会在以后扩展这个项目。您可以在这里找到相关信息:GitHub - Cloudflare/workers-oauth-provider。
为了简化起见,我们选择了第一个选项,使用 Auth0。
[!NOTE]
由于此实现代理了上游 OAuth 服务器,默认将访问令牌从 OAuth 服务器转发到客户端的方法会将用户的上游访问令牌暴露给下游客户端和 MCP 主机。这对许多用例来说是不合适的,因此这种方法重新实现了@modelcontextprotocol/typescript-sdk中的一些类来解决这个问题。
请注意,虽然我们在代理上游授权服务器,但我们 不会 将最终用户的认证令牌返回给 MCP 客户端/主机——相反,我们会自己颁发令牌,并允许客户端/主机使用该令牌与我们的服务器进行授权。这可以防止恶意客户端或主机滥用令牌,或在令牌泄露时被滥用。
使用 Auth0 设置 OAuth
要开始使用 Auth0,请执行以下步骤:
- 在 Auth0.com 上创建一个 Auth0 账户。
- 创建至少一个连接到 IDP(如 Google 或 GitHub)的连接。您可以在这里学习如何操作。
- 将连接提升为 域级连接。由于每个 MCP 客户端都会注册新的 OAuth 客户端,因此您无法按应用程序/客户端配置 IDP 连接。这意味着您的连接需要对域中的所有应用程序可用。您可以在这里学习如何操作。
- 启用动态客户端注册(Auth0 也称其为“动态应用程序注册”)。您可以在这里学习如何操作。
完成上述所有设置后,您需要以下信息:
- 您的 Auth0 客户端 ID
- 您的 Auth0 客户端密钥
- 您的 Auth0 租户域名
确保将这些信息填写到您的 .env 文件中。复制 .env.template 并更新值以匹配您的配置和密钥。
运行服务器
此仓库包含两个独立的独立服务器:
- 一个 无状态 的可流式 HTTP 服务器实现,位于
src/app.stateless.ts。它仅支持可流式的 HTTP 传输,并且(理论上)适合无服务器部署。- 在src/app.stateful.ts中提供了一个有状态的SSE和可流式HTTP实现。此应用程序提供了这两种传输方式,但即使使用redis存储策略时也保持内存中的状态(连接必须在内存中持久化),因此它不适合无服务器部署或简单的水平扩展。
你可以使用 bun 运行其中任何一个:
shell
bun run src/app.stateless.ts
或者,
bun run src/app.stateful.ts
将所有内容整合在一起
要测试我们的支持可流式HTTP和OAuth的MCP服务器,你有几个选择。
如上所述,Python MCP SDK不支持这些功能,所以目前你可以将我们的远程服务器插入到像Cursor或Claude Desktop这样的MCP主机中,或者直接插入到TypeScript/JavaScript应用程序中——但不能插入到Python应用中。
将你的服务器接入MCP主机(Cursor / Claude)
由于大多数MCP主机不支持可流式HTTP(在许多方面优于SSE)_或_OAuth,我们建议使用mcp-remote npm包来处理OAuth授权,并将远程传输桥接到STDIO传输以供主机使用。
命令如下所示:
shell
bunx mcp-remote --transport http-first https://some-domain.server.com/mcp
或者,
npx mcp-remote --transport http-first https://some-domain.server.com/mcp
对于--transport选项,你有几个选择:
http-first(默认):首先尝试HTTP传输,如果HTTP因404错误失败,则回退到SSE。sse-first:首先尝试SSE传输,如果SSE因405错误失败,则回退到HTTP。http-only:仅使用HTTP传输,如果服务器不支持则失败。sse-only:仅使用SSE传输,如果服务器不支持则失败。
[!NOTE]
如果你使用src/app.stateless.ts启动了无状态版本的服务器,那么SSE传输不可用,因此你应该使用--transport http-only。如果你使用这个入口点,不应该期望SSE传输工作。
将你的服务器接入代理
你可以使用StreamableHTTPClientTransport将你的可流式HTTP服务器接入JS/TS中的代理。然而,这不适用于受OAuth保护的服务器。相反,你应该在客户端使用Authorization头,并在服务器端使用有效的访问令牌。
你可以通过客户端凭证、API密钥或其他方式实现这一点。这种模式在这个仓库中不受支持,但使用Vercel AI SDK看起来会是这样:
typescript
import { openai } from "@ai-sdk/openai";
import { experimental_createMCPClient as createMcpClient, generateText } from "ai";
import { StreamableHTTPClientTransport } from "@modelcontextprotocol/sdk/client/streamableHttp.js";
const mcpClient = await createMcpClient({
transport: new StreamableHTTPClientTransport(
new URL("http://localhost:5050/mcp"), {
requestInit: {
headers: {
Authorization: "Bearer YOUR TOKEN HERE",
},
},
// TODO 如果需要添加OAuth客户端提供商
authProvider: undefined,
}),
});
const tools = await mcpClient.tools();
await generateText({
model: openai("gpt-4o"),
prompt: "Hello, world!",
tools: {
...(await mcpClient.tools())
}
});