send_draft
Send an existing draft email, moving it to the Sent folder and removing the draft status.
Instructions
Send an existing draft email. The draft must have recipients (to/cc/bcc) and a from address. After sending, the email is moved to the Sent folder and the draft keyword is removed.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| emailId | Yes | The ID of the draft email to send |
Implementation Reference
- src/index.ts:1222-1238 (handler)The MCP tool handler for 'send_draft'. Extracts emailId from args, validates it, and calls client.sendDraft(emailId). Returns success message with submission ID.
case 'send_draft': { const { emailId } = args as any; if (!emailId) { throw new McpError(ErrorCode.InvalidParams, 'emailId is required'); } const submissionId = await client.sendDraft(emailId); return { content: [ { type: 'text', text: `Draft sent successfully. Submission ID: ${submissionId}`, }, ], }; } - src/jmap-client.ts:615-713 (helper)The JMAP client method that executes the actual send_draft logic. Fetches the draft email, validates it's a draft, checks recipients and from address, resolves the identity, and submits the draft via EmailSubmission/set with onSuccessUpdateEmail to move to Sent folder and remove draft keyword.
async sendDraft(emailId: string): Promise<string> { const session = await this.getSession(); // Fetch the existing email to verify it's a draft const getRequest: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], methodCalls: [ ['Email/get', { accountId: session.accountId, ids: [emailId], properties: ['id', 'from', 'to', 'cc', 'bcc', 'replyTo', 'keywords'], }, 'getEmail'] ] }; const getResponse = await this.makeRequest(getRequest); const email = this.getListResult(getResponse, 0)[0]; if (!email) { throw new Error(`Email with ID '${emailId}' not found`); } if (!email.keywords?.$draft) { throw new Error('Cannot send a non-draft email'); } // Collect all recipients for the envelope const allRecipients: { email: string }[] = [ ...(email.to || []), ...(email.cc || []), ...(email.bcc || []), ]; if (allRecipients.length === 0) { throw new Error('Draft has no recipients'); } // Determine identity from the email's from field const fromEmail = email.from?.[0]?.email; if (!fromEmail) { throw new Error('Draft has no from address'); } const identities = await this.getIdentities(); const selectedIdentity = identities.find(id => matchesIdentity(id.email, fromEmail)); if (!selectedIdentity) { throw new Error('From address on draft does not match any sending identity'); } // Find the Sent mailbox const mailboxes = await this.getMailboxes(); const sentMailbox = this.findMailboxByRoleOrName(mailboxes, 'sent', 'sent'); if (!sentMailbox) { throw new Error('Could not find Sent mailbox'); } const sentMailboxIds: Record<string, boolean> = {}; sentMailboxIds[sentMailbox.id] = true; // Submit the draft const request: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail', 'urn:ietf:params:jmap:submission'], methodCalls: [ ['EmailSubmission/set', { accountId: session.accountId, create: { submission: { emailId, identityId: selectedIdentity.id, envelope: { mailFrom: { email: fromEmail }, rcptTo: allRecipients.map(addr => ({ email: addr.email })), } } }, onSuccessUpdateEmail: { '#submission': { mailboxIds: sentMailboxIds, 'keywords/$draft': null, 'keywords/$seen': true, } } }, 'submitDraft'] ] }; const response = await this.makeRequest(request); const submissionResult = this.getMethodResult(response, 0); if (submissionResult.notCreated?.submission) { const err = submissionResult.notCreated.submission; throw new Error(`Failed to submit draft: ${err.type}${err.description ? ' - ' + err.description : ''}`); } const submissionId = submissionResult.created?.submission?.id; if (!submissionId) { throw new Error('Draft submission returned no submission ID'); } return submissionId; } - src/index.ts:402-415 (registration)The tool registration and input schema for 'send_draft'. Defines the tool name, description, and input schema requiring emailId.
{ name: 'send_draft', description: 'Send an existing draft email. The draft must have recipients (to/cc/bcc) and a from address. After sending, the email is moved to the Sent folder and the draft keyword is removed.', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'The ID of the draft email to send', }, }, required: ['emailId'], }, },