关于 🌊 HTTP + SSE MCP Server w/ OAuth
这是一个参考实现,展示如何构建支持 Streamable HTTP 与 SSE 传输并采用 OAuth 授权的远程 MCP 服务,基于 MCP 规范的授权扩展。它面向需要自建远程服务的 TypeScript 开发者,可替换其中的 MCP 服务逻辑与 OAuth 凭据。
主要功能
- 01Streamable HTTP 与 SSE 传输支持
- 02基于 Express 的 OAuth 授权代理
- 03动态客户端注册(RFC7591)支持
- 04提供无状态与有状态两种入口
- 05可通过 mcp-remote 接入 Cursor 或 Claude
适用场景
- 01为自定义 MCP 服务添加 OAuth 授权的远程访问
- 02在 Cursor 或 Claude Desktop 中通过 mcp-remote 连接远程服务
- 03参考 Auth0 配置动态客户端注册
MCP 相关
该项目是远程 MCP 服务的参考实现,通过 Streamable HTTP 与 SSE 向 MCP 客户端提供服务,并以 OAuth 进行授权。
配置
本地服务的配置在各客户端中通用,复制后粘贴到客户端的 MCP 配置文件即可。
我们还没有获取到这个服务的安装包或远程地址,请按项目 README 中的说明安装。
工具
本地服务需要在你的电脑上运行后才能读取工具,维护者暂未提供工具列表。
详细介绍
🌊 支持 OAuth 的 HTTP + SSE MCP 服务
简介
本仓库提供了一个参考实现,用于创建支持 Streamable HTTP 和 SSE 传输协议、并根据 MCP 规范通过 OAuth 进行授权的远程 MCP 服务。
请注意,本仓库中的 MCP 服务在逻辑上与处理报告 SSE + HTTP 传输协议的应用以及 OAuth 分离。
因此,你可以轻松地复刻本仓库,并接入自己的 MCP 服务和 OAuth 凭据,从而构建一个具备自有功能的 SSE/HTTP + OAuth MCP 服务。
不过,为什么要这样做?
好问题!MCP 规范于 2025 年 3 月 25 日新增了基于 OAuth 的授权规范。截至 2025 年 5 月 1 日:
- Typescript SDK 包含了许多构建支持 Streamable HTTP 的 OAuth 授权 MCP 服务所需的基础组件,但没有相关文档或教程
- Python SDK 既未实现 Streamable HTTP 传输协议,也未实现 Typescript SDK 中提供的 OAuth 基础组件
- Cursor 和 Claude 桌面版等 MCP 主机应用普遍不支持 Streamable HTTP 传输协议,不过可以直接将其集成到使用 JS/TS SDK 的
StreamableHttpClientTransport类编写的 JavaScript Agent 中
在 Naptha AI,我们非常希望构建一个使用 Streamable HTTP 传输协议、通过 OAuth 授权的 MCP 服务,但找不到任何参考实现,于是决定自己动手构建一个!
依赖项
Bun 是一款快速的一体化 JavaScript 运行时,也是本仓库推荐使用的运行时和包管理器。我们仅使用 npm + tsc 进行了有限的兼容性测试。
概览
本仓库提供以下内容:
- 一个 MCP 服务,你可以轻松替换为自己的服务
- 一个同时管理 SSE 和 Streamable HTTP 传输协议及 OAuth 授权的 express.js 应用。
你可以将自己的凭据和 MCP 服务接入这个 express 应用。
请注意,虽然这个 express 应用实现了必需的 OAuth 端点,包括 /authorize 和授权服务器元数据端点(RFC8414),但它并未实现 OAuth 授权服务器!
此示例会将 OAuth 请求代理到支持动态客户端注册的上游 OAuth 服务器(RFC7591)。使用此示例需要自备授权服务器。我们推荐使用 Auth0;请参阅下文的“设置 OAuth”部分。
配置你的服务
关于 OAuth 和动态客户端注册的说明
要使用此示例,你需要一个 OAuth 授权服务器。请勿自行实现! 为了创建演示,我们使用了 Auth0 ——这是一个很不错的选择,不过还有许多其他选项。
MCP 规范要求支持一项不常见的 OAuth 功能,具体来说就是 RFC7591 中的动态客户端注册。MCP 规范规定,MCP 客户端和服务端应支持动态客户端注册协议,这样 MCP 客户端(无论客户端传输方式在哪里运行)都可以在无需用户注册的情况下获取客户端 ID。这样一来,新的客户端(Agent、应用等)就可以自动向新的服务注册。更多详情请参阅 MCP 规范中的授权部分。但遗憾的是,这意味着你不能直接代理到 Google 或 GitHub 这类不支持动态客户端注册的提供商(它们要求你在其 UI 中注册客户端)。
因此,你有两个选择:
- 选择像 Auth0 这样的上游 OAuth 提供商,它支持使用 Google 和 GitHub 等 OIDC 身份提供商进行身份验证,并且_支持_动态客户端注册;或者
- 在应用中自行实现动态客户端注册(也就是说,Express 应用不再只是一个简单的 OAuth 代理,而是一个完整或部分完整的 OAuth 服务器)。Cloudflare 为其 Workers OAuth MCP 服务实现了类似功能,我们之后可能会将其扩展到本项目中。你可以在这里找到相关内容。
为求简单,我们选择了前一种方案,使用 Auth0。
说明
由于此实现会代理上游 OAuth 服务器,默认将 OAuth 服务器的访问令牌转发给客户端的做法,会导致用户的上游访问令牌暴露给下游客户端和 MCP 主机。这不适用于许多使用场景,因此此方案重新实现了部分@modelcontextprotocol/typescript-sdk类来解决此问题。
请注意,虽然我们代理了上游授权服务器,但_不会_将最终用户的身份验证令牌返回给 MCP 客户端/主机,而是签发自己的令牌,并允许客户端/主机使用该令牌向我们的服务进行授权。这样可以防止恶意客户端或主机滥用令牌,也能避免令牌泄露后被滥用。
使用 Auth0 设置 OAuth
开始使用 Auth0:
- 在 Auth0.com 创建一个 Auth0 账号。
- 至少创建一个连接到 Google 或 GitHub 等身份提供商的连接。你可以在此了解如何操作。
- 将此连接提升为_域级连接_。由于每个 MCP 客户端都会注册新的 OAuth 客户端,因此你无法按应用/客户端配置身份提供商连接。这意味着你的连接需要对域中的所有应用开放。你可以在此了解如何操作。
- 启用动态客户端注册(Auth0 也称之为“动态应用注册”)。你可以在此了解如何操作。
完成上述设置后,你需要准备以下信息:
- 你的 Auth0 客户端 ID
- 你的 Auth0 客户端密钥
- 你的 Auth0 租户域名
请务必将这些信息填入 .env。复制 .env.template,然后使用你的配置和密钥更新相应值。
运行服务
此代码库包含两个独立的独立服务:
src/app.stateless.ts中的无状态流式 HTTP 服务实现。它仅支持流式 HTTP 传输,(理论上)适合无服务器部署src/app.stateful.ts中同时支持 SSE 和流式 HTTP 的有状态实现。此应用支持两种传输方式,但即使使用redis存储策略,也会在内存中维护状态(连接必须持久保存在内存中),因此不适合无服务器部署或简单的水平扩展。
你可以使用 bun 运行其中任意一个:
bun run src/app.stateless.ts
# or,
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 传输。
命令如下:
bunx mcp-remote --transport http-first https://some-domain.server.com/mcp
# or,
npx mcp-remote --transport http-first https://some-domain.server.com/mcp
--transport 选项有几种选择:
http-first(默认):优先尝试 HTTP 传输;如果 HTTP 返回 404 错误,则回退到 SSEsse-first:优先尝试 SSE 传输;如果 SSE 返回 405 错误,则回退到 HTTPhttp-only:仅使用 HTTP 传输;如果服务不支持,则失败sse-only:仅使用 SSE 传输;如果服务不支持,则失败
说明
如果你使用
src/app.stateless.ts启动服务的_无状态_版本,则无法使用 SSE 传输,因此应使用--transport http-only。如果使用此入口点,不要期待 SSE 传输能够正常工作。
将服务接入 Agent
你可以在 JS/TS 中使用 StreamableHTTPClientTransport 将流式 HTTP 服务接入 Agent。但这无法用于受 OAuth 保护的服务。相反,你应在客户端使用 Authorization 标头,并在服务端使用有效的访问令牌。
你可以使用客户端凭据、API 密钥或其他方式实现此功能。此代码库不支持这种模式,但使用 Vercel AI SDK 时,代码示例如下:
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 add OAuth client provider if you want
authProvider: undefined,
}),
});
const tools = await mcpClient.tools();
await generateText({
model: openai("gpt-4o"),
prompt: "Hello, world!",
tools: {
...(await mcpClient.tools())
}
});
评论
一个开放协议,规定了 AI 应用如何以统一方式连接外部工具与数据源。客户端负责与模型对话,MCP 服务负责提供工具、资源和提示模板,两者通过标准消息通信,同一个服务可以被不同客户端复用。