MCP 交互式登录
使用交互式登录时,MCP 客户端只需配置一个 URL,无需其他任何内容:
{
"type": "http",
"url": "https://gateway.example.com/mcp/jira"
}
用户第一次打开或使用该工具时,会在同一个浏览器窗口中完成三步:登录身份提供商、授权上游 MCP 服务器、在同意页上允许该客户端。此后,客户端持有一个可自动续期的网关会话,网关则使用该用户自己的令牌调用上游,并按需刷新。用户不会再被询问。上游令牌绝不会到达客户端,网关会话令牌也绝不会到达上游。
交互式登录需要 1.8.0 之后发布的网关。
最小示例:Keycloak 和 Jira
本示例让用户在 Keycloak 登录,并通过 mcp-atlassian 连接到 Jira。Keycloak 群组 jira-users 的成员可以使用这些工具。测试版本为 Keycloak 26.0、Jira Software 10.3.26、mcp-atlassian 0.23.1 和 Open WebUI 0.12.5。
本示例中网关的公开地址是 https://gateway.example.com。客户端连接到单服务器入口 https://gateway.example.com/mcp/jira。会话只在签发它的服务器入口上有效,因此聚合入口 /mcp 不提供这种登录方式。
创建 Keycloak 客户端
在 Realm(此处为 corp)中创建一个名为 aisix-mcp 的 OpenID Connect 客户端:
- 打开 Client authentication 和 Standard flow。
- 将
https://gateway.example.com/mcp-oauth/login/callback添加为有效的重定向 URI。 - 添加一个 Group Membership Mapper,将令牌声明名称设为
groups,关闭 Full group path,打开 Add to ID token。
创建 Jira 链接
在 Jira 中进入 Administration > Applications > Application links,创建一个 External application 链接,方向选择 Incoming:
- 将重定向 URL 设为
https://gateway.example.com/mcp-oauth/callback。 - 将 Scope 设为
READ。 - 保存生成的客户端 ID 和客户端密钥。
运行 mcp-atlassian
只需设置 JIRA_URL 和 ATLASSIAN_OAUTH_ENABLE。启用 OAuth 后,该服务器会使用网关随每个请求 发送的令牌:
export JIRA_URL="https://jira.example.com"
export ATLASSIAN_OAUTH_ENABLE="true"
mcp-atlassian --transport streamable-http --host 0.0.0.0 --port 9000 --path /mcp
这种配置还会列出 Confluence 工具。由于没有配置 Confluence URL,调用这些工具会失败。
配置网关
网关需要五项配置:公开 URL、带 login 的信任提供商、可访问该服务器的调用方 API Key、从群组到该 Key 的声明映射,以及 MCP 服务器本身。
AISIX Enterprise 或 AISIX Cloud
导出连接信息和前面步骤中的密钥:
export AISIX_CP="YOUR_AISIX_CONTROL_PLANE_API_URL"
export AISIX_TOKEN="YOUR_ADMIN_TOKEN"
export ENV_ID="YOUR_ENVIRONMENT_ID"
export KEYCLOAK_CLIENT_SECRET="YOUR_KEYCLOAK_CLIENT_SECRET"
export JIRA_CLIENT_ID="YOUR_JIRA_CLIENT_ID"
export JIRA_CLIENT_SECRET="YOUR_JIRA_CLIENT_SECRET"
设置公开 URL 并创建信任提供商:
curl -sS -X PATCH "$AISIX_CP/environments/$ENV_ID" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{"mcp_resource_url": "https://gateway.example.com/mcp"}'
curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/oidc_providers" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "corp",
"issuer": "https://sso.example.com/realms/corp",
"audiences": ["aisix-mcp"],
"identity_claim": "preferred_username",
"login": {
"client_id": "aisix-mcp",
"client_secret": "'"$KEYCLOAK_CLIENT_SECRET"'"
}
}'
创建调用方 API Key,并将群组映射到它:
export API_KEY_ID=$(curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/api_keys" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"display_name": "jira-users",
"allowed_models": [],
"mcp_access": { "allow": ["jira__*"] }
}' | jq -r '.api_key.id')
curl -sS -X POST "$AISIX_CP/environments/$ENV_ID/claim_mappings" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "jira-users",
"jwt_provider": "corp",
"priority": 100,
"match": [
{ "claim": "groups", "op": "contains", "values": ["jira-users"] }
],
"resolve": { "api_key_id": "'"$API_KEY_ID"'" }
}'
创建 MCP 服务器:
curl -sS -X POST "$AISIX_CP/mcp_servers" \
-H "Authorization: Bearer $AISIX_TOKEN" \
-H "Content-Type: application/json" \
-d '{
"name": "jira",
"url": "https://mcp-atlassian.example.com/mcp",
"auth_type": "oauth2_authorization_code",
"client_id": "'"$JIRA_CLIENT_ID"'",
"secret": "'"$JIRA_CLIENT_SECRET"'",
"token_url": "https://jira.example.com/rest/oauth2/latest/token",
"authorization_url": "https://jira.example.com/rest/oauth2/latest/authorize",
"scopes": ["READ"],
"allowed_environments": ["'"$ENV_ID"'"]
}'
在控制台中,提供商表单有 网关登录 部分,MCP 服务器表单的认证类型中提供 OAuth 2.0(用户各自登录)。环境的 MCP 访问 页面设置资源 URL 后,网关登录 部分还会显示两个重定向 URI 供复制。
AISIX OSS
将以下条目添加到 resources.yaml:
mcp_auth_settings:
- resource_url: https://gateway.example.com/mcp
oidc_providers:
- name: corp
issuer: https://sso.example.com/realms/corp
audiences: ["aisix-mcp"]
identity_claim: preferred_username
login:
client_id: aisix-mcp
client_secret: ${KEYCLOAK_CLIENT_SECRET}
api_keys:
- display_name: jira-users
key_env: JIRA_USERS_KEY
allowed_models: []
mcp_access:
allow: ["jira__*"]
claim_mappings:
- name: jira-users
jwt_provider: corp
priority: 100
match:
- claim: groups
op: contains
values: ["jira-users"]
resolve:
api_key: jira-users
mcp_servers:
- name: jira
url: https://mcp-atlassian.example.com/mcp
auth_type: oauth2_authorization_code
client_id: ${JIRA_CLIENT_ID}
secret: ${JIRA_CLIENT_SECRET}
token_url: https://jira.example.com/rest/oauth2/latest/token
authorization_url: https://jira.example.com/rest/oauth2/latest/authorize
scopes: ["READ"]
即使没有人出示,每个调用方 API Key 条目也需要凭证,因此还要设置 JIRA_USERS_KEY,但不要分发它。没有控制面的网关把登录数据保存在内存中。在依赖它之前,请参阅没有控制面的网关。
检查 Challenge
不带凭证的请求会收到 401,其中指向该服务器自己的登录入口:
curl -sS -i -X POST "https://gateway.example.com/mcp/jira" \
-H "Content-Type: application/json" \
-H "Accept: application/json, text/event-stream" \
-d '{"jsonrpc":"2.0","id":1,"method":"tools/list","params":{}}'
响应带有以下响应头:
WWW-Authenticate: Bearer resource_metadata="https://gateway.example.com/.well-known/oauth-protected-resource/mcp/jira"
连接客户端
将 URL 提供给客户端。在 Open WebUI 中,在管理设置里添加工具,类型选 MCP,认证选 OAuth 2.1,然后选择 Register Client,并设置工具的可见性,让用户能看到它。Claude Code 和 MCP TypeScript SDK 也经过测试,用法相同。
客户端的重定向 URI 必须是 https,或回环地址上的 http。同意页会显示客户端要把用户送回的地址,因为这个页面正是保护点击了他人链接的用户的关键。
配置参考
MCP 服务器
auth_type: oauth2_authorization_code 可用于 type: mcp 服务器,不可用于 type: openapi 服务器。所有 OAuth 字段都是可选的:
| 字段 | 省略时 |
|---|---|
client_id | 网关向上游注册一次,作为公共客户端。 |
secret | 网关作为公共客户端进行认证。secret 需要 client_id。 |
token_url | 从上游发现。 |
authorization_url | 从上游发现。 |
registration_url | 从上游发现,且仅在省略 client_id 时使用。 |
scopes | 网关请求上游受保护资源元数据中要求的 Scope。 |
发现过程依次读取上游的 RFC 9728 和 RFC 8414 元数据。配置了 authorization_url、token_url 和 client_id 的服务器完全不会请求元数据,Jira 示例就是如此。