docs(mcp): unify multi-client agent integration

This commit is contained in:
xqzp2026
2026-09-07 17:54:33 +09:30
parent ae0393da57
commit aa12a3c71d
3 changed files with 218 additions and 279 deletions
@@ -8,7 +8,7 @@
## 1. 背景与目标
现有 SaaS(xiaoqu-franchisee)是面向 PC 后台人工操作的系统,接口鉴权(uuid + 短信登录态 + 签名)不适合机器调用。本方案新建一套**独立系统**,将仓库、客户等业务数据以 **MCP 服务**形式暴露给智能体,同时提供 CLI 兜底与 Web 管理后台。
现有 SaaS(xiaoqu-franchisee)是面向 PC 后台人工操作的系统,接口鉴权(uuid + 短信登录态 + 签名)不适合机器调用。本方案新建一套**独立系统**,将仓库、客户等业务数据以 **MCP 服务**形式暴露给 xiaoqu-ai Android/iOS/Web、xiaoqu-operations-ai 及外部 MCP 客户端,同时提供 CLI 兜底与 Web 管理后台。
设计约束(讨论中明确):
@@ -20,11 +20,11 @@
## 2. 总体架构
```
Kimi Code ──┐
├─ MCP (Streamable HTTP, Bearer ak) ─┐
Claude ─────┤ │
│ ▼
xq CLI ─────┘ (Go 单二进制, 同为 MCP 客户端) ┌─────────────────────────┐
Kimi/Claude/xq CLI ── MCP (Bearer ak) ──────────┐
xiaoqu-ai Android/iOS/Web ─► Gateway/Agent ─────┤
运营助手 Android/iOS ─────► ops-assistant-service┤
▼
┌─────────────────────────┐
│ xiaoqu-agent-service │
│ (单 jar, Spring Boot 3) │
│ │
@@ -44,7 +44,7 @@ xq CLI ─────┘ (Go 单二进制, 同为 MCP 客户端) ┌──
└──────────┘ └────────────┘
```
关键分层:**业务工具只写一遍**,MCP 端点(Kimi/Claude/CLI 同为 MCP 客户端)、后台页面共享同一套鉴权、权限引擎与数据访问层。
关键分层:**业务工具只写一遍**。Kimi/Claude/CLI 和两个产品的服务端 Agent 是 MCP Client;Android/iOS/Web 业务客户端只访问本产品 Gateway/Agent API,不直接持有服务间密钥或 MCP Token。所有 MCP 调用共享同一套鉴权、权限引擎与数据访问层。
## 3. 权限模型(三层)
@@ -1,302 +1,232 @@
# xiaoqu-ai 每用户 MCP Token 接入开发设计文档
# xiaoqu 多端 Agent 统一接入 xiaoqu-mcp 开发设计
## 1. 需求概述
## 1. 设计结论
### 1.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 职责
| 层 | 职责 |
|---|---|
| 需求标题 | xiaoqu-ai 每用户独立 MCP Token 接入 |
| 优先级 | high |
| 目标客户端 | xiaoqu-ai iOS ClawMobile |
| 目标服务 | xiaoqu-agent-service `/mcp` |
| 认证要求 | 每个登录用户使用自己的 MCP Token |
| 预估工时 | 12-16h |
| iOS/Android/Web | 登录、会话 UI、流式展示、确认交互;只携带产品登录凭证 |
| 产品 Gateway | 验证登录态,注入可信用户/租户上下文,代理 Agent 请求 |
| 产品 Agent Runtime | 编排 Agent、维护 capability allowlist、获取用户 MCP Token、调用 MCP |
| xiaoqu-agent-service | 校验 MCP Token、过滤工具、执行行级权限、查询业务数据、记录审计 |
### 1.2 成功标准
## 4. 认证与 Token
- 用户 A 的 MCP 请求只能携带用户 A 的 Token,不能使用用户 B 或应用公共 Token。
- 登录、刷新 Token、切换账号、退出登录后,Claw/MCP 会话不会串用旧用户凭证。
- MCP 请求使用 `Authorization: Bearer <token>`,服务端继续执行用户、租户、作用域和审计校验。
- Token 不出现在 prompt、工具参数、普通日志、崩溃信息或 Git 文件中。
- 用户无 MCP Token、Token 过期、Token 被撤销时,客户端显示可恢复错误,不发送无凭证请求。
### 4.1 三类凭证
### 1.3 明确不做
| 凭证 | 使用者 | 用途 |
|---|---|---|
| 产品登录 Token | iOS/Android/Web → 产品 Gateway | 证明当前产品用户身份 |
| `X-Open-Key` | 产品 Agent Runtime → `/api/open/token/ensure` | 服务间获取用户 MCP Token |
| 用户 MCP Token `xqak_*` | 产品 Agent Runtime → `/mcp` | 绑定 MCP 用户、scope、权限和审计 |
- 不把一个固定 Token 写入 App、公共配置文件或所有用户共用。
- 不把数据库中保存的 Token 哈希当作客户端 Token 返回;明文仅通过签发/ensure 响应经加密密文解密返回,不落日志。
- 不让客户端提交任意 `user_id`、`tenant_id` 来冒充用户身份。
`X-Open-Key` 和 MCP Token 均只存在于受信任服务端。
## 2. 现状分析
### 4.2 用户映射
### 2.1 相关现有代码
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`。
| 文件 | 层级 | 当前功能 | 改动类型 |
|---|---|---|---|
| `xiaoqu-ai/ios/Sources/XiaoquCRM/Core/Dependencies/ClawBridge.swift` | iOS 依赖 | 静态初始化 Claw 配置,并创建 MCP Server 列表 | 修改 |
| `xiaoqu-ai/claw-code-parity/rust/crates/claw-mobile/src/ffi.rs` | Rust FFI | 接收 MCP 配置,连接、发现工具、调用工具 | 修改 |
| `xiaoqu-ai/claw-code-parity/rust/crates/claw-mobile/src/mcp_http.rs` | Rust HTTP | JSON-RPC HTTP/SSE MCP 客户端 | 修改 |
| `xiaoqu-agent-service/src/main/java/me/iiv/agent/security/AkAuthFilter.java` | 服务鉴权 | 仅解析 `Authorization: Bearer <ak>` | 保持兼容 |
| `xiaoqu-agent-service/src/main/java/me/iiv/agent/security/AuthService.java` | 服务鉴权 | AK SHA-256 哈希查询、用户加载、过期检查 | 修改/复用 |
| `xiaoqu-agent-service/src/main/java/me/iiv/agent/admin/AdminTokenService.java` | 服务 Token | 管理员为指定用户签发 AK,保存哈希 + AES-GCM 密文 | 扩展复用 |
禁止接受客户端或模型传入的手机号、邮箱、`user_id`、`tenant_id`、`scopeCode`
覆盖可信上下文。scope 由服务端配置固定,启用前与管理后台实际配置核对。
### 2.2 当前缺口
### 4.3 服务端缓存与生命周期
1. `ClawBridge.liveValue` 是静态值,Token 只在进程初始化时读取一次。
2. 当前 FFI 将 `apiKey` 发送为 `X-API-Key`,与服务端要求的 Bearer 不一致。
3. iOS FFI 未把配置文件中的 MCP Server 自动接入 `FfiEngineConfig`。
4. 多 MCP Client 共存时,工具调用没有稳定的“工具名 → 所属 Server”映射。
5. ~~当前管理端签发接口需要管理员会话,不适合作为普通用户登录后的自动取 Token 接口。~~ **已解决(2026-09-04)**:xiaoqu-agent-service 已交付 Open API `POST /api/open/token/ensure`(get-or-create,服务间密钥鉴权),见 §4.1。
- 缓存键至少包含:产品、租户、产品用户 ID、scope。
- 切换账号或退出登录时,产品会话失效;不得让新用户复用旧用户 MCP Session。
- MCP 返回 401 时,只清除当前用户缓存并重新 `ensure` 一次。
- 只读调用可在重连后重试一次;有副作用调用不得自动重放。
- 日志仅记录用户 ID、scope、工具名、状态和耗时,不记录凭证明文或手机号/邮箱明文。
## 3. 方案设计
## 5. xiaoqu-mcp 契约
### 3.1 认证模型
### 5.1 `POST /api/open/token/ensure`
采用两级凭证:
认证:`X-Open-Key: <server-only-key>`。
1. xiaoqu-ai 登录 `accessToken`:证明当前用户登录身份,只用于向业务后端换取 MCP Token。
2. MCP Token(`xqak_*`):绑定 `user_id`,受 MCP scope、租户/行级权限、过期时间和撤销状态约束,只用于 `/mcp`。
客户端不得把 `accessToken` 和 MCP Token 混为一谈。MCP 服务仍只接受 MCP Token。
### 3.2 Token 生命周期
| 场景 | 行为 |
|---|---|
| 登录成功 | 用当前用户登录态换取 MCP Token,按用户保存 |
| MCP Token 过期/401 | 清除当前用户 MCP Token,重新换取一次;同一请求不自动重放工具副作用 |
| accessToken 刷新 | 保留 MCP Token;若服务返回 401,再重新换取 |
| 切换账号 | 先断开旧 MCP Client,再加载新账号 Token 并建立新 Client |
| 退出登录 | 销毁旧 Claw/MCP Client,删除当前账号本地 MCP Token;当前永久 Token 以服务端状态为准,不做客户端侧远程失效调用 |
| 多账号 | Keychain key 必须包含稳定用户 ID,禁止所有账号共用 `mcpToken` |
~~建议 MCP Token 默认有效期 15 分钟,并提供换取接口;长期 AK 仅用于受控开发/管理场景。~~
**2026-09-04 更新(与已交付实现对齐)**:现行模型为**长期有效 + 禁用回收**(与管理后台签发模型一致),不做 15 分钟短周期。`ensure` 语义天然兼容短周期模式(过期 token 不计入"已存在",会触发新建并返回新明文),将来如需缩短有效期,只调整签发时的 `expireTime`,协议不变。
## 4. API 契约
> **2026-09-04 更新**:本节按 xiaoqu-agent-service 已交付的 Open API 重写。核心变化——agent-service 不直接验证 xiaoqu-ai 登录态(它没有 JWT 基础设施,也不懂租户),改为**两段式**:iOS 用登录态调 xiaoqu-ai 业务后端;业务后端拿着登录用户的手机号/邮箱,用服务间密钥调 agent-service 的 `ensure` 接口取 MCP Token。
### 4.1 服务端原语(已交付):POST `/api/open/token/ensure`
描述: 按 手机号/邮箱 + 作用域编码 确保用户有有效 MCP Token(get-or-create)。
认证:
- `X-Open-Key: <服务间静态密钥>`(dev 配置在 `application-dev.yml` 的 `app.open.api-key`,生产走环境变量;配置为空 = 接口关闭)。
- 只允许 xiaoqu-ai 业务后端等服务端调用方持有,**不得下发到 App**。
请求体:
- `mobile`: string — 登录用户手机号;与 `email` 至少传一个,手机号优先。
- `email`: string — 登录用户邮箱。
- `scopeCode`: string (required) — 作用域编码(如 `operation`);iOS 用哪个作用域由双方约定后在此固定。
响应 200:
- `created`: bool — 本次是否新建。
- `token`: string — **Token 明文,两条路径均返回**(2026-09-07 起支持二次获取);`created=true` 为新签发的明文,`created=false` 为解密既有密文得到的明文。
- `keyPrefix`: string — Token 前缀(如 `xqak_abcd1234`),用于标识和对账。
- `expireTime`: datetime | null — 现行为 null(永久,靠禁用回收)。
- `userId` / `userName` / `scopeCode`: 归属信息。
错误(统一 `{"message": "原因"}`):
- `400`: 参数缺失 / 作用域不存在或已停用。
- `401`: `X-Open-Key` 无效。
- `403`: Open API 未启用(服务端未配置密钥)。
- `404`: 人员不存在或非企业账号(只认 `enterprise_type` 0/1 的企业账号)。
服务端处理(已实现):
- 复用 `agent_api_key` 哈希存储与 `token_scope` 绑定;一作用域一 Token。
- 已存在有效 Token(正常且未过期)→ 解密 `key_cipher` 回传明文(服务端保存 SHA-256 + AES-GCM 密文,密钥由 `app.token-cipher-key` 派生);历史无密文 Token 自动轮换(禁用旧把、签发新把)。
- 不存在则自动创建永久 Token,memo 标记 `open-api 自动签发(<scopeCode>)`。
- 全程写 `audit_log`(`tool_name=open.token.ensure`、`client_type=open-api`,手机号/邮箱脱敏,不记录 Token)。
**身份约束**:业务后端不得接受客户端或模型传入的 `user_id`、`mobile`、`email` 作为身份依据。必须先从已验证的 xiaoqu-ai `accessToken` 会话解析用户,再由业务后端查询该用户的手机号/邮箱并调用 `ensure`。`X-Open-Key` 只允许服务端保管。
### 4.1b xiaoqu-ai 业务后端门面(待 xiaoqu-ai 侧实现)
iOS 面向的接口(建议沿用原设计):POST `/api/v1/mcp/session-token`,`Authorization: Bearer <xiaoqu-access-token>`。业务后端从登录态解析用户 → 服务端查询手机号/邮箱 → 调 `ensure` → 返回 `{token, expires_at, user_id}`。不得使用请求体中的 `user_id/mobile/email` 覆盖登录态身份;客户端身份字段(client、device_id)由业务后端自行记录,agent-service 不感知。
### 4.2 POST `/mcp`
描述: MCP Streamable HTTP JSON-RPC 端点,现有接口保持不变。
请求 Header:
- `Authorization: Bearer <mcp-token>` (required)
- `Content-Type: application/json` (required)
- `Accept: application/json, text/event-stream` (required)
- `Mcp-Session-Id: <id>` (required after initialize response contains it)
请求/响应:
- 保持 MCP `initialize` → `notifications/initialized` → `tools/list` → `tools/call` 顺序。
- `tools/call` 必须继续执行 Token 作用域、用户和租户数据范围校验。
错误:
- `401`: MCP Token 缺失、无效、撤销或过期。
- `200` + MCP `isError=true`: Token 有效但无权调用该工具,客户端展示权限错误,不切换账号重试。
### 4.3 客户端配置文件
配置文件只保存非用户敏感配置:
请求:
```json
{
"mcpServers": {
"xiaoqu": {
"type": "http",
"url": "http://<host>:8088/mcp"
}
}
"mobile": "<trusted-user-mobile>",
"email": "<trusted-user-email>",
"scopeCode": "<server-configured-scope>"
}
```
Token 不写入该文件。客户端运行时读取当前用户 Token,并动态加入 `Authorization` Header。
- `mobile`、`email` 至少一个,手机号优先。
- 现有有效 Token:解密 `key_cipher` 返回同一明文。
- 历史无密文 Token:禁用旧 Token,自动签发新 Token。
- 不存在有效 Token:创建长期 Token;回收通过禁用实现。
- 响应和审计不得记录 Token 明文。
## 5. 客户端实现设计
错误语义:`400` 参数/scope 错误,`401` Open Key 无效,`403` Open API 未启用,
`404` 用户不存在或不属于允许的企业账号。
### 5.1 ClawBridge
### 5.2 `POST /mcp`
- 将 `ClawBridge` 从静态全局凭证改为按当前用户创建的实例。
- `connectMcp(userID, mcpToken)` 创建只属于当前用户的 FFI Engine。
- `disconnectMcp()` 在切换账号、退出登录和重新换 Token 前调用。
- 不在 `liveValue` 捕获 `accessToken` 或 MCP Token;通过依赖注入传入当前账号凭证。
### 5.2 FFI / HTTP
- `FfiMcpServerConfig` 增加明确的认证模式,或直接将 `apiKey` 改为 `bearerToken`。
- 使用 `Authorization: Bearer <mcpToken>`,禁止发送 `X-API-Key`。
- `initialize` 后保存 `Mcp-Session-Id`,发送 `notifications/initialized`,再调用 `tools/list`。
- 工具发现时保存 `tool_name → server_name` 映射;调用只发送到所属 Server,不按 Client 顺序盲试。
- Token 过期只触发一次重新换取和重新连接;并发请求共享刷新锁,避免同一用户并发签发多个 Token。
### 5.3 Keychain
- Key 名:`mcpToken.<user_id>`,值为 MCP Token;使用 ThisDeviceOnly 存储。
- `device_id` 单独生成并持久化,不从 Token 派生。
- 日志只记录 Token 是否存在、剩余有效期和用户 ID,不记录 Token 内容。
## 6. 数据模型变更
### 6.1 现有表复用
| 表 | 用途 | 变更 |
|---|---|---|
| `agent_api_key` | MCP Token 哈希、用户绑定、状态、过期时间 | 复用;确认 `user_id/status/expire_time/key_hash` 索引 |
| `token_scope` | Token 与作用域绑定 | 复用 |
| `audit_log` | MCP 调用和 Token 生命周期审计 | 扩展事件类型/元数据(若当前字段不足) |
### 6.2 建议新增字段(仅在现有字段不足时)
> 2026-09-04 注:已交付实现暂不新增字段——ensure 自动签发的 Token 用 `memo` 标记(`open-api 自动签发(<scopeCode>)`),`create_id=0` 表示系统签发。若后续需要按来源/设备撤销或统计,再按下表加独立 migration。
| 表 | 字段 | 类型/约束 | 说明 |
|---|---|---|---|
| `agent_api_key` | `source` | `VARCHAR(32) NOT NULL DEFAULT 'admin'` | `admin` / `user_session`,便于撤销和审计 |
| `agent_api_key` | `device_id` | `VARCHAR(128) NULL` | 用户会话 Token 的设备绑定 |
索引:`(user_id, source, status, expire_time)`。新增字段必须通过新的 Flyway migration,禁止修改已执行 migration。
## 7. 变更文件清单
### xiaoqu-agent-service
| 文件/目录 | 层级 | 类型 | 改动说明 |
|---|---|---|---|
| ~~`security/` JWT 验证组件~~ | Security | ~~新增~~ 取消 | agent-service 不验证 xiaoqu-ai 登录态,改由业务后端两段式调用 |
| `open/OpenTokenController.java` 等 | Open API | **ensure 已交付** | `ensure` 使用 X-Open-Key 鉴权 |
| `open/OpenKeyFilter.java` | Filter | **已交付** | 服务间静态密钥,常量时间比较,配置为空即关闭 |
| `admin/AdminTokenService.java` | Service | **ensure 已交付;密文保存待实现** | 一作用域一 Token;签发时保存哈希和加密密文 |
| `common/GlobalExceptionHandler.java` | Handler | **已交付** | 错误统一 `{"message": ...}` |
| `security/AkAuthFilter.java` | Filter | 保持兼容 | `/mcp` Bearer ak 校验不变 |
| `src/test/...open` | Test | **部分已交付** | 覆盖 ensure 的参数、已存在/新建/404/停用/缺参测试 |
### xiaoqu-ai
| 文件/目录 | 层级 | 类型 | 改动说明 |
|---|---|---|---|
| `ios/.../ClawBridge.swift` | iOS | 修改 | 按登录用户注入 Token,重建/销毁 Claw 实例 |
| `claw-mobile/src/ffi.rs` | Rust FFI | 修改 | Bearer Header、会话通知、Server 路由映射 |
| `claw-mobile/src/mcp_http.rs` | Rust HTTP | 修改 | 通用 Header、Session ID 和 JSON-RPC 初始化流程 |
| `ios/.../KeychainClient.swift` | iOS 存储 | 修改 | 账号隔离的 MCP Token 存取和删除 |
| `ios/.../AuthFeature.swift` | 登录生命周期 | 修改 | 登录/刷新/切换/退出触发 MCP 凭证生命周期 |
| `ios/Tests/...` | Test | 新增 | 用户切换、401 刷新、Token 不串号测试 |
| `claw-code-parity/.../settings*.json` | 配置 | 修改 | 仅保存 URL,不保存用户 Token |
## 8. 开发任务拆分
| # | 任务标题 | 工时 | 依赖 | 完成标准 |
|---|---|---:|---|---|
| 1 | ~~服务端确认 JWT 与租户映射、Token exchange 契约~~ → xiaoqu-ai 业务后端实现门面接口(登录态 → ensure) | 2h | - | 门面 401/403 语义确定;明文按 user_id 加密落库 |
| 2 | ~~服务端实现用户 MCP Token 签发与审计~~ **已交付**(ensure + 审计,2026-09-04) | - | - | 已上线并实测幂等/脱敏/错误语义 |
| 2b | ~~服务端增加二次获取能力~~ | - | - | 已按当前边界关闭,不再新增接口 |
| 3 | iOS Keychain 账号隔离和认证生命周期接入 | 3h | #1 | 登录/切换/退出无旧 Token 残留 |
| 4 | Rust MCP HTTP Bearer、Session、初始化通知 | 3h | #3 | initialize/list/call 真实握手成功 |
| 5 | MCP 工具到 Server 路由映射 | 2h | #4 | 多 Server 下工具调用不发错目标 |
| 6 | 集成与安全回归测试 | 2-4h | #2-#5 | 用户隔离、401 刷新、权限拒绝和日志脱敏通过 |
实施顺序:#1 → #2;#3 与 #4 可并行;完成后 #5 → #6。
## 9. 测试与验收
### 9.1 服务端
- 用户 A Token 调用 `tools/list` / `tools/call` 成功,审计 `user_id=A`。
- 用户 B Token 调用时只能看到/调用 B 作用域允许的数据。
- A 的 Token 调用不存在或已撤销 Token 返回 401;A 的 Token 调 B 身份参数不得改变审计身份。
- A 无某工具 scope 时返回 MCP `isError=true`,不得执行 Handler。
- 签发响应后数据库和日志中搜索不到 Token 明文。
### 9.2 iOS
- A 登录后抓包确认所有 MCP 请求为 `Authorization: Bearer <A-MCP-Token>`。
- 切换 B 后,旧连接关闭,后续请求只携带 B Token。
- A Token 401 时只刷新 A 的 Token,不影响 B 或其他账号。
- 退出后旧 Claw 不再产生网络请求。
- 配置文件、prompt、工具参数和日志不包含 Token。
### 9.3 联调命令
```bash
# 业务后端侧:用服务间密钥 ensure 用户 MCP Token(示例,密钥与明文不写入仓库)
curl -X POST http://<host>:8088/api/open/token/ensure \
-H 'X-Open-Key: <服务间密钥>' \
-H 'Content-Type: application/json' \
-d '{"mobile":"<登录用户手机号>","scopeCode":"operation"}'
# iOS 侧:走 xiaoqu-ai 业务后门门面(待实现)
curl -X POST http://<xiaoqu-ai-host>/api/v1/mcp/session-token \
-H 'Authorization: Bearer <xiaoqu-access-token>' \
-H 'Content-Type: application/json' \
-d '{"client":"xiaoqu-ai-ios","device_id":"<device-id>"}'
# 使用返回的用户 MCP Token 完成 MCP 握手
curl -X POST http://<host>:8088/mcp \
-H 'Authorization: Bearer <user-mcp-token>' \
-H 'Content-Type: application/json' \
-H 'Accept: application/json, text/event-stream' \
-d '{"jsonrpc":"2.0","id":1,"method":"initialize","params":{"protocolVersion":"2025-03-26","capabilities":{},"clientInfo":{"name":"xiaoqu-ai","version":"1.0"}}}'
```http
Authorization: Bearer <user-mcp-token>
Content-Type: application/json
Accept: application/json, text/event-stream
```
## 10. 技术风险与回退
调用顺序:`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. 开发顺序
| # | 工作 | 完成标准 |
|---|---|---|
| accessToken 与 MCP AK 类型混用 | 高 | 强制 Token exchange;服务端只接受明确的 MCP AK |
| 静态 Claw 实例持有旧用户 Token | 高 | 用户切换/退出先销毁实例,增加生命周期测试 |
| 多 MCP Server 工具路由错误 | 高 | 建立工具归属映射,不采用首个 Client 试调用 |
| Token 泄露到日志/崩溃 | 高 | 脱敏中间件、禁止打印 Header、静态扫描和测试 |
| Token exchange 不可用 | 中 | MCP 功能降级为不可用并提示重试;不得使用公共 Token 兜底 |
| 新字段迁移失败 | 中 | 字段变更独立 Flyway migration;回滚只撤销新增字段/索引 |
| 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 泄露、串号、越权或副作用自动重放 |
回退策略:保留现有管理员签发 Token 和 `/mcp` 协议不变;先关闭用户自动换取入口,客户端显示 MCP 不可用。禁止回退到全局共享 Token。
## 8. 验收标准
## 11. 设计结论
- 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。
公共 MCP 配置文件只负责“连哪个服务”;用户 Token 由登录用户上下文动态决定。实现完成后,xiaoqu-ai 的每个用户拥有独立 MCP 权限边界,服务端仍能按用户、租户、作用域和审计链路追踪调用。
## 9. 风险与回退
| 风险 | 应对 |
|---|---|
| 产品用户与 MCP 用户映射错误 | 只用 Gateway 可信身份查询手机号/邮箱;联调覆盖同名和缺失用户 |
| 服务端缓存串用户 | 复合缓存键;登录切换和并发测试 |
| 两个 Agent Runtime 暴露不同工具 | 各自维护明确 allowlist,并用同一 MCP schema 做契约测试 |
| MCP 不可用 | Agent 返回稳定的工具不可用错误,不回退公共 Token 或数据库直连 |
回退只关闭各 Agent Runtime 的 MCP capability 开关,iOS、Android、Web 继续使用原有
Agent 功能;不得回退为客户端共享固定 Token。
@@ -2,8 +2,8 @@
## 1. 目标
让 `ops-assistant-service` 通过服务端 MCP Client 调用
`xiaoqu_mcp_service`,保持现有:
让 `ops-assistant-service` 作为运营项目唯一 MCP Client 调用
`xiaoqu_mcp_service`,同时服务运营 iOS、Android。保持现有:
```text
Router → Planner → Executor → Answer
@@ -11,6 +11,15 @@ 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 服务。
@@ -123,7 +132,7 @@ validate_tool_input
## 6. 开发顺序
1. 先在 xiaoqu-ai 业务后端完成登录态到 `ensure` 的门面,并验证 Token 明文加密落库。
1. 先确认 xiaoqu-mcp 的 `ensure` 契约和 Token 密文落库;运营链路不依赖 xiaoqu-ai 业务后端门面。
2. 增加 `Settings` 配置和启动校验。
3. 实现 `mcp_token_provider.py` 和 `mcp_client.py`,补充 JSON-RPC/Session 单元测试。
4. 增加 3 个只读 capability,并接入 Executor。