From 58e51e96a9e7c42298df849a81c6475c3f63492f Mon Sep 17 00:00:00 2001 From: xqzp2026 Date: Mon, 7 Sep 2026 18:53:12 +0930 Subject: [PATCH] docs(mcp): split integration design by product --- .../doc/xiaoqu-ai-mcp-integration-design.md | 153 ++++++++++++ .../xiaoqu-ai-mcp-per-user-token-design.md | 232 ------------------ .../doc/xiaoqu-mcp-integration-setup.md | 156 ------------ .../xiaoqu-mcp-multi-product-agent-design.md | 137 +++++++++++ ...qu-operations-ai-mcp-integration-design.md | 112 +++++++++ 5 files changed, 402 insertions(+), 388 deletions(-) create mode 100644 xiaoqu-agent-service/doc/xiaoqu-ai-mcp-integration-design.md delete mode 100644 xiaoqu-agent-service/doc/xiaoqu-ai-mcp-per-user-token-design.md delete mode 100644 xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md create mode 100644 xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md create mode 100644 xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md diff --git a/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-integration-design.md b/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-integration-design.md new file mode 100644 index 0000000..331e5cd --- /dev/null +++ b/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-integration-design.md @@ -0,0 +1,153 @@ +# xiaoqu-ai 接入 xiaoqu-mcp 详细设计 + +## 1. 目标与边界 + +本文是 [`xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计`](./xiaoqu-mcp-multi-product-agent-design.md) 的 xiaoqu-ai 落地设计。 + +xiaoqu-ai 的 iOS、Android、Web 共用一套服务端 MCP 接入,不在三个客户端分别实现 MCP Client: + +```text +iOS / Android / Web + │ 产品登录 Token + ▼ +xiaoqu-ai Gateway / Agent Runtime + │ 用户 MCP Token(仅服务端持有) + ▼ +xiaoqu-agent-service /mcp +``` + +项目目录: + +- iOS、Web、Gateway、Agent Service:`/Users/tonylai/xiaoquai/xiaoqu-ai` +- Android:`/Users/tonylai/jiaxk/xiaoqu-ai-android` + +## 2. 当前代码依据 + +| 端/服务 | 当前入口 | 结论 | +|---|---|---| +| iOS 销售助手 | `ios/Sources/XiaoquCRM/Features/SalesAgent/SalesAgentClient.swift` | 继续调用 `/api/v1/sales-agent/*` REST/SSE | +| iOS 管理助手 | `ios/Sources/XiaoquCRM/Features/AI/Networking/ManagerAgentClient.swift` | 继续调用服务端 Agent,不新增 MCP Token | +| Android | `app/src/main/java/com/xiaoquai/crm/data/remote/WebSocketManager.kt`、`ApiService.kt` | 继续走 Gateway WebSocket/REST,`TokenManager` 只保存产品 Token | +| Web | `web/src/hooks/use-agent-chat.ts` | 继续调用 `/api/v1/agents/{id}/invoke` | +| Gateway | `gateway/internal/agent/tools.go`、`gateway/internal/handler/runtime_tool_invoke_handlers.go` | 已有统一 ToolExecutor、数据范围校验和运行时工具入口 | +| Agent Service | `agent-service/agent_service/tools/runtime_gateway_tools.py` | 已使用当前用户 JWT调用 Gateway runtime catalog/tool invoke | + +iOS 的 `ClawBridge.swift` 是独立端侧 Agent 模式,不属于本次服务端 xiaoqu-mcp 接入,不修改其 Keychain、MCP 配置或 Token。 + +## 3. 统一接入点 + +MCP 适配器放在 Gateway 的工具执行层,三端最终复用同一实现: + +```text +Web / Android Agent 请求 ──► Gateway AgentRuntime ─┐ + ├─► Gateway ToolExecutor +iOS Sales Agent ──► agent-service │ │ + └─ runtime_gateway_tools ───────┘ ▼ + xiaoqu-mcp +``` + +这样保留现有客户端和 Agent 编排,只把新的远程工具接入集中在 Gateway。Agent Service 继续转发已验证的用户 JWT,不自行持有第二份 xiaoqu-mcp Token 缓存。 + +## 4. Gateway 变更 + +### 4.1 配置 + +在 Gateway 启动配置中增加: + +| 变量 | 默认值 | 说明 | +|---|---|---| +| `XQ_MCP_ENABLED` | `false` | 是否启用 xiaoqu-mcp 工具 | +| `XQ_MCP_URL` | unset | xiaoqu-mcp 内网地址 | +| `XQ_MCP_OPEN_KEY` | unset | Token 初始化凭证 | +| `XQ_MCP_SCOPE_CODE` | unset | xiaoqu-ai 固定 scope;启用前必须在管理后台确认 | +| `XQ_MCP_TIMEOUT_SECONDS` | `10` | MCP 请求超时 | + +启用时 URL、Open Key 和 scope 必填。Open Key 只来自部署 Secret,不写入 YAML、客户端配置或仓库。 + +### 4.2 最小模块 + +新增 `gateway/internal/connector/xiaoqumcp/`: + +- `token_provider.go` + - 输入 Gateway 已验证的 `tenantID`、`userID`。 + - 从用户资料存储取得可信 mobile/email 并调用 `ensure`。 + - 按租户、用户和 scope 缓存 Token;401 时只刷新当前用户一次。 +- `client.go` + - 管理 MCP JSON-RPC、Session ID、Bearer Header、超时和错误映射。 + - 按租户、用户和 scope 隔离 Session。 + +使用 Go 标准 HTTP Client 和项目现有错误/日志设施,不增加 MCP 框架依赖。 + +### 4.3 ToolExecutor 接入 + +在 `gateway/internal/agent/tools.go` 注册稳定的本地工具名,由 Handler 调用 `xiaoqumcp.Client`。不得把远端 `tools/list` 原样注册给模型。 + +首期只接只读工具。候选映射以 xiaoqu-mcp 当前工具为基础,但必须先为 xiaoqu-ai 创建并审核独立 scope: + +| Gateway 本地工具 | MCP 工具 | 状态 | +|---|---|---| +| `xiaoqu.agent_count` | `agent.count` | scope 绑定后启用 | +| `xiaoqu.agent_list` | `agent.list.query` | scope 绑定后启用 | +| `xiaoqu.property_list` | `property.list.query` | scope 绑定后启用 | + +每个工具继续使用现有 `RequiredDataScope`、Agent Capability 绑定和参数 schema。MCP 返回结果先做字段白名单投影,再进入模型上下文、卡片渲染和 `chat_tool_logs`。 + +## 5. 用户与权限流程 + +```text +产品登录 Token + → Gateway middleware 得到 userID / tenantID / role + → Agent Capability + RequiredDataScope 校验 + → 可信用户资料解析 mobile/email + → token_provider.ensure(scopeCode) + → mcp_client.tools/call + → xiaoqu-mcp 再校验 Token scope 与数据权限 +``` + +`agent.InvokeRequest` 已携带 `UserID`、`TenantID`、`UserJWT` 和 `UserRole`。MCP Handler 必须使用这些可信字段,不接受模型参数覆盖身份或 scope。 + +Agent Service 的 `runtime_gateway_tools.py` 继续转发入站用户 JWT;无 JWT 时保持 fail-closed,不允许自签或使用公共用户身份。 + +## 6. 客户端影响 + +| 客户端 | 变更 | +|---|---| +| iOS | 无 MCP 网络层变更;继续消费现有 REST/SSE、确认卡和工具结果 | +| Android | 无 MCP 网络层变更;Gateway Token 仍由现有 `TokenManager` 管理 | +| Web | 无 MCP 网络层变更;继续调用 Agent invoke API 并渲染返回结果 | + +如果新工具需要专用展示,只扩展现有服务端响应投影和客户端卡片 schema;不得因此向客户端下发 MCP Token、Session ID 或原始 Header。 + +## 7. 错误与生命周期 + +- 用户切换或产品会话失效时,当前用户的 MCP Token 缓存和 Session 一并失效。 +- MCP 401:清理当前用户缓存,重新 `ensure` 并重连一次。 +- MCP 403:返回权限不足,不刷新 Token。 +- MCP 超时或不可用:返回现有稳定工具错误,不回退公共 Token 或数据库直连。 +- 只读请求最多重试一次;写工具不在首期范围。 +- 日志和 `chat_tool_logs` 只记录工具名、状态、耗时与脱敏错误,不记录 Token、Header、mobile/email 或未投影原始结果。 + +## 8. 开发顺序 + +1. 在目标环境创建或确认 xiaoqu-ai 独立 scope,并绑定首期只读工具。 +2. 增加 Gateway 配置与启动校验。 +3. 实现 `token_provider.go` 和 `client.go`,覆盖 Token、Session、超时和错误映射测试。 +4. 在 ToolExecutor 注册固定工具并复用现有 DataScope 校验。 +5. 验证 Agent Service runtime gateway 路径仍只转发用户 JWT。 +6. 分别用 iOS、Android、Web 完成同用户、跨用户和跨租户联调。 + +## 9. 项目验收 + +- 同一用户在 iOS、Android、Web 调用同一工具时,结果契约和数据权限一致。 +- 三端请求、存储、日志中均无 MCP Token、Session ID 或 `X-Open-Key`。 +- Web/Android Gateway Agent 与 iOS Sales Agent 最终调用同一个 Gateway MCP Handler。 +- 用户 A/B、不同租户和 scope 的 Token 缓存与 MCP Session 完全隔离。 +- 无本地 Capability、无 RequiredDataScope 或无 MCP scope 时均不能调用。 +- MCP 401 只刷新当前用户;MCP 不可用不影响未启用该 Capability 的原有 Agent 功能。 + +## 10. 当前不做 + +- 不为 iOS、Android、Web 分别实现 MCP Client。 +- 不改造 iOS ClawBridge。 +- 不把远端全部工具动态暴露给 Agent。 +- 不在首期接入写工具或副作用自动重试。 diff --git a/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-per-user-token-design.md b/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-per-user-token-design.md deleted file mode 100644 index 30249b7..0000000 --- a/xiaoqu-agent-service/doc/xiaoqu-ai-mcp-per-user-token-design.md +++ /dev/null @@ -1,232 +0,0 @@ -# xiaoqu 多端 Agent 统一接入 xiaoqu-mcp 开发设计 - -## 1. 设计结论 - -xiaoqu-ai 的 iOS、Android、Web **共用一套 Agent/MCP 接入链路**: - -```text -iOS / Android / Web - │ 产品登录 Token - ▼ -xiaoqu-ai Gateway / Agent Runtime - │ 用户 MCP Token(仅服务端持有) - ▼ -xiaoqu-agent-service /mcp -``` - -运营项目是另一套产品和 Agent 编排服务,因此保留自己的入口,但复用同一个 -xiaoqu-mcp 服务、Token 原语、工具契约、权限和审计规则: - -```text -运营 iOS / Android - │ 产品登录 Token - ▼ -ops-assistant-service - │ 用户 MCP Token(仅服务端持有) - ▼ -xiaoqu-agent-service /mcp -``` - -共同的是服务能力和协议,不是 Swift/Kotlin UI 代码。客户端不实现 MCP Client, -不保存 MCP Token,也不接触 `X-Open-Key`。 - -### 1.1 范围 - -| 产品 | 客户端 | 项目目录 | -|---|---|---| -| xiaoqu-ai | iOS、Web、服务端 Agent | `/Users/tonylai/xiaoquai/xiaoqu-ai` | -| xiaoqu-ai | Android | `/Users/tonylai/jiaxk/xiaoqu-ai-android` | -| xiaoqu-operations-ai | iOS、Android、服务端 Agent | `/Users/tonylai/jiaxk/xiaoqu-operations-ai` | -| xiaoqu-mcp | MCP 服务、Token、权限、审计 | 当前仓库 `xiaoqu-agent-service` | - -### 1.2 明确不做 - -- 不为 iOS、Android、Web 分别实现 MCP Client。 -- 不提供面向 App/Web 的 MCP Token 换取接口。 -- 不把固定 MCP Token、`X-Open-Key` 写入 App、浏览器配置、prompt 或 Git。 -- 不让客户端、Planner 或模型提交 `user_id`、手机号、邮箱、租户或 scope 作为授权依据。 -- 不改造现有 iOS ClawBridge;它是独立的端侧 Agent 模式,不属于本次 xiaoqu-mcp 接入链路。 - -## 2. 当前代码依据 - -| 项目/端 | 当前入口 | 结论 | -|---|---|---| -| xiaoqu-ai iOS | `ios/Sources/XiaoquCRM/Features/SalesAgent/SalesAgentClient.swift`、`Features/AI/Networking/ManagerAgentClient.swift` | 已通过 Agent REST/SSE 调用,继续复用 | -| xiaoqu-ai Android | `app/src/main/java/com/xiaoquai/crm/data/remote/WebSocketManager.kt`、`ApiService.kt` | 已通过 Gateway WebSocket/REST 调用,继续复用 | -| xiaoqu-ai Web | `web/src/hooks/use-agent-chat.ts` | 已调用 Gateway `/api/v1/agents/{id}/invoke`,继续复用 | -| xiaoqu-ai Agent | `agent-service`、`gateway` | 增加服务端 MCP Client 和用户 Token Provider | -| 运营 iOS/Android | `ios`、`android` | 已调用 `ops-assistant-service` REST/WebSocket,继续复用 | -| 运营 Agent | `services/ops-assistant-service` | 在 Executor 工具适配层增加 MCP Client | - -xiaoqu-ai iOS 的 `ClawBridge.swift` 当前具备端侧 MCP 能力,但 Android 和 Web 没有。 -为了让三端真正共用一套,本次不把新的 xiaoqu MCP Token 接入 ClawBridge,统一走服务端 Agent。 - -## 3. 统一架构 - -```text -┌─────────────────────────────────────────────────────┐ -│ xiaoqu-ai │ -│ iOS ─┐ │ -│ Android ──► Gateway ──► Agent Runtime ──┐ │ -│ Web ─┘ │ │ -└────────────────────────────────────────┼────────────┘ - │ MCP -┌────────────────────────────────────────┼────────────┐ -│ xiaoqu-operations-ai │ │ -│ iOS ─┐ │ │ -│ Android ──► ops-assistant-service ─────┤ │ -└────────────────────────────────────────┼────────────┘ - ▼ - xiaoqu-agent-service - Token + Scope + Tool - 行级权限 + Audit -``` - -### 3.1 职责 - -| 层 | 职责 | -|---|---| -| iOS/Android/Web | 登录、会话 UI、流式展示、确认交互;只携带产品登录凭证 | -| 产品 Gateway | 验证登录态,注入可信用户/租户上下文,代理 Agent 请求 | -| 产品 Agent Runtime | 编排 Agent、维护 capability allowlist、获取用户 MCP Token、调用 MCP | -| xiaoqu-agent-service | 校验 MCP Token、过滤工具、执行行级权限、查询业务数据、记录审计 | - -## 4. 认证与 Token - -### 4.1 三类凭证 - -| 凭证 | 使用者 | 用途 | -|---|---|---| -| 产品登录 Token | iOS/Android/Web → 产品 Gateway | 证明当前产品用户身份 | -| `X-Open-Key` | 产品 Agent Runtime → `/api/open/token/ensure` | 服务间获取用户 MCP Token | -| 用户 MCP Token `xqak_*` | 产品 Agent Runtime → `/mcp` | 绑定 MCP 用户、scope、权限和审计 | - -`X-Open-Key` 和 MCP Token 均只存在于受信任服务端。 - -### 4.2 用户映射 - -1. Gateway 验证产品登录 Token。 -2. Agent Runtime 从可信上下文取得产品用户、租户和数据范围。 -3. Agent Runtime 从受信任用户资料源取得手机号/邮箱。 -4. Agent Runtime 用固定 `scopeCode` 调用 `POST /api/open/token/ensure`。 -5. xiaoqu-agent-service 返回该 MCP 用户对应的 Token。 -6. Agent Runtime 使用此 Token 调用 `/mcp`。 - -禁止接受客户端或模型传入的手机号、邮箱、`user_id`、`tenant_id`、`scopeCode` -覆盖可信上下文。scope 由服务端配置固定,启用前与管理后台实际配置核对。 - -### 4.3 服务端缓存与生命周期 - -- 缓存键至少包含:产品、租户、产品用户 ID、scope。 -- 切换账号或退出登录时,产品会话失效;不得让新用户复用旧用户 MCP Session。 -- MCP 返回 401 时,只清除当前用户缓存并重新 `ensure` 一次。 -- 只读调用可在重连后重试一次;有副作用调用不得自动重放。 -- 日志仅记录用户 ID、scope、工具名、状态和耗时,不记录凭证明文或手机号/邮箱明文。 - -## 5. xiaoqu-mcp 契约 - -### 5.1 `POST /api/open/token/ensure` - -认证:`X-Open-Key: `。 - -请求: - -```json -{ - "mobile": "", - "email": "", - "scopeCode": "" -} -``` - -- `mobile`、`email` 至少一个,手机号优先。 -- 现有有效 Token:解密 `key_cipher` 返回同一明文。 -- 历史无密文 Token:禁用旧 Token,自动签发新 Token。 -- 不存在有效 Token:创建长期 Token;回收通过禁用实现。 -- 响应和审计不得记录 Token 明文。 - -错误语义:`400` 参数/scope 错误,`401` Open Key 无效,`403` Open API 未启用, -`404` 用户不存在或不属于允许的企业账号。 - -### 5.2 `POST /mcp` - -```http -Authorization: Bearer -Content-Type: application/json -Accept: application/json, text/event-stream -``` - -调用顺序:`initialize` → `notifications/initialized` → `tools/list` → `tools/call`。 -初始化响应包含 Session ID 时,后续请求携带 `Mcp-Session-Id`。 - -权限同时在两个位置收口: - -```text -产品 Agent capability allowlist - ∩ MCP Token scope - ∩ 当前用户/租户/行级数据范围 -``` - -`tools/list` 按 scope 过滤;`tools/call` 再次强制校验,不能只依赖提示词或隐藏菜单。 - -## 6. 最小变更清单 - -### 6.1 xiaoqu-agent-service - -`ensure`、Token 密文、Bearer 鉴权、scope 和审计已交付,保持协议兼容;本轮主要配合 -两个 Agent Runtime 联调,不为客户端增加新接口。 - -### 6.2 xiaoqu-ai - -| 层 | 变更 | -|---|---| -| Gateway/Agent Runtime | 增加用户 MCP Token Provider 和 MCP HTTP Client;接入现有工具适配层 | -| iOS | 保持现有 Agent REST/SSE 调用;不修改 Keychain 和 ClawBridge 的 MCP Token | -| Android | 保持现有 Gateway REST/WebSocket 调用;`TokenManager` 只保存产品 Token | -| Web | 保持现有 `/agents/{id}/invoke` 等 Agent API;浏览器不感知 MCP | - -### 6.3 xiaoqu-operations-ai - -在现有 `Router → Planner → Executor → Answer` 中,只修改 Executor 工具适配层: - -```text -validate capability - → trusted user context - → MCP Token Provider - → MCP tools/call - → result projection - → execution trace / Answer -``` - -具体设置见 `xiaoqu-mcp-integration-setup.md`。 - -## 7. 开发顺序 - -| # | 工作 | 完成标准 | -|---|---|---| -| 1 | 确认两个产品的用户映射和实际 scopeCode | 不依赖客户端身份字段,scope 已在管理后台存在 | -| 2 | xiaoqu-ai Agent Runtime 接入 Token Provider + MCP Client | iOS/Android/Web 调同一 Agent 能力并得到一致结果 | -| 3 | ops-assistant-service 接入 Token Provider + MCP Client | 运营 iOS/Android 经同一服务端链路成功 | -| 4 | 多端与跨用户安全回归 | 无 Token 泄露、串号、越权或副作用自动重放 | - -## 8. 验收标准 - -- xiaoqu-ai 同一用户在 iOS、Android、Web 调用同一能力,工具结果和数据权限一致。 -- 三端请求只出现产品登录 Token,不出现 MCP Token 或 `X-Open-Key`。 -- 用户 A/B、不同租户、不同 scope 的 MCP Session 和缓存完全隔离。 -- 运营 iOS/Android 经 `ops-assistant-service` 调用,客户端同样不持有 MCP Token。 -- 无 scope 时 Planner/Executor 不能调用;绕过 `tools/list` 直接 `tools/call` 仍被服务端拒绝并审计。 -- MCP 401 只刷新当前用户;有副作用调用不自动重放。 -- Token 不进入 prompt、工具参数、trace、普通日志、崩溃信息和 Git。 - -## 9. 风险与回退 - -| 风险 | 应对 | -|---|---| -| 产品用户与 MCP 用户映射错误 | 只用 Gateway 可信身份查询手机号/邮箱;联调覆盖同名和缺失用户 | -| 服务端缓存串用户 | 复合缓存键;登录切换和并发测试 | -| 两个 Agent Runtime 暴露不同工具 | 各自维护明确 allowlist,并用同一 MCP schema 做契约测试 | -| MCP 不可用 | Agent 返回稳定的工具不可用错误,不回退公共 Token 或数据库直连 | - -回退只关闭各 Agent Runtime 的 MCP capability 开关,iOS、Android、Web 继续使用原有 -Agent 功能;不得回退为客户端共享固定 Token。 diff --git a/xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md b/xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md deleted file mode 100644 index 84c1f86..0000000 --- a/xiaoqu-agent-service/doc/xiaoqu-mcp-integration-setup.md +++ /dev/null @@ -1,156 +0,0 @@ -# xiaoqu-operations-ai 接入 xiaoqu-mcp 开发设置 - -## 1. 目标 - -让 `ops-assistant-service` 作为运营项目唯一 MCP Client 调用 -`xiaoqu_mcp_service`,同时服务运营 iOS、Android。保持现有: - -```text -Router → Planner → Executor → Answer -``` - -每次调用使用当前已验证用户对应的 MCP Token,不把 MCP Token 放进提示词、Planner 输出、工具参数或普通日志。 - -客户端边界: - -| 端 | 调用入口 | MCP Token | -|---|---|---| -| 运营 iOS | `ops-assistant-service` REST/WebSocket | 不下发 | -| 运营 Android | `ops-assistant-service` REST/WebSocket | 不下发 | - -客户端只持产品登录凭证;`ops-assistant-service` 从已验证身份映射用户后,服务端获取并使用 MCP Token。 - -## 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。 -- 业务后端必须从已验证用户会话查询手机号/邮箱,不能信任客户端或模型传入的身份字段。 -- 首次创建返回 Token 明文;xiaoqu-mcp 同时保存 SHA-256 哈希和 AES-GCM 加密密文(密钥由 `app.token-cipher-key` 派生)。 -- 已存在 Token 解密回传明文(二次获取),重复调用幂等复用同一把 Token;历史无密文 Token 自动轮换(禁用旧把、签发新把)。 -- Token 密文解密失败必须报错;不得回退到哈希值或公共 Token。 - -## 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`,首次签发返回明文;后续直接复用本地持久化 token。 - - 不接受 `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 缓存;如需远端立即失效,走服务端管理端吊销能力。 -- 日志只记录用户 ID、工具名、scope 和错误码,不记录 Token、Authorization Header 或手机号/邮箱明文。 -- `tools/list` 仅用于校验远端能力;真正可调用集合以本地 allowlist 为准。 - -## 6. 开发顺序 - -1. 先确认 xiaoqu-mcp 的 `ensure` 契约和 Token 密文落库;运营链路不依赖 xiaoqu-ai 业务后端门面。 -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 权限。 -- 不在本次首期引入写工具;写工具需另行设计审批、幂等和补偿协议。 diff --git a/xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md b/xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md new file mode 100644 index 0000000..cfd146c --- /dev/null +++ b/xiaoqu-agent-service/doc/xiaoqu-mcp-multi-product-agent-design.md @@ -0,0 +1,137 @@ +# xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计 + +## 1. 设计结论 + +xiaoqu-ai 与 xiaoqu-operations-ai 共用 xiaoqu-mcp 的服务能力、Token 原语、工具协议、权限模型和审计规则,但保留各自的 Agent Runtime: + +```text +xiaoqu-ai iOS / Android / Web + │ 产品登录 Token + ▼ +xiaoqu-ai Gateway / Agent Runtime ───┐ + │ 用户 MCP Token(仅服务端持有) +xiaoqu-operations-ai iOS / Android │ + │ 产品登录 Token │ + ▼ │ +ops-assistant-service ───────────────┤ + ▼ + xiaoqu-agent-service /mcp + Token + Scope + Tool + 行级权限 + Audit +``` + +共同的是后端协议,不是 Swift、Kotlin 或 Web UI 代码。任何客户端都不实现本次 xiaoqu-mcp Client,不保存用户 MCP Token,也不接触 `X-Open-Key`。 + +## 2. 范围与文档分工 + +| 层级 | 项目 | 项目目录 | 文档 | +|---|---|---|---| +| 总体 | xiaoqu-mcp | 当前仓库 `xiaoqu-agent-service` | 本文 | +| 详细设计 | xiaoqu-ai iOS、Android、Web、服务端 Agent | `/Users/tonylai/xiaoquai/xiaoqu-ai`、`/Users/tonylai/jiaxk/xiaoqu-ai-android` | [`xiaoqu-ai-mcp-integration-design.md`](./xiaoqu-ai-mcp-integration-design.md) | +| 详细设计 | xiaoqu-operations-ai iOS、Android、服务端 Agent | `/Users/tonylai/jiaxk/xiaoqu-operations-ai` | [`xiaoqu-operations-ai-mcp-integration-design.md`](./xiaoqu-operations-ai-mcp-integration-design.md) | + +本文只定义两个产品必须共同遵守的架构和安全契约。项目代码落点、配置、Capability 映射和项目级验收只写在对应详细设计中。 + +## 3. 统一职责 + +| 层 | 职责 | +|---|---| +| iOS / Android / Web | 登录、会话 UI、流式展示和确认交互;只携带产品登录凭证 | +| 产品 Gateway | 验证登录态,建立可信用户、租户和数据范围上下文 | +| 产品 Agent Runtime | 编排 Agent,维护本地 Capability allowlist,获取用户 MCP Token 并调用 MCP | +| xiaoqu-agent-service | 校验 MCP Token,按 scope 过滤工具,执行用户/租户/行级权限并记录审计 | + +两个产品可以暴露不同的 Capability 集合,但都必须通过同一套 MCP 鉴权和工具契约执行。运营项目不得直接调用 xiaoqu-ai Agent,也不得复制或直连 xiaoqu-mcp 数据库。 + +## 4. 认证与 Token 契约 + +### 4.1 三类凭证 + +| 凭证 | 使用链路 | 用途 | +|---|---|---| +| 产品登录 Token | 客户端 → 产品 Gateway | 证明当前产品用户身份 | +| `X-Open-Key` | 产品 Agent Runtime → `/api/open/token/ensure` | 服务间获取指定用户的 MCP Token | +| 用户 MCP Token `xqak_*` | 产品 Agent Runtime → `/mcp` | 绑定 MCP 用户、scope、权限和审计 | + +`X-Open-Key` 和用户 MCP Token 只能存在于受信任服务端,不得进入客户端、prompt、模型输出、工具参数、普通日志或 Git。 + +### 4.2 用户映射 + +1. Gateway 验证产品登录 Token。 +2. Agent Runtime 从可信上下文取得产品用户、租户和数据范围。 +3. Agent Runtime 从受信任用户资料源取得手机号或邮箱。 +4. Agent Runtime 使用服务端固定的 `scopeCode` 调用 `POST /api/open/token/ensure`。 +5. xiaoqu-agent-service 返回该 MCP 用户对应的 Token。 +6. Agent Runtime 使用该 Token 调用 `/mcp`。 + +客户端、Planner 和模型不得提供或覆盖 `user_id`、手机号、邮箱、租户、数据范围或 `scopeCode`。每个产品启用前必须核对管理后台中的真实 scope 和工具绑定。 + +### 4.3 `POST /api/open/token/ensure` + +认证:`X-Open-Key: `。 + +```json +{ + "mobile": "", + "email": "", + "scopeCode": "" +} +``` + +- `mobile`、`email` 至少一个,手机号优先。 +- 已有有效 Token:解密 `key_cipher` 并返回同一明文。 +- 历史无密文 Token:禁用旧 Token 并自动签发新 Token。 +- 不存在有效 Token:创建长期 Token;回收通过禁用实现。 +- 密文解密失败必须报错,不得回退哈希值或公共 Token。 + +错误语义:`400` 参数或 scope 错误,`401` Open Key 无效,`403` Open API 未启用,`404` 用户不存在或不属于允许的企业账号。 + +## 5. MCP 协议与权限 + +调用入口:`POST /mcp`。 + +```http +Authorization: Bearer +Content-Type: application/json +Accept: application/json, text/event-stream +``` + +调用顺序:`initialize` → `notifications/initialized` → `tools/list` → `tools/call`。初始化响应包含 Session ID 时,后续请求携带 `Mcp-Session-Id`。 + +最终可执行能力为: + +```text +产品 Agent capability allowlist + ∩ MCP Token scope + ∩ 当前用户权限 + ∩ 当前租户/数据范围 +``` + +`tools/list` 必须按 Token scope 过滤,`tools/call` 必须再次强制校验。不得仅依赖提示词、本地隐藏菜单或 Planner 输出做授权。 + +## 6. 生命周期与错误处理 + +- 缓存键至少包含产品、租户、产品用户 ID 和 scope。 +- 登录用户变化或退出登录时,旧用户的 Token 缓存和 MCP Session 必须失效。 +- MCP 返回 401 时,只清理当前用户缓存并重新 `ensure` 一次。 +- 只读调用可在重新连接后重试一次;有副作用调用不得自动重放。 +- MCP 不可用时返回稳定的工具不可用错误,不得回退公共 Token 或数据库直连。 +- 日志只记录用户 ID、scope、工具名、状态和耗时,不记录凭证明文或手机号/邮箱明文。 + +## 7. 联调顺序 + +| # | 工作 | 完成标准 | +|---|---|---| +| 1 | 确认两个产品的用户映射与实际 scope | scope 已存在并绑定所需工具,不依赖客户端身份字段 | +| 2 | 完成 xiaoqu-ai 服务端接入 | iOS、Android、Web 经同一服务端适配器调用 | +| 3 | 完成 xiaoqu-operations-ai 服务端接入 | 运营 iOS、Android 经 ops-assistant-service 调用 | +| 4 | 跨用户、跨租户和跨产品回归 | 无 Token 泄露、串号、越权或副作用自动重放 | + +## 8. 总体验收与回退 + +- 所有客户端请求只出现产品登录 Token,不出现用户 MCP Token 或 `X-Open-Key`。 +- 用户 A/B、不同租户、不同 scope 的 Token 缓存和 MCP Session 完全隔离。 +- 绕过 `tools/list` 直接调用 `tools/call` 仍会被服务端拒绝并审计。 +- 两个产品的本地 allowlist 可以不同,但同名 MCP 工具的请求和响应契约一致。 +- 任一产品回退时,只关闭该 Agent Runtime 的 MCP Capability 开关;客户端继续使用原有 Agent 功能。 +- 禁止回退为客户端共享固定 Token、公共服务端 Token 或数据库直连。 diff --git a/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md b/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md new file mode 100644 index 0000000..e055a1d --- /dev/null +++ b/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md @@ -0,0 +1,112 @@ +# xiaoqu-operations-ai 接入 xiaoqu-mcp 详细设计 + +## 1. 目标与边界 + +本文是 [`xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计`](./xiaoqu-mcp-multi-product-agent-design.md) 的运营项目落地设计。 + +`ops-assistant-service` 是运营项目唯一 MCP Client,同时服务运营 iOS 和 Android,并保留现有编排: + +```text +运营 iOS / Android + │ 产品登录 Token + ▼ +Router → Planner → Executor → Answer + │ + ▼ + xiaoqu-mcp +``` + +客户端不新增 MCP 代码。项目目录:`/Users/tonylai/jiaxk/xiaoqu-operations-ai`。 + +## 2. 当前代码依据 + +- `services/ops-assistant-service` 是 FastAPI + Python 3.12 服务。 +- 当前依赖中没有 MCP SDK,也没有 `tools/list` / `tools/call` 客户端。 +- `src/ops_assistant_service/capability_registry.py` 维护静态 `CAPABILITY_REGISTRY`。 +- `src/ops_assistant_service/executor.py` 当前执行 `customer_issues.*`、`smartclean.query` 等本地 Capability。 +- 用户身份来自 Gateway JWT,并通过 `ops_identity_mappings` 解析租户、用户和 `scope_tags`。 +- 最小接入点是 Executor 的工具适配层,不新增第二套编排器。 + +## 3. 配置设计 + +在 `src/ops_assistant_service/config.py` 的 `Settings` 中增加以下 `OPS_ASSISTANT_` 配置: + +| 变量 | 默认值 | 说明 | +|---|---|---| +| `XQ_MCP_ENABLED` | `false` | 是否启用 MCP Capability | +| `XQ_MCP_URL` | unset | xiaoqu-mcp 内网地址 | +| `XQ_MCP_OPEN_KEY` | unset | Token 初始化凭证,类型使用 `SecretStr` | +| `XQ_MCP_SCOPE_CODE` | `operation` | 服务端固定 scope,禁止由 Planner 或客户端选择 | +| `XQ_MCP_TIMEOUT_SECONDS` | `10` | MCP 请求超时 | + +启用时必须同时提供 URL、Open Key 和 scope。`operation` 已存在于 dev 种子,其他环境启用前仍需核对实际 scope 和工具绑定;生产配置不得写入仓库。 + +## 4. 最小模块 + +只新增两个模块: + +- `src/ops_assistant_service/mcp_client.py` + - 使用现有 `httpx` 完成 JSON-RPC、Session ID、Bearer Header、超时和错误映射。 + - 按租户、`user_id` 和 scope 隔离 MCP Session。 +- `src/ops_assistant_service/mcp_token_provider.py` + - 接收已验证的 `SharedIdentity`。 + - 从受信任用户资料源取得 mobile/email 并调用 `ensure`。 + - 按租户、`user_id` 和 scope 缓存 Token;401 时只刷新当前用户一次。 + - 不接受客户端或 Planner 传入的身份字段作为覆盖参数。 + +不引入通用 MCP 框架、不新增 Agent,也不直连 xiaoqu-mcp 数据库。 + +## 5. 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 在调用前执行本地 Capability、用户 `scope_tags`、租户/DataScope 校验;xiaoqu-mcp 再执行 Token scope 和服务端数据权限校验。 + +远端结果经过字段白名单投影后再进入 Answer。Token、Authorization Header 和未授权字段不得进入 execution trace。 + +## 6. Executor 接入 + +在 `executor.py` 增加 MCP 只读分支: + +```text +validate_tool_input + → 校验本地 Capability 和参数白名单 + → 读取 SharedIdentity / DataScope + → mcp_token_provider 获取当前用户 Token + → mcp_client 调用 tools/call + → 结果字段投影 + → 现有 execution_trace / Answer +``` + +现有 `customer_issues.*` 和 `smartclean.query` 保持不变。MCP 不可用时返回稳定的 `TOOL_UNAVAILABLE`,不得回退公共 Token 或数据库直连。 + +## 7. 开发顺序 + +1. 核对目标环境中的 `operation` scope、工具绑定和用户资料映射。 +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。 + +## 8. 项目验收 + +- 运营 iOS、Android 继续只调用 `ops-assistant-service`,客户端无 MCP Token。 +- 用户 A/B、不同租户和 scope 的 Token 与 MCP Session 不互用。 +- Planner 不能选择未注册的远程工具,Executor 不能绕过 `scope_tags` 和 DataScope。 +- `tools/list`、`tools/call` 使用同一用户 Session 和 Bearer Token。 +- MCP 401 只刷新当前用户;只读请求最多重试一次。 +- 测试、日志、trace、配置和 Git 历史中均无凭证明文。 + +## 9. 当前不做 + +- 不把 MCP Client 放进运营 iOS 或 Android。 +- 不动态注册全部远程工具。 +- 不在首期引入写工具;写工具需另行设计审批、幂等和补偿协议。 +- 不复制 xiaoqu-mcp 数据库,不调用 xiaoqu-ai Agent 代查数据。