构建 Model Context Protocol (MCP) ↗ server 时,既需要让用户登录(authentication),也需要让用户授权 MCP client 访问其账户上的资源(authorization)。
Model Context Protocol 使用 OAuth 2.1 的子集进行授权 ↗。OAuth 允许用户授予有限资源访问权限,而无需共享 API 密钥或其他凭据。
Cloudflare 提供 OAuth Provider Library ↗,实现 OAuth 2.1 协议的 provider 端,便于为 MCP server 添加授权。
OAuth Provider Library 有四种用法:
- 使用 Cloudflare Access 作为 OAuth provider。
- 直接与第三方 OAuth provider 集成,例如 GitHub 或 Google。
- 与自有 OAuth provider 集成,包括你可能已依赖的 authorization-as-a-service provider,如 Stytch、Auth0 或 WorkOS。
- Worker 自行处理 authorization 与 authentication。运行在 Cloudflare 上的 MCP server 处理完整 OAuth 流程。
以下各节说明这些选项,并链接到可运行的代码示例。
Cloudflare Access 可为 MCP server 添加单点登录(SSO)功能。用户通过已配置的身份提供商或一次性 PIN向 MCP server 认证,仅当身份符合 Access 策略时才授予访问权限。
要以 Cloudflare Access 作为 OAuth 提供方部署示例 MCP 服务器 ↗,请参阅 使用 Access for SaaS 保护 MCP 服务器。
OAuth Provider Library ↗ 可配置为使用第三方 OAuth 提供方(如 GitHub 或 Google)。完整示例见 GitHub 示例。
使用第三方 OAuth 提供方时,须向 OAuthProvider 提供实现该提供方 OAuth 流程的处理函数。
import MyAuthHandler from "./auth-handler";
export default new OAuthProvider({
apiRoute: "/mcp",
// Your MCP server:
apiHandler: MyMCPServer.serve("/mcp"),
// Replace this handler with your own handler for authentication and authorization with the third-party provider:
defaultHandler: MyAuthHandler,
authorizeEndpoint: "/authorize",
tokenEndpoint: "/token",
clientRegistrationEndpoint: "/register",
});请注意,如 Model Context Protocol 规范所定义 ↗,使用第三方 OAuth provider 时,MCP Server(你的 Worker)会生成并向 MCP client 签发自己的 token:
sequenceDiagram
participant B as User-Agent (Browser)
participant C as MCP Client
participant M as MCP Server (your Worker)
participant T as Third-Party Auth Server
C->>M: Initial OAuth Request
M->>B: Redirect to Third-Party /authorize
B->>T: Authorization Request
Note over T: User authorizes
T->>B: Redirect to MCP Server callback
B->>M: Authorization code
M->>T: Exchange code for token
T->>M: Third-party access token
Note over M: Generate bound MCP token
M->>B: Redirect to MCP Client callback
B->>C: MCP authorization code
C->>M: Exchange code for token
M->>C: MCP access token
更多细节请参阅 Workers OAuth Provider Library ↗ 的文档说明。
若应用已实现 OAuth 提供方,或使用授权即服务提供方,用法与上文 (2) 第三方 OAuth 提供方 相同。
可将 auth provider 用于:
- 允许用户通过邮件、社交登录、SSO(单点登录)和 MFA(多因素认证)向 MCP server 认证。
- 定义直接映射到 MCP tool 的 scope 与权限。
- 向用户展示与所请求权限对应的同意页。
- 强制执行权限,使 Agent 只能调用被允许的工具。
从使用 Stytch 的远程 MCP 服务器 ↗ 开始,让用户通过邮件、Google 登录或企业 SSO 登录,并授权其 AI Agent 代表其查看与管理公司 OKR。Stytch 会根据用户在组织内的角色与权限,限制授予 AI Agent 的范围。MCP 客户端授权时,每位用户会看到同意页,列出根据其角色可授予 Agent 的权限。
更多面向消费者的用例:部署用于待办应用的远程 MCP 服务器,使用 Stytch 进行身份验证与 MCP 客户端授权。用户可通过邮件登录并立即访问账户关联的待办列表,并授权任意 AI 助手帮助管理任务。
从使用 Auth0 的远程 MCP 服务器开始,让用户通过邮件、社交登录或企业 SSO 认证,并通过 AI Agent 与其待办与个人数据交互。MCP 服务器代表用户安全连接 API 端点,并准确展示 Agent 在用户同意后能访问的资源。此实现中,访问令牌在长时间交互期间会自动刷新。
设置步骤:首先部署受保护的 API 端点:
然后部署通过 Auth0 处理身份验证、并将 AI Agent 安全连接到你 API 端点的 MCP 服务器。
从使用 WorkOS AuthKit 的远程 MCP 服务器开始,认证用户并管理授予 AI Agent 的权限。此示例中,MCP 服务器根据用户角色与访问权限动态暴露工具。所有已认证用户可使用 add 工具,但仅在 WorkOS 中被分配 image_generation 权限的用户才能授权 AI Agent 访问图像生成工具。这展示了 MCP 服务器如何根据已认证用户的角色与权限有条件地向 AI Agent 暴露能力。
从使用 Descope ↗ Inbound Apps 的远程 MCP 服务器开始,认证并授权用户(例如邮件、社交登录、SSO)通过 AI Agent 与其数据交互。利用 Descope 自定义范围定义与管理权限,实现更细粒度控制。
MCP 服务器使用 OAuth Provider Library ↗ 可独立完成完整 OAuth 授权流程,无需第三方参与。
Workers OAuth Provider Library ↗ 是实现 fetch() 处理函数 的 Cloudflare Worker,处理发往 MCP 服务器的请求。
如下所示,你提供 MCP 服务器 API 的处理函数、认证与授权逻辑,以及 OAuth 端点的 URI 路径:
export default new OAuthProvider({
apiRoute: "/mcp",
// Your MCP server:
apiHandler: MyMCPServer.serve("/mcp"),
// Your handler for authentication and authorization:
defaultHandler: MyAuthHandler,
authorizeEndpoint: "/authorize",
tokenEndpoint: "/token",
clientRegistrationEndpoint: "/register",
});完整 OAuthProvider 用法(含模拟认证流程)见快速入门示例。
此情况下的授权流程如下:
sequenceDiagram
participant B as User-Agent (Browser)
participant C as MCP Client
participant M as MCP Server (your Worker)
C->>M: MCP Request
M->>C: HTTP 401 Unauthorized
Note over C: Generate code_verifier and code_challenge
C->>B: Open browser with authorization URL + code_challenge
B->>M: GET /authorize
Note over M: User logs in and authorizes
M->>B: Redirect to callback URL with auth code
B->>C: Callback with authorization code
C->>M: Token Request with code + code_verifier
M->>C: Access Token (+ Refresh Token)
C->>M: MCP Request with Access Token
Note over C,M: Begin standard MCP message exchange
请记住——authentication(身份验证)与 authorization(授权)不同 ↗。MCP Server 可自行处理授权,同时仍依赖外部身份验证服务先认证用户。快速入门示例 提供模拟身份验证流程。你需要实现自有身份验证 handler——自行处理身份验证,或使用外部身份验证服务。
用户通过 OAuth Provider 认证后,身份信息可在 tool 内访问。访问方式取决于使用 McpAgent 还是 createMcpHandler。
McpAgent 的第三个类型参数定义认证上下文形状。在 init() 与工具处理函数中通过 this.props 访问。
import { McpAgent } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
type AuthContext = {
claims: { sub: string; name: string; email: string };
permissions: string[];
};
export class MyMCP extends McpAgent<Env, unknown, AuthContext> {
server = new McpServer({ name: "Auth Demo", version: "1.0.0" });
async init() {
this.server.tool("whoami", "Get the current user", {}, async () => ({
content: [{ type: "text", text: `Hello, ${this.props.claims.name}!` }],
}));
}
}在工具处理函数内使用 getMcpAuthContext() 访问相同信息。底层使用 AsyncLocalStorage。
import { createMcpHandler, getMcpAuthContext } from "agents/mcp";
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
function createServer() {
const server = new McpServer({ name: "Auth Demo", version: "1.0.0" });
server.tool("whoami", "Get the current user", {}, async () => {
const auth = getMcpAuthContext();
const name = (auth?.props?.name as string) ?? "anonymous";
return {
content: [{ type: "text", text: `Hello, ${name}!` }],
};
});
return server;
}可根据用户权限控制可用工具。两种方式:在工具处理函数内检查权限,或条件注册工具。
export class MyMCP extends McpAgent<Env, unknown, AuthContext> {
server = new McpServer({ name: "Permissions Demo", version: "1.0.0" });
async init() {
this.server.tool("publicTool", "Available to all users", {}, async () => ({
content: [{ type: "text", text: "Public result" }],
}));
this.server.tool(
"adminAction",
"Requires admin permission",
{},
async () => {
if (!this.props.permissions?.includes("admin")) {
return {
content: [
{ type: "text", text: "Permission denied: requires admin" },
],
};
}
return {
content: [{ type: "text", text: "Admin action completed" }],
};
},
);
if (this.props.permissions?.includes("special_feature")) {
this.server.tool("specialTool", "Special feature", {}, async () => ({
content: [{ type: "text", text: "Special feature result" }],
}));
}
}
}在处理函数内检查会向 LLM 返回错误信息,LLM 可向用户解释拒绝原因。条件注册意味着 LLM 看不到用户无权访问的工具——完全无法尝试调用。