启用第三方 AI 代理访问你的 MCP 服务器
本指南将带你通过 mcp-auth 集成 Logto 与 MCP 服务器,实现使用标准 OpenID Connect 流程对用户进行认证 (Authentication),并安全地获取他们的身份声明信息。
你将学会如何:
- 将 Logto 配置为 MCP 服务器的授权 (Authorization) 服务器。
- 在 MCP 服务器中设置 “whoami” 工具,用于返回当前用户的身份声明 (Claims)。
- 使用第三方 AI 代理(MCP 客户端)测试整个流程。
完成本教程后,你的 MCP 服务器将能够:
- 在你的 Logto 租户中认证 (Authentication) 用户。
- 为 “whoami” 工具调用返回身份声明 (Claims)(如
sub、username、name、email等)。
第三方 AI 代理(MCP 客户端)与你自己的 MCP 客户端的区别
让我们来看一个例子。假设你是一名开发者,运行着一个 MCP 服务器来管理邮件访问和自动化。
官方邮件应用(你自己的 MCP 客户端)
- 你为用户提供了一个官方邮件应用,用于阅读和管理他们的邮件。
- 工作方式:官方邮件应用通过 Logto 连接到你的 MCP 服务器进行用户认证 (Authentication)。当 Alice 登录时,她会自动获得邮件访问权限,无需额外的权限页面,因为这是你信任的应用。
第三方 AI 代理(第三方 MCP 客户端)
- 你正在围绕 MCP 服务器构建生态系统,另一位开发者创建了 “SmartMail AI”(一个可以自动总结邮件和安排会议的 AI 助手),并将其作为第三方客户端集成。
- 工作方式:SmartMail AI(第三方 MCP 客户端)希望通过你的 MCP 服务器访问用户邮件。当 Alice 使用她的账户登录 SmartMail AI 时:
- 她会看到一个用户授权页面 (Consent screen),询问是否允许 SmartMail AI 读取她的邮件和日历。
- Alice 可以允许或拒绝此访问。
- 只有她同意的数据才会被共享给 SmartMail AI,SmartMail AI 无法在未重新获得明确同意的情况下访问更多数据。
这种访问(权限)控制确保了用户数据的安全。即使你的 MCP 服务器管理所有数据,像 SmartMail AI 这样的第三方应用也只能访问用户明确允许的数据。他们无法绕过这一流程,因为这是由你在 MCP 服务器中实现的访问控制所强制执行的。
总结
| 客户端类型 | 示例 | 是否需要授权 (Consent) | 谁控制 |
|---|---|---|---|
| 官方邮件应用 | 你自己的邮件应用 | 否 | 你(开发者) |
| 第三方 AI 代理 | SmartMail AI 助手 | 是 | 其他开发者 |
如果你想将 MCP 服务器与你自己的 AI 代理或应用集成,请参考 为你的 MCP 驱动应用启用 Logto 认证 (Authentication) 指南。
前置条件
- 一个 Logto Cloud(或自托管)租户
- Node.js 或 Python 环境
理解架构
- MCP 服务器:向 MCP 客户端开放工具和资源的服务器。
- MCP 客户端:用于发起认证 (Authentication) 流程并测试集成的客户端。本指南中将以第三方 AI agent 作为客户端。
- Logto:作为 OpenID Connect 提供方(授权 (Authorization) 服务器),管理用户身份。
下面的非规范时序图展示了整个流程:
由于 MCP 仍在快速迭代,上述流程图可能不是最新。请参考 mcp-auth 文档获取最新信息。
在 Logto 中配置第三方 AI 代理
为了让第三方 AI 代理访问你的 MCP server,你需要在 Logto 中设置一个第三方应用。该应用将用于代表 AI 代理,并获取认证 (Authentication) 和授权 (Authorization) 所需的凭证。
第三方应用是由外部开发者(非资源所有者)创建的应用程序,需要用户同意才能访问受保护资源。与第一方应用(你自己的应用程序)不同,第三方应用会显示用户授权页面 (Consent screen),在访问用户数据前请求用户批准特定权限。这确保了用户可以控制与外部服务共享哪些数据。
了解更多,请参阅 第三方应用。
允许开发者在 Logto 中创建第三方应用
如果你正在构建一个市场,或希望允许开发者在 Logto 中创建第三方应用,可以利用 Logto Management API 以编程方式创建第三方应用。这样开发者就可以注册他们的应用,并获取认证 (Authentication) 所需的凭证。
你需要托管自己的服务来处理客户端注册流程。该服务将与 Logto Management API 交互,代表开发者创建第三方应用。
另外,你也可以在 Logto 控制台手动创建第三方应用,以熟悉整个流程。
在 Logto 中手动创建第三方应用
你可以在 Logto 控制台手动创建第三方应用,用于测试或临时集成。当你想快速测试集成而不实现完整的客户端注册流程时,这非常有用。
-
登录到你的 Logto 控制台。
-
前往 应用程序 → 创建应用程序 → 第三方应用 -> OIDC。
-
填写应用名称及其他必填项,然后点击 创建应用程序。
-
点击 权限 标签页,在 用户 部分点击“添加”。
-
在弹出的对话框中 -> 用户数据 -> 选择
profile、email权限,然后点击 保存。 -
在第三方应用中,配置 scopes 以请求
openid profile email权限(scopes)。注意:
openid是 OIDC 必需的,profile和email是你在上一步添加的权限。 -
相应地配置你的第三方应用的 重定向 URI。记得在 Logto 中也要更新重定向 URI。
在底层,第三方应用是一个标准的 OAuth 2.0 / OIDC 客户端。这意味着你(或第三方开发者)可以使用任何 OAuth 2.0 / OIDC 库或框架与 Logto 集成。
需要注意的几点:
- 创建第三方应用时,请根据应用的架构选择合适的应用类型:
- 传统 Web:使用客户端密钥进行认证 (Authentication)。
- 单页应用 / 原生应用:使用 PKCE 实现无需客户端密钥的安全授权 (Authorization)。
- 我们的大多数快速入门指南是为第一方应用编写的,但你仍然可以将它们作为第三方应用集成的参考。
- 主要区别在于,第三方应用会显示用户授权页面 (Consent screen),请求用户明确授权访问他们的数据。
完整集成指南请参见 第三方应用。
设置 MCP 服务器
创建项目并安装依赖
- Python
- Node.js
mkdir mcp-server
cd mcp-server
uv init # 或使用你自己的项目结构
uv add "mcp[cli]" starlette uvicorn mcpauth # 或使用你喜欢的包管理器
mkdir mcp-server
cd mcp-server
npm init -y
npm install @modelcontextprotocol/sdk express mcp-auth # 或使用你喜欢的包管理器
使用 Logto 配置 MCP 认证 (Authentication)
记得将 <your-logto-issuer-endpoint> 替换为你之前复制的发行者 (Issuer) 端点。
- Python
- Node.js
在 whoami.py 中:
from mcpauth import MCPAuth
from mcpauth.config import AuthServerType
from mcpauth.utils import fetch_server_config
auth_issuer = '<your-logto-issuer-endpoint>'
auth_server_config = fetch_server_config(auth_issuer, type=AuthServerType.OIDC)
mcp_auth = MCPAuth(server=auth_server_config)
在 whoami.js 中:
import { MCPAuth, fetchServerConfig } from 'mcp-auth';
const authIssuer = '<your-logto-issuer-endpoint>';
const mcpAuth = new MCPAuth({
server: await fetchServerConfig(authIssuer, { type: 'oidc' }),
});
实现令牌验证
由于我们需要验证访问令牌 (Access token) 并获取用户信息,因此需要如下实现访问令牌 (Access token) 验证:
- Python
- Node.js
import requests
from mcpauth.types import AuthInfo
def verify_access_token(token: str) -> AuthInfo:
endpoint = auth_server_config.metadata.userinfo_endpoint
response = requests.get(
endpoint,
headers={"Authorization": f"Bearer {token}"},
)
response.raise_for_status()
data = response.json()
return AuthInfo(
token=token,
subject=data.get("sub"),
issuer=auth_server_config.metadata.issuer,
claims=data,
)
const verifyToken = async (token) => {
const { userinfoEndpoint, issuer } = mcpAuth.config.server.metadata;
const response = await fetch(userinfoEndpoint, {
headers: { Authorization: `Bearer ${token}` },
});
if (!response.ok) throw new Error('Token verification failed');
const userInfo = await response.json();
return {
token,
issuer,
subject: userInfo.sub,
claims: userInfo,
};
};
实现 "whoami" 工具
现在,让我们实现 "whoami" 工具,它会使用客户端发送的访问令牌 (Access token) 请求 userinfo 端点,并返回当前用户的身份声明 (Claims)。
由于当前 SDK 版本尚未正式支持 Streamable HTTP 传输,本示例使用 SSE 传输。理论上,你可以使用任何兼容 HTTP 的传输方式。
- Python
- Node.js
from mcp.server.fastmcp import FastMCP
from starlette.applications import Starlette
from starlette.routing import Mount
from starlette.middleware import Middleware
mcp = FastMCP("WhoAmI")
@mcp.tool()
def whoami() -> dict:
"""
返回当前用户的身份信息。
"""
return (
mcp_auth.auth_info.claims
if mcp_auth.auth_info
else {"error": "Not authenticated"}
)
bearer_auth = Middleware(mcp_auth.bearer_auth_middleware(verify_access_token))
app = Starlette(
routes=[
mcp_auth.metadata_route(), # 提供 OIDC 元数据用于发现
Mount('/', app=mcp.sse_app(), middleware=[bearer_auth]),
],
)
运行服务器:
uvicorn whoami:app --host 0.0.0.0 --port 3001
import { McpServer } from '@modelcontextprotocol/sdk/server/mcp.js';
import express from 'express';
// 创建 MCP 服务器并注册 whoami 工具
const server = new McpServer({ name: 'WhoAmI', version: '0.0.0' });
server.tool('whoami', ({ authInfo }) => ({
content: [
{ type: 'text', text: JSON.stringify(authInfo?.claims ?? { error: 'Not authenticated' }) },
],
}));
// Express 应用 & MCP Auth 中间件
const app = express();
app.use(mcpAuth.delegatedRouter());
app.use(mcpAuth.bearerAuth(verifyToken));
// SSE 传输(如 SDK 文档所示)
const transports = {};
app.get('/sse', async (_req, res) => {
const transport = new SSEServerTransport('/messages', res);
transports[transport.sessionId] = transport;
res.on('close', () => delete transports[transport.sessionId]);
await server.connect(transport);
});
app.post('/messages', async (req, res) => {
const sessionId = String(req.query.sessionId);
const transport = transports[sessionId];
if (transport) await transport.handlePostMessage(req, res, req.body);
else res.status(400).send('No transport found for sessionId');
});
app.listen(3001);
运行服务器:
node whoami.js
测试集成效果
- 启动 MCP 服务器。
- 启动 AI 代理。
- 在客户端中调用
whoami工具,获取当前用户的身份声明 (Claims)。 - 客户端应处理 401 未授权 (Unauthorized) 响应,并将用户重定向到 Logto 进行认证 (Authentication)。
- 认证 (Authentication) 成功后,客户端应收到访问令牌 (Access token),并使用它向 MCP 服务器发起请求。
- 客户端应能够使用访问令牌 (Access token) 从 MCP 服务器获取身份声明 (Claims)。
- Python
- Node.js
完整的 MCP 服务器代码可以在 mcp-auth/python 仓库中找到。
完整的 MCP 服务器代码可以在 mcp-auth/js 仓库中找到。