- 统一地址:https://test-apim-gateway.xxx.com/mcp-agg/1.0.0/.well-known/oauth-protected-resource
- 返回样式
{"resource": "https://test-apim-gateway.xxx.com/test/1.0.0/mcp","authorization_servers": ["https://kc.com/auth/realms/xx/protocol/openid-connect/token"],"scopes_supported": ["openid","wso2-role"]
}
- 在apimgt.org.wso2.carbon.apimgt.gateway项目中做了相关适配合
在admin平台添加自定义的key manager ,状态为关闭,避免对其它api产生影响

设置kc相关的idp配置,状态为关闭,否则会影响默认的key manager

这样,在访问这个mcp端点时,authorization_servers就变成了kc的地址,而不是wso2默认的key manager的地址。
查看是否安装成功,如果没有生效,执行下面代码也可能让它生效,有时是缓存问题
C:\Users\User>curl -k -s "https://apim.xxx.com/api/am/publisher/v4/swagger.yaml" | grep -A 25 "refresh-tools"/mcp-servers/{mcpServerId}/refresh-tools:post:tags:- MCP Serverssummary: Refresh MCP tools from the backenddescription: |Re-synchronizes tool definitions from the upstream MCP endpoint for MCP servers of subtype **SERVER_PROXY**,or reapplies MCP tool mappings from the current stored OpenAPI / backend definition for other MCP server subtypes.The MCP server metadata is persisted the same way as a regular update, and matching per-tool policies andscopes are preserved when tool names are unchanged (proxy subtype).operationId: refreshMCPServerToolsparameters:- $ref: "#/components/parameters/mcpServerId"responses:"200":description: |OK.MCP server tools refreshed; response body is the updated MCP server.content:application/json:schema:$ref: "#/components/schemas/MCPServer""400":$ref: "#/components/responses/BadRequest""403":$ref: "#/components/responses/Forbidden"
--\ \"https://127.0.0.1:9443/api/am/publisher/v4/mcp-servers/7a2298c4-c905-403f-8fac-38c73301631f/refresh-tools\""/mcp-servers/{mcpServerId}/comments:get:tags:- Commentssummary: Retrieve MCP Server Commentsdescription: |Get a list of Comments that are already added to MCP ServeroperationId: getAllCommentsOfMCPServerparameters:- $ref: "#/components/parameters/mcpServerId"- $ref: "#/components/parameters/requestedTenant"- $ref: "#/components/parameters/limit"- $ref: "#/components/parameters/offset"- $ref: "#/components/parameters/includeCommenterInfo"responses:"200":description: |OK.Comments list is returned.content:application/json:schema:$ref: "#/components/schemas/CommentList""404":$ref: "#/components/responses/NotFound"
workbuddy认证流程
sequenceDiagramparticipant UI as 连接器面板participant WB as WorkBuddy 主进程participant Store as .credentials.jsonparticipant IdP as 企查查 OAuthparticipant MCP as MCP 服务UI->>WB: 点击连接 qcc-companyWB->>IdP: 发现元数据 + 注册/加载 client_idWB->>Store: saveClientInfo (mcpClientInfo)WB->>UI: 打开浏览器授权页IdP->>WB: 回调 workbuddy://.../oauth/callback?code=WB->>IdP: code 换 access_token + refresh_tokenWB->>Store: saveTokens (mcpOAuth)WB->>MCP: 带 Bearer 调用 tools/list、tools/call
流程
flowchart LRWB[WorkBuddy 客户端]GW[apim-gateway.xxx.com<br/>WSO2 APIM 资源服务器]KC[testcas.xxx.com<br/>Keycloak 授权服务器]WB -->|POST /mcp-law-agg/1.0.0/mcp| GWGW -->|401 + WWW-Authenticate| WBWB -->|读 oauth-protected-resource| GWWB -->|授权页 / 换 token| KCGW -->|校验 Bearer| GW
流程
sequenceDiagramparticipant WB as WorkBuddyparticipant GW as APIM 网关participant IdP as 授权服务器WB->>GW: POST /mcp (initialize 或 tools/list,无 Token)GW-->>WB: 401 + WWW-AuthenticateWB->>WB: auth() 发现 OAuth 元数据WB->>IdP: 打开浏览器授权页Note over WB: 用户登录并同意IdP-->>WB: workbuddy://.../oauth/callback?code=WB->>IdP: 用 code 换 access_tokenWB->>GW: 带 Bearer 重试 MCP 请求
流程
sequenceDiagramparticipant WB as WorkBuddyparticipant Meta as APIM 元数据participant KC as Keycloakparticipant GW as APIM GatewayWB->>Meta: GET oauth-protected-resourceMeta-->>WB: authorization_servers = Keycloak realmWB->>KC: OAuth 登录 + 换票KC-->>WB: KC JWTWB->>GW: MCP + Bearer KC JWTNote over GW: 网关校验 KC JWT 或自动换 WSO2 tokenGW-->>WB: 200 / tools
workbuddy连接器流程
flowchart LRA[连接器 UI 显示法律聚合] --> B[用户 mcp.json 有条目]C[点击连接] --> D[查找 connectors/mcp-law-agg/mcp.json]D --> E[文件不存在 → 报错]
本地部署一下拦截器
| 文件 | 作用 |
|---|---|
connectors-marketplace/connectors/test/mcp.json |
MCP 端点配置(连接时读取) |
connectors-marketplace/connectors/test/connector-meta.json |
名称、描述、示例 |
connectors-marketplace/connectors/test/skills/SKILL.md |
连接成功后的 Skill |
connectors-marketplace/connectors/test/icon.svg |
连接器图标 |
connectors-marketplace/icons/test.svg |
市场列表图标 |
connectors-marketplace/.codebuddy-connector/connectors.json |
已登记 test(含 type: "mcp") |

