bulk_move
Move multiple emails to a specified mailbox in a single API call. Provide an array of email IDs and the target mailbox ID to batch file messages efficiently.
Instructions
Move multiple emails to a mailbox in one call. Use when the user wants to file a set of known email IDs into the same destination mailbox. Do not use to apply labels while preserving the current mailbox set; use bulk_add_labels.
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| emailIds | Yes | Array of email IDs to move | |
| targetMailboxId | Yes | ID of target mailbox |
Implementation Reference
- src/jmap-client.ts:1168-1214 (handler)Core handler: Fetches current mailboxIds for each email, builds JMAP patches that remove all existing mailbox associations and adds the target mailboxId, then calls Email/set to perform the move.
async bulkMove(emailIds: string[], targetMailboxId: string): Promise<void> { const session = await this.getSession(); // Fetch current mailboxIds for all emails to build proper JMAP patches const getRequest: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], methodCalls: [ ['Email/get', { accountId: session.accountId, ids: emailIds, properties: ['id', 'mailboxIds'] }, 'getEmails'] ] }; const getResponse = await this.makeRequest(getRequest); const emails: any[] = this.getListResult(getResponse, 0); const mailboxMap: Record<string, Record<string, boolean>> = {}; emails.forEach((e: any) => { mailboxMap[e.id] = e.mailboxIds || {}; }); // Build patch per email: remove all current mailboxes, add target const updates: Record<string, any> = {}; emailIds.forEach(id => { const patch: Record<string, boolean | null> = {}; for (const mbId of Object.keys(mailboxMap[id] || {})) { patch[`mailboxIds/${mbId}`] = null; } patch[`mailboxIds/${targetMailboxId}`] = true; updates[id] = patch; }); const request: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], methodCalls: [ ['Email/set', { accountId: session.accountId, update: updates }, 'bulkMove'] ] }; const response = await this.makeRequest(request); const result = this.getMethodResult(response, 0); if (result.notUpdated && Object.keys(result.notUpdated).length > 0) { throw new Error('Failed to move some emails.'); } } - src/tool-definitions.ts:729-752 (schema)Schema definition for bulk_move: accepts emailIds (string array) and targetMailboxId (string), both required.
writeTool( 'bulk_move', 'Bulk Move', description( 'Move multiple emails to a mailbox in one call.', 'Use when the user wants to file a set of known email IDs into the same destination mailbox.', 'Do not use to apply labels while preserving the current mailbox set; use bulk_add_labels.', ), { type: 'object', properties: { emailIds: { ...stringArraySchema, description: 'Array of email IDs to move', }, targetMailboxId: { type: 'string', description: 'ID of target mailbox', }, }, required: ['emailIds', 'targetMailboxId'], }, { idempotentHint: true }, ), - src/mcp-server.ts:374-381 (registration)Registration/dispatch in the MCP server's switch statement: validates params, calls client.bulkMove(), returns success message.
case 'bulk_move': { const { emailIds: rawEmailIdsBM, targetMailboxId } = args as any; const emailIds = normalizeStringArray(rawEmailIdsBM); if (emailIds.length === 0) throw new McpError(ErrorCode.InvalidParams, 'emailIds array is required and must not be empty'); if (!targetMailboxId) throw new McpError(ErrorCode.InvalidParams, 'targetMailboxId is required'); await client.bulkMove(emailIds, targetMailboxId); return { content: [{ type: 'text', text: `${emailIds.length} emails moved successfully` }] }; }