MWE审批MCP
Click on "Deploy Server".
Wait a few minutes for the server to deploy. Once ready, it will show a "Started" state.
In the chat, type
@followed by the MCP server name and your instructions, e.g., "@MWE审批MCP列出我的待审批任务"
That's it! The server will respond to your query, and you can continue using it as needed.
Here is a step-by-step guide with screenshots.
MWE审批MCP
MWE审批MCP 是部署在 https://dingtalk.mwexk.com/mcp 的自托管钉钉 OA 审批 MCP Server。
当前版本:0.14.0。
当前架构
WorkBuddy / Codex
-> 本服务 OAuth 2.1 + PKCE
-> 钉钉 OAuth 验证真实企业用户
-> 本服务签发限 audience/scope 的短期 MCP token
-> Streamable HTTP /mcp
-> MWE审批MCP 企业内部应用 access token
-> 钉钉 OA OpenAPI
钉钉 bpms_task_change
-> 官方 Stream 长连接(仅上游事件摄取)
-> 本地待审批/已处理索引只提供自托管 Streamable HTTP;不提供 stdio。
已删除 AIHub 版本,不使用
mcp-gw.dingtalk.com。不提供或兼容
/platform/tools/*旧路由。钉钉
userAccessToken只用于登录身份验证,不作为 MCP Bearer、不持久化。OA OpenAPI 仍使用
MWE审批MCP企业内部应用的 App ID/App Secret。MCP token 中的
corpId + unionId + userId按请求绑定审批调用者,模型输入不能覆盖身份。钉钉 Stream 是服务端使用应用凭证建立的出站事件通道,不是 MCP 传输,不新增公网工具端点。
Related MCP server: DingTalk MCP Server V2
公共工具
正常 tools/list 只有三个按业务角色/主对象聚合的工具:
approval_inbox # 当前审批人:批量发现待审批或已处理任务
approval_task # 审批参与者:查看、同意、拒绝、评论
approval_request # 申请人:准备附件、提交、评论、撤销三个工具按业务角色和生命周期划分:approval_task 负责审批参与者对已知实例的查看、同意、拒绝和评论;approval_request 负责申请人准备/创建审批,并允许对本人发起的实例评论或撤销;approval_inbox 只读批量发现待审批/已处理记录并把 processInstanceId 交给 approval_task。评论是同一个内部动作,仅角色 scope 与实例关系校验不同;未实现的退回能力不会被公开宣称。
发现当前 OAuth 用户的待审批(recordStatus 省略时默认为 pending;limit=1 为单条,最多 20 条):
{
"recordStatus": "pending",
"page": 1,
"limit": 20
}发现当前 OAuth 用户已经处理的审批任务:
{
"recordStatus": "completed",
"page": 1,
"limit": 20
}事件索引覆盖不足时,可显式调用普通 OA 实例 ID 列表接口做最近 1–30 天的有限刷新;服务端只扫描 APPROVAL_INBOX_PROCESS_CODES 中的精确模板,单次最多检查 40 个候选实例,并逐实例验证当前 OAuth 用户的任务:
{
"recordStatus": "completed",
"refreshWindowDays": 7,
"page": 1,
"limit": 20
}若响应中的 refresh.truncated=true,继续把同一响应的 refresh.nextCursor 原样传回,并保持 recordStatus 与 refreshWindowDays 不变;循环到 truncated=false。cursor 由服务端 HMAC 认证,最多有效 24 小时,绑定当前用户、原时间窗口、状态和模板集合;篡改会失败关闭,且不包含用户、实例或模板明文:
{
"recordStatus": "completed",
"refreshWindowDays": 7,
"refreshCursor": "上一次响应的 refresh.nextCursor",
"page": 1,
"limit": 20
}approval_inbox 从 bpms_task_change 事件索引取候选项,然后逐项调用审批详情确认任务属于登录用户并与请求状态一致,才返回唯一 processInstanceId 和可用的 taskId。同一实例的多个任务事件合并成一条;taskCount/verifiedTaskCount 保留完整计数,taskIds 最多返回 20 个并用 taskIdsTruncated=true 声明截断,decisionResults 仅从当前详情仍有效的任务重建。若源事件缺少任务 ID,则仅返回实例 ID,并标记 taskIdUnavailable=true。刷新统计中的 indexedRecordCount 按唯一实例计数,indexedTaskCount 按写入的任务证据计数。候选详情失败时不会越过该页,而是返回可重试 cursor。completed 保存最近 30 天的 finish 事件并返回 decisionResult=agree|refuse|redirect,取消事件不会被当作已审批。普通 OA 没有免费的全量回填 API,因此两类响应都固定声明 coverage=partial 和 resyncRequired=true:它们只覆盖事件连接激活及保留窗口内的任务;若 5000 条容量边界截断更早记录,还会返回 capacityTruncated=true 并推进 coverageSince。该工具不冒充钉钉官方 DWS 的全历史收件箱。
刷新适配器同时接受钉钉详情中的毫秒时间戳和 ISO-8601 时间字符串;createdAt/completedAt/updatedAt 使用真实业务时间,不以本次扫描时刻替代。
读取审批:
{
"action": "view",
"processInstanceId": "审批实例ID"
}换取选定附件的临时下载链接:
{
"action": "view",
"processInstanceId": "审批实例ID",
"attachmentAction": "download",
"attachmentIds": ["详情中的fileId"],
"maxAttachments": 3
}服务端不下载、解析或 OCR 附件。Agent 客户端必须立即下载临时链接,并自行执行大小限制、重定向 Host 校验、文件识别、解析和 OCR。
同意审批:
{
"action": "approve",
"processInstanceId": "审批实例ID",
"taskId": "view返回的当前任务ID",
"requestId": "每次业务决定稳定复用的UUID",
"confirm": true,
"remark": "符合要求"
}拒绝时把 action 改为 reject,并提供非空 remark。写操作会重新读取当前任务,确认任务仍属于 OAuth 登录用户,并使用持久幂等账本阻止重复决定。
发起审批
approval_request 使用“默认拒绝 + 精确允许列表”。首版只接受:
expense_reimbursement:费用报销,固定processCode=PROC-2DB91B79-3CDD-421D-A223-0489A7BAB2C0。payment_request:付款申请,固定processCode=PROC-5E238117-7121-4CB3-8219-9F11A2E42BE4。
加班审批及所有其他模板均拒绝。公开 Schema 不接受 processCode、申请人、审批人、抄送人或流程节点;申请人来自 OAuth 绑定的钉钉用户,审批流完全沿用 OA 后台模板。提交前服务端调用官方 forecast 接口,按实时模板解析必需的发起人自选节点,并仅把 forecast 返回的 actorKey/userId 注入 targetSelectActioners;客户端不能选择或覆盖。必填自选节点无法解析时,在附件 commit 前失败关闭。
先用 dryRun 验证付款申请:
{
"action": "submit",
"template": "payment_request",
"fields": {
"documentNumber": "FK-20260817-001",
"payee": "收款单位",
"currency": "CNY",
"applicationDate": "2026-08-17",
"lines": [{
"purpose": "项目采购",
"amount": 880,
"reason": "合同付款",
"expenseDepartment": "研发部"
}]
},
"confirm": false,
"dryRun": true,
"requestId": "稳定复用的UUID"
}deptId 为可选部门提示,不是客户端提供的身份或权限依据。服务端始终查询 OAuth 申请人的实时通讯录:账号只属于一个部门时自动使用该部门,并规范化客户端遗留的根部门 1;账号属于多个部门且无法唯一确定时返回 DEPARTMENT_SELECTION_REQUIRED 及安全的部门 ID/名称候选,Agent 再用候选中的 deptId 重试。
评论审批实例
approval_task action=comment 可由当前 OAuth 用户对其实际参与的审批实例添加评论;approval_request action=comment 允许申请人对本人发起的精确允许列表实例补充说明或附件。两者调用相同内部动作。纯文本评论直接使用 phase=submit;附件评论先用 phase=prepare 获取审批钉盘直传信息,由 Agent 客户端完成 PUT,再用同一工具的 phase=submit 提交匹配的 fileName/fileSize/uploadKey/spaceId。服务端重新校验审批空间、commit 文件并要求钉钉完整返回 fileId/fileName/fileSize/fileType/spaceId 后才构造评论附件。评论正文为 1–1024 字符,附件最多 20 个、单文件最大 20 MiB、合计不超过 50 MiB;commentUserId 始终由服务端注入。真实写入要求 confirm=true 和稳定 UUID requestId。
{
"action": "comment",
"phase": "prepare",
"processInstanceId": "审批实例ID",
"text": "请查看补充材料",
"attachments": [{ "fileName": "ROI说明.pdf", "fileSize": 4096 }],
"confirm": true
}Agent 完成返回地址的直传后提交评论:
{
"action": "comment",
"phase": "submit",
"processInstanceId": "审批实例ID",
"text": "请查看补充材料",
"uploads": [{
"fileName": "ROI说明.pdf",
"fileSize": 4096,
"uploadKey": "prepare返回值",
"spaceId": "prepare返回值"
}],
"requestId": "稳定复用的UUID",
"confirm": true
}发起人补充材料时,把示例中的工具名从 approval_task 改为 approval_request,参数完全相同。审批写入仅依据 OAuth scope 与实时业务归属校验:approval_task 决策要求 approval:decide 且任务属于当前用户,approval_request 写操作要求 approval:create 且申请人身份由服务端绑定;不再维护部署级用户白名单。
钉钉当前公开的官方 OA 服务端 API 没有“保存到钉钉草稿箱”接口,发起审批接口也没有草稿标志。prepare 与 submit + dryRun=true 会读取实时模板、校验完整表单并构建最终请求,但不会创建审批实例,也不会在钉钉客户端草稿箱生成条目;项目不会用本地记录冒充钉钉草稿。
费用报销字段为 date、reason、counterparty 和至少一条 items;每条明细包含 amount、category(仅 AI费用 或 其它)、expenseDepartment、remark。
附件采用两阶段直传:
Agent 调用
action=prepare,传文件名、大小和模板允许的附件字段。MCP 返回钉钉签名的 HTTPS
PUT地址和请求头;Agent 直接把文件上传到钉钉,文件字节不经过 MCP。Agent 调用
action=submit,提交uploadKey、spaceId、文件名和大小;MCP 提交文件元数据并发起审批。带附件的最终创建请求由服务端注入与审批钉盘空间一致的
microappAgentId,并将附件表单值标记为componentType=DDAttachment;客户端不能覆盖这两个字段。
单文件最大 20 MiB、每单最多 10 个、合计最大 50 MiB。费用报销仅允许附件字段 invoice/other,付款申请仅允许 attachment。实际提交和撤销必须 confirm=true 且提供稳定 UUID requestId;幂等命名空间绑定 OAuth 申请人。附件提交、审批创建或撤销结果不确定时会失败关闭,禁止自动换 UUID 重试。
IDEMPOTENCY_OUTCOME_UNKNOWN 会返回安全诊断字段:failureStage、已 commit/总附件数、当前附件序号、原始安全错误码、HTTP 状态、上游错误码和 upstreamRequestId。这些诊断会与 uncertain 幂等项一起持久化,同 UUID 回读结果一致;账本回读只重建这组允许字段,不保存表单、文件名、uploadKey、用户 ID 或临时 URL。失败阶段仅限 attachment_context、attachment_commit、form_build 和 approval_create。若审批已返回实例 ID,仅随后的幂等账本写入失败,服务保留成功结果并标记 idempotencyPersistence=failed,同时尽力持久化最小实例恢复记录,不再吞掉实例 ID。
OAuth 端点
端点 | 用途 |
| MCP Protected Resource Metadata |
| 本站 Authorization Server Metadata |
| MCP 客户端授权入口 |
| 钉钉 OAuth 回调 |
| 授权码或 refresh token 换 MCP token |
| 受限公共客户端动态注册 |
| 撤销 refresh token family |
| OAuth 保护的 Streamable HTTP MCP |
| 存活检查 |
Access token 默认 60 分钟;refresh token 默认 7 天、每次使用都在 Store 内原子轮换,并从本次刷新重新计算 7 天窗口。服务端仅保留当前 token 与最近一代重放墓碑:重放最近一代会撤销当前 family,更早 token 直接按无效凭证拒绝,因此持续刷新时每个 family 状态保持 O(1)。授权事务、客户端注册和 refresh 哈希保存在 MCP_AUTH_STORE_PATH,原始 token 不落盘;成功签发 token 后动态客户端注册会滑动续期,停止使用 30 天后自动清理。升级到本版本时,仍未过期的旧 8 小时 refresh token 会在启动阶段一次性迁移到新窗口,已过期 token 不复活。
动态客户端注册采用服务端幂等复用:经过校验且语义相同的公共客户端元数据(包括回调地址、客户端名称和协议能力)返回已有 client_id,并优先复用仍绑定有效 refresh family 的旧注册;不同元数据保持隔离。/register 重复请求本身不会延长注册寿命,只有成功签发或刷新 token 才滑动续期。既有重复记录不批量删除,停止使用后按 30 天 TTL 自然清理。安全审计只记录“新建/复用”结果,不记录 client ID、元数据、授权 URL 或凭据。
服务端在 OAuth scope 不变时更新,不要求用户重新登录钉钉:客户端应沿用现有 refresh token 静默换取 access token,重新执行 initialize 与 tools/list。Codex 的 URL-only 配置使用 startup_timeout_sec = 30;codex mcp list/get 只读配置,不会为已启动任务热回填缺失工具,Codex Desktop 需在 MCP servers 设置执行 Restart 或重启应用后创建新任务。initialize.serverInfo.version、/healthz 的 version/toolsRevision 以及 /mcp 的 x-mcp-server-version/x-mcp-tools-revision 可用于检测工具版本;MCP 响应要求重新验证缓存。升级到 0.14.0 仅需用原持久卷与原签名密钥重建服务端,不变更 issuer、audience、scope 或 /app/data/auth,已授权客户端无需重新授权。只有新增 scope、refresh token 超过 7 天滚动窗口、用户主动撤销或检测到 token 重放时,才需要交互式重新授权。服务端的 DCR 幂等复用可减少客户端注册漂移,但不能修复客户端本地凭据丢失或工具注册表缓存故障,也不能强制不支持刷新机制的客户端主动清除其本地工具缓存。
开发者后台
在 MWE审批MCP 企业内部应用中配置精确 OAuth 回调:
https://dingtalk.mwexk.com/oauth/dingtalk/callback并确认应用具备:
登录用户身份/个人信息权限。
根据 unionId 映射企业 userId 的通讯录权限
qyapi_get_member。审批实例读写和审批表单读取权限。
附件直传所需的
Storage.UploadInfo.Read与Storage.File.Write权限。H5 微应用能力及其 AgentId;将正整数配置为
DINGTALK_AGENT_ID。未配置时,无附件审批仍可使用,附件prepare会明确失败。事件订阅的推送方式选择
Stream模式推送,并订阅“审批任务开始、结束、取消/转交”(bpms_task_change)。生产配置DINGTALK_APPROVAL_EVENTS_ENABLED=true,索引保存到APPROVAL_INBOX_PATH。
Premium.Workflow.ReadWrite.All 及 OA 高级版待审批/已处理列表不是生产依赖。Todo.Todo.Read 只在 2026-08-17 做过可行性探测:当前企业的未完成和已完成企业待办都返回 0,不能用它冒充 OA 收件箱;服务代码不调用该接口。
附件上传 URL 必须经过 HTTPS 主机白名单校验。2026-08-17 的企业实测返回
sh-dualstack.trans.dingtalk.com,因此默认精确允许 .trans.dingtalk.com;同时保留
钉钉可能返回的 .aliyuncs.com 存储域。不要把白名单放宽为任意 .dingtalk.com。
OAuth 授权范围包含 approval:read、approval:decide 与 approval:create。未认证请求的 HTTP 401 challenge 只声明 resource_metadata,由客户端从 metadata 的 scopes_supported 选择授权范围;服务端不再用 scope=approval:read 覆盖 metadata。本站 /authorize 只校验并保存 MCP 授权事务,然后立即跳转到钉钉官方 OAuth 页面,不展示自建权限确认页。客户端以较小 scope 连接后,调用发起审批或审批决定动作时,服务端仍会按 MCP Authorization 规范返回 HTTP 403 insufficient_scope challenge;不要把缺 scope 仅包装成 HTTP 200 的工具业务错误。所有实际写操作仍要求工具层 confirm=true。
无效、过期、签名错误或已被替换的 MCP access token 统一返回 HTTP 401 invalid_token 及 canonical resource_metadata challenge,不得因底层 JWT 异常返回 500。这样客户端可以进入标准 refresh/login 恢复链路,而不会把 MCP 标记为服务端故障。
本地验证
cd "D:\codex项目\金蝶领星钉钉三端数据同步开发\approval-mcp"
npm ci
npm test
npm run typecheck
npm run build生成 Ed25519 PKCS#8 签名私钥:
mkdir -p secrets
openssl genpkey -algorithm Ed25519 -out secrets/mcp-signing-private.pem
openssl rand -base64 32 > secrets/mcp-audit-hmac.key
chmod 600 secrets/mcp-signing-private.pem secrets/mcp-audit-hmac.key复制 .env.example 配置真实环境变量。服务不会自动读取 .env;Compose、systemd 或密钥管理器必须显式注入。
启动:
npm run build
node .\dist\transports\http.js客户端
WorkBuddy 与 Codex 的无密钥 OAuth 配置模板和测试顺序见:
客户端配置中只出现公开 MCP URL,不填写 App Secret、Bearer token 或钉钉 userAccessToken。
部署
使用
compose.example.yaml部署应用。MCP 签名私钥和独立审计 HMAC key 均以只读 Secret 文件挂载。
deploy/cvm/edge/dingtalk.conf只转发/mcp、OAuth/metadata 端点和/healthz。当前 CVM 入口以
/public/cvm-web-edge/README.md为准:应用只发布一个唯一的 loopback 后端端口,由edge-nginx转发;不得绑定0.0.0.0。127.0.0.1:3000已登记给唯一的正式 DingTalk 服务。后续并行灰度须通过APPROVAL_HOST_PORT选择并登记另一个经ss -lntp验证为空闲的临时端口;临时实例不得继续占用正式端口。容器 Router 与共享外部 Docker 网络是后续全站入口迁移目标,不在本次 MCP 鉴权升级中单点切换。
OAuth/审批状态、事件驱动的待审批及最近 30 天已处理索引、幂等账本和最多 30 天审计日志位于持久卷
/app/data。同一应用 Client ID 同时只能运行一个生产 Stream 消费者。灰度容器不得在旧生产容器仍运行时开启
DINGTALK_APPROVAL_EVENTS_ENABLED。
详细安全与模块设计见:
This server cannot be deployed
Maintenance
Related MCP Connectors
MCP server connecting AI agents to 100+ apps (Gmail, Slack, Notion, GitHub) via one-click OAuth.
MCP server providing attendance data queries via the CloudTime API.
MCP server for stocksense-ai documentation, generated by doc2mcp.
Related MCP Servers
- FlicenseNot gradedqualityCmaintenanceA Model Control Protocol server that provides access to DingDing (Chinese workplace collaboration platform) API features, including retrieving access tokens, department lists, user information, and searching users by name.2-
- AlicenseNot gradedqualityDmaintenanceA Model Control Protocol server for integrating with DingTalk, enabling users to send messages, retrieve conversation/user information, and query calendar events through Claude.6MIT
- FlicenseCqualityDmaintenanceMCP server that enables AI agents to control all DingTalk features (messaging, calendar, tasks, approvals, etc.) via natural language using the DingTalk Workspace CLI.831-
- AlicenseNot gradedqualityCmaintenanceMCP server for Feishu/Lark API integration, enabling AI agents to send messages, manage groups, create and edit documents and spreadsheets, and search knowledge bases.MIT