- 新增 4.1 生产环境对接现状(MCP 入口、ensure 换签、握手流程) - operation scope(id=2) 当前绑定 24 个工具;移除未绑定的 agent.count - xiaoqu-ai scope(id=3) 当前绑定 12 个工具;代理域工具改为实际可用映射 - 开发顺序改为按 4.1 清单对照核验
12 KiB
xiaoqu-operations-ai 接入 xiaoqu-mcp 详细设计
1. 目标与边界
本文是 xiaoqu 多产品 Agent 统一接入 xiaoqu-mcp 总体设计 的运营项目落地设计,代码基线为 /Users/tonylai/wangheng/xiaoqu-operations-ai。
运营 iOS、Android、Web 共用 ops-assistant-service 中的一套 MCP Client:
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。
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 握手流程:
- 服务端持
X-Open-Key调 ensure:{"mobile":"<企业账号手机号>","scopeCode":"operation"}→ 返回xqak_...(长期有效,幂等,created=false 表示复用已有) POST /mcp发initialize,从响应头取mcp-session-id- 发
notifications/initialized,之后tools/list/tools/call必须带Mcp-Session-Id头 Authorization: Bearer xqak_...(Bearer 前缀只拼一次;库中存的令牌值本身不含前缀)
5. 最小模块
5.1 mcp_token_provider.py
输入为 Worker 已持有的运营 tenant_id、user_id 和固定 scope:
- 从
ops_identity_mappings反查可信的shared_tenant_id、shared_user_id。 - 通过现有只读共享用户 Port 获取权威 mobile;权威数据源存在 email 时可作为备用。
- 使用固定
scopeCode调用/api/open/token/ensure。 - 按运营租户、运营用户、共享用户和 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未绑定到生产operationscope(该工具只绑在test全量域,见 4.1 节),已从首期 Capability 移除;如需使用须先在管理后台为operation补绑该工具。
后续批次可按 4.1 节已绑定的清单扩展 Capability(仓库、工单、合同、商城物流等),每项必须在 CAPABILITY_REGISTRY 中声明输入/输出 schema、风险、审批、超时重试、审计事件和结果模板,并将精确 adapter_id 加入 EXECUTION_ADAPTER_IDS。已有本地 Capability 不改名、不改 Adapter、不改变执行语义。
7. 身份与权限
最终授权取交集:
本地 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. 开发顺序
- 核对各环境
operationscope 和工具绑定(prod 已于 2026-09-16 核验,见 4.1 节;新环境上线时按 4.1 清单对照)和用户映射数据源。 - 扩展只读 SharedUser Port,完成运营 UUID 到 mobile/email 的可信解析测试。
- 增加 Settings 配置、启用校验和
XiaoquMcpRuntime生命周期。 - 实现 Token Provider、MCP Client 和 Adapter 的协议单元测试。
- 注册三项本地 Capability,并让旧 Executor 与 contextual v2 共用同一 Adapter。
- 覆盖用户隔离、租户隔离、scope 拒绝、401、403、超时、脱敏和 Worker 重启测试。
- 分别从 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 或共享用户数据库。
- 不在首期引入写工具;写工具需另行审批幂等、确认和补偿协议。