MCP Oauth2
1. 介绍
mcp-oauth2 是一个 Kong plugin/policy,通过 OAuth2/OIDC(通常与 Keycloak 一起使用)来保护 MCP HTTP endpoints。
它提供:
- 通过 /.well-known/oauth-protected-resource (RFC 9728) 提供 Client discovery,以便 MCP 客户端可以了解授权服务器和支持的范围。
- JWT access-token validation:检查 iss(发行者)、aud(受众)和使用 JWKS 的签名。
- (可选)通过将范围映射到允许的 MCP JSON-RPC 方法来提供 authorization by scope/role(例如,从像 mcp_scopes 这样的声明中)。
2. APIM 控制台设置
步骤 1:创建一个新的 API,后端设置为 MCP 服务器。
MCP 服务器 : http://wikipedia-mcp.default.svc.cluster.local:8080/mcp
因为我们需要通过这个 API 从 VS Code 访问 MCP 服务器,而 VS Code 不支持带有额外路径段的 URL,所以我们将基础路径设置为 “/”(无路径)。

步骤 2:将 mcp-oauth2 策略应用于 API。

步骤 3:点击 mcp-oauth2 策略按钮以显示下面的配置详情。
Section 1: PROTECTED RESOURCE METADATA (RFC 9728)
| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| Protected Resource Metadata Path | string | Yes | /.well-known/oauth-protected-resource | • 插件提供的 RFC 9728 受保护资源元数据文档的 URL 路径。 • MCP 客户端获取此信息以发现授权服务器和支持的范围。 • 几乎不需要更改。 |
- 一个受信任的授权服务器数组。每个 Keycloak 领域(或其他 OAuth2 提供者)为此 MCP 端点发放令牌时添加一个条目。
- 点击 + 以添加更多条目。
| Field | Type | Required | Description |
| --- | --- | --- | --- |
| Url | string | Yes | • public URL of the authorization server(客户端进行身份验证/获取令牌的地方)。
• 当 jwks_uri 是相对路径时,它也是 base URL for building the JWKS URL。
Example: https://keycloak-demo.apimags.skcloud.io/realms/master | | Issuer | string | Yes | • 与 JWT 中的 iss (发行者)声明匹配。
• 如果令牌的发行者不匹配,则会被拒绝。
• 必须是一个 exact string match • 包括协议、主机、端口和路径(没有尾部斜杠)。
• Find it at:<realm-url>/.well-known/openid-configuration → 发行者字段。 | | Audience | string | Yes | • 与 JWT 中的 aud (受众)声明匹配。
• 防止其他服务的令牌被接受。
• Must exactly match 在 Keycloak 受众映射器中配置的值。 | | JWKS URI | 字符串 | 是 | • 插件获取授权服务器的公共签名密钥(JWKS)以验证JWT签名的端点。
• 可以是: Relative path (附加到Url):/.well-known/jwks.json Absolute URL (按原样使用):https://keycloak-demo.apimags.skcloud.io /realms/master/protocol/openid-connect/certs 当Kong无法通过与客户端相同的主机名访问授权服务器时,请使用绝对URL(例如,Docker/k8s网络)。 Default: /.well-known/jwks.json |

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| Scopes Supported | 字符串数组 | 是 | 空 | 列在受保护资源元数据文档中,以告知客户端请求哪些范围。这是 informational only • 它指导客户端发现,但不强制执行任何内容。 输入每个范围并按 Enter 添加。 Example: mcp:tools |

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| Authorization Header Name | 字符串 | 是 | 授权 | • Authorization Header Name = 您的服务器/插件将读取的HTTP头名称,以查找用于令牌验证的访问令牌。 • 通常保持为 Authorization. • 客户端然后发送令牌,如:Authorization: Bearer <access_token>• 仅在您的API期望在不同的头中获取令牌时更改它 • (例如,X-Authorization)。 |
Section 4: UPSTREAM TOKEN FORWARDING

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| Forward Authorization Header | 布尔值 | 否 | 假 | • 假 - 在转发到 MCP 服务器之前剥离授权头(更安全的默认值)。 • 真 - 将 Bearer <token> 头转发到上游 MCP 服务器。 • 如果您的 MCP 服务器需要读取令牌(例如,提取用户身份或声明),请启用。 |

| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| Enable ACL | 布尔值 | 否 | 假 | 假 - 任何有效的令牌都可以访问所有 MCP 方法(仍然会进行令牌验证)。 真 - 在验证令牌后,插件还会检查令牌的作用域/角色,以决定特定的 JSON-RPC 方法是否被允许。 |
| ACL Claim | 字符串 | 否 | 作用域 | • 包含用户权限的 JWT 声明的名称。插件读取此声明并使用其值进行 ACL 检查。 • 常见值: → mcp_scopes - 来自 Keycloak 协议映射器的自定义声明 ⚠️ 必须 exactly match 在 Keycloak 协议映射器中设置令牌声明名称。 |
将每个作用域/角色映射到允许调用的 MCP JSON-RPC 方法列表。仅在 Enable ACL 为真时有效

| Field | Type | Required | Description |
|---|---|---|---|
| Scope | string | Yes (per entry) | 范围/角色名称。必须与JWT声明中指定的值匹配 ACL Claim. Example: mcp:tools |
| Methods | array of strings | Yes (per entry) | 该范围允许调用的MCP JSON-RPC方法名称。输入每个方法名称并按 Enter 添加。 Example: tools/list, tools/call |
⚠️ When ACL is enabled but Scope Method Mapping is empty: 所有请求被拒绝。您必须配置至少一个映射。
💡 Permissions are additive: 用户只需要一个允许该方法的范围。如果令牌中的任何范围允许该方法,则授予访问权限。
Section 7: TIMEOUTS & CACHING| Field | Type | Required | Default | Description |
|---|---|---|---|---|
| Token Cache TTL (s) | number | No | 300 | 验证的令牌在Kong的共享内存中缓存的时间(以秒为单位)。在此期间,使用相同令牌的重复请求跳过JWT解码和签名验证。 即使缓存,插件在每个请求上也会重新检查JWT exp 声明并使过期的令牌失效。 |
| HTTP Timeout (ms) | number | No | 10000 | 从授权服务器获取JWKS(公共签名密钥)的HTTP请求的超时(以毫秒为单位)。如果授权服务器在此时间内未响应,则令牌验证失败。 • 如果授权服务器在慢网络上或冷启动,请增加此值 • 如果授权服务器无法访问,请减少此值以加快失败速度 |
3. Keycloak 设置
3.1. Keycloak OAuth2 基础
这些步骤配置 Keycloak ,以便MCP客户端可以发现授权服务器并获取具有正确受众的令牌。
3.1.1 创建客户端范围
What this does: 创建一个客户端可以请求的命名范围。当客户端请求该范围时,Keycloak 会将其包含在令牌中 - 并且与此范围附加的任何映射器也会触发。
Step 1: Navigate to Keycloak Admin → Client scopes → Create client scope

| Field | Value | Why |
|---|---|---|
| Name | mcp:tools | 您的 MCP 客户端将请求的范围名称 |
| Protocol | OpenID Connect | 标准 OAuth2/OIDC 协议 |
| Display on consent screen | ON | 显示客户端在登录期间请求的内容 |
| Consent screen text | 访问 MCP 工具 | 用户可读的描述 |
| Include in token scope | ON | 确保范围值出现在 JWT 范围声明中 |
3.1.2 添加受众映射器
What this does: 向访问令牌添加一个 aud(受众)声明。该插件验证令牌的受众是否与其配置的值匹配 - 这防止为 other 服务发出的令牌被您的 MCP 端点接受。
Step 1: Navigate to The mcp:tools scope you just created → Mappers tab → Configure a new mapper → Select Audience

