feat: expose readable submission choices

This commit is contained in:
2026-08-20 00:40:36 +00:00
parent 34c3c03a5f
commit ecb2f570e0
15 changed files with 454 additions and 114 deletions
@@ -5,10 +5,10 @@ Create only XFT approvals and these DingTalk templates: `出差申请单_北极
1. Call `approval_list_submission_templates` and use only its returned `templateId`.
2. Call `approval_get_submission_schema`. Compare every required field in `fields` and `inputContract` with information the user has already supplied. Never guess a missing value.
If `submissionCapability.status` is `client_only`, stop API construction and tell the user to start that template in its native client. All six current DingTalk candidates are client-only: five contain unsupported business suites, while live enterprise-API tests rejected the ordinary table form in `业务招待申请_北极大`. XFT whitelist templates use their formal create APIs and are verified against live status after submission.
For an XFT department, city, or expense type, call `approval_get_submission_options` with the same `templateId`; use only a returned `code`. Department results are restricted to the current signed-in user's member record. If the user has not provided an expense type, call it with `kind=expense_type`, present only the returned `label` values as user-readable choices, and map the selected label to its `code` internally. Never show, explain, or ask the user for `expenseNumber`, `expenseTypeNumber`, `expTypeCode`, or any other internal expense code. If the tool cannot return expense types, stop and offer retry or native-client completion instead of asking for a code.
Treat every XFT selection as a label-only user interaction. For a field with `optionSource`, call `approval_get_submission_options` with the same `templateId`, show only returned `label` values, and retain its `code` only inside the tool request. Department results are restricted to the current signed-in user's member record. For an inline `options` field show only `options[].label`; for an inline enum show only `enumLabels` values. Never show, explain, or ask the user for a field code, city code, organization code, employee number, archive number, numeric cabin value, `expenseNumber`, `expenseTypeNumber`, `expTypeCode`, or other internal ID. If `selectionCapability.status=client_only`, do not ask for the internal value or guess it: a required field stops MCP creation and requires the native client; an optional field may be skipped, or the user may choose native-client completion. If a required option source cannot return readable labels, stop and offer retry or native-client completion.
For DingTalk, do not ask for or fill applicant and top-level department controls marked `serverManaged`; keep `deptId=-1` unless the user selected a department that the gateway can verify belongs to them. The gateway derives the default department from the authenticated user's live directory record.
3. While any required field is missing or ambiguous, ask the user for the missing information and repeat the check after each answer. Prefer one concise question at a time, grouping fields only when they naturally belong together. Do not call `approval_prepare_submission` until all required fields are complete.
4. Explicitly show the remaining optional fields with short explanations and ask whether the user wants to add any. The user may choose to skip all optional fields; record that choice instead of silently omitting them.
4. Explicitly show the remaining user-visible, API-supported optional business fields with short explanations and ask whether the user wants to add any. Do not present fields marked `userVisible=false`, `serverManaged`, `internallyDerived`, or `selectionCapability.status=client_only` as ordinary questions. Briefly mention client-only optional capabilities only when they are relevant to the user's request. The user may choose to skip all optional fields; record that choice instead of silently omitting them.
5. Call `approval_prepare_submission` with `allowDuplicateInvoices=false`. It automatically checks invoice identifiers and expense signatures against the signed-in user's submitted DingTalk and XFT history for the most recent 30 days. If it reports another missing, invalid, unsupported, or selection-required field, return to the collection loop and ask until validation succeeds. Never work around validation.
6. If preparation returns `duplicate_confirmation_required` or `duplicate_check_unavailable`, no submit-capable `confirmationId` was created. Show the duplicate check's masked invoice evidence, matching source/business number/status, and coverage warning. Ask whether the user still wants to submit. Never infer consent from the original creation request. Only after the user explicitly accepts that exact risk, call prepare again with the same content and `allowDuplicateInvoices=true`. A rejected, revoked, or void prior bill is still a warning; a pending, approved, or unknown prior bill is blocking by default. If another active preparation reserved the same invoice, wait for or finish that preparation rather than overriding it.
7. Show the successful returned preview, including `duplicateCheck`, and obtain explicit final confirmation to create the approval. Do not treat answers to field questions, the choice to skip optional fields, or the duplicate-risk acknowledgement as this final submission confirmation.
@@ -18,6 +18,6 @@ If the user explicitly asks to undo a DingTalk or XFT approval just created thro
For an XFT submission or recall result that needs reconciliation, call `approval_get_submission_status` with the `businessId` returned by `approval_submit`. It is read-only and revalidates that the bill belongs to the signed-in user's initiated bills. Report `statusName` and the compact flow history. `completed` means the workflow finished immediately under the enterprise's approval rules (for example, no other effective approver); do not retry it. A fresh short-lived `revokeId` is returned only while the live bill status is pending approval; it still requires the user's explicit authorization before one recall attempt. Never treat status lookup alone as permission to repeat a write, and never retry `completed`, `upstream_state_mismatch`, or `outcome_uncertain`.
Never ask for or pass an applicant user ID, employee number, owner, or approval status. The gateway derives them from the authenticated user. For XFT custom fields, use the live `fieldNumber` and its declared `valueFormat`; people use roster-number arrays, departments use organization-code arrays, and tables use `sequence`/`columnValues`/`titleCode`. If a required field type is unsupported or XFT reports a missing employee profile or permission, explain the requirement and stop.
Never ask for or pass an applicant user ID, employee number, owner, or approval status. The gateway derives them from the authenticated user. For XFT custom fields, keep live `fieldNumber`, option codes, table `titleCode`, and serialization details internal to the tool call. Departments must be selected by returned department names; person fields currently require the native client because the MCP has no safe name lookup. If a required field type is unsupported or XFT reports a missing employee profile or permission, explain the business requirement without exposing internal identifiers and stop.
Local file paths and file bytes are not valid attachment field values. When the live Schema requires an upstream attachment number but the MCP has no attachment-upload tool, preserve any local draft and direct the user to complete that form in the native client. Never pass a local path, base64 content, or an invented attachment number to `approval_prepare_submission`.
@@ -66,7 +66,7 @@ To resume:
4. For each attachment, verify the regular file still exists and recompute SHA-256. If it is missing or its hash changed, show the attachment display name and require the user to select or confirm the file again. A changed modification time with the same hash may update metadata without changing the business content.
5. Continue the required and optional field collection from `create-approval.md`.
Treat a stored expense-type code as an internal form value. On resume, query `approval_get_submission_options(kind=expense_type)` again, verify that the stored code still maps to a live label, and use only that label in the conversation. If the mapping changed or disappeared, show the current labels and ask the user to choose again; never expose the stored code.
Treat every stored selection value as an internal form value. On resume, refresh its live `optionSource` or inline Schema, verify that the stored value still maps to a current label, and use only that label in the conversation. If the mapping changed or disappeared, show the current labels and ask the user to choose again; never expose stored city, department, expense, enum, archive, person, or other internal values. A newly `client_only` required field freezes API preparation and requires native-client completion.
Delete only the selected local file. Confirm deletion in business terms. Deleting a local draft never calls an upstream revoke or approval API.