MCP 地址:
https://test-apim-gateway.xxx.com/mcp-law-agg/1.0.0/mcp
keycloak开始PKCE
Keycloak 要求「客户端必须启用 PKCE」——在服务端配置
WorkBuddy 不能在连接器配置里给 DCR 增加自定义字段(当前版本没有该入口)。应在 Keycloak 侧处理:
方案 A:Realm / Client Policy 强制 PKCE(推荐)
Realm → Client policies → Policies
添加 PKCE Enforcer(pkce-enforcer)
对 public 客户端或 DCR 创建的客户端生效
这样即使用 DCR 注册的客户端未勾选 PKCE,每次授权仍强制带 code_challenge,与 WorkBuddy 行为一致。
方案 B:预建固定客户端(不用 DCR)
在 Keycloak 手动创建客户端:
Client authentication:OFF(public)
Standard flow:ON
PKCE:S256(或 Proof Key for Code Exchange Code Challenge Method = S256)
Valid redirect URI:
workbuddy://workbuddy/mcp/connector%3Atest/oauth/callback
并限制或关闭匿名 DCR,避免 WorkBuddy 每次连接再注册新 client。
注意:WorkBuddy 每次点「连接」会清本地 client_id 再 DCR;要稳定用预建 client,需要产品支持静态 client_id,或接受 DCR 后由脚本批量改客户端属性。
方案 C:DCR 之后用 Admin API 补属性
对 DCR 生成的 client_id 调用 Admin REST API,设置:
"attributes": {
"pkce.code.challenge.method": "S256"
}
或在 UI:Clients → 该客户端 → Advanced → PKCE Code Challenge Method = S256。
这是 注册之后 的配置,不是让 WorkBuddy 在 DCR 里传 PKCE。
问题
能否统一用一个 Keycloak 客户端?
Keycloak 侧可以预建一个固定客户端,但 WorkBuddy 目前没有官方配置项指定「法律聚合」用固定 client_id。
预建客户端需要至少:
- Client ID:固定 UUID(由你们在 KC 创建)
- Redirect URI(必须完全一致):
workbuddy://workbuddy/mcp/connector%3Amcp-law-agg/oauth/callback - Access Type:public(WorkBuddy 使用 PKCE,token_endpoint_auth_method: none)
- Standard Flow:开启
- PKCE:S256
但仅预建还不够:WorkBuddy 每次点连接仍会 deleteClientInfo,然后再次 DCR,KC 里还是会不断增加动态客户端。
手动写入 .credentials.json 的 mcpClientInfo 也会被下一次点「连接」清掉,无法稳定复用。
可行建议
-
运维侧(Keycloak)
预建一个客户端,例如 workbuddy-mcp-law-agg(名称自定)
在 realm 限制或关闭面向公网的 DCR(Initial Access Token、注册策略等),避免匿名无限注册
定期清理 WorkBuddy Connector (connector:mcp-law-agg) 这类动态客户端 -
联调侧(减少重复点连接)
先修好 oauth-protected-resource 和 token 类型问题,争取 一次授权成功。成功后 WorkBuddy 会保存 token,一般不必反复点「连接」,KC 客户端增长会明显变慢。 -
产品侧(根本方案)
向 WorkBuddy 提需求,例如:
连接器支持配置 静态 client_id / client_secret
或:用户已授权时,点「连接」不要清除 client 作用域的凭证
在 WorkBuddy 改之前,无法在客户端配置里做到「统一只用一个 KC 客户端且稳定复用」。
结论:可以、也应当在 Keycloak 统一预建一个客户端;但按当前 WorkBuddy 逻辑,每次点「连接」仍会触发新的 DCR。要真正复用固定 client,需要 WorkBuddy 支持静态 client,或 connect 时不再清除已保存的 client_id。
如果你需要,我可以写一段给 Keycloak 管理员的「预建客户端 + 限制 DCR」检查清单(含 redirect URI 和 PKCE 配置项)。
难点
- 目前mcp通过wso2系统进行权限控制
- 用户体系依赖于kc
- workbuddy对接kc后,生成的是kc的token
- kc的token无法直接对mcp进行资源访问
- ?需要在code to token环节,将kc的token置换为wso2的token,再为用户生成一个应用的token,这样才能应用到订阅的mcp服务上面
- ?DCR注册客户端后,如何让PKCE生效,目前通过监听client_register进行添加pkce,是有延时的,这是workbuddy会出现pkce的错误
- ?平
对接到kc后如何置换wso2应用token
涉及的平台
- wso2平台,用来做mcp服务的管理和mcp的资源认证
- keycloak平台,是一个idp认证平台,用来做用户认证,并与wso2平台进行token的交互
- workDubby工具,它是一个C端工具,用来调用mcp服务
具体流程
- wos2-mcp服务添加.well-known/oauth-protected-resource端点
- 访问mcp资源返回401时,同时输出响应头www-authenticate
Bearer resource_metadata="https://test-apim-gateway.xxx.com/weather/1.0/.well-known/oauth-protected-resource", error="invalid_token", error_description="Access token is missing or invalid" - 从resource_metadata端点获取authorization_servers,进行认证
- 跳到authorization_server所对应的idp进行认证
- 通过DCR(动态客户端注册)向IDP添加一个客户端
- 在IDP中添加wos2的IDP,用来与wso2通讯
- 注册客户端后,向idp发起登录请求,登录成功后返回code
- 通过code获取token,同时修改idp源码
- 访问wso2认证接口,进行token的交换
- 拿到wso2接口访问用户的应用,没有应用需要新建
- 拿到默认应用后,获取它的consumerkey,没有就新建
- 通过consumerkey和应用ID获取应用的token
- 使用应用token覆盖kc生成的token,并返回给workbuddy
- ?判断应用是否订阅了当前mcp
- ?如果没有订阅,需要先为它进行mcp的订阅
- 通过token访问mcp资源

