get_thread
Retrieve all emails in a conversation thread by providing the thread ID.
Instructions
Get all emails in a conversation thread
Input Schema
| Name | Required | Description | Default |
|---|---|---|---|
| threadId | Yes | ID of the thread/conversation |
Implementation Reference
- src/index.ts:812-825 (registration)Tool registration in ListToolsRequestSchema: defines name 'get_thread' with description 'Get all emails in a conversation thread' and input schema requiring threadId.
{ 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'], }, }, - src/index.ts:1590-1610 (handler)Tool handler in CallToolRequestSchema: extracts threadId from args, initializes JmapClient, calls client.getThread(threadId), and returns the thread data as JSON.
case 'get_thread': { const { threadId } = args as any; if (!threadId) { throw new McpError(ErrorCode.InvalidParams, 'threadId is required'); } const client = initializeClient(); try { const thread = await client.getThread(threadId); return { content: [ { type: 'text', text: JSON.stringify(thread, null, 2), }, ], }; } catch (error) { // Provide helpful error information throw new McpError(ErrorCode.InternalError, `Thread access failed: ${redactBearerTokens(error instanceof Error ? error.message : String(error))}`); } } - src/jmap-client.ts:1270-1324 (handler)The actual implementation of getThread() on JmapClient: accepts a threadId (or emailId to resolve), calls Thread/get JMAP method, then Email/get to fetch all emails in the thread.
async getThread(threadId: string): Promise<any[]> { const session = await this.getSession(); // First, check if threadId is actually an email ID and resolve the thread let actualThreadId = threadId; // Try to get the email first to see if we need to resolve thread ID try { const emailRequest: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], methodCalls: [ ['Email/get', { accountId: session.accountId, ids: [threadId], properties: ['threadId'] }, 'checkEmail'] ] }; const emailResponse = await this.makeRequest(emailRequest); const email = this.getListResult(emailResponse, 0)[0]; if (email && email.threadId) { actualThreadId = email.threadId; } } catch (error) { // If email lookup fails, assume threadId is correct } // Use Thread/get with the resolved thread ID const request: JmapRequest = { using: ['urn:ietf:params:jmap:core', 'urn:ietf:params:jmap:mail'], methodCalls: [ ['Thread/get', { accountId: session.accountId, ids: [actualThreadId] }, 'getThread'], ['Email/get', { accountId: session.accountId, '#ids': { resultOf: 'getThread', name: 'Thread/get', path: '/list/*/emailIds' }, properties: ['id', 'subject', 'from', 'to', 'cc', 'replyTo', 'receivedAt', 'preview', 'hasAttachment', 'keywords', 'threadId'] }, 'emails'] ] }; const response = await this.makeRequest(request); const threadResult = this.getMethodResult(response, 0); // Check if thread was found if (threadResult.notFound && threadResult.notFound.includes(actualThreadId)) { throw new Error(`Thread with ID '${actualThreadId}' not found`); } return this.getListResult(response, 1); }