Documentation
¶
Overview ¶
Package mail provides declarative shortcuts for the DingTalk mail (邮箱) service: mailbox / message / draft / thread / folder / tag / user / attachment / template / contact / auto-reply / rule operations. Each shortcut maps 1:1 onto an MCP tool declared in internal/helpers/mail.go.
Index ¶
Constants ¶
This section is empty.
Variables ¶
View Source
var ContactList = shortcut.Shortcut{ Service: "mail", Command: "+contact-list", Product: "mail", Description: "列出指定邮箱的所有邮件联系人", Intent: "当你想查看某邮箱通讯录里有哪些联系人、或需要取得联系人 ID 以便更新或删除时使用;传入邮箱和每页数量,返回联系人列表,支持分页。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{ Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent", }, Contract: corecmd.ContractDecl{ Identity: contract.ToolIdentitySpec{ ProductID: "mail", Name: "shortcut_contact_list", CanonicalPath: "mail.shortcut_contact_list", CLIPath: "mail +contact-list", PrimaryCLIPath: "mail +contact-list", }, Description: "列出指定邮箱的所有邮件联系人", Parameters: []contract.ParamDecl{{Name: "limit", Property: "limit"}}, Interface: &contract.InterfaceSpec{ Mode: "composite", Availability: "available", Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.", }, Selection: contract.SelectionSpec{ AgentSummary: "列出指定邮箱的所有邮件联系人", UseWhen: []string{"当你想查看某邮箱通讯录里有哪些联系人、或需要取得联系人 ID 以便更新或删除时使用;传入邮箱和每页数量,返回联系人列表,支持分页。"}, AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"}, Examples: []string{"dws mail +contact-list --email user@company.com --limit 20"}, }, }, Flags: []shortcut.Flag{ {Name: "email", Type: shortcut.FlagString, Desc: "用户邮箱地址", Required: true}, {Name: "limit", Type: shortcut.FlagString, Desc: "每页返回数量,必须是 1-100 之间的整数", Required: true}, {Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标,取自响应中的 nextCursor"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintCustom, Flags: []string{"limit"}, Description: "--limit 必须在 1-100 之间"}}, Validate: func(rt *shortcut.RuntimeContext) error { return mailValidateStringPageSize(rt, "limit", true) }, Tips: []string{ `dws mail +contact-list --email user@company.com --limit 20`, }, Execute: func(rt *shortcut.RuntimeContext) error { size, err := mailStringPageSize(rt, "limit", true) if err != nil { return err } params := map[string]any{ "email": rt.Str("email"), "size": size, } if rt.Changed("cursor") { params["cursor"] = rt.Str("cursor") } data, err := rt.CallMCPData("mail", "list_user_mail_contacts", params) if err != nil { return err } contacts, err := mailProjectCollection(data, "mail/list_user_mail_contacts", "contacts", []string{"id"}, map[string][]string{ "id": {"id"}, "contactEmail": {"contactEmail", "email"}, "displayName": {"displayName", "name"}, }) if err != nil { return err } complete, next, err := mailPage(data, "mail/list_user_mail_contacts", "", rt.Str("cursor")) if err != nil { return err } return mailOutputPage(rt, "contacts", contacts, complete, next) }, }
ContactCreate 创建邮件联系人。 ContactList 列举邮件联系人。
View Source
var DraftCreate = shortcut.Shortcut{ OutputRollout: output.RolloutUnifiedActive, Service: "mail", Command: "+draft-create", Product: "mail", Description: "创建邮件草稿并按 messageId 读回验证", Intent: "撰写一封新邮件但暂不发送;取得稳定草稿 ID 后读取同一对象,并核对主题和正文。", Risk: shortcut.RiskWrite, Safety: mailWriteSafety("non_idempotent"), Contract: mailWriteContract("+draft-create", "创建邮件草稿并按 messageId 读回验证", "撰写一封新邮件但暂不发送;取得稳定草稿 ID 后读取同一对象,并核对主题和正文。", []contract.ParamDecl{{Name: "from", Property: "from"}, {Name: "to", Property: "toRecipients"}, {Name: "cc", Property: "ccRecipients"}, {Name: "body", Property: "body"}}, `dws mail +draft-create --from user@company.com --subject "草稿" --body "正文" --format json`), Flags: []shortcut.Flag{ {Name: "from", Type: shortcut.FlagString, Required: true, Desc: "发件邮箱"}, {Name: "to", Type: shortcut.FlagStringSlice, Desc: "收件邮箱,可多次指定或逗号分隔"}, {Name: "cc", Type: shortcut.FlagStringSlice, Desc: "抄送邮箱,可多次指定或逗号分隔"}, {Name: "subject", Type: shortcut.FlagString, Required: true, Desc: "草稿主题"}, {Name: "body", Type: shortcut.FlagString, Desc: "草稿正文"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintAtLeastOne, Flags: []string{"to", "cc", "subject", "body"}}}, Execute: func(rt *shortcut.RuntimeContext) error { params := map[string]any{"from": rt.Str("from"), "subject": rt.Str("subject")} if recipients := mailRecipients(rt.StrSlice("to")); len(recipients) > 0 { params["toRecipients"] = recipients } if recipients := mailRecipients(rt.StrSlice("cc")); len(recipients) > 0 { params["ccRecipients"] = recipients } if rt.Changed("body") { params["body"] = rt.Str("body") } if rt.DryRun() { return rt.Output(map[string]any{"value": map[string]any{"dryRun": true, "executed": false, "operation": "mail/create_draft"}}) } written, err := rt.CallMCPWriteDataStrict("mail", "create_draft", params) if err != nil { return err } receipt, err := mailDraftMessage(written, "mail/create_draft") if err != nil { return err } id := mailFirstString(receipt, "id") verified, err := mailReadMessage(rt, rt.Str("from"), id) if err != nil { return err } if mailFirstString(verified, "subject") != rt.Str("subject") { return mailResponseError("mail/create_draft", "verification_mismatch", "草稿读回主题与请求不一致") } if rt.Changed("body") && mailFirstString(verified, "markdownBody", "body") != rt.Str("body") { return mailResponseError("mail/create_draft", "verification_mismatch", "草稿读回正文与请求不一致") } if err := mailVerifySender(verified, "mail/create_draft", rt.Str("from")); err != nil { return err } if rt.Changed("to") { if err := mailVerifyAddresses(verified, "mail/create_draft", "toRecipients", mailRecipients(rt.StrSlice("to"))); err != nil { return err } } if rt.Changed("cc") { if err := mailVerifyAddresses(verified, "mail/create_draft", "ccRecipients", mailRecipients(rt.StrSlice("cc"))); err != nil { return err } } return rt.Output(map[string]any{"value": verified}) }, }
View Source
var DraftEdit = shortcut.Shortcut{ OutputRollout: output.RolloutUnifiedActive, Service: "mail", Command: "+draft-edit", Product: "mail", Description: "更新已有草稿并精确读回验证", Intent: "修改尚未发送的草稿;要求至少提供一个变化字段,并在写后按原 messageId 核对修改内容。", Risk: shortcut.RiskWrite, Safety: mailWriteSafety("unknown"), Contract: mailWriteContract("+draft-edit", "更新已有草稿并精确读回验证", "修改尚未发送的草稿;要求至少提供一个变化字段,并在写后按原 messageId 核对修改内容。", []contract.ParamDecl{{Name: "id", Property: "id"}, {Name: "from", Property: "from"}, {Name: "to", Property: "toRecipients"}, {Name: "cc", Property: "ccRecipients"}, {Name: "body", Property: "body"}}, `dws mail +draft-edit --from user@company.com --id <messageId> --subject "新主题" --format json`), Flags: []shortcut.Flag{ {Name: "from", Type: shortcut.FlagString, Required: true, Desc: "发件邮箱"}, {Name: "id", Type: shortcut.FlagString, Required: true, Desc: "草稿 messageId"}, {Name: "to", Type: shortcut.FlagStringSlice, Desc: "新收件邮箱"}, {Name: "cc", Type: shortcut.FlagStringSlice, Desc: "新抄送邮箱"}, {Name: "subject", Type: shortcut.FlagString, Desc: "新主题"}, {Name: "body", Type: shortcut.FlagString, Desc: "新正文"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintAtLeastOne, Flags: []string{"to", "cc", "subject", "body"}}}, Execute: func(rt *shortcut.RuntimeContext) error { if !rt.Changed("to") && !rt.Changed("cc") && !rt.Changed("subject") && !rt.Changed("body") { return apperrors.NewValidation("至少指定 --to、--cc、--subject、--body 之一") } params := map[string]any{"from": rt.Str("from"), "id": rt.Str("id")} if rt.Changed("to") { params["toRecipients"] = mailRecipients(rt.StrSlice("to")) } if rt.Changed("cc") { params["ccRecipients"] = mailRecipients(rt.StrSlice("cc")) } if rt.Changed("subject") { params["subject"] = rt.Str("subject") } if rt.Changed("body") { params["body"] = rt.Str("body") } if rt.DryRun() { return rt.Output(map[string]any{"value": map[string]any{"dryRun": true, "executed": false, "operation": "mail/update_draft"}}) } written, err := rt.CallMCPWriteDataStrict("mail", "update_draft", params) if err != nil { return err } receipt, err := mailDraftMessage(written, "mail/update_draft") if err != nil { return err } if mailFirstString(receipt, "id") != rt.Str("id") { return mailResponseError("mail/update_draft", "identity_mismatch", "更新回执的 messageId 与请求不一致") } verified, err := mailReadMessage(rt, rt.Str("from"), rt.Str("id")) if err != nil { return err } if rt.Changed("subject") && mailFirstString(verified, "subject") != rt.Str("subject") { return mailResponseError("mail/update_draft", "verification_mismatch", "草稿读回主题与请求不一致") } if rt.Changed("body") && mailFirstString(verified, "markdownBody", "body") != rt.Str("body") { return mailResponseError("mail/update_draft", "verification_mismatch", "草稿读回正文与请求不一致") } if err := mailVerifySender(verified, "mail/update_draft", rt.Str("from")); err != nil { return err } if rt.Changed("to") { if err := mailVerifyAddresses(verified, "mail/update_draft", "toRecipients", mailRecipients(rt.StrSlice("to"))); err != nil { return err } } if rt.Changed("cc") { if err := mailVerifyAddresses(verified, "mail/update_draft", "ccRecipients", mailRecipients(rt.StrSlice("cc"))); err != nil { return err } } return rt.Output(map[string]any{"value": verified}) }, }
View Source
var FolderList = shortcut.Shortcut{ Service: "mail", Command: "+folder-list", Product: "mail", Description: "列出顶层文件夹或指定父文件夹下的子文件夹", Intent: "当你需要了解某个邮箱有哪些文件夹、或要取得文件夹 ID 以便移动邮件、按文件夹列信/建规则时使用;传入邮箱(可选父文件夹 ID 查子级),返回文件夹列表及其 ID。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{ Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent", }, Contract: corecmd.ContractDecl{ Identity: contract.ToolIdentitySpec{ ProductID: "mail", Name: "shortcut_folder_list", CanonicalPath: "mail.shortcut_folder_list", CLIPath: "mail +folder-list", PrimaryCLIPath: "mail +folder-list", }, Description: "列出顶层文件夹或指定父文件夹下的子文件夹", Parameters: []contract.ParamDecl{{Name: "folder", Property: "folder"}}, Interface: &contract.InterfaceSpec{ Mode: "composite", Availability: "available", Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.", }, Selection: contract.SelectionSpec{ AgentSummary: "列出顶层文件夹或指定父文件夹下的子文件夹", UseWhen: []string{"当你需要了解某个邮箱有哪些文件夹、或要取得文件夹 ID 以便移动邮件、按文件夹列信/建规则时使用;传入邮箱(可选父文件夹 ID 查子级),返回文件夹列表及其 ID。"}, AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"}, Examples: []string{"dws mail +folder-list --email user@company.com"}, }, }, Flags: []shortcut.Flag{ {Name: "email", Type: shortcut.FlagString, Desc: "邮件所属邮箱地址,不能为空", Required: true}, {Name: "folder", Type: shortcut.FlagString, Desc: "父文件夹 ID,不传则返回顶层文件夹"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintCustom, Flags: []string{"email"}, Description: "不能为空"}}, Validate: func(rt *shortcut.RuntimeContext) error { return mailValidateRequiredText(rt, "email") }, Tips: []string{ `dws mail +folder-list --email user@company.com`, }, Execute: func(rt *shortcut.RuntimeContext) error { params := map[string]any{ "email": rt.Str("email"), } if rt.Changed("folder") { params["folderId"] = rt.Str("folder") } data, err := rt.CallMCPData("mail", "list_folders", params) if err != nil { return err } folders, err := mailProjectCollection(data, "mail/list_folders", "folders", []string{"id"}, map[string][]string{ "id": {"id"}, "name": {"displayName"}, "parentId": {"parentFolderId"}, }) if err != nil { return err } return rt.Output(mailBusinessCollectionPayload("folders", folders)) }, }
FolderList 列举邮件文件夹。
View Source
var Message = shortcut.Shortcut{ OutputRollout: output.RolloutUnifiedActive, Service: "mail", Command: "+message", Product: "mail", Description: "读取一封邮件的完整正文与附件元数据", Intent: "已知单个 messageId,需要读取完整正文和附件元数据时使用;自动解析当前邮箱,并要求返回邮件 ID 与请求精确一致。", Risk: shortcut.RiskRead, Safety: mailReadSafety(), Contract: mailReadContract("+message", "读取一封邮件的完整正文与附件元数据", "已知单个 messageId,需要读取完整正文和附件元数据时使用;自动解析当前邮箱,并要求返回邮件 ID 与请求精确一致。", mailObjectResult("身份匹配的单封邮件详情"), []contract.ParamDecl{{Name: "id", Property: "messageId"}}, `dws mail +message --id <messageId> --format json`), Flags: []shortcut.Flag{ {Name: "id", Type: shortcut.FlagString, Required: true, Desc: "邮件 messageId,不能为空"}, {Name: "email", Type: shortcut.FlagString, Desc: "邮箱地址;不传时自动取当前身份首个邮箱"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintCustom, Flags: []string{"id"}, Description: "不能为空"}}, Validate: func(rt *shortcut.RuntimeContext) error { return mailValidateRequiredText(rt, "id") }, Execute: func(rt *shortcut.RuntimeContext) error { email, err := mailResolveMailbox(rt) if err != nil { return err } message, err := mailReadMessage(rt, email, rt.Str("id")) if err != nil { return err } return rt.Output(map[string]any{"value": message}) }, }
View Source
var Messages = shortcut.Shortcut{ OutputRollout: output.RolloutUnifiedActive, Service: "mail", Command: "+messages", Product: "mail", Description: "按请求顺序读取多封邮件并逐封验证身份", Intent: "需要一次读取多个 messageId 时使用;按输入顺序逐封读取,任何缺失、错型或身份不匹配都会使整次任务失败。", Risk: shortcut.RiskRead, Safety: mailReadSafety(), Contract: mailReadContract("+messages", "按请求顺序读取多封邮件并逐封验证身份", "需要一次读取多个 messageId 时使用;按输入顺序逐封读取,任何缺失、错型或身份不匹配都会使整次任务失败。", mailCollectionResult("messages", "身份匹配且保持请求顺序的邮件详情"), []contract.ParamDecl{{Name: "ids", Property: "messageIds"}}, `dws mail +messages --ids <id1>,<id2> --format json`), Flags: []shortcut.Flag{ {Name: "ids", Type: shortcut.FlagStringSlice, Required: true, Desc: "邮件 messageId 列表,1-100 个且每项不能为空"}, {Name: "email", Type: shortcut.FlagString, Desc: "邮箱地址;不传时自动取当前身份首个邮箱"}, }, Constraints: []shortcut.Constraint{{ Kind: shortcut.ConstraintCustom, Flags: []string{"ids"}, Description: "1-100 个且不能为空", }}, Validate: func(rt *shortcut.RuntimeContext) error { return mailValidateMessageIDs(rt.StrSlice("ids")) }, Execute: func(rt *shortcut.RuntimeContext) error { ids := rt.StrSlice("ids") email, err := mailResolveMailbox(rt) if err != nil { return err } messages := make([]map[string]any, 0, len(ids)) for index, id := range ids { id = strings.TrimSpace(id) if id == "" { return apperrors.NewValidation(fmt.Sprintf("--ids 第 %d 项为空", index)) } message, err := mailReadMessage(rt, email, id) if err != nil { return err } messages = append(messages, message) } return rt.Output(mailBusinessCollectionPayload("messages", messages)) }, }
View Source
var TagList = shortcut.Shortcut{ Service: "mail", Command: "+tag-list", Product: "mail", Description: "列出指定邮箱下的所有邮件标签", Intent: "当你要查看邮箱里有哪些标签、或需要取得标签 ID 以便给邮件/会话加标签时使用;传入邮箱地址,返回全部邮件标签及其 ID。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{ Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent", }, Contract: corecmd.ContractDecl{ Identity: contract.ToolIdentitySpec{ ProductID: "mail", Name: "shortcut_tag_list", CanonicalPath: "mail.shortcut_tag_list", CLIPath: "mail +tag-list", PrimaryCLIPath: "mail +tag-list", }, Description: "列出指定邮箱下的所有邮件标签", Interface: &contract.InterfaceSpec{ Mode: "composite", Availability: "available", Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.", }, Selection: contract.SelectionSpec{ AgentSummary: "列出指定邮箱下的所有邮件标签", UseWhen: []string{"当你要查看邮箱里有哪些标签、或需要取得标签 ID 以便给邮件/会话加标签时使用;传入邮箱地址,返回全部邮件标签及其 ID。"}, AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"}, Examples: []string{"dws mail +tag-list --email user@company.com"}, }, }, Flags: []shortcut.Flag{ {Name: "email", Type: shortcut.FlagString, Desc: "用户的邮箱地址", Required: true}, }, Tips: []string{ `dws mail +tag-list --email user@company.com`, }, Execute: func(rt *shortcut.RuntimeContext) error { data, err := rt.CallMCPData("mail", "list_tags", map[string]any{ "email": rt.Str("email"), }) if err != nil { return err } tags, err := mailProjectCollection(data, "mail/list_tags", "tags", []string{"id"}, map[string][]string{ "id": {"id"}, "name": {"name"}, "parentId": {"parentId"}, }) if err != nil { return err } return rt.Output(mailBusinessCollectionPayload("tags", tags)) }, }
TagList 列举邮件标签。
View Source
var TemplateCreate = shortcut.Shortcut{ OutputRollout: output.RolloutUnifiedActive, Service: "mail", Command: "+template-create", Product: "mail", Description: "创建个人邮件模板并按模板 ID 读回", Intent: "保存可复用邮件主题和正文;创建后按稳定模板 ID 读取并核对名称、主题、正文和服务端回显的草稿状态。", Risk: shortcut.RiskWrite, Safety: mailWriteSafety("non_idempotent"), Contract: mailWriteContract("+template-create", "创建个人邮件模板并按模板 ID 读回", "保存可复用邮件主题和正文;创建后按稳定模板 ID 读取并核对名称、主题、正文和服务端回显的草稿状态。", []contract.ParamDecl{{Name: "email", Property: "email"}, {Name: "body", Property: "body"}, {Name: "is-draft", Property: "isDraft", InterfaceType: "boolean"}}, `dws mail +template-create --email user@company.com --name "模板" --subject "主题" --body "正文" --format json`), Flags: []shortcut.Flag{ {Name: "email", Type: shortcut.FlagString, Required: true, Desc: "模板所属邮箱"}, {Name: "name", Type: shortcut.FlagString, Required: true, Desc: "模板名称"}, {Name: "subject", Type: shortcut.FlagString, Required: true, Desc: "模板主题"}, {Name: "body", Type: shortcut.FlagString, Required: true, Desc: "模板正文"}, {Name: "is-draft", Type: shortcut.FlagBool, Desc: "是否创建可编辑的草稿模板;默认 false", Aliases: []string{"draft"}}, }, Execute: func(rt *shortcut.RuntimeContext) error { isDraft := mailRequestedTemplateDraftMode(rt) draftModeExplicit := rt.Changed("is-draft") || rt.Changed("draft") params := map[string]any{"email": rt.Str("email"), "name": rt.Str("name"), "subject": rt.Str("subject"), "body": rt.Str("body"), "isDraft": isDraft} if rt.DryRun() { return rt.Output(map[string]any{"value": map[string]any{"dryRun": true, "executed": false, "operation": "mail/create_user_message_template", "isDraft": isDraft}}) } written, err := rt.CallMCPWriteDataStrict("mail", "create_user_message_template", params) if err != nil { return err } if err := mailRequireSuccess(written, "mail/create_user_message_template"); err != nil { return err } id := mailFirstString(written, "id") if id == "" { return mailResponseError("mail/create_user_message_template", "missing_stable_id", "创建模板响应缺少稳定 ID") } verified, err := mailReadTemplate(rt, rt.Str("email"), id) if err != nil { return err } if mailFirstString(verified, "name") != rt.Str("name") || mailTemplateSubject(verified) != rt.Str("subject") || mailTemplateBody(verified) != rt.Str("body") { return mailResponseError("mail/create_user_message_template", "verification_mismatch", "模板读回内容与请求不一致") } if draft, present, err := mailTemplateDraftMode(verified); err != nil { return err } else if draftModeExplicit && !present { return mailResponseError("mail/create_user_message_template", "missing_verification_field", "显式草稿模式请求缺少 isDraft 读回证据") } else if present && draft != isDraft { return mailResponseError("mail/create_user_message_template", "verification_mismatch", "模板读回 isDraft 与请求不一致") } return rt.Output(map[string]any{"value": verified}) }, }
View Source
var TemplateList = shortcut.Shortcut{ Service: "mail", Command: "+template-list", Product: "mail", Description: "列出指定邮箱的所有邮件模板", Intent: "当你想查看某个邮箱下已有哪些邮件模板、或需要取得模板 ID 以便查看详情或更新时使用;传入邮箱和每页数量,返回模板列表,支持分页。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{ Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent", }, Contract: corecmd.ContractDecl{ Identity: contract.ToolIdentitySpec{ ProductID: "mail", Name: "shortcut_template_list", CanonicalPath: "mail.shortcut_template_list", CLIPath: "mail +template-list", PrimaryCLIPath: "mail +template-list", }, Description: "列出指定邮箱的所有邮件模板", Parameters: []contract.ParamDecl{{Name: "limit", Property: "limit"}}, Interface: &contract.InterfaceSpec{ Mode: "composite", Availability: "available", Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.", }, Selection: contract.SelectionSpec{ AgentSummary: "列出指定邮箱的所有邮件模板", UseWhen: []string{"当你想查看某个邮箱下已有哪些邮件模板、或需要取得模板 ID 以便查看详情或更新时使用;传入邮箱和每页数量,返回模板列表,支持分页。"}, AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"}, Examples: []string{"dws mail +template-list --email user@company.com --limit 20"}, }, }, Flags: []shortcut.Flag{ {Name: "email", Type: shortcut.FlagString, Desc: "用户邮箱地址", Required: true}, {Name: "limit", Type: shortcut.FlagString, Desc: "每页返回数量,必须是 1-100 之间的整数", Required: true}, {Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标,取自响应中的 nextCursor"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintCustom, Flags: []string{"limit"}, Description: "--limit 必须在 1-100 之间"}}, Validate: func(rt *shortcut.RuntimeContext) error { return mailValidateStringPageSize(rt, "limit", true) }, Tips: []string{ `dws mail +template-list --email user@company.com --limit 20`, }, Execute: func(rt *shortcut.RuntimeContext) error { size, err := mailStringPageSize(rt, "limit", true) if err != nil { return err } params := map[string]any{ "email": rt.Str("email"), "size": size, } if rt.Changed("cursor") { params["cursor"] = rt.Str("cursor") } data, err := rt.CallMCPData("mail", "list_user_message_templates", params) if err != nil { return err } templates, err := mailProjectCollection(data, "mail/list_user_message_templates", "templates", []string{"id"}, map[string][]string{ "id": {"id"}, "name": {"name"}, "subject": {"subject"}, }) if err != nil { return err } complete, next, err := mailPage(data, "mail/list_user_message_templates", "", rt.Str("cursor")) if err != nil { return err } return mailOutputPage(rt, "templates", templates, complete, next) }, }
TemplateCreate 创建邮件模板。 TemplateList 列举邮件模板。
View Source
var TemplateUpdate = shortcut.Shortcut{ OutputRollout: output.RolloutUnifiedActive, Service: "mail", Command: "+template-update", Product: "mail", Description: "更新草稿模板并按原 ID 读回验证", Intent: "修改可编辑的草稿模板;写响应成功后按原模板 ID 读取并核对所有请求变更。", Risk: shortcut.RiskWrite, Safety: mailWriteSafety("unknown"), Contract: mailWriteContract("+template-update", "更新草稿模板并按原 ID 读回验证", "修改可编辑的草稿模板;写响应成功后按原模板 ID 读取并核对所有请求变更。", []contract.ParamDecl{{Name: "email", Property: "email"}, {Name: "id", Property: "id"}, {Name: "body", Property: "body"}}, `dws mail +template-update --email user@company.com --id <templateId> --subject "新主题" --format json`), Flags: []shortcut.Flag{ {Name: "email", Type: shortcut.FlagString, Required: true, Desc: "模板所属邮箱"}, {Name: "id", Type: shortcut.FlagString, Required: true, Desc: "模板 ID;仅草稿模板可更新"}, {Name: "name", Type: shortcut.FlagString, Desc: "新模板名称"}, {Name: "subject", Type: shortcut.FlagString, Desc: "新主题"}, {Name: "body", Type: shortcut.FlagString, Desc: "新正文"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintAtLeastOne, Flags: []string{"name", "subject", "body"}}}, Execute: func(rt *shortcut.RuntimeContext) error { if !rt.Changed("name") && !rt.Changed("subject") && !rt.Changed("body") { return apperrors.NewValidation("至少指定 --name、--subject、--body 之一") } params := map[string]any{"email": rt.Str("email"), "id": rt.Str("id")} for _, flag := range []string{"name", "subject", "body"} { if rt.Changed(flag) { params[flag] = rt.Str(flag) } } if rt.DryRun() { return rt.Output(map[string]any{"value": map[string]any{"dryRun": true, "executed": false, "operation": "mail/update_user_message_template"}}) } written, err := rt.CallMCPWriteDataStrict("mail", "update_user_message_template", params) if err != nil { return err } if err := mailRequireSuccess(written, "mail/update_user_message_template"); err != nil { return err } verified, err := mailReadTemplate(rt, rt.Str("email"), rt.Str("id")) if err != nil { return err } if rt.Changed("name") && mailFirstString(verified, "name") != rt.Str("name") { return mailResponseError("mail/update_user_message_template", "verification_mismatch", "模板读回名称与请求不一致") } if rt.Changed("subject") && mailTemplateSubject(verified) != rt.Str("subject") { return mailResponseError("mail/update_user_message_template", "verification_mismatch", "模板读回主题与请求不一致") } if rt.Changed("body") && mailTemplateBody(verified) != rt.Str("body") { return mailResponseError("mail/update_user_message_template", "verification_mismatch", "模板读回正文与请求不一致") } return rt.Output(map[string]any{"value": verified}) }, }
View Source
var Thread = shortcut.Shortcut{ OutputRollout: output.RolloutUnifiedActive, Service: "mail", Command: "+thread", Product: "mail", Description: "读取完整邮件会话并精确验证 conversationId", Intent: "已知一个 conversationId,需要查看同一主题的会话上下文时使用;自动解析邮箱并拒绝空对象或错会话。", Risk: shortcut.RiskRead, Safety: mailReadSafety(), Contract: mailReadContract("+thread", "读取完整邮件会话并精确验证 conversationId", "已知一个 conversationId,需要查看同一主题的会话上下文时使用;自动解析邮箱并拒绝空对象或错会话。", mailObjectResult("身份匹配的邮件会话详情"), []contract.ParamDecl{{Name: "id", Property: "conversationId"}}, `dws mail +thread --id <conversationId> --format json`), Flags: []shortcut.Flag{ {Name: "id", Type: shortcut.FlagString, Required: true, Desc: "邮件会话 conversationId,不能为空"}, {Name: "email", Type: shortcut.FlagString, Desc: "邮箱地址;不传时自动取当前身份首个邮箱"}, }, Constraints: []shortcut.Constraint{{Kind: shortcut.ConstraintCustom, Flags: []string{"id"}, Description: "不能为空"}}, Validate: func(rt *shortcut.RuntimeContext) error { return mailValidateRequiredText(rt, "id") }, Execute: func(rt *shortcut.RuntimeContext) error { email, err := mailResolveMailbox(rt) if err != nil { return err } data, err := rt.CallMCPData("mail", "get_thread", map[string]any{"email": email, "conversationId": rt.Str("id")}) if err != nil { return err } conversation, err := mailRequireObject(data, "mail/get_thread", "conversation") if err != nil { return err } if err := mailRequireIdentity(conversation, "mail/get_thread", rt.Str("id"), "id"); err != nil { return err } return rt.Output(map[string]any{"value": conversation}) }, }
View Source
var ThreadList = shortcut.Shortcut{ Service: "mail", Command: "+thread-list", Product: "mail", Description: "列出指定邮箱文件夹下的邮件会话(thread)", Intent: "当你想按会话(同一往来主题的邮件串)而非单封邮件来浏览某个文件夹时使用;传入邮箱和文件夹 ID,可按时间范围和升降序筛选,返回会话列表及其 conversationId,供 +thread 查看详情。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{ Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent", }, Contract: corecmd.ContractDecl{ Identity: contract.ToolIdentitySpec{ ProductID: "mail", Name: "shortcut_thread_list", CanonicalPath: "mail.shortcut_thread_list", CLIPath: "mail +thread-list", PrimaryCLIPath: "mail +thread-list", }, Description: "列出指定邮箱文件夹下的邮件会话(thread)", Parameters: []contract.ParamDecl{ {Name: "folder", Property: "folder"}, {Name: "limit", Property: "limit"}, {Name: "start", Property: "start"}, {Name: "end", Property: "end"}, {Name: "ascending", Property: "ascending"}, }, Interface: &contract.InterfaceSpec{ Mode: "composite", Availability: "available", Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.", }, Selection: contract.SelectionSpec{ AgentSummary: "列出指定邮箱文件夹下的邮件会话(thread)", UseWhen: []string{"当你想按会话(同一往来主题的邮件串)而非单封邮件来浏览某个文件夹时使用;传入邮箱和文件夹 ID,可按时间范围和升降序筛选,返回会话列表及其 conversationId,供 +thread 查看详情。"}, AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"}, Examples: []string{"dws mail +thread-list --email user@company.com --folder 104 --limit 20"}, }, }, Flags: []shortcut.Flag{ {Name: "email", Type: shortcut.FlagString, Desc: "会话所属邮箱地址", Required: true}, {Name: "folder", Type: shortcut.FlagString, Desc: "邮件文件夹 ID(不是文件夹名称)", Required: true}, {Name: "limit", Type: shortcut.FlagInt, Default: "20", Desc: "本次列出的会话数,最大 100"}, {Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标,首次请求可不传"}, {Name: "start", Type: shortcut.FlagString, Desc: "开始 UTC 时间,如 2024-01-01T00:00:00Z"}, {Name: "end", Type: shortcut.FlagString, Desc: "结束 UTC 时间,如 2024-12-31T23:59:59Z"}, {Name: "ascending", Type: shortcut.FlagBool, Desc: "是否按时间升序"}, }, Constraints: []shortcut.Constraint{ {Kind: shortcut.ConstraintCustom, Flags: []string{"limit"}, Description: "--limit 必须在 1-100 之间"}, {Kind: shortcut.ConstraintCustom, Flags: []string{"start", "end"}, Description: "--start/--end 必须是 UTC RFC3339 时间,且 end 不能早于 start"}, }, Validate: func(rt *shortcut.RuntimeContext) error { return mailValidateThreadList(rt) }, Tips: []string{ `dws mail +thread-list --email user@company.com --folder 104 --limit 20`, }, Execute: func(rt *shortcut.RuntimeContext) error { params := map[string]any{ "email": rt.Str("email"), "folderId": rt.Str("folder"), "size": rt.Int("limit"), } if rt.Changed("cursor") { params["cursor"] = rt.Str("cursor") } if rt.Changed("start") { params["startTime"] = rt.Str("start") } if rt.Changed("end") { params["endTime"] = rt.Str("end") } if rt.Changed("ascending") { params["isAscending"] = rt.Bool("ascending") } data, err := rt.CallMCPData("mail", "list_mailbox_threads", params) if err != nil { return err } threads, err := mailProjectCollection(data, "mail/list_mailbox_threads", "result.conversations", []string{"id"}, map[string][]string{ "conversationId": {"id"}, "subject": {"subject"}, "lastUpdated": {"lastModifiedDateTime"}, "isRead": {"isRead"}, }) if err != nil { return err } complete, next, err := mailPage(data, "mail/list_mailbox_threads", "result", rt.Str("cursor")) if err != nil { return err } return mailOutputPage(rt, "threads", threads, complete, next) }, }
ThreadList 列出指定邮箱文件夹下的邮件会话。
View Source
var UserSearch = shortcut.Shortcut{ Service: "mail", Command: "+user-search", Product: "mail", Description: "按关键词或工号搜索邮箱用户(仅企业邮箱)", Intent: "当你只知道同事的姓名或工号、需要查出其企业邮箱地址以便发信或添加联系人时使用;提供关键词或工号(至少其一),返回匹配的企业邮箱用户列表。", Risk: shortcut.RiskRead, Safety: contract.SafetySpec{ Effect: "read", Risk: "low", Confirmation: "not_required", Idempotency: "idempotent", }, Contract: corecmd.ContractDecl{ Identity: contract.ToolIdentitySpec{ ProductID: "mail", Name: "shortcut_user_search", CanonicalPath: "mail.shortcut_user_search", CLIPath: "mail +user-search", PrimaryCLIPath: "mail +user-search", }, Description: "按关键词或工号搜索邮箱用户(仅企业邮箱)", Parameters: []contract.ParamDecl{ {Name: "employee-no", Property: "employeeNo"}, {Name: "limit", Property: "limit"}, }, Interface: &contract.InterfaceSpec{ Mode: "composite", Availability: "available", Reason: "Reviewed built-in shortcut adapter: the executable CLI owns validation, optional multi-step orchestration, output projection, and confirmation; the complete command contract is not represented by one pinned MCP interface_ref.", }, Selection: contract.SelectionSpec{ AgentSummary: "按关键词或工号搜索邮箱用户(仅企业邮箱)", UseWhen: []string{"当你只知道同事的姓名或工号、需要查出其企业邮箱地址以便发信或添加联系人时使用;提供关键词或工号(至少其一),返回匹配的企业邮箱用户列表。"}, AvoidWhen: []string{"需要该 Shortcut 未公开的底层参数、原始响应或不同执行语义时,改用对应原子命令"}, Examples: []string{ "dws mail +user-search --keyword \"张三\"", "dws mail +user-search --email user@company.com --employee-no \"E123456\"", }, }, }, Flags: []shortcut.Flag{ {Name: "keyword", Type: shortcut.FlagString, Desc: "搜索关键词;显式提供时不能为空(未提供 --employee-no 时为必填)"}, {Name: "employee-no", Type: shortcut.FlagString, Desc: "按工号精确搜索;显式提供时不能为空"}, {Name: "email", Type: shortcut.FlagString, Desc: "搜索目标邮箱地址"}, {Name: "cursor", Type: shortcut.FlagString, Desc: "分页游标,取自响应中的 nextCursor"}, {Name: "limit", Type: shortcut.FlagString, Desc: "每页返回数量,必须是 1-100 之间的整数"}, }, Constraints: []shortcut.Constraint{ {Kind: shortcut.ConstraintAtLeastOne, Flags: []string{"keyword", "employee-no"}}, {Kind: shortcut.ConstraintCustom, Flags: []string{"keyword", "employee-no"}, Description: "不能为空"}, {Kind: shortcut.ConstraintCustom, Flags: []string{"limit"}, Description: "1-100"}, }, Validate: func(rt *shortcut.RuntimeContext) error { if err := mailValidateStringPageSize(rt, "limit", false); err != nil { return err } for _, name := range []string{"keyword", "employee-no"} { if rt.Changed(name) && strings.TrimSpace(rt.Str(name)) == "" { return apperrors.NewValidation("--" + name + " 显式提供时不能为空") } } if strings.TrimSpace(rt.Str("keyword")) == "" && strings.TrimSpace(rt.Str("employee-no")) == "" { return apperrors.NewValidation("--keyword 和 --employee-no 至少需要一个非空值") } return nil }, Tips: []string{ `dws mail +user-search --keyword "张三"`, `dws mail +user-search --email user@company.com --employee-no "E123456"`, }, Execute: func(rt *shortcut.RuntimeContext) error { size, err := mailStringPageSize(rt, "limit", false) if err != nil { return err } params := map[string]any{} if rt.Str("keyword") != "" { params["keyword"] = rt.Str("keyword") } if rt.Str("employee-no") != "" { params["employeeNo"] = rt.Str("employee-no") } if rt.Changed("email") { params["email"] = rt.Str("email") } if rt.Changed("cursor") { params["cursor"] = rt.Str("cursor") } if rt.Changed("limit") { params["size"] = size } data, err := rt.CallMCPData("mail", "search_mail_users", params) if err != nil { return err } users, err := mailProjectCollection(data, "mail/search_mail_users", "users", []string{"id", "email"}, map[string][]string{ "name": {"name"}, "email": {"email"}, "employeeNo": {"employeeNo"}, "userId": {"id"}, }) if err != nil { return err } complete, next, err := mailPage(data, "mail/search_mail_users", "", rt.Str("cursor")) if err != nil { return err } return mailOutputPage(rt, "users", users, complete, next) }, }
UserSearch 按关键词或工号搜索邮箱用户。
Functions ¶
This section is empty.
Types ¶
This section is empty.
Click to show internal directories.
Click to hide internal directories.