// +---------------------------------------------------------------------- declare(strict_types=1); namespace modules\service\mcp\tools; use app\services\message\channel\EmailChannel; use modules\service\mcp\McpException; use crmeb\interfaces\McpToolInterface; use think\facade\Log; /** * MCP 工具:发送邮件 * * 通过系统邮件渠道(后台已启用的邮件驱动)向任意收件邮箱发送邮件。 * 供 AI 对话(确认后发送)与自动化任务(无人值守直发)共用。 */ class EmailSendTool implements McpToolInterface { /** 正文长度上限(字符),超出截断保护下游 SMTP */ private const MAX_CONTENT_LENGTH = 20000; /** * 工具名称 * @return string */ public function name(): string { return 'crmeb_email_send'; } /** * 交互声明:发送邮件为对外副作用动作,对话场景需用户确认后才真实执行; * 自动化任务无人值守场景由调用方按自主级别豁免确认 * @return string */ public function interaction(): string { return 'execute'; } /** * 工具描述 * @return string */ public function description(): string { return '通过系统邮件渠道向指定收件邮箱发送邮件。' . '需要传入收件邮箱、中文邮件主题与正文(正文按 HTML 渲染,必须传入邮件客户端兼容的内联样式 HTML,' . '不支持 Markdown,纯文本换行需用

/
标签)。' . '发送失败时会返回失败原因,请勿使用相同参数原样重试'; } /** * 工具中文名(管理后台展示) * @return string */ public function label(): string { return '发送邮件'; } /** * 工具分类(管理后台分组展示) * @return string */ public function category(): string { return '消息通知'; } /** * 入参 Schema * @return array */ public function inputSchema(): array { return [ 'type' => 'object', 'required' => ['to', 'subject', 'content'], 'properties' => [ 'to' => ['type' => 'string', 'description' => '收件邮箱'], 'subject' => ['type' => 'string', 'description' => '邮件主题,用中文概括邮件内容'], 'content' => ['type' => 'string', 'description' => '邮件正文,邮件客户端兼容的简单 HTML(内联样式),Markdown 源码会被原样显示禁止传入'], ], ]; } /** * 执行发送 * @param array $args 参数(to/subject/content) * @return array 发送结果 * @throws McpException 参数缺失或邮箱不合法 */ public function execute(array $args): array { $to = strtolower(trim((string)($args['to'] ?? ''))); $subject = trim((string)($args['subject'] ?? '')); $content = (string)($args['content'] ?? ''); if ($to === '' || !filter_var($to, FILTER_VALIDATE_EMAIL)) { throw new McpException('收件邮箱不能为空且必须是合法邮箱地址'); } if ($subject === '') { throw new McpException('邮件主题不能为空'); } if (trim($content) === '') { throw new McpException('邮件正文不能为空'); } if (mb_strlen($content) > self::MAX_CONTENT_LENGTH) { $content = mb_substr($content, 0, self::MAX_CONTENT_LENGTH) . "\n…(正文过长已截断)"; } try { $result = app()->make(EmailChannel::class)->sendEmail($to, $subject, $content); } catch (\Throwable $e) { Log::error('[MCP EmailSendTool] 发送异常', ['exception' => get_class($e)]); throw new McpException('邮件发送失败,请检查邮件驱动配置与服务状态'); } if (!$result) { throw new McpException('邮件发送失败:请检查后台邮件驱动是否已配置并启用'); } return [ 'success' => true, 'to' => $to, 'subject' => $subject, 'message' => '邮件发送成功', ]; } }