11 - MCP 产品定义(Agent 操作面)
本文定义 Clip票夹 对 AI Agent 暴露的产品能力:定位、边界、工具清单、鉴权与安全、异步约定、分阶段落地。
实现仍走现有 REST(08-api.md);MCP 是意图层包装,不平行造第二套业务 API。
机器可读请求/响应形状以 packages/shared 的 zod 为准;本文与代码冲突时先改文档达成共识再动代码(见 AGENTS.md)。
1. 产品定位
| 项 |
定义 |
| 名称 |
Clip票夹 MCP(工作名:clip-invoice) |
| 形态 |
符合 Model Context Protocol 的 Tools 服务器;由宿主(Claude Desktop、Cursor、自研 Agent 运行时等)通过 stdio 或内网 HTTP/SSE 连接 |
| 服务对象 |
代表已授权用户操作其工作区的 Agent(个人助理、团队报销 bot、内部编排流水线) |
| 解决的问题 |
浏览器 UI 适合人点选;Agent 需要稳定、可组合、可机器解析的「报销操作系统」入口,避免让模型直接拼 REST 路径与头字段 |
| 核心价值 |
用自然语言完成「收票 → 核对 → 组批 → 导出」闭环,且不越权、不碰凭据、不自动对外提交 |
| 非目标 |
公有云多租户开放平台、任意第三方 OAuth 应用市场、无人值守 OA 提交、跨组织数据联邦 |
一句话:MCP 是工作区上下文下的报销工具箱;人确认边界内的写操作,系统保留与 Web 相同的隔离与审计红线。
2. 与现有产品的关系
┌─────────────┐ session cookie ┌──────────┐
│ Web (人) │ ───────────────────────►│ │
└─────────────┘ │ API │
│ /api/v1 │──► PostgreSQL / 队列 / 文件
┌─────────────┐ Bearer + workspace │ │
│ MCP Server │ ───────────────────────►│ │
│ (Agent 面) │ 意图 → REST 映射 └──────────┘
└─────────────┘
▲
│ MCP tools/list · tools/call
┌─────────────┐
│ Agent 宿主 │
└─────────────┘
| 原则 |
说明 |
| 单一事实源 |
业务状态只在 PostgreSQL;MCP 不缓存权威数据,读必打 API |
| 契约复用 |
金额(分)、错误码、角色能力、工作区头语义与 08-api.md 一致 |
| 意图收敛 |
宿主侧暴露 少量高内聚 tool(约 12–20 个),内部再调一个或多个 REST 端点 |
| 权限同构 |
Agent 令牌绑定用户身份与工作区成员角色;能力不超过该成员在 Web 上能做的事,并可再收窄 |
| UI 仍是主路径 |
设置凭据、成员治理、排障下载继续以 Web 为准;MCP 不替代管理控制台 |
3. 用户与场景
3.1 典型使用者
| 角色 |
诉求 |
| 教职工 / 学生 |
「把这几张票上传并归到交通费,按额度组一单,导出 Excel」 |
| 课题组助理 |
「看看共享工作区未入批发票,按项目额度拆两批」 |
| 内部 Agent 编排 |
定时摘要待办、失败任务重试提醒、导出物就绪后通知人下载 |
3.2 主旅程(Agent)
- 定位上下文:列出可访问工作区 → 选定当前
workspaceId(后续写操作默认落在此空间)。
- 感知状态:工作区摘要(待报销金额/张数、队列积压、未匹配附件、额度剩余)。
- 收票:上传文件 → 轮询解析任务 → 处理失败/重复。
- 核对:检索发票 → 必要时修正白名单字段、配对附件、重放 OCR(优先免费 replay)。
- 组单:建批 → 入批/拆批 → 排序 →(可选)标记已报销
paid(需确认)。
- 交付:触发导出 → 轮询导出任务 → 取得受控下载信息,交还人或下游系统。
- 收尾:查看通知、标记已读;异常用机器可读
code 决定重试或升级给人。
3.3 明确不做的旅程
- 配置或读取邮箱授权码、财务门户密码、Webhook secret。
- 创建/删除工作区、转让 owner、管理系统用户。
- 管理员跨工作区排障下载。
- OA/财务系统自动提交报销单(半自动填报若落地,仅允许「生成预填结果 + 人确认」)。
4. 设计原则
- 工作区钉死:会话级或 tool 级必须有明确
workspaceId;禁止用「猜默认空间」跨空间扫数据。
- 金额只传分:tool 入参与出参中的金额字段均为 integer 分;展示文案可由 Agent 自行格式化。
- 异步可轮询:上传解析、导出均为任务型;tool 返回
taskId / exportId,另提供状态查询;MCP 层不要求宿主支持 SSE(SSE 为可选增强)。
- 默认瘦响应:列表与摘要省略
ocr_raw_json、完整附件二进制等大字段;详情按 flag 增量拉取。
- 错误可分支:失败返回与 API 对齐的
code(如 DUPLICATE_FILE、BATCH_STATE_INVALID),message 给人读,details 给模型结构化重试。
- 危险写二次确认:软删发票、批次标
paid、删批等 tool 要求显式 confirm: true(或宿主侧 human-in-the-loop 批准)。
- 凭据零暴露:任何 tool 响应不得出现解密后的密钥、完整 session、信封主密钥;账户类只读若开放,仅掩码元数据。
- 幂等友好:上传/入批/导出支持可选
idempotencyKey(产品层约定;落地时与 API 幂等策略对齐),避免 Agent 重试造成双批或双导出。
- 审计可区分:经 MCP 的写操作标记
actor_type=agent(及 token id),与浏览器操作区分,便于追责与限流。
5. 能力分层
| 层级 |
名称 |
产品含义 |
默认 |
| L0 |
探活与发现 |
健康、协议版本、当前身份与可访问工作区 |
必开 |
| L1 |
只读报销面 |
摘要、发票/任务/批次/额度/导出状态/通知 |
必开 |
| L2 |
写报销闭环 |
上传、重试、修正、附件配对、组批、导出触发 |
默认开(受角色与 token scope) |
| L3 |
增强只读 |
财务门户概览、历史单打印 JSON、额度快照 |
可选,只读 |
| L4 |
触发类运维 |
手动触发邮件拉取 / 财务同步(不改凭据) |
可选,manager+ |
| — |
治理与密钥 |
成员、凭据 CRUD、频道 secret、admin 排障 |
永不进 MCP |
角色下限与 08-api.md §3.7 一致:viewer 仅 L0–L1(及 L3 若开);member 可达 L2;manager 才涉及 L4 与批次删/改名等。
6. 工具目录(产品契约)
命名采用 动词_名词 蛇形,稳定后不轻易改名;新增 tool 只追加。
下列「映射 REST」指语义归属,实现可聚合多次调用。
6.1 L0 — 上下文
| Tool |
说明 |
主要入参 |
主要出参 |
映射 REST |
最低角色 |
get_health |
服务是否可达 |
— |
状态 |
GET /healthz |
无 |
get_caller |
当前身份与 token 能力集 |
— |
user、scopes、默认 workspace |
session/token 内省 |
登录 |
list_workspaces |
可访问工作区列表与摘要 |
— |
items[] |
GET /workspaces |
登录 |
set_workspace |
设定后续调用的当前工作区(会话态)或校验 id |
workspaceId |
确认后的上下文 |
本地会话 + 成员校验 |
member 关系 |
若实现选择「无会话态、每 tool 必传 workspaceId」,可取消 set_workspace,改为强制参数;产品上二选一写死,避免混用。
6.2 L1 — 只读
| Tool |
说明 |
主要入参 |
主要出参 |
映射 REST |
最低角色 |
get_workspace_summary |
仪表盘 + 额度摘要合一 |
workspaceId? |
待报销金额分/张数、已解析数、未匹配附件、队列积压、quota[] |
GET /stats/dashboard + GET /quota/summary |
viewer |
search_invoices |
发票检索分页 |
status、category、日期、batchId、keyword、uncategorized、page… |
瘦字段 items + total |
GET /invoices |
viewer |
get_invoice |
发票详情 |
invoiceId、includeOcrRaw?(默认 false) |
字段、附件清单、可选 raw |
GET /invoices/:id |
viewer |
list_tasks |
解析任务列表 |
status、page… |
items |
GET /tasks |
viewer |
get_task |
单任务状态 |
taskId |
status、invoiceId?、error? |
GET /tasks/:id |
viewer |
list_batches |
批次列表 |
status?、page… |
items(含聚合金额分) |
GET /batches |
viewer |
get_batch |
批次详情与票列表 |
batchId |
聚合 + invoices[] |
GET /batches/:id |
viewer |
list_unmatched_attachments |
待配对附件 |
page… |
items(信号字段,全文可截断) |
GET /attachments?status=unmatched |
viewer |
get_export |
导出任务状态 |
exportId |
status、kind、错误或就绪元信息 |
GET /exports/:id |
viewer |
list_notifications |
站内通知 |
unreadOnly? |
items |
GET /notifications |
登录 |
list_rules |
分类规则 |
scope? |
items |
GET /rules |
viewer |
6.3 L2 — 写闭环
| Tool |
说明 |
主要入参 |
主要出参 |
映射 REST |
最低角色 |
确认 |
upload_invoices |
上传并入解析队列 |
files[](路径或 blob 由宿主传入)、idempotencyKey? |
逐文件 accepted / taskId / reason / restored |
POST /uploads |
member |
否 |
retry_task |
重试失败任务 |
taskId |
新状态 |
POST /tasks/:id/retry |
member |
否 |
update_invoice |
白名单字段修正 |
invoiceId + 允许字段子集 |
更新后摘要 |
PATCH /invoices/:id |
member |
否 |
reparse_invoice |
重解析 |
invoiceId、mode: replay\|refetch |
taskId |
POST /invoices/:id/re-parse |
member |
refetch 建议确认 |
match_attachment |
附件挂到发票 |
attachmentId、invoiceId |
挂载结果 |
POST /attachments/:id/match |
member |
否 |
create_batch |
建批 |
name、note?、targetAmountFen? |
batch |
POST /batches |
member |
否 |
add_invoices_to_batch |
入批 |
batchId、invoiceIds[] |
结果 |
POST /batches/:id/invoices |
member |
否 |
remove_invoice_from_batch |
拆批 |
batchId、invoiceId |
结果 |
DELETE .../invoices/:invoiceId |
member |
否 |
reorder_batch |
批内排序 |
batchId、invoiceIds[](全量序) |
结果 |
PATCH /batches/:id/order |
member |
否 |
update_batch |
改名或状态流转 |
batchId、name?、status?(仅 paid) |
batch |
PATCH /batches/:id |
manager(删/状态)/ 改名按 API |
status=paid 要 confirm |
delete_batch |
删批(票回归集区) |
batchId、confirm: true |
空 |
DELETE /batches/:id |
manager |
是 |
delete_invoice |
软删发票 |
invoiceId、confirm: true |
空 |
DELETE /invoices/:id |
member |
是 |
export_batch |
触发导出 |
batchId、kind: xlsx\|pdf\|zip |
exportId |
POST .../export/{kind} |
member |
否 |
get_download_url |
导出物或原件受控下载信息 |
exportId 或 workspaceFileId 等 |
短时可用下载描述(非永久公网链) |
GET /exports/:id/download 等 |
viewer |
否 |
create_rule |
新建分类规则 |
scope、匹配条件、category… |
rule |
POST /rules |
member(scope 内) |
否 |
mark_notifications_read |
已读 |
ids[] 或 all |
空 |
PATCH /notifications/read |
登录 |
否 |
补传附件(POST /invoices/:id/attachments)可并入 upload_invoices 的 invoiceId 可选参数,或单独 attach_file_to_invoice;产品实现时择一,避免两个入口语义漂移。
6.4 L3 — 增强只读(可选)
| Tool |
说明 |
映射 REST |
最低角色 |
get_finance_portal |
门户现场概览(余额、在途单等),不落库、不回密码 |
GET /finance-accounts/:id/portal |
viewer |
get_finance_claim_print |
历史报销单打印数据 JSON |
GET .../claims/:serialNo/print |
viewer |
list_quota_snapshots |
额度快照序列 |
GET /quota/plans/:id/snapshots |
viewer |
6.5 L4 — 触发类(可选)
| Tool |
说明 |
映射 REST |
最低角色 |
trigger_mail_fetch |
手动拉邮 |
POST /mail-accounts/:id/fetch |
manager |
trigger_finance_sync |
手动同步财务 |
POST /finance-accounts/:id/sync |
manager |
6.6 永不暴露的能力(产品黑名单)
| 能力域 |
代表端点/动作 |
原因 |
| 认证管理 |
改密、admin 建号/封禁 |
身份治理须人 |
| 工作区治理 |
建/删空间、成员 CRUD、owner 转让 |
组织变更 |
| 凭据写 |
mail/finance/notify-channel 的 secret 读写与 test 中的敏感回显 |
密钥红线 |
| 排障 |
/admin/files/...、跨工作区 access |
审计特权 |
| 原始队列/SQL |
直接操作 BullMQ 或数据库 |
绕过领域不变量 |
| OA 自动提交 |
无人值守提交报销 |
02-features.md 明确排除 |
7. 鉴权与令牌产品
7.1 身份模型
| 项 |
定义 |
| 主体 |
始终是真实用户(或显式的服务账号用户),MCP 不发明独立于成员表的幽灵身份 |
| 传输 |
Authorization: Bearer <token> 调 API;MCP 进程持有 token,不把 cookie 塞进通用 LLM 日志 |
| 工作区 |
每个业务 tool 校验:token 对应用户是该空间活动成员,且角色满足 tool 下限 |
| 与 Web 关系 |
用户可在 Web「设置 → 开发者 / Agent」创建、吊销、查看令牌;吊销立即生效 |
7.2 Token 能力集(scope)
签发时可子集化,默认建议:
| Scope |
含义 |
workspace:list |
列出工作区 |
invoice:read / invoice:write |
发票与附件读/写 |
task:read / task:write |
任务查询与重试 |
batch:read / batch:write |
批次与导出触发 |
quota:read |
额度 |
rule:read / rule:write |
规则 |
notification:read / notification:write |
通知 |
finance:read |
L3 门户只读 |
ops:trigger |
L4 拉取/同步 |
admin |
禁止出现在 MCP 令牌 |
另可选约束:
- 单工作区钉死:token 仅允许一个
workspace_id,忽略或拒绝其它 id。
- 只读令牌:仅 L0–L1(+L3),适合「问答式」Agent。
- TTL 与轮换:短有效期 + 刷新或重新签发;支持用户一键吊销。
7.3 限流(产品口径)
| 维度 |
建议 |
| 上传 |
与 API 相同文件限制;Agent token 可额外按小时计次 |
reparse refetch |
严格限次(消耗 OCR 配额) |
| 导出 |
按工作区并发/日限额,防止打爆 worker |
| 全局 |
429 + RATE_LIMITED,Agent 应退避而非紧密重试 |
8. 数据与错误约定
8.1 字段口径
与 08-api.md §1 一致:
- ID:uuid 字符串
- 金额:integer 分
- 业务日期:
YYYY-MM-DD
- 时间戳:ISO 8601 UTC
- 分页:
page / pageSize / sort,响应含 total
成功:直接返回资源 JSON(可与 REST 同形或经 MCP 层做字段裁剪)。
失败:
{
"code": "BATCH_STATE_INVALID",
"message": "批次非 collecting,无法入批",
"details": {/* 可选 */},
}
跨工作区或无权限资源:表现为不存在(404 语义 / NOT_FOUND 类 code),不泄露其它空间是否持有该 id。
8.3 异步模式
upload_invoices / reparse_invoice / export_batch
→ 立即返回 taskId 或 exportId
→ Agent 调 get_task / get_export 直到终态
→ done 后再 get_invoice / get_download_url
终态枚举与 API 对齐:任务 done | failed | duplicate | ...;导出 done | failed。
产品要求:tool 描述(description)中写清「须轮询」,降低模型误判同步完成。
8.4 文件如何进 MCP
宿主与 MCP 之间的文件传递依赖 MCP 实现能力(path、base64、资源 URI 等)。产品要求:
- 仅允许约定扩展名:
pdf / jpg / jpeg / png
- 单文件大小上限与 API 一致(默认 ≤ 10MB)
- 单次上传 ≤ 20 个文件
- MCP Server 将文件转为对 API 的 multipart,不在磁盘上留下可被其它租户读取的明文缓存(临时文件用完即删)
9. 安全与合规红线
- 多用户数据必须带工作区边界;复合外键 + RLS + 成员校验与 Web 共用。
- 密钥与 OCR key 不进 MCP 日志、trace、tool 回显。
ocr_raw_json 默认不下发;需要时显式 includeOcrRaw 且仍受工作区权限约束。
- 下载只给受控、短时、需鉴权的获取方式,不做永久公开 URL。
- Agent 写操作全量审计:谁、哪个 token、哪个工作区、哪个 tool、关键资源 id、结果 code。
- 内网部署默认:MCP 只在内网或 localhost 暴露;若 HTTP 传输须 TLS 与内网 ACL。
- 提示注入:tool 返回的发票备注、商户名、OCR 文本视为不可信数据,系统提示中应要求模型不得将其当作指令执行(实现侧可在 description 与 wrapper 中重复此约束)。
10. 宿主集成(产品体验)
10.1 连接方式(目标形态)
| 模式 |
适用 |
说明 |
| stdio |
桌面宿主本机 |
用户配置 command + env(CLIP_API_BASE、CLIP_TOKEN) |
| 内网 HTTP |
团队 Agent 网关 |
统一入口,token 按用户下发 |
10.2 配置项(用户可见)
| 配置 |
含义 |
| API Base URL |
如 https://clip.example.edu/api/v1 |
| Access Token |
Web 签发的 Agent 令牌 |
| Default Workspace ID |
可选;与 token 钉死策略一致时只读展示 |
10.3 Agent 系统提示(产品侧建议文案要点)
- 先
list_workspaces / get_workspace_summary,再改数据。
- 金额按分计算;展示给人时再换算元。
- 解析与导出必须轮询至终态。
- 标
paid、删除类操作前向用户确认。
- 不要索要或保存用户邮箱/门户密码。
- 不要声称已向学校财务系统提交成功(本系统不提供该能力)。
11. 成功指标(产品)
| 指标 |
意图 |
| 主闭环完成率 |
Agent 从上传到导出成功的会话占比 |
| 危险操作确认率 |
需 confirm 的调用在无确认时被拒绝的比例(护栏有效) |
| 越权尝试 |
跨工作区 / 超 scope 调用应为 0 成功 |
| 平均 tool 轮次 |
完成「组一单并导出」的中位调用次数(工具是否够复合) |
| 人机升级率 |
因凭据、歧义附件、额度策略而转人工的比例(预期存在,用于改进 L3/配对) |
12. 分阶段落地
与 06-roadmap.md 业务里程碑解耦,MCP 可并行推进,但依赖对应 REST 已可用。
| 阶段 |
目标 |
范围 |
验收 |
| MCP-0 |
契约与鉴权 |
OpenAPI/ zod 导出、Agent token 模型、审计字段、本文档评审 |
能签发只读 token;无业务 tool 也可 |
| MCP-1 |
只读可用 |
L0 + L1 tools;stdio 连接样例 |
Agent 正确回答「待报销多少、有哪些失败任务」 |
| MCP-2 |
写闭环 |
L2 主路径(上传→任务→票→批→导出);confirm 护栏 |
端到端导出物可下载;重复上传/入批行为与 Web 一致 |
| MCP-3 |
增强 |
L3/L4、可选 SSE 订阅桥、智能组单 tool(若 M3 组单 API 就绪) |
门户只读与触发拉取可用;自动提交仍不存在 |
| MCP-4 |
生态 |
官方 MCP 包发布说明、宿主配置模板、限流与用量面板 |
内网文档可自助接入 |
智能组单(suggest_batch)作为 L2 增强 tool 单列:只返回建议 invoiceIds 与金额分,不自动入批;入批须用户确认后再调 add_invoices_to_batch。(后端 POST /batches/suggest 已落地,见 03 §5.6。)
13. 文档与实现同步
| 变更类型 |
同步动作 |
| 新增/改名/删除 tool |
更新本文 §6 + MCP server 注册表 + 集成测试 |
| 入参出参字段 |
packages/shared zod → 生成 tool schema → 本文示例 |
| 角色或黑名单 |
本文 §5–§6 与 08 §3.7 |
| REST 路径变更 |
08 与本文映射表同一 commit |
相关文档:
14. 名词表
| 术语 |
含义 |
| MCP |
Model Context Protocol,宿主与工具服务器之间的标准协议 |
| Tool |
Agent 可调用的具名操作,含 JSON Schema 入参与结构化出参 |
| Agent 令牌 |
用户签发的、带 scope 与可选工作区钉死的 Bearer 凭证 |
| 瘦字段 |
列表默认返回的最小字段集,省略 raw OCR 与大文本 |
| 确认(confirm) |
危险写操作的显式布尔入参或宿主人工批准门闩 |
| 报销闭环 |
上传解析 → 核对 → 组批 → 导出 的完整路径 |
15. 开放问题(写入时未锁死,实现前需拍板)
- 会话态 workspace:
set_workspace 会话缓存 vs 每 tool 必传 workspaceId。
- 下载交付:MCP 直接返回短时 URL vs 返回「请到 Web 下载」的占位(内网 Agent 无浏览器时前者更必要)。
- 服务账号:是否允许非自然人的 bot 用户加入工作区(审计与责任主体)。
- Idempotency-Key:先做 MCP 层去重表,还是并入 API 正式契约。
- 多文件上传在 MCP 的编码:path 引用(stdio 本机)与远程宿主的 base64 体积上限。
拍板后更新本节为「已决」并改 §6/§8 正文,避免实现分叉。