From 12f69504e41fa02effb9884c5a726430e3853ec5 Mon Sep 17 00:00:00 2001 From: xqzp2026 Date: Mon, 7 Sep 2026 12:27:02 +0930 Subject: [PATCH] =?UTF-8?q?docs(mcp):=20=E7=BB=9F=E4=B8=80=E6=9C=8D?= =?UTF-8?q?=E5=8A=A1=E6=96=87=E6=A1=A3=E7=9B=AE=E5=BD=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../doc/agent-mcp-service-design.md | 0 .../doc/agent-mcp-tools-reference.md | 0 .../xiaoqu-ai-mcp-per-user-token-design.md | 27 +++- .../doc/xiaoqu-mcp-integration-setup.md | 146 ++++++++++++++++++ 4 files changed, 170 insertions(+), 3 deletions(-) rename {xiaoqu-franchisee => xiaoqu-agent-service}/doc/agent-mcp-service-design.md (100%) rename {xiaoqu-franchisee => xiaoqu-agent-service}/doc/agent-mcp-tools-reference.md (100%) rename {xiaoqu-franchisee => xiaoqu-agent-service}/doc/xiaoqu-ai-mcp-per-user-token-design.md (90%) create mode 100644 xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md diff --git a/xiaoqu-franchisee/doc/agent-mcp-service-design.md b/xiaoqu-agent-service/doc/agent-mcp-service-design.md similarity index 100% rename from xiaoqu-franchisee/doc/agent-mcp-service-design.md rename to xiaoqu-agent-service/doc/agent-mcp-service-design.md diff --git a/xiaoqu-franchisee/doc/agent-mcp-tools-reference.md b/xiaoqu-agent-service/doc/agent-mcp-tools-reference.md similarity index 100% rename from xiaoqu-franchisee/doc/agent-mcp-tools-reference.md rename to xiaoqu-agent-service/doc/agent-mcp-tools-reference.md diff --git a/xiaoqu-franchisee/doc/xiaoqu-ai-mcp-per-user-token-design.md b/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-per-user-token-design.md similarity index 90% rename from xiaoqu-franchisee/doc/xiaoqu-ai-mcp-per-user-token-design.md rename to xiaoqu-agent-service/doc/xiaoqu-ai-mcp-per-user-token-design.md index cf4feb5..566ce8a 100644 --- a/xiaoqu-franchisee/doc/xiaoqu-ai-mcp-per-user-token-design.md +++ b/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-per-user-token-design.md @@ -67,7 +67,7 @@ | MCP Token 过期/401 | 清除当前用户 MCP Token,重新换取一次;同一请求不自动重放工具副作用 | | accessToken 刷新 | 保留 MCP Token;若服务返回 401,再重新换取 | | 切换账号 | 先断开旧 MCP Client,再加载新账号 Token 并建立新 Client | -| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;服务端撤销或等待短期过期 | +| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;当前永久 Token 不会自动失效,需要显式调用 revoke | | 多账号 | Keychain key 必须包含稳定用户 ID,禁止所有账号共用 `mcpToken` | ~~建议 MCP Token 默认有效期 15 分钟,并提供换取接口;长期 AK 仅用于受控开发/管理场景。~~ @@ -115,11 +115,32 @@ - 不存在则自动创建永久 Token,memo 标记 `open-api 自动签发()`。 - 全程写 `audit_log`(`tool_name=open.token.ensure`、`client_type=open-api`,手机号/邮箱脱敏,不记录 Token)。 -**关键约束与对策**:`created=false` 时拿不到明文。因此 xiaoqu-ai 业务后端必须在首次 `created=true` 时**自己持久化明文**(按 user_id 关联,加密存储),之后下发给本用户的客户端;若明文丢失,只能由管理员在后台禁用旧 Token 后再调 `ensure` 触发新建(或将来加 rotate 接口)。 +**关键约束与对策**:`created=false` 时拿不到明文。因此 xiaoqu-ai 业务后端必须在首次 `created=true` 时**自己持久化明文**(按 user_id 关联,加密存储),之后下发给本用户的客户端。 + +**身份约束**:业务后端不得接受客户端或模型传入的 `user_id`、`mobile`、`email` 作为身份依据。必须先从已验证的 xiaoqu-ai `accessToken` 会话解析用户,再由业务后端查询该用户的手机号/邮箱并调用 `ensure`。`X-Open-Key` 只允许服务端保管。 + +### 4.1a Token 丢失与主动失效(建议补充) + +当前 `ensure` 在 `created=false` 时不会返回明文,无法解决业务后端首次落库失败或 Token 泄露后的换发问题。建议增加两个服务间接口,均使用 `X-Open-Key` 鉴权: + +`POST /api/open/token/rotate` + +- 按用户和 `scopeCode` 禁用当前有效 Token,创建新 Token。 +- 仅本次返回新 Token 明文;旧 Token 立即失效。 +- 记录旧 Token、新 Token 前缀、用户和 scope,禁止记录 Token 明文。 +- 具备幂等保护,避免并发换发产生多个有效 Token。 + +`POST /api/open/token/revoke` + +- 按用户和 `scopeCode` 禁用当前 Token。 +- 不返回 Token 明文;重复调用视为成功。 +- 用于退出登录后的主动失效、设备解绑和安全事件处置。 + +在 `rotate/revoke` 尚未实现前,退出登录只能清理客户端本地凭证,不能声称服务端 Token 已失效;管理员后台禁用仍是唯一服务端回收方式。 ### 4.1b xiaoqu-ai 业务后端门面(待 xiaoqu-ai 侧实现) -iOS 面向的接口(建议沿用原设计):POST `/api/v1/mcp/session-token`,`Authorization: Bearer `。业务后端从登录态解析用户 → 取手机号/邮箱 → 调 `ensure` → 返回 `{token, expires_at, user_id}`。客户端身份字段(client、device_id)由业务后端自行记录,agent-service 不感知。 +iOS 面向的接口(建议沿用原设计):POST `/api/v1/mcp/session-token`,`Authorization: Bearer `。业务后端从登录态解析用户 → 服务端查询手机号/邮箱 → 调 `ensure` → 返回 `{token, expires_at, user_id}`。不得使用请求体中的 `user_id/mobile/email` 覆盖登录态身份;客户端身份字段(client、device_id)由业务后端自行记录,agent-service 不感知。 ### 4.2 POST `/mcp` diff --git a/xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md b/xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md new file mode 100644 index 0000000..eb80443 --- /dev/null +++ b/xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md @@ -0,0 +1,146 @@ +# xiaoqu-operations-ai 接入 xiaoqu-mcp 开发设置 + +## 1. 目标 + +让 `ops-assistant-service` 通过服务端 MCP Client 调用 +`xiaoqu_mcp_service`,保持现有: + +```text +Router → Planner → Executor → Answer +``` + +每次调用使用当前已验证用户对应的 MCP Token,不把 MCP Token 放进提示词、Planner 输出、工具参数或普通日志。 + +## 2. 当前代码结论 + +- `ops-assistant-service` 是 FastAPI + Python 3.12 服务。 +- 当前依赖中没有 MCP SDK,也没有 `tools/list` / `tools/call` 客户端。 +- `Executor` 目前直接调用本地 capability:`customer_issues.*`、`smartclean.query`。 +- 用户身份来自 Gateway JWT,并通过 `ops_identity_mappings` 解析租户、用户和 `scope_tags`。 +- 当前最合适的接入点是 Executor 的工具适配层,不新增第二套编排器。 + +## 3. xiaoqu-mcp 服务契约 + +### 3.1 MCP 调用 + +- 地址:`POST /mcp` +- 认证:`Authorization: Bearer ` +- 初始化顺序:`initialize` → `notifications/initialized` → `tools/list` → `tools/call` +- 初始化后保存 `Mcp-Session-Id`。 +- `tools/list` 已按 Token scope 过滤,`tools/call` 仍由服务端再次鉴权。 + +### 3.2 Token 初始化 + +服务间调用:`POST /api/open/token/ensure` + +```http +X-Open-Key: +Content-Type: application/json +``` + +请求体包含 `mobile` 或 `email`,以及固定的 `scopeCode`(首期建议 `operation`)。 + +约束: + +- `X-Open-Key` 只能配置在受信任后端,不能下发 App。 +- 业务后端必须从已验证用户会话查询手机号/邮箱,不能信任客户端或模型传入的身份字段。 +- `created=true` 才返回 Token 明文;业务后端必须立即加密保存。 +- `created=false` 不返回明文;Token 丢失前需先实现 rotate,退出登录需实现 revoke 才能立即失效。 + +## 4. 推荐实现 + +### 4.1 配置 + +在 `config.py` 增加以下 `OPS_ASSISTANT_` 配置: + +| 变量 | 默认值 | 说明 | +|---|---|---| +| `XQ_MCP_ENABLED` | `false` | 是否启用 MCP capability | +| `XQ_MCP_URL` | unset | 例如 `http://xiaoqu-mcp:8088`,只存地址,不存用户 Token | +| `XQ_MCP_OPEN_KEY` | unset | 仅用于服务端 Token 初始化,使用 `SecretStr` | +| `XQ_MCP_SCOPE_CODE` | `operation` | 固定作用域,禁止由 Planner 或客户端选择 | +| `XQ_MCP_TIMEOUT_SECONDS` | `10` | MCP 请求超时 | + +启用时必须同时提供 URL、Open Key 和 scope;生产环境禁止明文写入仓库。 + +### 4.2 新增最小模块 + +建议只增加两个模块: + +- `src/ops_assistant_service/mcp_client.py` + - 管理 HTTP 请求、JSON-RPC、Session ID、Bearer Header、超时和错误映射。 + - 进程内按 `user_id` 缓存 MCP Session;账号或 Token 变化时销毁。 +- `src/ops_assistant_service/mcp_token_provider.py` + - 接收已验证的 `SharedIdentity`。 + - 从受信任用户资料源取得 mobile/email。 + - 调用 `ensure`,首次创建时加密持久化明文。 + - 不接受 `user_id/mobile/email` 作为客户端覆盖参数。 + +不引入通用 MCP 框架、第二个 Agent 或数据库直连;现有 `httpx` 已足够完成首期只读接入。 + +### 4.3 Capability 映射 + +在 `capability_registry.py` 增加固定的本地 capability,禁止把远端 `tools/list` 原样暴露给 Planner: + +| 本地 capability | MCP 工具 | 首期权限 | +|---|---|---| +| `xiaoqu.agent_count` | `agent.count` | read | +| `xiaoqu.agent_list` | `agent.list.query` | read | +| `xiaoqu.property_list` | `property.list.query` | read | + +Planner 只能选择注册表中的名称和 schema。Executor 在调用前同时检查: + +```text +应用允许 ∩ 用户 scope_tags ∩ 租户/数据范围 +``` + +远端返回结果先经过字段白名单投影,再交给 Answer;Token、原始 Header 和未授权字段不得进入 trace。 + +### 4.4 Executor 接入点 + +在 `executor.py` 增加一个只读分支: + +```text +validate_tool_input + → 校验本地 capability 和参数白名单 + → 读取 SharedIdentity / DataScope + → 获取当前用户 MCP Token + → MCP tools/call + → 结果投影 + → 现有 execution_trace / Answer 链路 +``` + +现有 `customer_issues.*` 和 `smartclean.query` 保持不变。MCP 不可用时返回稳定的 `TOOL_UNAVAILABLE`,不得回退到公共 Token 或直接访问 xiaoqu-mcp 数据库。 + +## 5. 生命周期与安全 + +- 登录用户变化:先关闭旧 Session,再建立新用户 Session。 +- MCP 返回 401:清理当前用户 Token,最多执行一次重新获取和重连;不自动重放副作用请求。 +- 退出登录:清理本地/服务端 Token 缓存;实现 revoke 后再主动撤销远端 Token。 +- 日志只记录用户 ID、工具名、scope 和错误码,不记录 Token、Authorization Header 或手机号/邮箱明文。 +- `tools/list` 仅用于校验远端能力;真正可调用集合以本地 allowlist 为准。 + +## 6. 开发顺序 + +1. 先在 xiaoqu-ai 业务后端完成登录态到 `ensure` 的门面,并验证 Token 明文加密落库。 +2. 增加 `Settings` 配置和启动校验。 +3. 实现 `mcp_token_provider.py` 和 `mcp_client.py`,补充 JSON-RPC/Session 单元测试。 +4. 增加 3 个只读 capability,并接入 Executor。 +5. 增加用户隔离、scope 拒绝、401 重连、Token 脱敏和 MCP 不可用测试。 +6. 先在 development/test 环境启用,完成联调后再进入 staging。 + +## 7. 验收标准 + +- 用户 A 的请求始终使用 A 的 MCP Token,用户 B 不能复用。 +- A 无对应 scope 时,Planner/Executor 均不能调用该 MCP 工具。 +- `tools/list`、`tools/call` 使用同一用户 Session 和 Bearer Token。 +- 切换用户后旧 Session 不再发出请求。 +- MCP 401 只刷新当前用户,且同一请求不重复执行副作用。 +- 测试、日志、trace、配置文件和 Git 历史中均无 Token 明文。 + +## 8. 当前不做 + +- 不把 MCP Client 放进 iOS/Android 端。 +- 不把全部远程工具动态注册给 Planner。 +- 不复制 xiaoqu-mcp 数据库或绕过 MCP 权限。 +- 不在本次首期引入写工具;写工具需另行设计审批、幂等和补偿协议。