McpToolKit 无服务器多租户工具套件
一种无服务器、多租户的MCP服务器实现,运行在Vercel上,采用流式计算模式,允许多个用户连接到同一端点,同时通过Redis保持会话状态。
服务介绍
MCPToolKit - 生产就绪的 MCP 服务器框架
我们解决的问题
1. 在生产环境中扩展 MCP 服务器
标准的 FastMCP 框架在生产环境中面临重大挑战:
- 状态管理:传统的 FastMCP 服务器将状态保存在内存中,这使得水平扩展变得困难
- 无服务器环境限制:无服务器环境要求无状态架构,而 FastMCP 并不是为此设计的
- 多租户支持:在同一服务器上运行多个租户需要复杂的会话管理
2. 委托 OAuth 支持
为 MCP 服务器管理身份验证是复杂的:
- 工具级认证:用户只有在工具需要时才应进行身份验证
- 第三方集成:支持 Notion、Slack 等服务的 OAuth 需要复杂的令牌管理
- 安全性:在维护安全性的同时管理多种身份验证流程是具有挑战性的
我们的解决方案
MCPToolKit 提供了一个生产就绪的框架,解决了这些问题,同时保持与 FastMCP 的完全兼容。其工作原理如下:
graph TD
A[LLM Client<br/>e.g. Claude, ChatGPT, Cursor] --> B[Load Balancer]
B --> C[MCP Server Instance 1<br/>with Redis State]
B --> D[MCP Server Instance 2<br/>with Redis State]
B --> E[MCP Server Instance N<br/>with Redis State]
C --> F[Redis<br/>Session State & OAuth Tokens]
D --> F
E --> F
C --> H[MCP Authorization Server<br/>OAuth 2.1 & PKCE]
D --> H
E --> H
H --> G[OAuth Providers<br/>Notion, Slack, etc.]
style H fill:#f9f,stroke:#333,stroke-width:2px
该架构图说明了 MCPToolKit 如何实现生产就绪的 MCP 服务器:
-
LLM 客户端(例如 Claude、ChatGPT、Cursor)向 MCP 服务器发起请求。这些客户端可以是任何需要与 MCP 工具交互的应用程序。
-
负载均衡器将传入请求分发到多个 MCP 服务器实例,从而实现水平扩展和高可用性。
-
MCP 服务器实例(1 到 N)处理工具执行和资源访问。每个实例:
- 维护自己的 Redis 状态以实现会话持久化
- 可以独立处理请求
- 共享相同的代码库和配置
- 可以根据需求进行水平扩展
-
Redis作为中央状态存储,提供:
- 跨服务器重启的会话状态持久化
- OAuth 令牌存储和管理
- 服务器实例之间的共享状态
- 使服务器实例无状态
-
MCP 授权服务器(用粉红色突出显示)管理所有与 OAuth 相关的操作:
- 实现带 PKCE 的 OAuth 2.1 以确保安全认证
- 处理令牌发放和刷新
- 管理同意流程
- 为所有服务器实例集中管理 OAuth 逻辑
-
OAuth 提供者(例如 Notion、Slack)是用户可以进行身份验证的第三方服务。授权服务器安全地管理这些连接。
这种架构实现了:
- 通过无状态服务器实例实现真正的水平扩展
- 集中的 OAuth 管理
- 通过多个服务器实例实现高可用性
- 安全的令牌管理
- 跨会话的一致用户体验
从 FastMCP 迁移
从 FastMCP 迁移到 MCPToolKit 很简单。以下是如何更新现有的 FastMCP 服务器:
# Before (FastMCP)
- from mcp.server.fastmcp import FastMCP
-
- # Create an MCP server
- mcp = FastMCP("Demo")
# After (MCPToolKit)
+ from mcptoolkit import MCPToolKit
+ import os
+
+ # Create a production-ready MCP server
+ mcp = MCPToolKit(
+ name="Demo",
+ redis_url=os.environ["REDIS_URL"] # Required: Set REDIS_URL in your environment
+ )
# Your tools and resources remain exactly the same
@mcp.tool()
def add(a: int, b: int) -> int:
"""Add two numbers"""
return a + b
@mcp.resource("greeting://{name}")
def get_greeting(name: str) -> str:
"""Get a personalized greeting"""
return f"Hello, {name}!"
迁移只需要进行一些简单的更改:
- 更改导入语句
- 设置
REDIS_URL环境变量(生产环境中必需) - 就这样!您现有的所有工具、资源和提示将继续像以前一样工作
对于本地开发,您可以设置环境变量:
export REDIS_URL="redis://localhost:6379/0"
对于无服务器部署,您还需要更新您的部署配置:
# Before (FastMCP)
- # api/index.py
- from mcp.server.fastmcp import FastMCP
-
- mcp = FastMCP("Demo")
- app = mcp.create_fastapi_app()
# After (MCPToolKit)
+ # api/index.py
+ from mcptoolkit.vercel import create_vercel_app
+ import os
+
+ app = create_vercel_app(
+ name="Demo",
+ redis_url=os.environ["REDIS_URL"] # Required: Set REDIS_URL in your environment
+ )
主要特性:
- 基于 Redis 的状态管理:会话状态在服务器重启和函数调用之间保持持久
- 无服务器就绪:专为 Vercel、AWS Lambda 和其他无服务器平台设计
- 水平扩展:状态持久化支持真正的水平扩展
- 多租户支持:多个用户可以连接到同一个端点,并且会话相互隔离
2. 委托 OAuth 支持
from mcptoolkit import MCPToolKit, requires_auth
from mcptoolkit.auth.providers import NotionProvider, SlackProvider
# Define default scopes for each provider
default_notion_scopes = [
"read:database",
"write:page",
"read:page"
]
default_slack_scopes = [
"channels:read",
"chat:write",
"reactions:write"
]
server = MCPToolKit(name="Auth Server")
@server.tool()
@requires_auth(provider=NotionProvider(
scopes=default_notion_scopes,
consent_required=True # Require explicit user consent
))
def notion_search(query: str, ctx: Context) -> str:
# Access authenticated Notion client with specific scopes
notion = ctx.get_oauth_client("notion")
return notion.search(query)
@server.tool()
@requires_auth(provider=SlackProvider(
scopes=default_slack_scopes,
consent_required=True
))
def slack_message(channel: str, message: str, ctx: Context) -> str:
# Access authenticated Slack client with specific scopes
slack = ctx.get_oauth_client("slack")
return slack.post_message(channel, message)
主要特性:
- 惰性认证:仅当某个工具需要时用户才进行认证
- 提供商支持:内置对常见提供商的支持(Notion、Slack 等)
- 令牌管理:自动刷新令牌并存储
- 安全性:安全的令牌存储和传输
- 细粒度范围:对 OAuth 权限的精细控制
- 同意管理:带有逻辑权限分组的用户友好同意屏幕
- 人工介入:对高风险操作可选的审批要求
OAuth 流程
MCPToolKit 实现了带有 PKCE 的安全 OAuth 2.1 流程:
- LLM 客户端向 MCP 服务器发起请求
- 服务器响应 401 未授权,并返回重定向链接
- 用户登录到 OAuth 提供商并授予请求的权限范围
- 服务器将授权码返回给客户端
- 客户端使用授权码交换访问令牌和刷新令牌
- 使用这些令牌进行后续请求
- MCP 服务器调用第三方服务
授权服务器架构
MCPToolKit 支持两种授权服务器的部署模式:
-
嵌入式授权服务器
- MCP 服务器同时充当身份提供者和依赖方
- 直接处理登录、同意和令牌发放
- 管理令牌生命周期、刷新逻辑和撤销
- 最适合独立应用程序
-
外部授权服务器
- MCP 服务器作为依赖方
- 将 OAuth 流程委托给外部服务(例如 Stytch)
- 专注于工具级别的访问控制
- 最适合与现有身份基础设施集成
两种模式都支持:
- 带有 PKCE 的 OAuth 2.1
- 动态客户端注册
- 授权服务器元数据 (RFC 8414)
- 基于资源/动作的自定义权限范围
- 最终用户同意管理
- 每个提供商的细粒度权限范围定义
- 组织级可见性和控制
- 隐含权限(用户只能授予他们拥有的权限)
同意和访问管理
MCPToolKit 提供全面的同意和访问管理:
- 组织级可见性:查看您组织中授权的所有连接应用
- 细粒度权限:查看哪些成员授予了访问权限以及他们授权的范围
- 访问管理:随时撤销特定用户或应用的访问权限
- 用户友好的同意界面:以逻辑分组的形式展示基于角色的访问控制(RBAC)权限
- 隐含权限:用户只能赋予应用程序与自己相同的权限
- 人工介入:对高风险操作要求人工审批
高风险操作保护
@server.tool()
@requires_auth(provider=NotionProvider(
scopes=["delete:database"],
human_approval_required=True # Require explicit human approval
))
def delete_database(database_id: str, ctx: Context) -> str:
# This action will require explicit human approval
notion = ctx.get_oauth_client("notion")
return notion.delete_database(database_id)
架构
部署选项
Kubernetes
apiVersion: apps/v1
kind: Deployment
metadata:
name: mcp-server
spec:
replicas: 3
template:
spec:
containers:
- name: mcp-server
image: your-mcp-server
env:
- name: REDIS_URL
valueFrom:
secretKeyRef:
name: redis-credentials
key: url
无服务器 (Vercel)
# api/index.py
from mcptoolkit.vercel import create_vercel_app
app = create_vercel_app(
name="Serverless MCP",
redis_url=os.environ.get("REDIS_URL")
)
快速开始
- 安装 MCPToolKit:
pip install mcp-python-sdk
- 创建您的服务器:
from mcptoolkit import MCPToolKit, requires_auth
server = MCPToolKit(
name="My Production Server",
redis_url="redis://localhost:6379/0"
)
@server.tool()
def public_tool() -> str:
return "This tool doesn't require auth"
@server.tool()
@requires_auth(provider="notion")
def notion_tool() -> str:
return "This tool requires Notion auth"
- 部署到您选择的平台(Kubernetes、Vercel 等)
要求
- Python 3.9+
- Redis 实例(用于会话状态持久化)
- OAuth 提供者凭据(如果使用委托认证)
许可证
与 MCP Python SDK 相同。