download_attachment
Download email attachments from Fastmail by specifying email and attachment IDs, enabling access to files stored in messages through the MCP server.
Instructions
Download an email attachment
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
| emailId | Yes | ID of the email | |
| attachmentId | Yes | ID of the attachment |
Implementation Reference
- src/index.ts:987-1010 (handler)MCP tool handler for 'download_attachment': validates input parameters (emailId, attachmentId), initializes JmapClient, calls client.downloadAttachment(), returns the generated download URL as text content or a sanitized error.case 'download_attachment': { const { emailId, attachmentId } = args as any; if (!emailId || !attachmentId) { throw new McpError(ErrorCode.InvalidParams, 'emailId and attachmentId are required'); } const client = initializeClient(); try { const downloadUrl = await client.downloadAttachment(emailId, attachmentId); return { content: [ { type: 'text', text: `Download URL: ${downloadUrl}`, }, ], }; } catch (error) { // Sanitize error to avoid leaking attachment metadata throw new McpError( ErrorCode.InternalError, 'Attachment download failed. Verify emailId and attachmentId and try again.' ); } }
- src/index.ts:466-483 (schema)Tool definition including name, description, and input schema requiring emailId and attachmentId as strings.{ name: 'download_attachment', description: 'Download an email attachment', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email', }, attachmentId: { type: 'string', description: 'ID of the attachment', }, }, required: ['emailId', 'attachmentId'], }, },
- src/jmap-client.ts:445-497 (helper)JmapClient method implementing attachment download: fetches email via JMAP Email/get, locates attachment by ID or index, constructs signed download URL using session.downloadUrl template.async downloadAttachment(emailId: string, attachmentId: string): Promise<string> { const session = await this.getSession(); // Get the email with full attachment details const request: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], methodCalls: [ ['Email/get', { accountId: session.accountId, ids: [emailId], properties: ['attachments', 'bodyValues'], bodyProperties: ['partId', 'blobId', 'size', 'name', 'type'] }, 'getEmail'] ] }; const response = await this.makeRequest(request); const email = response.methodResponses[0][1].list[0]; if (!email) { throw new Error('Email not found'); } // Find attachment by partId or by index let attachment = email.attachments?.find((att: any) => att.partId === attachmentId || att.blobId === attachmentId ); // If not found, try by array index if (!attachment && !isNaN(parseInt(attachmentId))) { const index = parseInt(attachmentId); attachment = email.attachments?.[index]; } if (!attachment) { throw new Error('Attachment not found.'); } // Get the download URL from session const downloadUrl = session.downloadUrl; if (!downloadUrl) { throw new Error('Download capability not available in session'); } // Build download URL const url = downloadUrl .replace('{accountId}', session.accountId) .replace('{blobId}', attachment.blobId) .replace('{type}', encodeURIComponent(attachment.type || 'application/octet-stream')) .replace('{name}', encodeURIComponent(attachment.name || 'attachment')); return url; }
- src/index.ts:1141-1143 (registration)Tool listed in check_function_availability response for email functions.'get_email_attachments', 'download_attachment', 'advanced_search', 'get_thread', 'get_mailbox_stats', 'get_account_summary', 'bulk_mark_read', 'bulk_move', 'bulk_delete' ]