此仓库提供了一个参考实现,用于创建支持流式HTTP和SSE传输的远程MCP服务器,并通过基于OAuth的身份验证来授权,符合MCP规范。
请注意,本仓库中的MCP服务器在逻辑上与处理报告SSE+HTTP传输的应用程序以及OAuth是分开的。
因此,您可以轻松地分叉这个仓库,并插入您自己的MCP服务器和OAuth凭证,以构建一个具有您自己功能的运行中的SSE/HTTP+OAuth MCP服务器。
但是,为什么?
非常好的问题!MCP规范于2025年3月25日添加了基于OAuth的授权规范。截至2025年5月1日:
StreamableHttpClientTransport类编写的代理中在Naptha AI,我们确实想构建一个基于流式HTTP传输的OAuth授权MCP服务器,但找不到任何参考实现,所以我们决定自己构建一个!
Bun,一种快速的一体化JavaScript运行时,是推荐的运行时和包管理器。对npm+tsc进行了有限的兼容性测试。
此仓库提供了以下内容:
这个express应用程序就是您插入凭证和MCP服务器的地方。
请注意,虽然此express应用实现了所需的OAuth端点,包括/authorize和授权服务器元数据端点(RFC8414),但它不实现OAuth授权服务器!
此示例将OAuth代理到支持动态客户端注册的上游OAuth服务器(RFC7591)。要使用此示例,您需要自带授权服务器。我们建议使用Auth0;请参阅下面的"设置OAuth"部分。
要使用此示例,您需要一个OAuth授权服务器。**不要自行实现!**为了创建我们的演示,我们使用了Auth0——这是一个很好的选择,尽管还有许多其他选项。
MCP规范要求支持一种不常见的OAuth特性,即RFC7591,动态客户端注册。MCP规范规定,MCP客户端和服务器应支持动态客户端注册协议,以便MCP客户端(无论客户端传输位于何处)可以在无需用户注册的情况下获取客户端ID。这允许新客户端(代理、应用程序等)自动注册到新的服务器。更多细节可以在MCP规范的授权部分找到,这意味着不幸的是,您不能简单地直接代理到Google或GitHub这样的提供商,因为它们不支持动态客户端注册(它们要求您在其UI中注册客户端)。
这给您留下了两个选项:
为了简化,我们选择了前者,使用Auth0。
[!NOTE] 由于此实现代理了上游OAuth服务器,默认情况下将OAuth服务器的访问令牌转发给客户端会暴露用户的上游访问令牌给下游客户端及MCP主机。这对于许多用例来说不合适,因此这种方法重新实现了某些
@modelcontextprotocol/typescript-sdk类来解决这个问题。
请注意,虽然我们在代理上游授权服务器,但我们不会将最终用户的认证令牌返回给MCP客户端/主机——相反,我们会发出我们自己的令牌,并允许客户端/主机使用该令牌与我们的服务器进行授权。这可以防止恶意客户端或主机滥用令牌,或者如果令牌泄露被滥用。
开始使用Auth0:
一旦所有这些都已设置好,您将需要以下信息:
确保将这些信息填入您的.env。复制.env.template并更新值以匹配您的配置和密钥。
此仓库包括两个独立的服务器:
src/app.stateless.ts处实现的无状态流式HTTP服务器。仅支持流式HTTP传输,并且(理论上)适合无服务器部署。src/app.stateful.ts处实现的有状态SSE和流式HTTP。此应用程序提供两种传输方式,但在使用redis存储策略时仍会维护内存状态(连接必须在内存中持久化),因此不适合无服务器部署或简单的水平扩展。您可以使用bun运行其中任何一个:
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主机都不支持流式HTTP(在多个方面优于SSE)或OAuth,我们建议使用mcp-remotenpm包,该包将处理OAuth授权,并将远程传输桥接到主机的STDIO传输。
命令如下所示:
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错误失败,则回退到SSEsse-first:首先尝试SSE传输,如果SSE因405错误失败,则回退到HTTPhttp-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可以这样实现:
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())
}
});