search_emails
Search Fastmail emails by subject or content. Specify a query, optionally limit results and sort oldest first, to quickly retrieve relevant messages.
Instructions
Search emails by subject or content
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| query | Yes | Search query string | |
| limit | No | Maximum number of results (default: 20) | |
| ascending | No | Sort oldest first instead of newest first (default: false) |
Implementation Reference
- src/index.ts:413-985 (registration)Tool registration and input schema for 'search_emails' in the ListTools handler (lines 413-435). Defines the tool name, description, and input schema with 'query' (required string), 'limit' (default 20), and 'ascending' (optional boolean).
{ name: 'search_emails', description: 'Search emails by subject or content', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query string', }, limit: { type: ['number', 'string'], description: 'Maximum number of results (default: 20)', default: 20, }, ascending: { type: 'boolean', description: 'Sort oldest first instead of newest first (default: false)', }, }, required: ['query'], }, }, { name: 'list_contacts', description: 'List contacts from the address book', inputSchema: { type: 'object', properties: { limit: { type: 'number', description: 'Maximum number of contacts to return (default: 50)', default: 50, }, }, }, }, { name: 'get_contact', description: 'Get a specific contact by ID', inputSchema: { type: 'object', properties: { contactId: { type: 'string', description: 'ID of the contact to retrieve', }, }, required: ['contactId'], }, }, { name: 'search_contacts', description: 'Search contacts by name or email', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Search query string', }, limit: { type: 'number', description: 'Maximum number of results (default: 20)', default: 20, }, }, required: ['query'], }, }, { name: 'list_calendars', description: 'List all calendars', inputSchema: { type: 'object', properties: {}, }, }, { name: 'list_calendar_events', description: 'List events from a calendar', inputSchema: { type: 'object', properties: { calendarId: { type: 'string', description: 'ID of the calendar (optional, defaults to all calendars)', }, startDate: { type: 'string', description: 'Filter events starting from this date (ISO 8601, e.g. 2026-03-23T00:00:00Z)', }, endDate: { type: 'string', description: 'Filter events ending before this date (ISO 8601, e.g. 2026-03-30T00:00:00Z)', }, limit: { type: 'number', description: 'Maximum number of events to return (default: 50)', default: 50, }, }, }, }, { name: 'get_calendar_event', description: 'Get a specific calendar event by ID', inputSchema: { type: 'object', properties: { eventId: { type: 'string', description: 'ID of the event to retrieve', }, }, required: ['eventId'], }, }, { name: 'create_calendar_event', description: 'Create a new calendar event', inputSchema: { type: 'object', properties: { calendarId: { type: 'string', description: 'ID of the calendar to create the event in', }, title: { type: 'string', description: 'Event title', }, description: { type: 'string', description: 'Event description (optional)', }, start: { type: 'string', description: 'Start time in ISO 8601 format', }, end: { type: 'string', description: 'End time in ISO 8601 format', }, location: { type: 'string', description: 'Event location (optional)', }, participants: { type: 'array', items: { type: 'object', properties: { email: { type: 'string' }, name: { type: 'string' } } }, description: 'Event participants (optional)', }, }, required: ['calendarId', 'title', 'start', 'end'], }, }, { name: 'list_identities', description: 'List sending identities (email addresses that can be used for sending)', inputSchema: { type: 'object', properties: {}, }, }, { name: 'get_recent_emails', description: 'Get the most recent emails from inbox (like top-ten)', inputSchema: { type: 'object', properties: { limit: { type: ['number', 'string'], description: 'Number of recent emails to retrieve (default: 10, max: 50)', default: 10, }, mailboxName: { type: 'string', description: 'Mailbox to search (default: inbox)', default: 'inbox', }, ascending: { type: 'boolean', description: 'Sort oldest first instead of newest first (default: false)', }, }, }, }, { name: 'mark_email_read', description: 'Mark an email as read or unread', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email to mark', }, read: { type: 'boolean', description: 'true to mark as read, false to mark as unread', default: true, }, }, required: ['emailId'], }, }, { name: 'pin_email', description: 'Pin or unpin an email', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email to pin/unpin', }, pinned: { type: 'boolean', description: 'true to pin, false to unpin', default: true, }, }, required: ['emailId'], }, }, { name: 'delete_email', description: 'Delete an email (move to trash)', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email to delete', }, }, required: ['emailId'], }, }, { name: 'move_email', description: 'Move an email to a different mailbox', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email to move', }, targetMailboxId: { type: 'string', description: 'ID of the target mailbox', }, }, required: ['emailId', 'targetMailboxId'], }, }, { name: 'add_labels', description: 'Add labels (mailboxes) to an email without removing existing ones', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email to add labels to', }, mailboxIds: { type: 'array', items: { type: 'string' }, description: 'Array of mailbox IDs to add as labels', }, }, required: ['emailId', 'mailboxIds'], }, }, { name: 'remove_labels', description: 'Remove specific labels (mailboxes) from an email', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email to remove labels from', }, mailboxIds: { type: 'array', items: { type: 'string' }, description: 'Array of mailbox IDs to remove as labels', }, }, required: ['emailId', 'mailboxIds'], }, }, { name: 'get_email_attachments', description: 'Get list of attachments for an email', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email', }, }, required: ['emailId'], }, }, { name: 'download_attachment', description: 'Download an email attachment. If savePath is provided, saves the file to disk and returns the file path and size. Otherwise returns a download URL.', inputSchema: { type: 'object', properties: { emailId: { type: 'string', description: 'ID of the email', }, attachmentId: { type: 'string', description: 'ID of the attachment', }, savePath: { type: 'string', description: `File path to save the attachment to. Paths are restricted to ${getDownloadDir() || '~/Downloads/fastmail-mcp/'} (configurable via FASTMAIL_DOWNLOAD_DIR). Path traversal outside this directory is rejected for security. Parent directories will be created automatically.`, }, }, required: ['emailId', 'attachmentId'], }, }, { name: 'advanced_search', description: 'Advanced email search with multiple criteria', inputSchema: { type: 'object', properties: { query: { type: 'string', description: 'Text to search for in subject/body', }, from: { type: 'string', description: 'Filter by sender email', }, to: { type: 'string', description: 'Filter by recipient email', }, subject: { type: 'string', description: 'Filter by subject', }, hasAttachment: { type: 'boolean', description: 'Filter emails with attachments', }, isUnread: { type: 'boolean', description: 'Filter unread emails', }, isPinned: { type: 'boolean', description: 'Filter pinned emails', }, mailboxId: { type: 'string', description: 'Search within specific mailbox', }, after: { type: 'string', description: 'Emails after this date (ISO 8601)', }, before: { type: 'string', description: 'Emails before this date (ISO 8601)', }, limit: { type: ['number', 'string'], description: 'Maximum results (default: 50)', default: 50, }, ascending: { type: 'boolean', description: 'Sort oldest first instead of newest first (default: false)', }, }, }, }, { name: 'get_thread', description: 'Get all emails in a conversation thread', inputSchema: { type: 'object', properties: { threadId: { type: 'string', description: 'ID of the thread/conversation', }, }, required: ['threadId'], }, }, { name: 'get_mailbox_stats', description: 'Get statistics for a mailbox (unread count, total emails, etc.)', inputSchema: { type: 'object', properties: { mailboxId: { type: 'string', description: 'ID of the mailbox (optional, defaults to all mailboxes)', }, }, }, }, { name: 'get_account_summary', description: 'Get overall account summary with statistics', inputSchema: { type: 'object', properties: {}, }, }, { name: 'bulk_mark_read', description: 'Mark multiple emails as read/unread', inputSchema: { type: 'object', properties: { emailIds: { type: 'array', items: { type: 'string' }, description: 'Array of email IDs to mark', }, read: { type: 'boolean', description: 'true to mark as read, false as unread', default: true, }, }, required: ['emailIds'], }, }, { name: 'bulk_pin', description: 'Pin or unpin multiple emails', inputSchema: { type: 'object', properties: { emailIds: { type: 'array', items: { type: 'string' }, description: 'Array of email IDs to pin/unpin', }, pinned: { type: 'boolean', description: 'true to pin, false to unpin', default: true, }, }, required: ['emailIds'], }, }, { name: 'bulk_move', description: 'Move multiple emails to a mailbox', inputSchema: { type: 'object', properties: { emailIds: { type: 'array', items: { type: 'string' }, description: 'Array of email IDs to move', }, targetMailboxId: { type: 'string', description: 'ID of target mailbox', }, }, required: ['emailIds', 'targetMailboxId'], }, }, { name: 'bulk_delete', description: 'Delete multiple emails (move to trash)', inputSchema: { type: 'object', properties: { emailIds: { type: 'array', items: { type: 'string' }, description: 'Array of email IDs to delete', }, }, required: ['emailIds'], }, }, { name: 'bulk_add_labels', description: 'Add labels to multiple emails simultaneously', inputSchema: { type: 'object', properties: { emailIds: { type: 'array', items: { type: 'string' }, description: 'Array of email IDs to add labels to', }, mailboxIds: { type: 'array', items: { type: 'string' }, description: 'Array of mailbox IDs to add as labels', }, }, required: ['emailIds', 'mailboxIds'], }, }, { name: 'bulk_remove_labels', description: 'Remove labels from multiple emails simultaneously', inputSchema: { type: 'object', properties: { emailIds: { type: 'array', items: { type: 'string' }, description: 'Array of email IDs to remove labels from', }, mailboxIds: { type: 'array', items: { type: 'string' }, description: 'Array of mailbox IDs to remove as labels', }, }, required: ['emailIds', 'mailboxIds'], }, }, { name: 'check_function_availability', description: 'Check which MCP functions are available based on account permissions', inputSchema: { type: 'object', properties: {}, }, }, { name: 'test_bulk_operations', description: 'Test bulk operations by finding recent emails and performing safe operations (mark read/unread)', inputSchema: { type: 'object', properties: { dryRun: { type: 'boolean', description: 'If true, only shows what would be done without making changes (default: true)', default: true, }, limit: { type: 'number', description: 'Number of emails to test with (default: 3, max: 10)', default: 3, }, }, }, }, ], - src/index.ts:1237-1252 (handler)Handler for 'search_emails' in the CallTool switch. Validates 'query' is provided, clamps limit to 1-100, delegates to JmapClient.searchEmails(), and returns the results as JSON.
case 'search_emails': { const { query, limit, ascending } = args as any; if (!query) { throw new McpError(ErrorCode.InvalidParams, 'query is required'); } const validLimit = Math.min(Math.max(Number(limit) || 20, 1), 100); const emails = await client.searchEmails(query, validLimit, !!ascending); return { content: [ { type: 'text', text: JSON.stringify(emails, null, 2), }, ], }; } - src/jmap-client.ts:1246-1268 (helper)Core JMAP implementation: 'searchEmails' method on JmapClient. Issues a JMAP Email/query with text filter and Email/get to retrieve matching email metadata (id, subject, from, to, receivedAt, preview, hasAttachment).
async searchEmails(query: string, limit: number = 20, ascending: boolean = false): Promise<any[]> { const session = await this.getSession(); const request: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], methodCalls: [ ['Email/query', { accountId: session.accountId, filter: { text: query }, sort: [{ property: 'receivedAt', isAscending: ascending }], limit }, 'query'], ['Email/get', { accountId: session.accountId, '#ids': { resultOf: 'query', name: 'Email/query', path: '/ids' }, properties: ['id', 'subject', 'from', 'to', 'replyTo', 'receivedAt', 'preview', 'hasAttachment'] }, 'emails'] ] }; const response = await this.makeRequest(request); return this.getListResult(response, 1); }