Spaces:
Sleeping
Sleeping
| import type { FastMCP } from 'fastmcp'; | |
| import { UserError } from 'fastmcp'; | |
| import { z } from 'zod'; | |
| import { getGmailClient } from '../../clients.js'; | |
| import { prepareMimeRequest } from './helpers.js'; | |
| export function register(server: FastMCP) { | |
| server.addTool({ | |
| name: 'sendEmail', | |
| description: | |
| 'Sends a plain-text email from the authenticated Gmail account. Supports cc/bcc and optional threading by passing replyToMessageId (which copies threadId and sets In-Reply-To/References so the reply lands in the same thread).', | |
| parameters: z.strictObject({ | |
| to: z | |
| .union([z.string(), z.array(z.string()).min(1)]) | |
| .describe('Recipient email address, or an array of recipient email addresses.'), | |
| subject: z.string().describe('Email subject line.'), | |
| body: z.string().describe('Plain-text body of the email.'), | |
| cc: z.array(z.string()).optional().describe('Optional list of Cc recipients.'), | |
| bcc: z.array(z.string()).optional().describe('Optional list of Bcc recipients.'), | |
| replyToMessageId: z | |
| .string() | |
| .optional() | |
| .describe( | |
| 'Optional Gmail message ID to reply to. When set, the new email is threaded with the original and uses In-Reply-To/References headers.' | |
| ), | |
| }), | |
| execute: async (args, { log }) => { | |
| const gmail = await getGmailClient(); | |
| try { | |
| const { raw, threadId, toList } = await prepareMimeRequest(gmail, args); | |
| log.info( | |
| `Sending Gmail message to ${toList.join(', ')}${ | |
| args.replyToMessageId ? ` (reply to ${args.replyToMessageId})` : '' | |
| }` | |
| ); | |
| const send = await gmail.users.messages.send({ | |
| userId: 'me', | |
| requestBody: { | |
| raw, | |
| ...(threadId ? { threadId } : {}), | |
| }, | |
| }); | |
| return JSON.stringify( | |
| { | |
| success: true, | |
| id: send.data.id, | |
| threadId: send.data.threadId, | |
| labelIds: send.data.labelIds ?? [], | |
| to: toList, | |
| subject: args.subject, | |
| message: `Email sent to ${toList.join(', ')}.`, | |
| }, | |
| null, | |
| 2 | |
| ); | |
| } catch (error: any) { | |
| log.error(`Error sending Gmail message: ${error.message || error}`); | |
| if (error.code === 401) | |
| throw new UserError( | |
| 'Gmail authorization failed. Re-authorize to grant the gmail.modify scope.' | |
| ); | |
| if (error.code === 403) | |
| throw new UserError( | |
| 'Permission denied. The account does not have permission to send mail via this OAuth client.' | |
| ); | |
| if (error.code === 400) | |
| throw new UserError(`Gmail rejected the message: ${error.message || 'Bad request'}`); | |
| throw new UserError(`Failed to send email: ${error.message || 'Unknown error'}`); | |
| } | |
| }, | |
| }); | |
| } | |