Files
dingMCP/README.md
T
gitea-admin b3fa2315ba
CI / Client installer (macos-latest) (push) Canceled after 0s
CI / Client installer (ubuntu-latest) (push) Canceled after 0s
CI / Client installer (windows-latest) (push) Canceled after 0s
CI / test (22) (push) Canceled after 0s
CI / test (24) (push) Canceled after 0s
CI / release-smoke (push) Canceled after 0s
feat: add standalone login renewal
2026-08-18 08:04:37 +00:00

110 lines
11 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.
# 钉钉审批 MCP
一个面向 Agent 的统一审批 MCP。用户只登录一次钉钉,即可读取、处理和发起本人在钉钉 OA 与薪福通中的审批:
```text
Agent → 本地轻量 stdio 桥 → HTTPS/OAuth 统一网关
├─ 钉钉 OA OpenAPI
└─ 薪福通 OpenAPI
```
## 工具
- `approval_login`:检查登录或返回唯一授权链接。
- `approval_list_todos`:实时列出本人待办,签发短期 `approvalId`,并为可安全整组处理的结果签发 `selectionId`。
- `approval_list_history`:实时列出本人已经处理过或本人发起的审批;默认最近 30 天,可用 `startDate/endDate` 指定最长 120 天的时间窗口,并签发只能读取详情的短期 `approvalId`。
- `approval_get_detail`:返回摘要、结构化费用明细或受限 raw 分页。
- `approval_get_actions`:检查同意、拒绝与退回能力。
- `approval_process_batch`:一次最多处理 20 条;整批引用预检通过后才逐项执行,并在写后复核。
- `approval_list_submission_templates`:实时列出当前用户的提单候选模板。
- `approval_get_submission_schema`:返回结构化可填写字段,不传输巨大原始 Schema。
- `approval_get_submission_options`:查询当前用户可用的薪福通部门和城市编码。
- `approval_prepare_submission`:校验内容,自动查重本人近 30 天跨钉钉/薪福通的发票,并在安全时生成完整预览及短期确认引用;不创建单据。
- `approval_submit`:用户确认预览后,使用相同内容正式发起审批。
- `approval_revoke_submission`:使用提单成功后返回的 `revokeId` 撤销本人刚通过 MCP 发起的钉钉或薪福通审批。
- `approval_get_submission_status`:只读核验本人已发起薪福通单据的实时状态。
待办、历史和处理结果使用统一、精简的领域字段,不向 Agent 暴露钉钉任务 ID、薪福通员工号或上游原始响应。历史查询分为 `handled`(本人已经处理过)和 `submitted`(本人发起),结果没有 `selectionId` 和可执行动作。常见 `approvalId` 使用紧凑的 v4 无状态加密格式:薪福通通常约 60 字符、钉钉 OA 通常约 100 字符,并绑定当前用户和 30 分钟有效期;历史引用还绑定查询语义,只能读取详情。旧版未过期的 v2/v3 引用仍可读取。
一次写调用最多处理 20 条。整组同意/拒绝可直接使用 `selectionId`;超过 20 条时,Agent 可从同一次原始列表中按 `approvalId` 分组,不需要 `batchPlan`,也不能重新拉取后把新待办混入原授权。服务器在首个写入前校验整批 `approvalId` 和 `targetId`;任一引用无效时返回 `preflight_failed`、`writesStarted=false`,整批零写入且不自动补偿或重试。普通文本与结构化结果保持同一份精简数据,以兼容不渲染 `structuredContent` 的宿主。
Agent 不传 `userId`、员工号或单据所有者。网关从 OAuth Token 确定用户,再分别映射钉钉 OA 与薪福通身份。钉钉提单中的申请人和部门也由网关通过当前登录用户的通讯录档案实时填写;即使用户修改本地桥,也不能冒用他人身份或部门。
审批读取和处理范围不变;发起入口会列出当前企业的 4 个薪福通模板,以及以下 6 个钉钉模板:
- `北极大_人事假勤`:`出差申请单_北极大`、`外出_北极大`、`业务招待申请_北极大`
- `捷康_费用报销`:`差旅费用报销_捷康`、`业务招待费用报销_捷康`、`业务招待申请_捷康`
范围限制由网关按分类和模板名双重执行,不依赖本地 Skill。同分类中未列出的模板也不能发起。
模板出现在列表中不等于所有控件都能由开放 API 构造。`approval_get_submission_schema` 会返回 `submissionCapability`,准备阶段也会再次安全校验:
- 薪福通:白名单中的申请和报销模板均通过正式创建 API 发起,并在创建后实时核验状态。结果可能进入待审批,也可能因企业流程中没有其他有效审批人等规则即时完成;后者返回 `completed`,不会错误地当成接口失败,也不会签发不可用的撤回引用。
- 钉钉:当前 6 个候选模板均返回 `client_only`,必须在钉钉客户端发起。其中 5 个包含企业 OpenAPI 无法可靠构造的 `DDBizSuite` 考勤或费控套件;`业务招待申请_北极大`虽是普通明细表,但真实创建时新版企业接口返回系统异常、旧版接口拒绝组件格式,因此同样安全降级。
发起审批固定为“列模板 → 取 Schema → 自动查重/预览 → 用户确认 → 正式提交”。准备阶段从新表单提取发票代码与号码、数电票唯一编号、票据号码、附件哈希;没有唯一编号时,金额、日期与商户组合只作为疑似提示。网关实时刷新当前用户本人近 30 天的钉钉和薪福通已发起审批:原单待审批、已通过或状态未知时默认阻止,原单拒绝、撤回、作废或弱匹配时警告。任一来源查询不完整也不会当成“没有重复”。只有用户看过匹配单号、状态和覆盖情况并明确要求继续后,Agent 才能以 `allowDuplicateInvoices=true` 重新准备。
服务端临时保留不可逆的用户/发票 HMAC 指纹。首次使用时只为最近 30 天尚未索引的审批读取详情;以后准备提单会刷新精简历史状态,并只为新增审批补建指纹,不会重复逐张下载已索引发票。指纹和最小审批索引最多保留 35 天,过期后自动级联删除;10 分钟提单预留到期即删除。
安全准备后,确认引用绑定登录用户、模板版本、完整表单内容、重复风险决定和发票预留,10 分钟有效且只能使用一次;相同发票在另一个有效确认中时不会再签发确认,避免并发重复创建。正文和发票原号不落库。钉钉企业 OpenAPI 支持通过 `POST /v1.0/workflow/processes/forecast` 进行无副作用的流程预测;网关只在预测成功并补齐所有自选处理人后才生成提交确认。薪福通在正式提交时完成最终业务校验,并由网关写后读取 `billSts`:状态 `1(待审批)` 返回 `submitted` 和 `revokeId`,状态 `2(审批通过)` 返回 `completed` 且不提供撤回引用。
> 发布候选版限制:薪福通 `approve/recall` 撤回接口已经接入并具备本人归属校验与写后复核,但当前企业的真实待审批单仍返回 `XFTSYS9999`,尚无撤回成功样本。因此薪福通撤回属于实验能力;失败或结果不确定时绝不自动重试。查询、处理和发起审批不受此限制。
## 部署
服务器首次配置企业凭证和 Ed25519 发布签名私钥后,安装和以后每次更新统一执行:
```bash
./deploy/install.sh
```
卸载网关:
```bash
./deploy/uninstall.sh
```
该卸载命令只处理服务器网关,并默认保留 `.secrets`、`.runtime`、OAuth 数据库和 HTTPS 证书。详细步骤见 [部署文档](docs/deployment.md),安全边界见 [架构文档](docs/architecture.md)。
10 个提单模板的逐项字段与 API 能力结论见 [提单模板能力](docs/submission-templates.md)。
发布候选版的放行条件见 [发布检查清单](docs/release-checklist.md)。
部署完成后,把下面的唯一链接交给用户或其 Agent:
```text
https://approval.justconn.top/downloads/dingmcp/install.md
```
用户把这个链接交给 Agent 后,Agent 只需校验并运行一个原生 Node 安装器。安装器使用固定 Ed25519 公钥验证清单和发布物,把轻量客户端安装到稳定的用户数据目录,并统一返回 `bridgePath`、`skillPath`、`loginCommand` 和标准 `mcpConfig`;不再区分 Agent 品牌,不全局安装 npm 包,也不会猜测或直接修改 Agent 的私有配置。安装器会立即在受保护的本地会话中检查登录并返回机器可读 `postInstall`:未登录时直接带回当前授权链接,网关暂时不可用也不会使安装回滚。Agent 随后按自身官方方式安装 Skill、注册并信任本地 stdio MCP;不能热加载连接器时再新建会话或重启。用户端不安装 DWS,不需要企业凭证。
Agent 必须在安装后立即展示 `postInstall.authChallenge.directUrl`,不得等到第一次审批查询,也不得声称链接已另行推送。用户授权后,Agent 通过 `approval_login` 确认登录并用 `source=all` 做一次双来源只读验证;首次成功认证响应还会在普通 `content.text` 和 `structuredContent.onboarding` 中返回一次性的简短使用示例,展示后不会重复,也不会自动执行任何写操作。
授权链接约十分钟有效。过期后不需要更新或重装:MCP 已加载时再次调用 `approval_login`;尚未加载时执行安装结果中的 `loginCommand`(等价于稳定客户端的 `login --json`)。两条入口复用同一本地 OAuth 会话,都会返回当前有效链接或自动生成新链接。
首次授权会先显示网关自己的 OAuth 确认页,列出请求客户端、返回域名、权限范围或设备授权码;用户确认后才跳转钉钉。客户端名称由客户端自行声明,因此应同时核对域名或授权码。短暂的网关 5xx/网络故障不会清除本地 refresh token,也不会强迫用户重新登录。
客户端后续可使用稳定 CLI:
```text
dingtalk-approval login --json
dingtalk-approval print-path bridge
dingtalk-approval print-config --format mcp-json
dingtalk-approval doctor --json
dingtalk-approval update --json
dingtalk-approval uninstall --yes --json
```
实际 `dingtalk-approval` 路径由首次安装结果的 `cliPath` 给出。`login` 只使用现有运行时与登录会话;`update` 会复用已固定的清单地址与公钥,并保留 OAuth 登录会话。更新完成后应重新连接该 MCP,Agent 不能单独重启连接器时再新建会话或重启。卸载默认删除客户端运行时、Skill、安装记录和 OAuth 会话;Agent 中名为 `dingtalk-approval` 的连接器通过其官方入口删除。
## 数据与安全
- 所有写操作重新校验审批仍属于当前登录用户。
- `approvalId` 是绑定用户、来源和有效期的紧凑不透明加密引用;不包含展示字段或用户 ID 明文。
- 每批最多 20 条;引用预检失败时首个上游写调用不会发生。
- 提单的发起人、薪福通员工号和审批状态只能由网关填写。
- 审批正文、费用明细、附件和薪福通 Schema 不落库。
- 发票查重库只保存当前用户和发票标识的 HMAC、来源、上游业务单号、状态及时间,最多保留 35 天;不保存发票原号、商户原文或附件。
- 企业 AppSecret 与薪福通 AuthoritySecret 只保存在服务器。
- `failed`、`succeeded` 与 `outcome_uncertain` 有不同重试语义,后者禁止自动重试。
- OAuth 客户端、Token 和 MCP 会话都有数量及生命周期上限;过期数据会周期清理。