MCP U2M代理服务器
这是一个 启用OAuth的MCP代理服务器 它可以部署在copula Apps上或在本地运行。它将请求代理到需要用户到机器(U2M)OAuth身份验证的上游MCP服务器,通过基于浏览器的用户身份验证自动处理OAuth流。
特性
- 🔐 OAuth 2.0 PKCE身份验证 -基于浏览器的安全用户身份验证
- 🔄 自动令牌刷新 -通过主动刷新无缝处理令牌过期(用户长期保持身份验证)
- ⏰ 长期会议 请求:
offline_access持久刷新令牌的范围 - 🌐 完全支持MCP协议 -代理SSE、消息和所有MCP端点(包括版本化端点,如
/v1/sse,/v2/sse等等) - 🌉 协议网桥 -使用MCP SDK在可流式HTTP客户端和仅限SSE的上游服务器之间进行转换
- 💾 持久令牌存储 -保存凭据以供重用
- 👥 多用户支持 -通过标头管理每个用户的单独身份验证
- 🎨 漂亮的身份验证UI -清理OAuth回调页面
- 🚀 Rancher应用程序就绪 -可以部署为copula应用程序
用例
此代理非常适合:
- 连接到受OAuth保护的MCP服务器
- 集中多个客户端的身份验证
- 将MCP访问作为服务部署在Databricks上
- 支持OAuth的MCP集成的开发和测试
- 多用户环境,每个用户都需要自己的身份验证
多用户支持
代理支持 每用户身份验证,允许多个用户使用具有自己凭据的同一代理实例。
运作原理
- 用户识别:代理通过
X-Forwarded-User头球 - 单独的令牌:每个用户都有自己的OAuth令牌存储在
~/.mcp/auth/{user_id}/ - 孤立会话:用户独立进行身份验证,无法访问彼此的会话
- 自动回退:否时
X-Forwarded-User标头存在(例如,本地开发),代理使用default用户
用法
使用copula Apps或类似框架:
# The framework automatically sets the X-Forwarded-User header
curl -H "X-Forwarded-User: alice@example.com" http://localhost:8000/sse本地开发(无需标题):
# Uses 'default' user automatically
curl http://localhost:8000/sse令牌存储
令牌按用户存储:
~/.mcp/auth/
├── default/ # Local development user
│ ├── abc123_tokens.json # Access & refresh tokens
│ ├── abc123_client.json # OAuth client info
│ └── abc123_auth_state.json # OAuth state (temporary, during auth)
├── alice@example.com/ # User alice
│ ├── abc123_tokens.json
│ ├── abc123_client.json
│ └── abc123_auth_state.json
└── bob@example.com/ # User bob
├── abc123_tokens.json
├── abc123_client.json
└── abc123_auth_state.json先决条件
- Python 3.11+或copula应用程序环境
- Rancher命令行界面(用于copula部署)
uv(推荐)或pip
快速启动(地方发展)
1.配置上游服务器
设置上游MCP服务器URL:
export UPSTREAM_MCP_URL="https://your-mcp-server.com/v1"
export OAUTH_CALLBACK_PORT="8000" # optional, defaults to 8000
export DEBUG="1" # optional, enables detailed debug logging
# For deployed environments (Databricks Apps, etc.)
export OAUTH_REDIRECT_URL="https://your-app-url.com/oauth/callback"环境变量:
| 变量 | 必填 | 默认 | 描述 |
|---|---|---|---|
UPSTREAM_MCP_URL | ✅ 是 | - | 上游MCP服务器的基本URL(有或没有 /sse 后缀) |
OAUTH_CALLBACK_PORT | 没有 | 8000 | OAuth回调和web服务器的端口(仅限本地开发人员) |
OAUTH_REDIRECT_URL | 没有 | http://localhost:{port}/oauth/callback | 部署环境的完整OAuth回调URL |
DEBUG | 否 | 未设置 | 启用调试日志记录。吃起来 1, true, yes,或 on |
对于部署环境很重要:
- 当部署到copula Apps或其他平台时,您 必须 集
OAUTH_REDIRECT_URL到应用程序的公共URL - 例子:
OAUTH_REDIRECT_URL=https://your-app.databricksapps.com/oauth/callback - 这可确保OAuth回调到达您部署的应用程序,而不是本地主机
登录中:
代理使用Python的标准 logging 具有不同日志级别的框架:
- 调试:详细的请求/响应信息、标头、流块
- 信息:重要事件(身份验证成功、注册、令牌刷新)
- 警告:非关键问题(浏览器自动打开失败)
- 错误:需要注意的错误(身份验证失败、令牌刷新失败)
集 DEBUG=1 查看详细的调试日志。没有它,只显示INFO级别及以上。
2.安装依赖项
uv sync
# or
pip install -r requirements.txt3.启动代理服务器
# Using the main module
python -m custom_server.main
# Or with uvicorn
uvicorn custom_server.app:app --host 0.0.0.0 --port 80004.身份验证
打开浏览器并导航到:
http://localhost:8000/您将看到一个漂亮的web界面:
- ✅ 当前身份验证状态
- 🔐 启动OAuth流的“身份验证”按钮
- 📡 可用MCP端点(一旦通过身份验证)
- 🗑️ “清除凭据”按钮以重新进行身份验证
点击 “使用上游服务器进行身份验证” 按钮,代理将:
- 打开OAuth身份验证的新窗口
- 等待您登录并授权
- 自动保存令牌
- 显示您已准备好使用代理
5.使用代理
您的代理现已准备就绪!将MCP客户端连接到:
- SSE端点:
http://localhost:8000/sse - 消息端点:
http://localhost:8000/message
Claude Desktop示例:
{
"mcpServers": {
"mcp-proxy": {
"url": "http://localhost:8000/sse"
}
}
}地方发展
- 跑
uv同步:
uv sync- 在本地启动服务器。更改将触发重新加载:
uvicorn custom_server.app:app --reload在copula Apps上部署自定义MCP服务器
有两种方法可以在copula Apps上部署服务器:使用 databricks apps CLI或使用 databricks bundle CLI。根据您的喜好,您可以选择任何一种方法。
这两种方法都需要首先配置copula身份验证:
export DATABRICKS_CONFIG_PROFILE= # e.g. custom-mcp-server
databricks auth login --profile "$DATABRICKS_CONFIG_PROFILE"使用 databricks apps 命令行界面
使用部署服务器 databricks apps CLI,请执行以下步骤:
- 创建一个copula应用程序来托管您的MCP服务器:
databricks apps create mcp-custom-server- 为您的应用程序设置所需的环境变量:
# Set the upstream MCP server URL
databricks apps update mcp-custom-server --set-env UPSTREAM_MCP_URL="https://your-mcp-server.com/v1"
# IMPORTANT: Set the OAuth redirect URL to your app's URL
# Get your app URL first, then set the redirect URL
APP_URL=$(databricks apps get mcp-custom-server | jq -r .url)
databricks apps update mcp-custom-server --set-env OAUTH_REDIRECT_URL="${APP_URL}/oauth/callback"- 将源代码上传到copula并部署应用程序:
DATABRICKS_USERNAME=$(databricks current-user me | jq -r .userName)
databricks sync . "/Users/$DATABRICKS_USERNAME/my-mcp-server"
databricks apps deploy mcp-custom-server --source-code-path "/Workspace/Users/$DATABRICKS_USERNAME/my-mcp-server"使用 databricks bundle 命令行界面
使用部署服务器 databricks bundle CLI,请执行以下步骤
更新 app.yaml 在此目录中的文件中使用以下命令:
command: ["uvicorn", "custom_server.app:app"]- 在该目录中,运行以下命令以部署并运行AppDapps上的MCP服务器:
uv build --wheel
databricks bundle deploy
databricks bundle run custom-mcp-server协议桥:流式HTTP↔ 上海证券交易所
代理包括 协议网桥 这允许Streamable HTTP客户端连接到仅限SSE的上游服务器。
它解决的问题
- 你的客户:仅支持流式HTTP(
POST /mcp具有流式响应) - 上游服务器:仅支持SSE(
GET /sse对于活动+POST /message对于请求) - 解决方案:代理自动在两个协议之间进行转换!
如何使用它
流式HTTP客户端→ /mcp 端点:
# Client connects to the proxy using Streamable HTTP
client = MCPClient(url="http://localhost:8000/mcp", transport="streamable-http")
# Proxy translates to SSE for the upstream server
# All MCP operations work transparently
tools = await client.list_tools()
result = await client.call_tool("my_tool", {"arg": "value"})支持的端点
| 客户端类型 | 端点 | 上游转换 |
|---|---|---|
| 可流式传输的HTTP | POST /mcp | → SSE (GET /sse + POST /message) |
| 上海证券交易所 | GET /sse | → 直接代理到上游 |
| 上海证券交易所 | POST /message | → 直接代理到上游 |
运作原理
- 客户端发送 JSON-RPC请求
POST /mcp - 代理维护 与上游的持久SSE连接(每个用户一个)
- 代理转发 请求通过
POST /message向上游 - 代理接收 通过SSE流响应
- 代理返回 以流式HTTP形式响应客户端
支持的MCP方法
该桥支持所有标准MCP方法:
- ✅
initialize-初始化会话 - ✅
tools/list-列出可用工具 - ✅
tools/call-调用工具 - ✅
resources/list-列出资源 - ✅
resources/read-阅读资源 - ✅
prompts/list-列表提示 - ✅
prompts/get-获得提示
每个用户会话
网桥为每个用户维护单独的MCP会话(由 X-Forwarded-User 标题),确保:
- 🔒 每个用户的独立身份验证
- 🔄 持续连接以提高效率
- 🚀 初始连接后的低延迟
连接到MCP代理
已部署在Databricks Apps上
当部署在copula Apps上时,您可以使用以下任一方式进行连接:
SSE端点(建议用于支持SSE的客户端):
https://your-app-url.databricksapps.com/sse流式HTTP端点(仅适用于流式HTTP客户端):
https://your-app-url.databricksapps.com/mcp关于copula部署的重要注意事项:
- OAuth回调: 当在copula Apps上运行时,OAuth回调URL将基于您的应用程序的公共URL。请确保OAuth提供者可以访问此URL。
- 令牌持久性: 令牌按用户存储在
~/.mcp/auth/{user_id}/目录。在ConnectionApps上,确保此目录在应用程序重启时保持不变,或实现不同的存储机制(例如,Connectionsecrets)。
- 多用户支持: 代理自动使用
X-Forwarded-UserRancher Apps提供的标题。每个用户将通过web UI单独进行身份验证(http://your-app-url/).
- 首次身份验证: 用户通过web UI按需进行身份验证。不需要预身份验证。
地方发展
对于本地开发,您可以使用以下任一方式进行连接:
SSE端点:
http://localhost:8000/sse可流式传输的HTTP端点:
http://localhost:8000/mcp客户端配置示例
克劳德桌面(claude_desktop_config.json):
{
"mcpServers": {
"mcp-via-proxy": {
"url": "http://localhost:8000/sse"
}
}
}使用mcp远程:
npx mcp-remote http://localhost:8000/sseOAuth身份验证的工作原理
代理使用PKCE实现OAuth 2.0授权代码流:
- 用户标识: 从中提取用户ID
X-Forwarded-Userheader(或对本地开发使用“默认”) - 客户注册: 在每个用户首次运行时,代理将自己注册为上游服务器的OAuth客户端
- 授权: 打开浏览器进行用户身份验证和授权,请求
offline_access长期会话的范围 - 代币兑换: 交换访问和刷新令牌的授权码
- 令牌存储: 安全地将每个用户的令牌存储在
~/.mcp/auth/{user_id}/ - 自动刷新: 使用刷新令牌自动刷新过期的访问令牌(在过期前5分钟主动刷新)
- 请求代理: 将正确的用户承载令牌添加到所有代理请求中
用户保持身份验证多久?
简短回答:无限期 (只要上游OAuth服务器允许)
代理使用 刷新令牌 自动续订访问令牌,而无需用户重新进行身份验证:
- 访问令牌 通常在1小时后过期(由上游服务器设置)
- 刷新令牌 寿命长,可以持续数周、数月或无限期(取决于上游服务器设置)
- 代理主动刷新访问令牌 到期前5分钟,确保不间断服务
- 用户只需要验证一次,代理会自动处理所有后续的令牌续订
- 这
offline_access请求作用域以获取不会过期的持久刷新令牌
用户何时需要重新进行身份验证?
- 如果他们通过web UI手动清除凭据
- 如果刷新令牌本身过期(在以下情况下很少见
offline_access范围) - 如果上游OAuth服务器撤销令牌
- 如果令牌存储被删除(例如,服务器在没有持久存储的情况下重新启动)
令牌存储位置:
令牌按用户存储,以实现多用户支持:
~/.mcp/auth/
├── default/ # Default user (local development)
│ ├── _tokens.json # Access & refresh tokens
│ ├── _client_info.json # OAuth client registration
│ └── _auth_state.json # OAuth state (during auth flow)
├── alice@example.com/ # User alice
│ ├── _tokens.json
│ ├── _client_info.json
│ └── _auth_state.json
└── bob@example.com/ # User bob
├── _tokens.json
├── _client_info.json
└── _auth_state.json注: 这 auth_state.json 该文件是临时的,仅在活动的OAuth流期间存在。身份验证成功后,它会自动清理。
建筑
┌─────────────┐ ┌──────────────────────────────┐ ┌──────────────┐
│ MCP Client │◄───────►│ MCP Proxy │◄───────►│ Upstream │
│ (Claude) │ HTTP │ (Multi-User Support) │ OAuth │ MCP Server │
└─────────────┘ │ │ └──────────────┘
X-Forwarded-User │ ┌──────────┐ ┌──────────┐ │
Header │ │ User A │ │ User B │ │
│ │ Tokens │ │ Tokens │ │
│ └──────────┘ └──────────┘ │
└──────────────────────────────┘
│
│ OAuth Flow (Per User)
▼
┌──────────────┐
│ Browser │
│ (User Auth)│
└──────────────┘代理自动执行以下操作:
- 通过以下方式识别用户
X-Forwarded-Userheader(或对本地开发使用“默认”) - 为每个用户管理单独的OAuth令牌
- 使用正确的用户凭据将请求路由到上游服务器
- 支持任何版本化的MCP端点(
/v1/sse,/v2/message等等)
故障排除
状态参数无效错误
Authentication Failed: Invalid state parameter - possible CSRF attack当OAuth状态在身份验证启动和回调之间不匹配时,会发生此错误。这是现在 自动固定 通过将OAuth状态持久化到磁盘。
为什么会这样: 在已部署的环境中(如copula Apps),应用程序可能会在您开始身份验证和收到OAuth回调之间重新启动或重新加载。新版本保留OAuth状态(~/.mcp/auth/{user_id}/auth_state.json)因此,它可以在重启后幸存下来。
如果您仍然看到此错误:
- 确保存储目录(
~/.mcp/auth/)可写,并在重新启动后持续存在 - 清除凭据并重试
- 检查您是否没有使用没有共享存储的多个负载平衡实例
浏览器无法打开
如果浏览器没有自动打开:
Could not open browser automatically解决方案: 从终端复制授权URL并将其粘贴到浏览器中。
身份验证超时
✗ Authentication failed: Authentication timeout after 300 seconds解决方案: 请在5分钟内完成OAuth流程,或重新启动服务器并重试。
令牌已过期
HTTPException: 401 - Authentication required解决方案: 代理会自动刷新令牌,因此这种错误很少见。如果发生:
- 检查刷新令牌是否可用:查找指示“无可用刷新令牌”的日志
- 尝试提出另一个请求:代理将在下次请求时尝试刷新
- 检查上游服务器状态:OAuth服务器可能已关闭或刷新令牌已被吊销
- 重新验证:如果刷新失败,请访问
http://localhost:8000/然后单击“清除凭据”,然后重新进行身份验证
注: 通过改进的令牌刷新逻辑,用户应该无限期地保持身份验证,除非上游OAuth服务器出现问题。
上游连接被拒绝
httpx.ConnectError: Connection refused解决方案: 验证 UPSTREAM_MCP_URL 正确且服务器可访问。
多用户身份验证问题
如果用户遇到身份验证问题:
- 检查用户标识符:确保
X-Forwarded-User标题设置正确 - 检查每个用户的令牌:每个用户在中都有自己的令牌
~/.mcp/auth/{user_id}/ - 清除用户凭据:导航到
http://localhost:8000/然后单击“清除凭据” - 检查日志:启用调试模式
DEBUG=1查看每个请求使用哪个user_id
