MCP客户端+服务器,带增强认证
这个项目的目标是提供一个基于可流式HTTP的Model Context Protocol Typescript SDK的模型实现,既包括服务器端也包括客户端,并且使用由Better Auth提供的外部OAuth服务器。
目前来看,鉴于MCP和BetterAuth文档的空白,以及本仓库中展示(并绕过)的多个问题,这些问题反映了SDK的粗糙之处,因此还远未达到“模型”的水平。可能有些问题有更好的解决方法,非常欢迎反馈(和拉取请求)以改进实现。
资源
项目组件
auth-server启用MCP插件的更佳认证实现。MCP插件本质上启用了带有动态客户端注册功能的OAuth 2.1端点。该实现基于为您的应用程序构建的drizzle-orm SQLite数据库,且已配置好Better Auth及其迁移。auth-ui一个简单的Vite/TanStack-Router项目,包含一个通过GitHub登录Better Auth的重定向页面mcp-server一个带有StreamableHTTP传输的MCP服务器,以及用于代理OAuth请求到的ProxyOAuthServerProviderauth-servermcp-client-cli一个简单的交互式命令行界面,它使用MCP客户端SDK进行通信mcp-server并协商认证
设置
pnpm install
# Set up sqlite db
pnpm db:migrate
# Start the MCP Server, Auth Server, and UI
pnpm dev
# Start the MCP Client CLI
pnpm cli在交互式命令行界面中使用MCP
pnpm cli
> echo ping pong
# You will be prompted to sign in, complete this
> echo ping pong
# > Hello Nick Lucas: ping pong在Claude代码中使用MCP
claude mcp add mcp-demo http://localhost:3001/mcp -t http
claude
> /mcp
# Select mcp-demo
> 1. Authenticate
# Complete sign-in
> echo ping pong
# > Hello Nick Lucas: ping pong当前可能有解决方案但我未能找到的问题
/userinfo 功能未由 Better Auth 的 MCP 插件实现
为了传递用户详情、令牌过期时间等信息,这些信息需要由MCP服务器从认证服务器获取,但Better Auth目前并不提供OAuth /authinfo 尽管文档中记录了一个(端点),但(此处的)端点 .well-known/oauth-authorization-server这应该会随着他们的 支持OAuth 2.1 土地和MCP插件已被弃用。
一个替代方案是使用JWT,并使用认证服务器的JWKs来验证令牌,而Better Auth确实提供了JWT插件,但如何快速获取带有声明的JWT并返回给客户端并不明显。
目前我们不得不实施 /userinfo 手动设置端点。
OAuthClientProvider没有提供一种方式来惰性启动用于OAuth重定向的回调服务器
理想情况下,当MCP客户端SDK触发身份验证流程时,客户端将有机会启动一个HTTP服务器并确定其回调URL。如果你已经运行在一个URL上,因为你是网页客户端/API,这很容易实现,但作为CLI(命令行界面),你需要在启动服务器时选择一个可用的端口。但 get redirectUrl() 无法成为承诺,因此在创建OAuthClientProvider实例时,您必须已经知道端口号。
理想情况下,这个接口的设计应该稍作调整,以便HTTP服务器能够轻松启动并返回其URI
ProxyOAuthServerProvider会对client_secret进行验证,但在客户端注册后没有提供缓存它们的机制
上面的文件中写有丰富的细节,但基本上,MCP客户端、MCP服务器和认证服务器都需要知道给定客户端的client_id->client_secret映射。我们不得不实现 getClient 在实例化 ProxyOAuthServerProvider 时,但 registerClient 未暴露给用户空间。我能看到有两种解决方案:
- 在使用 ProxyOAuthServerProvider 时,不要验证 client_secret,因为这是认证服务器的责任,并删除
getClient因为不需要,所以从 ProxyOAuthServerProvider 中移除 - 提供一种方法来覆盖 ProxyOAuthServerProvider 所使用的 clientStore,以便您可以维护自己选择的缓存