| Field | Value | Why |
|---|---|---|
| Name | audience-config | 此映射器的描述性名称 |
| Included Custom Audience | audience-name | 必须与 Kong 插件配置中的受众字段匹配 |
| Add to access token | 开 | 插件读取访问令牌,而不是ID令牌 |
| Add to ID token | 关 | ID令牌是为客户端应用程序准备的,而不是资源服务器 |
![]() |
3.1.3. 创建新客户端
What this does: 为MCP客户端创建一个连接到Keycloak的客户端。
Step 1: Navigate to Clients → Create client

| Field | Value | Description |
|---|---|---|
| Client ID | Mcp-client | 客户端ID |


3.2. Keycloak: 基于组的ACL设置
本节设置 fine-grained access control 以便不同用户获得不同的MCP权限。架构是:
组 → 领域角色 → 协议映射器 → "mcp_scopes" JWT声明 → ACL
3.2.1. 创建领域角色
What these are: 命名权限层级。每个角色代表用户被允许调用的一组MCP方法。角色名称将出现在JWT的mcp_scopes声明中。
Step 1: Navigate to: Realm roles → Create role (repeat for each)
| Role Name | Purpose | Example MCP Methods |
|---|---|---|
| mcp:tools | 工具访问 | tools/list |
| mcp:resources | 资源访问 | resources/read |
![]() |
3.2.2. 创建组
What these are: 组织用户的容器。与其逐个用户分配角色,不如将角色分配给一个组,然后将用户添加到该组。
Step 1: Navigate to: Groups → Create group

| Group Name | Description |
|---|---|
| mcp-member | 基本成员访问 |
| mcp-admin | 对所有 MCP 工具和资源的完全访问 |
![]() |
3.2.3. 将角色分配给组
Step 1: Navigate to Groups → Click a group → Role mapping tab → Assign role| Group | Roles to Assign |
|---|---|
| Mcp-admin | mcp:tools, mcp:resources |
| Mcp-member | mcp:tools |
![]() |
点击 Assign

3.2.4. 创建协议映射器 (mcp_scopes 声明)
What this does: 告诉 Keycloak 在访问令牌中包含用户的领域角色(按 mcp: 前缀过滤),作为名为 mcp_scopes 的自定义声明。这是 Kong 插件读取以做出 ACL 决策的声明。 Step 4: Navigate to Client scopes → mcp:tools → Mappers tab → Add mapper → By configuration → Select User Realm Role



| Field | Value | Why |
|---|---|---|
| Name | mcp-scopes-mapper | 描述性名称 |
| Mapper Type | 用户领域角色 | 将用户的领域角色映射到令牌声明 |
| Token Claim Name | mcp_scopes | 必须与Kong插件配置中的acl_claim匹配 |
| Multivalued | 开 | 用户可以拥有多个角色 |
| Add to access token | 开 | 插件读取访问令牌 |
| Add to ID token | 关 | 不需要进行资源服务器验证 |
| Add to userinfo | 关 | 不需要 |
Critical: Token Claim Name (mcp_scopes) 必须 exactly match Kong插件配置中的acl_claim字段。 不匹配意味着插件无法在令牌中找到权限。
3.2.5. 添加用户到组
Step 1: Navigate to Users → Click on a user → Groups tab → Join group → Select the appropriate group
| User | Group | Resulting JWT mcp_scopes |
|---|---|---|
| canhng1 | Mcp-member | Mcp:tools, Mcp:resources |
| admin | mcp-admin | Mcp:resources |
![]() |
4. 使用Visual Studio Code进行测试
4.1. 将Kong代理转发到本地

4.2. 创建新的 MCP 服务器
打开 Vs Code,按 Ctrl + Shift + P 并选择 MCP: Add server…. 选择 HTTP 并输入 http://localhost:8100. 给服务器一个在 Visual Studio Code 中使用的唯一名称。在 mcp.json 中你现在应该能看到类似这样的条目:


4.3. 启动服务器并使用 Keyloak 认证连接到模拟 MCP 服务器





登录成功并连接到模拟 MCP 服务器,使用了 22 个工具。




