Files
xiaoqu_mcp_service/xiaoqu-agent-service/doc/xiaoqu-operations-ai-mcp-integration-design.md
T
my_local_kimi_dev2 4e5ee541e3 docs(mcp): update ops/ai integration docs with prod scope facts
- 新增 4.1 生产环境对接现状(MCP 入口、ensure 换签、握手流程)
- operation scope(id=2) 当前绑定 24 个工具;移除未绑定的 agent.count
- xiaoqu-ai scope(id=3) 当前绑定 12 个工具;代理域工具改为实际可用映射
- 开发顺序改为按 4.1 清单对照核验
2026-09-16 18:30:07 +08:00

212 lines
12 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# xiaoqu-operations-ai 接入 xiaoqu-mcp 详细设计
## 1. 目标与边界
本文是 [`xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计`](./xiaoqu-mcp-multi-product-agent-design.md) 的运营项目落地设计,代码基线为 `/Users/tonylai/wangheng/xiaoqu-operations-ai`。
运营 iOS、Android、Web 共用 `ops-assistant-service` 中的一套 MCP Client:
```text
iOS / Android / Web
│ 产品登录 Token
▼
ops-assistant-service REST
│
▼
Router → Resolver → Planner → Executor → Answer
│
▼
Capability Registry
│ 固定 adapter_id
▼
xiaoqu-mcp Adapter
│ 用户 MCP Token(仅服务端持有)
▼
xiaoqu-agent-service /mcp
```
WebSocket 只负责会话进度和完成通知,不承载 MCP 调用。客户端不新增 MCP 代码,不保存用户 MCP Token、MCP Session ID 或 `X-Open-Key`。
## 2. 当前代码依据
- `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。
因此不在 Web、路由层或某一条流水线中增加专用 MCP 分支。唯一接入点是两套流水线共用的 Capability Adapter 执行边界。
## 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,禁止由客户端或模型选择 |
| `XQ_MCP_TIMEOUT_SECONDS` | `10` | MCP 请求超时 |
启用时 URL、Open Key 和 scope 必填。凭证只由部署 Secret 注入。
### 4.1 生产环境对接现状(2026-09-16 在 prod svc 库核验)
| 项 | 生产实际值 |
|---|---|
| MCP 入口 | `https://agent.ququclean.com/mcp`(Streamable HTTP,POST) |
| Token 换取 | `POST https://agent.ququclean.com/api/open/token/ensure`,header `X-Open-Key` |
| 本系统 scope | `operation`(scope id=2,名称「运营app」),服务端固定,禁止客户端或模型选择 |
| 服务密钥要求 | 运营专用服务密钥只绑定了 `operation`;密钥未绑 scope 时 ensure 返回 403 |
**重要勘误**:dev 种子数据与生产不一致——生产承载全量工具的作用域是 `code='test'`(id=1),`operation`(id=2)是独立的运营作用域,两域绑定各自维护。上线核对时必须以 prod svc 库 `agent_scope` / `scope_tool` 为准。
**`operation` scope 当前绑定的工具(24 个)**:
- 代理/物业/站点/设备:`agent.list.query`(2)、`agent.detail.query`(3)、`district.list.query`(4)、`district.detail.get`(5)、`property.list.query`(6)、`property.detail.query`(7)、`hardware.list.query`(14)、`hardware.detail.query`(15)
- 商城订单:`mall.order.list.query`(9)、`mall.order.detail.query`(10)、`mall.order.logistics.query`(11)
- 合同:`contract.list.query`(12)、`contract.detail.query`(13)
- 仓库:`warehouse.out.list.query`(20)、`warehouse.out.detail.query`(21)、`warehouse.in.list.query`(22)、`warehouse.in.detail.query`(23)、`warehouse.stock.list.query`(26)、`warehouse.todo.list.query`(24)
- 工单:`delivery.order.list.query`(27)、`delivery.order.detail.query`(28)、`install.order.list.query`(29)、`install.order.detail.query`(30)
- 自检:`integration.context.get`(19)
**未绑定到 `operation` 的工具**:`agent.count`(1)、`sys.ping`(8)、`*.changes.list`(16-18,数据同步专用)、`contracts.current.batch_get`(25,sync 专用)。首期 Capability 选择须以上表为准。
**Token 换取与 MCP 握手流程**:
1. 服务端持 `X-Open-Key` 调 ensure:`{"mobile":"<企业账号手机号>","scopeCode":"operation"}` → 返回 `xqak_...`(长期有效,幂等,created=false 表示复用已有)
2. `POST /mcp` 发 `initialize`,从响应头取 `mcp-session-id`
3. 发 `notifications/initialized`,之后 `tools/list` / `tools/call` 必须带 `Mcp-Session-Id` 头
4. `Authorization: Bearer xqak_...`(Bearer 前缀只拼一次;库中存的令牌值本身不含前缀)
## 5. 最小模块
### 5.1 `mcp_token_provider.py`
输入为 Worker 已持有的运营 `tenant_id`、`user_id` 和固定 scope:
1. 从 `ops_identity_mappings` 反查可信的 `shared_tenant_id`、`shared_user_id`。
2. 通过现有只读共享用户 Port 获取权威 mobile;权威数据源存在 email 时可作为备用。
3. 使用固定 `scopeCode` 调用 `/api/open/token/ensure`。
4. 按运营租户、运营用户、共享用户和 scope 缓存 Token。
现有 `SharedUserPort` 尚未暴露 mobile/email,需要最小扩展其只读 DTO 和 PostgreSQL Adapter;不得复制用户表,也不得使用 Web、Planner、工具参数或模型输出中的身份字段。
### 5.2 `mcp_client.py`
使用现有 `httpx.Client` 管理:
- `initialize`、`notifications/initialized`、`tools/list`、`tools/call`。
- Bearer Header、`Mcp-Session-Id`、JSON-RPC ID 和响应校验。
- 连接、读取、写入和总超时。
- 按运营租户、运营用户和 scope 隔离 Session。
- 关闭时释放 HTTP Client,不持久化 MCP Session。
### 5.3 `mcp_adapter.py`
`XiaoquMcpAdapter` 只接收已经验证的 Capability Contract、运营身份和参数:
- 根据固定映射选择远端 MCP 工具。
- 调用 Token Provider 和 MCP Client。
- 校验远端响应并执行字段白名单投影。
- 返回现有 Executor/Answer 可消费的字典,不返回 Token、Header 或原始未授权字段。
不引入通用 MCP 框架或第二套 Agent 编排器。
## 6. Capability 映射
首期只注册只读 Capability:
| 本地 Capability | `adapter_id` | MCP 工具 | 权限 |
|---|---|---|---|
| `xiaoqu.agent_list` | `mcp.xiaoqu.agent_list.v1` | `agent.list.query` | read |
| `xiaoqu.property_list` | `mcp.xiaoqu.property_list.v1` | `property.list.query` | read |
> 勘误(2026-09-16):原首期清单中的 `agent.count` **未绑定到生产 `operation` scope**(该工具只绑在 `test` 全量域,见 4.1 节),已从首期 Capability 移除;如需使用须先在管理后台为 `operation` 补绑该工具。
后续批次可按 4.1 节已绑定的清单扩展 Capability(仓库、工单、合同、商城物流等),每项必须在 `CAPABILITY_REGISTRY` 中声明输入/输出 schema、风险、审批、超时重试、审计事件和结果模板,并将精确 `adapter_id` 加入 `EXECUTION_ADAPTER_IDS`。已有本地 Capability 不改名、不改 Adapter、不改变执行语义。
## 7. 身份与权限
最终授权取交集:
```text
本地 Capability allowlist
∩ 当前运营用户 scope_tags / DataScope
∩ MCP Token scope
∩ xiaoqu-mcp 用户、租户和行级数据范围
```
- REST 和 WebSocket 只负责验证产品登录态并建立运营身份。
- Planner 只能选择 Registry 中的 Capability,不决定用户、租户或 scope。
- Worker 必须在每次执行前确认 `ops_identity_mappings` 仍有效,不能仅依赖消息入库时的旧快照。
- `tools/list` 与 `tools/call` 使用同一用户 Token 和 MCP Session;服务端在两个入口都强制鉴权。
## 8. 异步执行与生命周期
Web、iOS、Android 提交消息后获得 `202`;Worker 在后台完成 Agent 和工具执行,再由消息查询或 WebSocket 返回进度与结果。
- `build_pipeline_runtime()` 创建一个进程级 `XiaoquMcpRuntime`,但 Token 和 Session 按用户隔离。
- Worker 重启后 Session 不恢复;首次调用重新 `ensure` 和 `initialize`。
- 用户映射失效、scope 版本变化或 MCP 401 时,只清理该用户缓存。
- MCP 401:重新 `ensure` 和连接一次;只读调用最多重试一次。
- MCP 403:直接返回权限不足,不刷新 Token。
- 超时、协议错误或服务不可用:映射为稳定的 `TOOL_UNAVAILABLE`,并让当前 Execution Attempt 进入可审计失败状态。
- 不自动重放写操作;首期不接写工具。
## 9. 开发顺序
1. 核对各环境 `operation` scope 和工具绑定(prod 已于 2026-09-16 核验,见 4.1 节;新环境上线时按 4.1 清单对照)和用户映射数据源。
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 不能选择未注册工具;冻结 Contract、`scope_tags`、DataScope 或 MCP scope 任一不满足即拒绝。
- 现有七项本地 Capability 的契约和行为不受影响。
- MCP 401 只刷新当前用户;MCP 不可用时不回退公共 Token、数据库直连或 xiaoqu-ai Agent。
- 测试、日志、trace、配置和 Git 历史中无凭证明文。
## 11. 当前不做
- 不在运营 iOS、Android、Web 中实现 MCP Client。
- 不动态注册全部远程工具。
- 不恢复或假定已删除的 `smartclean.query`。
- 不复制 xiaoqu-mcp 或共享用户数据库。
- 不在首期引入写工具;写工具需另行审批幂等、确认和补偿协议。