# MCP 接入的 Keycloak 管理员配置清单 这里创建的是 OAuth 认证配置。OpenCode 到 Agent、Agent 到 Server 的 MCP 协议客户端已实现,无需再开发或安装 MCP 客户端程序。 以下使用已创建的请求方客户端 `agent-mcp`,MCP 资源的 audience 使用 `tjwater-mcp`。它们位于现有业务登录使用的同一个 realm。 ## 1. 请求方客户端 agent-mcp 在 Keycloak 的 Clients 中打开 `agent-mcp`: | 设置 | 值 | | --- | --- | | Client type / Protocol | OpenID Connect | | Client authentication | On;这是 confidential 客户端 | | Service accounts roles | On;用于启动时读取 MCP 工具目录 | | Standard token exchange | On;用于按当前用户或 worker 的身份换取 MCP 令牌 | | Standard flow | Off;该客户端不负责浏览器登录 | | Direct access grants | Off | | Access token lifespan | 建议300秒,不超过900秒 | | 签名算法 | RS256 | 保存后,在 Credentials 中取得 Client Secret。密钥只写入 Agent 的忽略文件 `.local.env`: ```dotenv TJWATER_MCP_CLIENT_ID='agent-mcp' TJWATER_MCP_CLIENT_SECRET='' ``` 不要把密钥放入聊天、前端配置、OpenCode 配置或 Git。Server 只配置 `TJWATER_MCP_HOST_CLIENT_ID='agent-mcp'`,不需要保存这个客户端的密钥。 ## 2. 资源客户端及 scopes 创建或复用表示 MCP 服务的 OIDC 客户端 `tjwater-mcp`;关闭浏览器登录、直接密码登录和服务账户。这个 ID 是目标 audience,不是另一个需要部署的服务。 创建以下 OIDC Client scopes,并作为 Optional scopes 分配给 `agent-mcp`;启用 Include in token scope: - `mcp.catalog.read` - `mcp.query` - `mcp.context.bind` 在这些 scopes 中添加 Audience mapper,使对应 token 的 `aud` 包含目标客户端 `tjwater-mcp`。应用按实际 grant 请求 scope:服务账户请求只有 `mcp.catalog.read`;用户/worker 的交换请求才包含查询和上下文 scope。不要给服务账户额外分配项目成员、管理或设备控制权限。 ## 3. 允许当前业务身份交换 在原有前端登录客户端和 worker 客户端的专用 scope 中添加 Audience mapper,使源 token 的 `aud` 包含请求方 `agent-mcp`。保留原业务 API 所需的 audience;不要给源 token 添加 MCP 目标 `tjwater-mcp`。 交换后必须保持用户主体 `sub` 不变,`azp=agent-mcp`,目标 `aud` 包含 `tjwater-mcp`。Keycloak 的 `audience` 参数只筛选已有映射的 audience,不能代替 Audience mapper。 版本和界面名称以当前 Keycloak 为准。配置依据:[Keycloak Standard Token Exchange](https://www.keycloak.org/securing-apps/token-exchange)。仅在公开发现中看到 token exchange,并不能证明具体客户端已启用或映射正确。 ## 4. 地址、数据库和启用顺序 正式环境提供该 realm 的 HTTPS issuer。本地开发可显式允许一个 RFC1918 IPv4 HTTP issuer:Agent 设置 `NODE_ENV=development`,Server 设置 `ENVIRONMENT=development`,两端 `TJWATER_MCP_DEV_HTTP_ISSUER_URI` 均填写与各自 `TJWATER_MCP_ISSUER_URI` 完全相同的地址(含 realm)。此例外不放行远程 HTTP MCP resource;Agent 只允许该 issuer 同源的远程 HTTP 发现及令牌端点,继续拒绝重定向。正式环境必须清空此项并使用 HTTPS 域名。配置值必须与发现文档和令牌 `iss` 一致,不能仅替换访问地址。 例如,同机开发时 Server `.env` 可配置为(内网地址按实际认证服务替换): ```dotenv ENVIRONMENT=development TJWATER_MCP_ENABLED=true TJWATER_MCP_ISSUER_URI=http://192.168.1.114:8088/realms/tjwater TJWATER_MCP_DEV_HTTP_ISSUER_URI=http://192.168.1.114:8088/realms/tjwater TJWATER_MCP_RESOURCE_URI=http://127.0.0.1:8000/mcp ``` Agent `.local.env` 使用相同的三个 URI,并设置 `NODE_ENV=development`、 `TJWATER_MCP_ENABLED=true`。开发模式不跳过签名、issuer、audience、scope 或项目权限校验, 也不自动接受另一种 issuer。切换 Server 环境还会开启开发 API 文档页面。 修改环境文件后需重启相应进程;已有进程不一定自动重读 `.env`,系统环境变量也可能覆盖文件。 应先确认 Server 的 `/.well-known/oauth-protected-resource/mcp` 公布的新 issuer, 再启动 Agent 检查工具目录。切回生产时,两端恢复相同 HTTPS issuer 并删除 HTTP 白名单。 业务身份也必须使用同一 issuer:供水前端只在被 Git 忽略的 `.env.development.local` 中将 `TJWATER_KEYCLOAK_ISSUER` 设为该内网地址, 保留 `.env.local` 的 HTTPS 配置供正常 production 构建使用。 Server 本机 `.env` 可额外设置 `OPERATIONAL_WORKER_DEV_TOKEN_URL` 为同一内网 issuer 发现文档中的 `token_endpoint`;该值仅在 `ENVIRONMENT=development` 时生效, 不替换原来的 `OPERATIONAL_WORKER_TOKEN_URL`。重启开发前端和 worker,并在浏览器重新登录。 旧 HTTPS 令牌不能用于内网 issuer 的交换;工具目录读取成功不等于业务身份交换成功。 接入人员随后执行: 1. 安装 MCP SDK 2 对应的 Server 依赖。 2. 对选定项目业务库备份并应用 [025_mcp_query_contexts.sql](../resources/sql/025_mcp_query_contexts.sql)。 3. 在 Agent 和 Server 的私有环境中配置相同的 issuer、resource、audience;resource 必须指向实际 Server `/mcp`,请求方客户端 ID 均使用 `agent-mcp`。 4. 验证带标准 `resource` 的目录 grant 和用户/worker token exchange 都成功;若身份平台拒绝,先解决兼容性,不移除参数重试。 5. 在受控测试实例中开启两端 `TJWATER_MCP_ENABLED` 并重载;通过测试项目查询、任务证据和越权拒绝验收后,再按部署流程启用正式环境。 完整环境变量与验收要求见 [MCP 实施文档](mcp-query-implementation.md)。在以上步骤完成前保持 MCP 开关关闭。