docs(mcp): align operations web integration design (#4)

This commit was merged in pull request #4.
This commit is contained in:
2026-09-07 17:51:24 +08:00
2 changed files with 138 additions and 69 deletions
@@ -10,7 +10,7 @@ xiaoqu-ai iOS / Android / Web
▼
xiaoqu-ai Gateway / Agent Runtime ───┐
│ 用户 MCP Token(仅服务端持有)
xiaoqu-operations-ai iOS / Android │
xiaoqu-operations-ai iOS / Android / Web
│ 产品登录 Token │
▼ │
ops-assistant-service ───────────────┤
@@ -28,7 +28,7 @@ ops-assistant-service ───────────────┤
|---|---|---|---|
| 总体 | 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) |
| 详细设计 | xiaoqu-operations-ai iOS、Android、Web、服务端 Agent | `/Users/tonylai/wangheng/xiaoqu-operations-ai` | [`xiaoqu-operations-ai-mcp-integration-design.md`](./xiaoqu-operations-ai-mcp-integration-design.md) |
本文只定义两个产品必须共同遵守的架构和安全契约。项目代码落点、配置、Capability 映射和项目级验收只写在对应详细设计中。
@@ -124,7 +124,7 @@ Accept: application/json, text/event-stream
|---|---|---|
| 1 | 确认两个产品的用户映射与实际 scope | scope 已存在并绑定所需工具,不依赖客户端身份字段 |
| 2 | 完成 xiaoqu-ai 服务端接入 | iOS、Android、Web 经同一服务端适配器调用 |
| 3 | 完成 xiaoqu-operations-ai 服务端接入 | 运营 iOS、Android 经 ops-assistant-service 调用 |
| 3 | 完成 xiaoqu-operations-ai 服务端接入 | 运营 iOS、Android、Web 经 ops-assistant-service 调用 |
| 4 | 跨用户、跨租户和跨产品回归 | 无 Token 泄露、串号、越权或副作用自动重放 |
## 8. 总体验收与回退
@@ -2,111 +2,180 @@
## 1. 目标与边界
本文是 [`xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计`](./xiaoqu-mcp-multi-product-agent-design.md) 的运营项目落地设计。
本文是 [`xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计`](./xiaoqu-mcp-multi-product-agent-design.md) 的运营项目落地设计,代码基线为 `/Users/tonylai/wangheng/xiaoqu-operations-ai`。
`ops-assistant-service` 是运营项目唯一 MCP Client,同时服务运营 iOS 和 Android,并保留现有编排:
运营 iOS、Android、Web 共用 `ops-assistant-service` 中的一套 MCP Client:
```text
运营 iOS / Android
iOS / Android / Web
│ 产品登录 Token
▼
Router → Planner → Executor → Answer
ops-assistant-service REST
│
▼
xiaoqu-mcp
Router → Resolver → Planner → Executor → Answer
│
▼
Capability Registry
│ 固定 adapter_id
▼
xiaoqu-mcp Adapter
│ 用户 MCP Token(仅服务端持有)
▼
xiaoqu-agent-service /mcp
```
客户端不新增 MCP 代码。项目目录:`/Users/tonylai/jiaxk/xiaoqu-operations-ai`。
WebSocket 只负责会话进度和完成通知,不承载 MCP 调用。客户端不新增 MCP 代码,不保存用户 MCP Token、MCP Session ID 或 `X-Open-Key`。
## 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 的工具适配层,不新增第二套编排器。
- `web/ops-assistant/src/api.ts` 通过 `/api/v1/ops-assistant/*` REST 提交消息和读取业务数据。
- `web/ops-assistant/src/ConversationPage.tsx` 使用 WebSocket 订阅会话进度,消息提交仍走 REST。
- `services/ops-assistant-service` 是 FastAPI + Python 3.12 服务,现有 `httpx` 足够实现 MCP HTTP Client。
- `worker.py` 同时运行旧流水线和 `contextual_fulfillment_v2`;新流水线包含 Router、Resolver、Planner、Recovery、Executor、Answer。
- `capability_registry.py` 维护静态 `CAPABILITY_REGISTRY`、冻结的 Capability Contract 和 `adapter_id` 白名单。
- 旧 `executor.py` 与新 `contextual_execution.py` 最终都调用 `execute_registered_tool()`。
- 当前 Registry 包含 `customer_issues.*`、`vision_analyze`、`work_items.list`、`read_only_todos.list`、`knowledge_entries.search`,已不包含 `smartclean.query`。
- Gateway JWT 在入口解析为共享身份,再通过 `ops_identity_mappings` 映射为运营 `tenant_id`、`user_id` 和 DataScope;后台 Worker 执行时使用运营 UUID。
## 3. 配置设计
因此不在 Web、路由层或某一条流水线中增加专用 MCP 分支。唯一接入点是两套流水线共用的 Capability Adapter 执行边界。
在 `src/ops_assistant_service/config.py` 的 `Settings` 中增加以下 `OPS_ASSISTANT_` 配置:
## 3. 设计决策
### 3.1 统一 Adapter
在 `CAPABILITY_REGISTRY` 中注册稳定的本地 Capability 和固定 `adapter_id`,由 `execute_registered_tool()` 将 MCP Adapter ID 路由到 `XiaoquMcpAdapter`。
```text
Planner 选择本地 Capability
→ 校验冻结的 Capability Contract
→ 校验输入 schema、scope_tags、DataScope
→ execute_registered_tool(adapter_id)
→ XiaoquMcpAdapter
→ MCP tools/call
```
不得把远端 `tools/list` 原样注册给 Planner。`tools/list` 只用于启动或联调时验证远端工具是否与本地冻结契约一致。
### 3.2 显式依赖传递
`XiaoquMcpRuntime` 由 `worker.build_pipeline_runtime()` 创建,并通过显式参数传给旧 `run_executor_tick` 和新 `run_contextual_executor_tick`。禁止用模块级全局变量或从请求上下文隐式读取用户。
`PipelineRuntime.close()` 负责关闭 MCP HTTP Client 和清理进程内 Session 缓存。未启用 MCP 时传入 `None`,现有本地 Capability 保持原行为。
## 4. 配置设计
在 `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_OPEN_KEY` | unset | Token 初始化凭证,使用 `SecretStr` |
| `XQ_MCP_SCOPE_CODE` | `operation` | 服务端固定 scope,禁止由客户端或模型选择 |
| `XQ_MCP_TIMEOUT_SECONDS` | `10` | MCP 请求超时 |
启用时必须同时提供 URL、Open Key 和 scope。`operation` 已存在于 dev 种子,其他环境启用前仍需核对实际 scope 和工具绑定;生产配置不得写入仓库。
启用时 URL、Open Key 和 scope 必填。`operation` 已存在于 xiaoqu-mcp dev 种子;development、test、staging、production 均须分别核对真实 scope 和工具绑定。凭证只由部署 Secret 注入。
## 4. 最小模块
## 5. 最小模块
只新增两个模块:
### 5.1 `mcp_token_provider.py`
- `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 传入的身份字段作为覆盖参数。
输入为 Worker 已持有的运营 `tenant_id`、`user_id` 和固定 scope:
不引入通用 MCP 框架、不新增 Agent,也不直连 xiaoqu-mcp 数据库。
1. 从 `ops_identity_mappings` 反查可信的 `shared_tenant_id`、`shared_user_id`。
2. 通过现有只读共享用户 Port 获取权威 mobile;权威数据源存在 email 时可作为备用。
3. 使用固定 `scopeCode` 调用 `/api/open/token/ensure`。
4. 按运营租户、运营用户、共享用户和 scope 缓存 Token。
## 5. Capability 映射
现有 `SharedUserPort` 尚未暴露 mobile/email,需要最小扩展其只读 DTO 和 PostgreSQL Adapter;不得复制用户表,也不得使用 Web、Planner、工具参数或模型输出中的身份字段。
在 `capability_registry.py` 注册固定的本地 Capability,不把远端 `tools/list` 原样暴露给 Planner:
### 5.2 `mcp_client.py`
| 本地 Capability | MCP 工具 | 首期权限 |
|---|---|---|
| `xiaoqu.agent_count` | `agent.count` | read |
| `xiaoqu.agent_list` | `agent.list.query` | read |
| `xiaoqu.property_list` | `property.list.query` | read |
使用现有 `httpx.Client` 管理:
Planner 只能选择注册表中的名称和 schema。Executor 在调用前执行本地 Capability、用户 `scope_tags`、租户/DataScope 校验;xiaoqu-mcp 再执行 Token scope 和服务端数据权限校验。
- `initialize`、`notifications/initialized`、`tools/list`、`tools/call`。
- Bearer Header、`Mcp-Session-Id`、JSON-RPC ID 和响应校验。
- 连接、读取、写入和总超时。
- 按运营租户、运营用户和 scope 隔离 Session。
- 关闭时释放 HTTP Client,不持久化 MCP Session。
远端结果经过字段白名单投影后再进入 Answer。Token、Authorization Header 和未授权字段不得进入 execution trace。
### 5.3 `mcp_adapter.py`
## 6. Executor 接入
`XiaoquMcpAdapter` 只接收已经验证的 Capability Contract、运营身份和参数:
在 `executor.py` 增加 MCP 只读分支:
- 根据固定映射选择远端 MCP 工具。
- 调用 Token Provider 和 MCP Client。
- 校验远端响应并执行字段白名单投影。
- 返回现有 Executor/Answer 可消费的字典,不返回 Token、Header 或原始未授权字段。
不引入通用 MCP 框架或第二套 Agent 编排器。
## 6. Capability 映射
首期只注册只读 Capability:
| 本地 Capability | `adapter_id` | MCP 工具 | 权限 |
|---|---|---|---|
| `xiaoqu.agent_count` | `mcp.xiaoqu.agent_count.v1` | `agent.count` | read |
| `xiaoqu.agent_list` | `mcp.xiaoqu.agent_list.v1` | `agent.list.query` | read |
| `xiaoqu.property_list` | `mcp.xiaoqu.property_list.v1` | `property.list.query` | read |
每项必须在 `CAPABILITY_REGISTRY` 中声明输入/输出 schema、风险、审批、超时重试、审计事件和结果模板,并将精确 `adapter_id` 加入 `EXECUTION_ADAPTER_IDS`。已有本地 Capability 不改名、不改 Adapter、不改变执行语义。
## 7. 身份与权限
最终授权取交集:
```text
validate_tool_input
→ 校验本地 Capability 和参数白名单
→ 读取 SharedIdentity / DataScope
→ mcp_token_provider 获取当前用户 Token
→ mcp_client 调用 tools/call
→ 结果字段投影
→ 现有 execution_trace / Answer
本地 Capability allowlist
∩ 当前运营用户 scope_tags / DataScope
∩ MCP Token scope
∩ xiaoqu-mcp 用户、租户和行级数据范围
```
现有 `customer_issues.*` 和 `smartclean.query` 保持不变。MCP 不可用时返回稳定的 `TOOL_UNAVAILABLE`,不得回退公共 Token 或数据库直连。
- REST 和 WebSocket 只负责验证产品登录态并建立运营身份。
- Planner 只能选择 Registry 中的 Capability,不决定用户、租户或 scope。
- Worker 必须在每次执行前确认 `ops_identity_mappings` 仍有效,不能仅依赖消息入库时的旧快照。
- `tools/list` 与 `tools/call` 使用同一用户 Token 和 MCP Session;服务端在两个入口都强制鉴权。
## 7. 开发顺序
## 8. 异步执行与生命周期
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。
Web、iOS、Android 提交消息后获得 `202`;Worker 在后台完成 Agent 和工具执行,再由消息查询或 WebSocket 返回进度与结果。
## 8. 项目验收
- `build_pipeline_runtime()` 创建一个进程级 `XiaoquMcpRuntime`,但 Token 和 Session 按用户隔离。
- Worker 重启后 Session 不恢复;首次调用重新 `ensure` 和 `initialize`。
- 用户映射失效、scope 版本变化或 MCP 401 时,只清理该用户缓存。
- MCP 401:重新 `ensure` 和连接一次;只读调用最多重试一次。
- MCP 403:直接返回权限不足,不刷新 Token。
- 超时、协议错误或服务不可用:映射为稳定的 `TOOL_UNAVAILABLE`,并让当前 Execution Attempt 进入可审计失败状态。
- 不自动重放写操作;首期不接写工具。
- 运营 iOS、Android 继续只调用 `ops-assistant-service`,客户端无 MCP Token。
## 9. 开发顺序
1. 核对各环境 `operation` scope、三项工具绑定和用户映射数据源。
2. 扩展只读 SharedUser Port,完成运营 UUID 到 mobile/email 的可信解析测试。
3. 增加 Settings 配置、启用校验和 `XiaoquMcpRuntime` 生命周期。
4. 实现 Token Provider、MCP Client 和 Adapter 的协议单元测试。
5. 注册三项本地 Capability,并让旧 Executor 与 contextual v2 共用同一 Adapter。
6. 覆盖用户隔离、租户隔离、scope 拒绝、401、403、超时、脱敏和 Worker 重启测试。
7. 分别从 iOS、Android、Web 提交对话请求,验证异步进度和最终结果一致。
## 10. 项目验收
- iOS、Android、Web 均只调用 `ops-assistant-service`,客户端没有 MCP Token、Session ID 或 Open Key。
- Web 消息走 REST,WebSocket 仅订阅进度/完成通知。
- 旧流水线和 `contextual_fulfillment_v2` 对同一 MCP Capability 使用同一个 Adapter。
- 用户 A/B、不同租户和 scope 的 Token 与 MCP Session 不互用。
- Planner 不能选择未注册的远程工具,Executor 不能绕过 `scope_tags` 和 DataScope。
- `tools/list`、`tools/call` 使用同一用户 Session 和 Bearer Token。
- MCP 401 只刷新当前用户;只读请求最多重试一次。
- 测试、日志、trace、配置和 Git 历史中均无凭证明文。
- Planner 不能选择未注册工具;冻结 Contract、`scope_tags`、DataScope 或 MCP scope 任一不满足即拒绝。
- 现有七项本地 Capability 的契约和行为不受影响。
- MCP 401 只刷新当前用户;MCP 不可用时不回退公共 Token、数据库直连或 xiaoqu-ai Agent。
- 测试、日志、trace、配置和 Git 历史中无凭证明文。
## 9. 当前不做
## 11. 当前不做
- 不把 MCP Client 放进运营 iOS 或 Android。
- 不在运营 iOS、Android、Web 中实现 MCP Client。
- 不动态注册全部远程工具。
- 不在首期引入写工具;写工具需另行设计审批、幂等和补偿协议。
- 不复制 xiaoqu-mcp 数据库,不调用 xiaoqu-ai Agent 代查数据。
- 不恢复或假定已删除的 `smartclean.query`。
- 不复制 xiaoqu-mcp 或共享用户数据库。
- 不在首期引入写工具;写工具需另行审批幂等、确认和补偿协议。