Files
Auto-Finance/docs/MCP.md
T
wandering 2e2519632a docs+chore: 定案——提交永远由人类执行,删掉 submit_execute 的全部规划
用户明确否决代提交工具。这不是「还没做」,是能力边界,所以凡是把它写成「待实现」的地方都改了,
免得以后有人以为补个实现就齐活。

- server.py INSTRUCTIONS 改成硬约束:本项目不提供也不计划提供点提交的工具;删除(#delete)、
  撤回同理。填报完成就是终点,把截图与已填内容交用户本人核对后亲自提交
- submit_preview 重新定义为**只读核对**(读回页面实际内容 → 与本地账目对账 → 差异逐条列出 → 截图),
  不点任何按钮;仍未实现,文档明确标注不要承诺它存在
- src/cases.py 删掉 submitted 阶段与 meta.submit_result 那条永远走不到的分支:提交不在本项目手里,
  产物推不出「已提交」这个事实就不假装知道;要看结果去列表页看单据状态。previewed 保留
  (它对应真会落盘的只读产物 submit_preview.json)
- 文案:browser.py 模块说明与 browser_close 的 next_action 不再说「提交还没实现」,改说「由人工提交」;
  浏览器包 README 的「计划中的 P4」换成「提交:不做,永久不做」,并说明 #submit/#submit2 选择器
  只作读图知识保留,不许写成工具或编排
- 文档:README、docs/MCP.md(新增「提交:不做,永久不做」一节)、操作指南(数据流标注到此为止、
  提交小节标明工具不提供该动作)、SKILL 的提交段改为永久约束;方案文档 §16 记录定案与理由,
  并把早期把 submit_execute 列为待办的 4 处改为删除线 + 指向 §16

验证:pytest 148 passed;ruff / format --check / mypy src / deptry 全绿;mcp_smoke 25 工具,
INSTRUCTIONS 2224 字节。--no-verify 提交并手跑等价检查。
2026-10-20 21:00:48 +08:00

16 KiB
Raw Blame History

last_reviewed
last_reviewed
2026-10-19

MCP 接口文档

本项目的唯一业务入口是 MCP 服务器:python -m src.mcp.server(stdio 传输),工具名在宿主里表现为 mcp__arb__<tool>。 没有 Web 界面,没有 CLI 产品入口,本项目也不调用任何大模型——读图与综合判断由宿主 Agent 完成, 本项目只提供确定性工具(渲染、金额匹配、规则校验、文档生成、浏览器填报)。

注册到宿主

DSH(profile patch,~/.dsh/profiles/<profile>/cordis.patch.yml):

- insert:
    - id: mcp-arb
      name: '@deepseek-ai/dsh-mcp-client'
      config:
        serverName: arb
        transport: stdio
        command: 'D:/Code/AI Agent/自动报销系统/.venv/Scripts/python.exe'
        args: ['-m', 'src.mcp.server']
        cwd: 'D:/Code/AI Agent/自动报销系统'

注意:DSH 对 stdio 子进程做了环境变量清洗(抹掉 *KEY|PASSWORD|SECRET|TOKEN 与全部 DSH_*), 所以财务系统凭据只能来自 scripts/data/config.json,不能靠环境变量注入。

通用 MCP 客户端等价配置:

{ "mcpServers": { "arb": { "command": "<项目根>/.venv/Scripts/python.exe", "args": ["-m", "src.mcp.server"], "cwd": "<项目根>" } } }

改完 Python 代码需要让宿主重连(DSH 会热插拔新工具条目,但不会重载已运行子进程里的代码; 杀掉 stdio 子进程即可自动重启并重同步工具)。

返回信封

所有工具返回统一信封,宿主判类型看 ok:

{ "ok": true,  "case_id": "...", "next_action": "..." }
{ "ok": false, "reason": "缺少 payment_records.csv", "next_action": "先 mcp__arb__match_payments 生成汇总" }
  • 不抛异常穿透协议:文件缺失、参数不合法、COM 失败都转成 ok: false + reason + next_action。
  • next_action 是给 Agent 的下一步指令,通常是具体工具名,照做即可。
  • 部分客户端会把结果包在 content[0].text(JSON 字符串)里,同时提供 structuredContent;两者内容一致。

会话目录

一个报销任务 = 一个 cases/<case_id>/ 目录(case_id 形如 20261019-6月毕业采集)。 阶段完全由产物推导,没有状态机文件,可随时断电重来、可人工改文件后继续。

cases/<case_id>/
├── case.json                 名称、类型、上传令牌(切勿提交)
├── config.json               可选,会话级凭据覆盖(白名单字段)
├── <材料>.pdf|jpg            发票、支付截图、申请单原件
├── inbox/                    手机扫码上传落地点,case_add_files(source="inbox") 收取
├── pages/<stem>/p01.png      渲染页面 + text.txt(PDF 文本层)+ meta.json(dpi/转正角/字符数)
├── .invoice_cache/*.json     单张票据抽取结果(宿主产出,本服务规范化后落盘)
├── match_result.json         匹配结果
├── invoice_groups.json       差旅/普通分组与计数
├── invoice_summary.csv       发票级汇总
├── payment_records.csv       支付记录汇总(申购单取数)
├── travel_applications.json  出差申请单
├── travel_info.json          差旅报销结构化数据
├── normal_info.json          普通报销结构化数据
├── artifacts/                申购单等文档产物
├── screenshots/              (P3 落地后出现)填报过程截图
└── submit_preview.json       (只读核对落地后出现)页面回读快照与对账结果

cases/ 整个目录都在 .gitignore 里:发票含身份证号与卡号。

标准流程

  1. case_create → 记下 case_id 与返回的 upload_page(手机扫码上传页)
  2. case_add_files(本地路径用正斜杠)或 case_add_files(source="inbox") 收手机上传
  3. case_render_pages → text_chars>0 先 case_read_text,否则读 pages/**.png
  4. 逐张按 extraction_spec 出 JSON → case_save_extraction;核对后小改用 case_patch_extraction
  5. match_payments → 逐条处理 review_required(未匹配、发票总额小于支付总额、一对多凑额)
  6. info_skeleton 取骨架 → 填值 → save_info(自动跑校验);validate_info 可反复试算不落盘
  7. 普通发票再 doc_requisition 生成申购单
  8. 每步之后 case_status 复查;case_reset(scope) 清派生产物重跑

硬约束

  • 不得回显密码,不得把凭据写进产物或日志
  • 不得臆造财务数据:拿不准的字段标出来问用户,禁止自己宣布"可以提交"
  • 产物只写 cases/<case_id>/
  • 文本层与读图结论冲突时(尤其编号、卡号、金额位数)必须复核,不要默认信任某一路

提交:不做,永久不做

提交由人类在财务系统里亲自点。 本项目不提供、也不计划提供 submit_execute 这类代提交工具 (用户明确否决)。填报完成就是 Agent 的终点:把 browser_page_state 的截图与已填内容报给用户, 由本人核对后自己提交。删除(#delete)、撤回同理,都在页面上手工做。

计划中的只有一个只读工具 submit_preview:把单据页上的实际内容读回来、与会话里的账目对账、 截图留证,帮人在点提交前发现问题。它不点任何按钮。目前尚未实现,不要调用、也不要承诺。

同样已下线的:Word 版易耗品出库单(模板自 2026-08 起不在仓库,历史上长期静默失败)。


工具参考

以下内容由运行中的服务器 schema 自动生成,与代码同源(scripts/mcp_smoke.py 打印同一份清单)。

会话与材料

每个报销任务是一个会话目录 cases/<case_id>/,阶段由产物推导。

mcp__arb__case_create

新建报销会话目录,返回 case_id 与目录路径。

name 是本次报销的人类可读名称(如「6月毕业采集」);
invoice_type 可选 travel|normal,未定时留空,抽取后由发票分类决定。
参数 类型 必填 默认 说明
name string 是 — —
invoice_type null/string 否 — —

mcp__arb__case_list

列出所有报销会话及其推导阶段、计数与待收文件数。

无参数。

mcp__arb__case_status

单个会话的完整态势:材料数、抽取完成/缺失/陈旧、匹配、信息、产物、收件箱、下一步建议。

参数 类型 必填 默认 说明
case_id string 是 — —

mcp__arb__case_add_files

把材料拷入会话目录。

paths 传绝对路径列表(从手机下载目录、微信文件目录等任意位置取文件);
source='inbox' 表示收取手机扫码上传落在 inbox 的文件。
参数 类型 必填 默认 说明
case_id string 是 — —
paths array/null 否 — —
source null/string 否 — —

mcp__arb__case_inbox

列出手机/PC 上传进来但尚未进入流程的文件。

参数 类型 必填 默认 说明
case_id string 是 — —

mcp__arb__case_reset

清理派生产物以便重跑。scope=extractions|match|info|pages|artifacts|all。

参数 类型 必填 默认 说明
case_id string 是 — —
scope string 否 "extractions" —

抽取交接

渲染页面与读文本层由宿主 Agent 自己判断,本服务不调用任何模型。

mcp__arb__case_render_pages

为材料准备可读取的页面图片与 PDF 文本层。

返回每项材料的页 PNG 路径、文本层字符数与自动转正角度。
有文本层(text_chars > 0)时应优先读文本层,比读图更准且省 token。
参数 类型 必填 默认 说明
case_id string 是 — —
files array/null 否 — —
dpi integer 否 150 —
force boolean 否 false —

mcp__arb__case_read_text

读取某材料渲染时提取的 PDF 文本层;无文本层时返回空。

参数 类型 必填 默认 说明
case_id string 是 — —
file string 是 — —

mcp__arb__extraction_spec

返回抽取字段定义。kind 省略给全部文档类型;'info' 给 travel_info/normal_info 骨架。

参数 类型 必填 默认 说明
kind null/string 否 — —

mcp__arb__case_save_extraction

把单张票据的抽取结果写入会话缓存(按 .json 约定)。

日期会规范成 YYYY-MM-DD、金额规范成两位小数字符串;
返回 warnings 提示必填缺失,但不阻断写入(完整性由 arb_validate_info 判定)。
参数 类型 必填 默认 说明
case_id string 是 — —
source_file string 是 — —
data object 是 — —

mcp__arb__case_patch_extraction

局部修正已落盘的抽取结果(人工核对后改字段用)。

只合并传入的键,返回每个被改字段的 from/to,避免静默覆盖;
未在 schema 中出现的键会告警但仍写入(有些字段是给下游自定义用的)。
参数 类型 必填 默认 说明
case_id string 是 — —
source_file string 是 — —
patch object 是 — —

匹配与校验

匹配与校验都是确定性代码;结论以 validate_info 为准,不要自己宣布可提交。

mcp__arb__match_payments

按缓存做发票与支付记录的金额匹配,并写出 CSV 与 invoice_groups.json。

返回 matched / unmatched_invoices / unmatched_payments / review_required。
未匹配与一对多凑额项必须复核,不要盲信贪心结果。
参数 类型 必填 默认 说明
case_id string 是 — —
tolerance null/number 否 — —

mcp__arb__info_skeleton

按校验规则生成「形状完整、值为空」的可填骨架。

规则里 required 为 true 的字段(含 remark 这类备注)要求键必须存在,
所以别凭记忆拼 JSON:先取骨架再逐个填值。骨架与 validation_rules.json 同源,不会漂移。
参数 类型 必填 默认 说明
info_type string 是 — —

mcp__arb__save_info

写 travel_info 或 normal_info 到缓存,并立刻跑一次规则校验。

info_type 取 travel|normal。完整性以返回的 validation 为准,
不要自己在 data 里塞 can_submit。
参数 类型 必填 默认 说明
case_id string 是 — —
info_type string 是 — —
data object 是 — —

mcp__arb__validate_info

校验报销信息完整性。省略 draft 时校验缓存里已存的 info。

也可传 draft 先试校验不落盘,适合改字段时反复迭代。
参数 类型 必填 默认 说明
case_id string 是 — —
info_type null/string 否 — —
draft null/object 否 — —

mcp__arb__rules_get

返回声明式校验规则(config/validation_rules.json),供补字段前对照。

参数 类型 必填 默认 说明
info_type null/string 否 — —

文档产物

Excel COM,需本机装有 Microsoft Excel;仅普通发票需要。模板顺序:REQUISITION_TEMPLATE → templates/ → 项目根。

mcp__arb__doc_requisition

生成低值易耗品申购单(Excel,仅普通发票)。

exclude 传物品名关键词可剔除不参与申购的发票;project 覆盖「对应实验实训项目」,
默认取 normal_info 的报销事由。需要 Windows + 已安装 Microsoft Excel。
参数 类型 必填 默认 说明
case_id string 是 — —
exclude array/null 否 — —
project null/string 否 — —

浏览器分步填报

需先 uv run playwright install chromium。默认有头;归属由 cases/<id>/.browser.lock 记录 (PID + 心跳,180 秒未心跳可被接管)。中途断了不必重来:用 browser_open_form 从列表页找回原单据 续填,不要再 browser_new_form 重复建单。提交不在这里,填完交人工核对。

mcp__arb__browser_open

打开(或复用)该会话的浏览器。

默认有头:提交不可逆,要看得见在点什么。同一会话重复调用直接复用现有浏览器。
被别的进程占用时返回 owned_by_other_process,别绕过去另开一个浏览器。
参数 类型 必填 默认 说明
case_id string 是 — —
headless boolean 否 false —

mcp__arb__browser_login

登录信息门户。遇到扫码或验证码不要死等:返回 need_human,人工完成后再次调用。

参数 类型 必填 默认 说明
case_id string 是 — —
wait_seconds integer 否 25 —

mcp__arb__browser_new_form

进入报销系统并新建单据。kind 省略时按会话里已有的 info 推断。

参数 类型 必填 默认 说明
case_id string 是 — —
kind null/string 否 — —

mcp__arb__browser_open_form

从列表页找回已存在的单据接着填(中途断了不必重来)。

match 用报销说明里的关键词,必须在当前列表页唯一命中;命中多张直接失败,
让宿主把关键词收紧——财务系统里点错别人的单据是不能接受的后果。preview_only=true
只搜索定位、不点开单据,用来先确认是哪一张。
参数 类型 必填 默认 说明
case_id string 是 — —
match string 是 — —
kind null/string 否 — —
preview_only boolean 否 false —

mcp__arb__browser_fill

执行一步填报。step 按 browser_new_form 返回的 steps 顺序,不能跳步。

参数 类型 必填 默认 说明
case_id string 是 — —
step string 是 — —

mcp__arb__browser_page_state

看当前页面:URL、标题、命中的表单标记、已完成步骤、最近截图。

at_portal 只表示在信息门户;进了报销系统要用 in_reimburse_system 与 on_form_page 判断。
参数 类型 必填 默认 说明
case_id string 是 — —
screenshot boolean 否 true —

mcp__arb__browser_steps

列出该会话单据类型的步骤顺序与含义(不要求浏览器已打开)。

参数 类型 必填 默认 说明
case_id string 是 — —

mcp__arb__browser_close

关闭浏览器并释放锁。不会提交也不会存草稿,填了一半的内容会丢。

参数 类型 必填 默认 说明
case_id string 是 — —