🌊 HTTP+SSE MCP服务器,带OAuth
引言
此仓库提供了一个参考实现,用于创建支持流式HTTP和SSE传输的远程MCP服务器,该服务器基于MCP规范通过OAuth授权。
请注意,此仓库中的MCP服务器在逻辑上与处理报告SSE+HTTP传输的应用程序以及OAuth是分开的。
因此,您可以轻松地分叉此仓库,并插入您自己的MCP服务器和OAuth凭据,以获得具有您自己功能的工作SSE/HTTP+OAuth MCP服务器。
但是,为什么呢?
好问题!MCP规范于2025年3月25日添加了基于OAuth的授权规范。目前,截至2025年5月1日:
- Typescript SDK包含许多构建块,用于实现具有流式HTTP的OAuth授权MCP服务器, 但是没有文档或教程 如何构建这样一个服务器
- Python SDK既不包含流式HTTP传输的实现,也不包含typescript SDK中存在的OAuth构建块的实现
- MCP主机应用程序(如Cursor和Claude桌面)广泛不支持流式HTTP传输,尽管它可以使用JS/TS SDK直接集成到用JavaScript编写的代理中
StreamableHttpClientTransport类
在 石脑油AI,我们真的很想在流式HTTP传输上构建一个OAuth授权的MCP服务器,但找不到任何参考实现,所以我们决定自己构建一个!
依赖项
包子,一个快速的一体化JavaScript运行时,是此存储库的推荐运行时和包管理器。已完成有限兼容性测试 npm + tsc.
概述
此存储库提供以下功能:
- MCP服务器,您可以轻松地用自己的服务器替换
- 一个管理express.js的应用程序 _两者_ SSE和流式HTTP传输 _和_ OAuth授权。
这个快速应用程序是您将凭据和MCP服务器插入其中的应用程序。
请注意,虽然此express应用程序实现了所需的OAuth端点,包括 /authorize 以及授权服务器元数据端点(RFC8414), _它没有实现OAuth授权服务器!_
配置服务器
OAuth和动态客户端注册说明
要使用此示例,您需要一个OAuth授权服务器。 _不要自己动手!_ 为了创建我们的演示,我们使用了 身份验证0 --这是一个很好的选择,尽管还有许多其他选择。
这给你留下了两个选择:
- 选择一个像Auth0这样的上游OAuth提供程序,它允许您使用像谷歌和GitHub这样的OIDC IDPs进行身份验证 _做_ 支持动态客户端注册,或
- 在应用程序中自己实现动态客户端注册(即,express应用程序不仅成为一个简单的OAuth代理,而且成为一个完整或部分完整的OAuth服务器)。Cloudflare为其Workers OAuth MCP服务器实现了类似的功能,我们稍后可能会扩展此项目。你可以找到 这里.
为了简单起见,我们选择了使用Auth0的前一个选项。
\[!注意\]\ 由于此实现代理了上游OAuth服务器,因此将访问令牌从OAuth服务器转发到客户端的默认方法将用户的上游访问令牌暴露给下游客户端和MCP主机。这不适合许多用例,因此这种方法重新实现了一些 @modelcontextprotocol/typescript-sdk 类来解决这个问题。请注意,当我们代理上游授权服务器时,我们 _不_ 将最终用户的身份验证令牌返回给MCP客户端/主机-相反,我们正在发行自己的令牌,并允许客户端/主机使用该令牌向我们的服务器进行授权。这可以防止恶意客户端或主机滥用令牌,或者在令牌泄露时被滥用。
使用Auth0设置OAuth
要开始使用Auth0,请执行以下操作:
- 在以下位置创建Auth0帐户 Auth0.com.
- 至少创建一个与IDP(如Google或GitHub)的连接。你可以 在这里学习如何做到这一点.
- 促进与 _域级连接_。由于每个MCP客户端都注册了新的OAuth客户端,因此您无法在每个应用程序/客户端的基础上配置IDP连接。这意味着您的连接需要可用于域中的所有应用程序。你可以 在这里学习如何做到这一点.
- 启用动态客户端注册(auth0也称此为“动态应用程序注册”)。你可以 在这里学习如何做到这一点.
所有这些设置完毕后,您将需要以下信息:
- 您的Auth0客户端ID
- 您的Auth0客户端密码
- 您的Auth0租户域
请确保将此信息填写到您的 .env.复制 .env.template 然后使用您的配置和机密更新这些值。
运行服务器
此存储库包括两个独立的服务器:
- 一 无状态 流式HTTP服务器的实现
src/app.stateless.ts。这只支持可流式传输的HTTP,并且(理论上)适用于无服务器部署 - 一 有状态的 SSE和流式HTTP在
src/app.stateful.ts。此应用程序提供这两种传输方式,但即使在使用时也会保持内存状态redis存储策略(连接必须持久化在内存中),因此它不适合无服务器部署或微不足道的水平扩展。
你可以用它们中的任何一个来运行 bun:
bun run src/app.stateless.ts
# or,
bun run src/app.stateful.ts把它们放在一起
要测试我们的MCP服务器是否支持流式HTTP和OAuth,您有几个选择。
如上所述,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传输将无法工作。
将服务器插入代理
您可以使用以下命令将Streamable HTTP服务器插入JS/TS中的代理 StreamableHTTPClientTransport。但是,这不适用于受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())
}
});
