@mcp-z/mcp-gmail 1.0.9 → 1.0.11
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/cjs/constants.js.map +1 -1
- package/dist/cjs/email/composition/rfc822-builder.js.map +1 -1
- package/dist/cjs/email/messages/fetch-message.js +15 -21
- package/dist/cjs/email/messages/fetch-message.js.map +1 -1
- package/dist/cjs/email/messages/messages.js +2 -2
- package/dist/cjs/email/messages/messages.js.map +1 -1
- package/dist/cjs/email/parsing/header-parsing.js.map +1 -1
- package/dist/cjs/email/parsing/headers-utils.js.map +1 -1
- package/dist/cjs/email/parsing/html-processing.js.map +1 -1
- package/dist/cjs/email/parsing/message-extraction.js.map +1 -1
- package/dist/cjs/email/querying/execute-query.js +7 -14
- package/dist/cjs/email/querying/execute-query.js.map +1 -1
- package/dist/cjs/email/querying/pagination.js +19 -24
- package/dist/cjs/email/querying/pagination.js.map +1 -1
- package/dist/cjs/email/querying/query-builder.js +4 -2
- package/dist/cjs/email/querying/query-builder.js.map +1 -1
- package/dist/cjs/email/querying/search-execution.js +2 -5
- package/dist/cjs/email/querying/search-execution.js.map +1 -1
- package/dist/cjs/index.js +10 -24
- package/dist/cjs/index.js.map +1 -1
- package/dist/cjs/labels/gmail-labels.js +2 -5
- package/dist/cjs/labels/gmail-labels.js.map +1 -1
- package/dist/cjs/lib/base64-encoding.js.map +1 -1
- package/dist/cjs/lib/create-store.js +2 -5
- package/dist/cjs/lib/create-store.js.map +1 -1
- package/dist/cjs/lib/date-conversion.js.map +1 -1
- package/dist/cjs/lib/messages-to-row.js +2 -2
- package/dist/cjs/lib/messages-to-row.js.map +1 -1
- package/dist/cjs/mcp/index.js +8 -19
- package/dist/cjs/mcp/index.js.map +1 -1
- package/dist/cjs/mcp/prompts/draft-email.js +3 -6
- package/dist/cjs/mcp/prompts/draft-email.js.map +1 -1
- package/dist/cjs/mcp/prompts/index.js.map +1 -1
- package/dist/cjs/mcp/prompts/query-syntax.js +3 -6
- package/dist/cjs/mcp/prompts/query-syntax.js.map +1 -1
- package/dist/cjs/mcp/resources/email.js +3 -6
- package/dist/cjs/mcp/resources/email.js.map +1 -1
- package/dist/cjs/mcp/resources/index.js.map +1 -1
- package/dist/cjs/mcp/tools/categories-list.js +4 -8
- package/dist/cjs/mcp/tools/categories-list.js.map +1 -1
- package/dist/cjs/mcp/tools/index.js.map +1 -1
- package/dist/cjs/mcp/tools/label-add.js +4 -8
- package/dist/cjs/mcp/tools/label-add.js.map +1 -1
- package/dist/cjs/mcp/tools/label-delete.d.cts +1 -1
- package/dist/cjs/mcp/tools/label-delete.d.ts +1 -1
- package/dist/cjs/mcp/tools/label-delete.js +36 -32
- package/dist/cjs/mcp/tools/label-delete.js.map +1 -1
- package/dist/cjs/mcp/tools/labels-list.js +4 -8
- package/dist/cjs/mcp/tools/labels-list.js.map +1 -1
- package/dist/cjs/mcp/tools/message-get.d.cts +21 -21
- package/dist/cjs/mcp/tools/message-get.d.ts +21 -21
- package/dist/cjs/mcp/tools/message-get.js +4 -8
- package/dist/cjs/mcp/tools/message-get.js.map +1 -1
- package/dist/cjs/mcp/tools/message-mark-read.js +4 -8
- package/dist/cjs/mcp/tools/message-mark-read.js.map +1 -1
- package/dist/cjs/mcp/tools/message-move-to-trash.d.cts +1 -1
- package/dist/cjs/mcp/tools/message-move-to-trash.d.ts +1 -1
- package/dist/cjs/mcp/tools/message-move-to-trash.js +36 -32
- package/dist/cjs/mcp/tools/message-move-to-trash.js.map +1 -1
- package/dist/cjs/mcp/tools/message-respond.js +5 -11
- package/dist/cjs/mcp/tools/message-respond.js.map +1 -1
- package/dist/cjs/mcp/tools/message-search.d.cts +25 -25
- package/dist/cjs/mcp/tools/message-search.d.ts +25 -25
- package/dist/cjs/mcp/tools/message-search.js +19 -25
- package/dist/cjs/mcp/tools/message-search.js.map +1 -1
- package/dist/cjs/mcp/tools/message-send.d.cts +3 -3
- package/dist/cjs/mcp/tools/message-send.d.ts +3 -3
- package/dist/cjs/mcp/tools/message-send.js +4 -8
- package/dist/cjs/mcp/tools/message-send.js.map +1 -1
- package/dist/cjs/mcp/tools/messages-export-csv.d.cts +2 -2
- package/dist/cjs/mcp/tools/messages-export-csv.d.ts +2 -2
- package/dist/cjs/mcp/tools/messages-export-csv.js +16 -22
- package/dist/cjs/mcp/tools/messages-export-csv.js.map +1 -1
- package/dist/cjs/schemas/gmail-query-schema.d.cts +2 -1
- package/dist/cjs/schemas/gmail-query-schema.d.ts +2 -1
- package/dist/cjs/schemas/gmail-query-schema.js +26 -30
- package/dist/cjs/schemas/gmail-query-schema.js.map +1 -1
- package/dist/cjs/schemas/index.js.map +1 -1
- package/dist/cjs/setup/config.js +11 -25
- package/dist/cjs/setup/config.js.map +1 -1
- package/dist/cjs/setup/http.js +15 -16
- package/dist/cjs/setup/http.js.map +1 -1
- package/dist/cjs/setup/index.js.map +1 -1
- package/dist/cjs/setup/oauth-google.js +9 -16
- package/dist/cjs/setup/oauth-google.js.map +1 -1
- package/dist/cjs/setup/runtime.d.cts +1 -1
- package/dist/cjs/setup/runtime.d.ts +1 -1
- package/dist/cjs/setup/runtime.js +29 -44
- package/dist/cjs/setup/runtime.js.map +1 -1
- package/dist/cjs/setup/stdio.js +15 -16
- package/dist/cjs/setup/stdio.js.map +1 -1
- package/dist/esm/constants.js.map +1 -1
- package/dist/esm/email/composition/rfc822-builder.js.map +1 -1
- package/dist/esm/email/messages/fetch-message.js.map +1 -1
- package/dist/esm/email/messages/messages.js.map +1 -1
- package/dist/esm/email/parsing/header-parsing.js.map +1 -1
- package/dist/esm/email/parsing/headers-utils.js.map +1 -1
- package/dist/esm/email/parsing/html-processing.js.map +1 -1
- package/dist/esm/email/parsing/message-extraction.js.map +1 -1
- package/dist/esm/email/querying/execute-query.js.map +1 -1
- package/dist/esm/email/querying/pagination.js.map +1 -1
- package/dist/esm/email/querying/query-builder.js.map +1 -1
- package/dist/esm/email/querying/search-execution.js.map +1 -1
- package/dist/esm/index.js.map +1 -1
- package/dist/esm/labels/gmail-labels.js.map +1 -1
- package/dist/esm/lib/base64-encoding.js.map +1 -1
- package/dist/esm/lib/create-store.js.map +1 -1
- package/dist/esm/lib/date-conversion.js.map +1 -1
- package/dist/esm/lib/messages-to-row.js.map +1 -1
- package/dist/esm/mcp/index.js.map +1 -1
- package/dist/esm/mcp/prompts/draft-email.js.map +1 -1
- package/dist/esm/mcp/prompts/index.js.map +1 -1
- package/dist/esm/mcp/prompts/query-syntax.js.map +1 -1
- package/dist/esm/mcp/resources/email.js.map +1 -1
- package/dist/esm/mcp/resources/index.js.map +1 -1
- package/dist/esm/mcp/tools/categories-list.js.map +1 -1
- package/dist/esm/mcp/tools/index.js.map +1 -1
- package/dist/esm/mcp/tools/label-add.js.map +1 -1
- package/dist/esm/mcp/tools/label-delete.d.ts +1 -1
- package/dist/esm/mcp/tools/label-delete.js.map +1 -1
- package/dist/esm/mcp/tools/labels-list.js.map +1 -1
- package/dist/esm/mcp/tools/message-get.d.ts +21 -21
- package/dist/esm/mcp/tools/message-get.js.map +1 -1
- package/dist/esm/mcp/tools/message-mark-read.js.map +1 -1
- package/dist/esm/mcp/tools/message-move-to-trash.d.ts +1 -1
- package/dist/esm/mcp/tools/message-move-to-trash.js.map +1 -1
- package/dist/esm/mcp/tools/message-respond.js.map +1 -1
- package/dist/esm/mcp/tools/message-search.d.ts +25 -25
- package/dist/esm/mcp/tools/message-search.js +13 -12
- package/dist/esm/mcp/tools/message-search.js.map +1 -1
- package/dist/esm/mcp/tools/message-send.d.ts +3 -3
- package/dist/esm/mcp/tools/message-send.js.map +1 -1
- package/dist/esm/mcp/tools/messages-export-csv.d.ts +2 -2
- package/dist/esm/mcp/tools/messages-export-csv.js +9 -8
- package/dist/esm/mcp/tools/messages-export-csv.js.map +1 -1
- package/dist/esm/schemas/gmail-query-schema.d.ts +2 -1
- package/dist/esm/schemas/gmail-query-schema.js +18 -21
- package/dist/esm/schemas/gmail-query-schema.js.map +1 -1
- package/dist/esm/schemas/index.js.map +1 -1
- package/dist/esm/setup/config.js.map +1 -1
- package/dist/esm/setup/http.js.map +1 -1
- package/dist/esm/setup/index.js.map +1 -1
- package/dist/esm/setup/oauth-google.js.map +1 -1
- package/dist/esm/setup/runtime.d.ts +1 -1
- package/dist/esm/setup/runtime.js.map +1 -1
- package/dist/esm/setup/stdio.js.map +1 -1
- package/dist/esm/types.js.map +1 -1
- package/package.json +11 -7
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/mcp-gmail/src/mcp/tools/message-move-to-trash.ts"],"sourcesContent":["import type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport type { ToolModule } from '@mcp-z/server';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { google } from 'googleapis';\nimport { z } from 'zod';\nimport { CHUNK_SIZE, MAX_BATCH_SIZE } from '../../constants.ts';\n\nconst inputSchema = z.object({\n ids: z.array(z.coerce.string().trim().min(1)).min(1).describe('Gmail message IDs to move to trash'),\n});\n\nconst successBranchSchema = z.object({\n type: z.literal('success'),\n totalRequested: z.number().describe('Total number of messages requested to trash'),\n successCount: z.number().describe('Number of messages successfully moved to trash'),\n failureCount: z.number().describe('Number of messages that failed to move'),\n results: z\n .array(\n z.object({\n id: z.string().describe('Message ID'),\n success: z.boolean().describe('Whether the operation succeeded'),\n error: z.string().optional().describe('Error message if operation failed'),\n })\n )\n .describe('Individual results for each message'),\n});\n\nconst outputSchema = z.discriminatedUnion('type', [successBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Move Gmail messages to trash (recoverable).',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\nasync function handler({ ids }: Input, extra: EnrichedExtra) {\n const logger = extra.logger;\n logger.info('gmail-message-move-to-trash called', { count: ids.length });\n\n if (!ids || ids.length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'Missing ids');\n }\n\n // Validate batch size to prevent memory exhaustion\n if (ids.length > MAX_BATCH_SIZE) {\n throw new McpError(ErrorCode.InvalidParams, `Batch size ${ids.length} exceeds maximum allowed size of ${MAX_BATCH_SIZE}`);\n }\n\n // Validate and sanitize IDs\n const validatedIds: string[] = [];\n const invalidIds: string[] = [];\n\n for (const id of ids) {\n const trimmedId = id.trim();\n if (!trimmedId) {\n invalidIds.push(id);\n continue;\n }\n // Basic Gmail message ID validation - should not contain certain characters\n if (trimmedId.includes('/') || trimmedId.includes('\\\\') || trimmedId.includes('<') || trimmedId.includes('>')) {\n invalidIds.push(id);\n continue;\n }\n validatedIds.push(trimmedId);\n }\n\n if (invalidIds.length > 0) {\n throw new McpError(ErrorCode.InvalidParams, `Found ${invalidIds.length} invalid IDs: ${invalidIds.join(', ')}`);\n }\n\n if (validatedIds.length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'No valid IDs found after validation');\n }\n\n try {\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n // Process operations in chunks to prevent memory exhaustion\n const allResults: Array<{ id: string; success: boolean; error?: string }> = [];\n\n for (let i = 0; i < validatedIds.length; i += CHUNK_SIZE) {\n const chunk = validatedIds.slice(i, i + CHUNK_SIZE);\n logger.info('Processing chunk', { chunkIndex: i / CHUNK_SIZE + 1, chunkSize: chunk.length, totalChunks: Math.ceil(validatedIds.length / CHUNK_SIZE) });\n\n const chunkResults = await Promise.allSettled(\n chunk.map(async (id) => {\n await gmail.users.messages.trash({\n userId: 'me',\n id: id,\n });\n return { id, success: true };\n })\n );\n\n // Process chunk results\n const processedChunkResults = chunkResults.map((result, chunkIndex) => {\n const id = chunk[chunkIndex];\n if (!id) {\n throw new Error(`Chunk index ${chunkIndex} is out of bounds for chunk of size ${chunk.length}`);\n }\n if (result.status === 'fulfilled') {\n return { id, success: true };\n }\n\n // Extract error message more robustly\n let errorMessage = 'Unknown error';\n if (result.reason) {\n if (typeof result.reason === 'string') {\n errorMessage = result.reason;\n } else if (result.reason instanceof Error) {\n errorMessage = result.reason.message;\n } else if (result.reason && typeof result.reason === 'object' && 'message' in result.reason) {\n errorMessage = String(result.reason.message);\n } else if (result.reason && typeof result.reason === 'object' && 'error' in result.reason) {\n errorMessage = String(result.reason.error);\n } else {\n errorMessage = String(result.reason);\n }\n }\n\n return { id, success: false, error: errorMessage };\n });\n\n allResults.push(...processedChunkResults);\n }\n\n const successCount = allResults.filter((r) => r.success).length;\n const failureCount = allResults.filter((r) => !r.success).length;\n\n logger.info('gmail-message-move-to-trash completed', {\n totalRequested: validatedIds.length,\n successCount,\n failureCount,\n chunksProcessed: Math.ceil(validatedIds.length / CHUNK_SIZE),\n });\n\n const result: Output = {\n type: 'success' as const,\n totalRequested: validatedIds.length,\n successCount,\n failureCount,\n results: allResults,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail-message-move-to-trash error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error moving messages to trash: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'message-move-to-trash',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["schemas","AuthRequiredBranchSchema","ErrorCode","McpError","google","z","CHUNK_SIZE","MAX_BATCH_SIZE","inputSchema","object","ids","array","coerce","string","trim","min","describe","successBranchSchema","type","literal","totalRequested","number","successCount","failureCount","results","id","success","boolean","error","optional","outputSchema","discriminatedUnion","config","description","result","handler","extra","logger","info","count","length","InvalidParams","validatedIds","invalidIds","trimmedId","push","includes","join","gmail","version","auth","authContext","allResults","i","chunk","slice","chunkIndex","chunkSize","totalChunks","Math","ceil","chunkResults","Promise","allSettled","map","users","messages","trash","userId","processedChunkResults","Error","status","errorMessage","reason","message","String","filter","r","chunksProcessed","content","text","JSON","stringify","structuredContent","InternalError","stack","undefined","createTool","name"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/mcp-gmail/src/mcp/tools/message-move-to-trash.ts"],"sourcesContent":["import type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport type { ToolModule } from '@mcp-z/server';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { google } from 'googleapis';\nimport { z } from 'zod';\nimport { CHUNK_SIZE, MAX_BATCH_SIZE } from '../../constants.ts';\n\nconst inputSchema = z.object({\n ids: z.array(z.coerce.string().trim().min(1)).min(1).describe('Gmail message IDs to move to trash'),\n});\n\nconst successBranchSchema = z.object({\n type: z.literal('success'),\n totalRequested: z.number().describe('Total number of messages requested to trash'),\n successCount: z.number().describe('Number of messages successfully moved to trash'),\n failureCount: z.number().describe('Number of messages that failed to move'),\n results: z\n .array(\n z.object({\n id: z.string().describe('Message ID'),\n success: z.boolean().describe('Whether the operation succeeded'),\n error: z.string().optional().describe('Error message if operation failed'),\n })\n )\n .describe('Individual results for each message'),\n});\n\nconst outputSchema = z.discriminatedUnion('type', [successBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Move Gmail messages to trash (recoverable).',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\nasync function handler({ ids }: Input, extra: EnrichedExtra) {\n const logger = extra.logger;\n logger.info('gmail-message-move-to-trash called', { count: ids.length });\n\n if (!ids || ids.length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'Missing ids');\n }\n\n // Validate batch size to prevent memory exhaustion\n if (ids.length > MAX_BATCH_SIZE) {\n throw new McpError(ErrorCode.InvalidParams, `Batch size ${ids.length} exceeds maximum allowed size of ${MAX_BATCH_SIZE}`);\n }\n\n // Validate and sanitize IDs\n const validatedIds: string[] = [];\n const invalidIds: string[] = [];\n\n for (const id of ids) {\n const trimmedId = id.trim();\n if (!trimmedId) {\n invalidIds.push(id);\n continue;\n }\n // Basic Gmail message ID validation - should not contain certain characters\n if (trimmedId.includes('/') || trimmedId.includes('\\\\') || trimmedId.includes('<') || trimmedId.includes('>')) {\n invalidIds.push(id);\n continue;\n }\n validatedIds.push(trimmedId);\n }\n\n if (invalidIds.length > 0) {\n throw new McpError(ErrorCode.InvalidParams, `Found ${invalidIds.length} invalid IDs: ${invalidIds.join(', ')}`);\n }\n\n if (validatedIds.length === 0) {\n throw new McpError(ErrorCode.InvalidParams, 'No valid IDs found after validation');\n }\n\n try {\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n // Process operations in chunks to prevent memory exhaustion\n const allResults: Array<{ id: string; success: boolean; error?: string }> = [];\n\n for (let i = 0; i < validatedIds.length; i += CHUNK_SIZE) {\n const chunk = validatedIds.slice(i, i + CHUNK_SIZE);\n logger.info('Processing chunk', { chunkIndex: i / CHUNK_SIZE + 1, chunkSize: chunk.length, totalChunks: Math.ceil(validatedIds.length / CHUNK_SIZE) });\n\n const chunkResults = await Promise.allSettled(\n chunk.map(async (id) => {\n await gmail.users.messages.trash({\n userId: 'me',\n id: id,\n });\n return { id, success: true };\n })\n );\n\n // Process chunk results\n const processedChunkResults = chunkResults.map((result, chunkIndex) => {\n const id = chunk[chunkIndex];\n if (!id) {\n throw new Error(`Chunk index ${chunkIndex} is out of bounds for chunk of size ${chunk.length}`);\n }\n if (result.status === 'fulfilled') {\n return { id, success: true };\n }\n\n // Extract error message more robustly\n let errorMessage = 'Unknown error';\n if (result.reason) {\n if (typeof result.reason === 'string') {\n errorMessage = result.reason;\n } else if (result.reason instanceof Error) {\n errorMessage = result.reason.message;\n } else if (result.reason && typeof result.reason === 'object' && 'message' in result.reason) {\n errorMessage = String(result.reason.message);\n } else if (result.reason && typeof result.reason === 'object' && 'error' in result.reason) {\n errorMessage = String(result.reason.error);\n } else {\n errorMessage = String(result.reason);\n }\n }\n\n return { id, success: false, error: errorMessage };\n });\n\n allResults.push(...processedChunkResults);\n }\n\n const successCount = allResults.filter((r) => r.success).length;\n const failureCount = allResults.filter((r) => !r.success).length;\n\n logger.info('gmail-message-move-to-trash completed', {\n totalRequested: validatedIds.length,\n successCount,\n failureCount,\n chunksProcessed: Math.ceil(validatedIds.length / CHUNK_SIZE),\n });\n\n const result: Output = {\n type: 'success' as const,\n totalRequested: validatedIds.length,\n successCount,\n failureCount,\n results: allResults,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail-message-move-to-trash error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error moving messages to trash: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'message-move-to-trash',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["schemas","AuthRequiredBranchSchema","ErrorCode","McpError","google","z","CHUNK_SIZE","MAX_BATCH_SIZE","inputSchema","object","ids","array","coerce","string","trim","min","describe","successBranchSchema","type","literal","totalRequested","number","successCount","failureCount","results","id","success","boolean","error","optional","outputSchema","discriminatedUnion","config","description","result","handler","extra","logger","info","count","length","InvalidParams","validatedIds","invalidIds","trimmedId","push","includes","join","gmail","version","auth","authContext","allResults","i","chunk","slice","chunkIndex","chunkSize","totalChunks","Math","ceil","chunkResults","Promise","allSettled","map","users","messages","trash","userId","processedChunkResults","Error","status","errorMessage","reason","message","String","filter","r","chunksProcessed","content","text","JSON","stringify","structuredContent","InternalError","stack","undefined","createTool","name"],"mappings":"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"}
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/mcp-gmail/src/mcp/tools/message-respond.ts"],"sourcesContent":["import { ComposeContentTypeSchema } from '@mcp-z/email';\nimport type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport type { ToolModule } from '@mcp-z/server';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { google } from 'googleapis';\nimport { z } from 'zod';\nimport { extractEmails } from '../../email/parsing/headers-utils.ts';\nimport { b64url } from '../../lib/base64-encoding.ts';\n\nconst inputSchema = z.object({\n id: z.coerce.string().trim().min(1).describe('Gmail message ID to reply to'),\n body: z.coerce.string().trim().min(1).describe('Reply body content (plain text or HTML)'),\n contentType: ComposeContentTypeSchema,\n});\n\nconst successBranchSchema = z.object({\n type: z.literal('success'),\n id: z.string().describe('Original message ID that was replied to'),\n});\n\nconst outputSchema = z.discriminatedUnion('type', [successBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Send a reply to a Gmail message',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\nasync function handler({ id, body, contentType }: Input, extra: EnrichedExtra) {\n const logger = extra.logger;\n\n if (!id || !body) {\n throw new McpError(ErrorCode.InvalidParams, 'Missing id or body');\n }\n\n logger.info('gmail.message.respond called', { id, contentType });\n\n try {\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n\n let full: unknown;\n try {\n const response = await gmail.users.messages.get({\n userId: 'me',\n id: id,\n format: 'metadata',\n metadataHeaders: ['From', 'To', 'Reply-To', 'Subject', 'Message-ID', 'References', 'In-Reply-To', 'Date'],\n });\n full = response.data;\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail.message.respond fetch error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error fetching message metadata: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n\n // Type-safe header extraction\n const fullData = full as { payload?: { headers?: unknown[] }; threadId?: unknown };\n const headersArray = Array.isArray(fullData?.payload?.headers) ? fullData.payload.headers : [];\n const headers = Object.fromEntries(\n headersArray.map((h: unknown) => {\n const header = h as { name?: unknown; value?: unknown };\n return [String(header.name ?? ''), String(header.value ?? '')];\n })\n );\n const fromHdr = headers.From || headers.from;\n const replyToHdr = headers['Reply-To'] || headers['reply-to'];\n const toAddr = (replyToHdr ? extractEmails(replyToHdr)[0] : extractEmails(fromHdr)[0]) ?? '';\n const subject = (headers.Subject || headers.subject) ?? '';\n const messageIdHeader = headers['Message-ID'] || headers['Message-Id'] || headers['message-id'];\n const references = (headers.References || headers.references) ?? '';\n const replySubject = /^re:/i.test(subject) ? subject : `Re: ${subject}`;\n const inReplyTo = messageIdHeader?.startsWith('<') ? messageIdHeader : messageIdHeader ? `<${messageIdHeader}>` : undefined;\n const refs = [references, inReplyTo].filter(Boolean).join(' ').trim();\n const mimeType = contentType === 'html' ? 'text/html' : 'text/plain';\n const lines = [`To: ${toAddr}`, `Subject: ${replySubject}`, `In-Reply-To: ${inReplyTo ?? ''}`, `References: ${refs}`, 'MIME-Version: 1.0', `Content-Type: ${mimeType}; charset=UTF-8`, '', body].join('\\r\\n');\n const raw = b64url(lines);\n\n // Prepare request body with conditional threadId for exactOptionalPropertyTypes\n const threadId = fullData?.threadId ? String(fullData.threadId) : undefined;\n await gmail.users.messages.send({\n userId: 'me',\n requestBody: {\n raw: raw,\n ...(threadId && { threadId }),\n },\n });\n\n logger.info('gmail.message.respond sent reply', { id });\n\n const result: Output = {\n type: 'success' as const,\n id,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail.message.respond error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error responding to message: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'message-respond',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["ComposeContentTypeSchema","schemas","AuthRequiredBranchSchema","ErrorCode","McpError","google","z","extractEmails","b64url","inputSchema","object","id","coerce","string","trim","min","describe","body","contentType","successBranchSchema","type","literal","outputSchema","discriminatedUnion","config","description","result","handler","extra","logger","InvalidParams","info","replyToHdr","headers","fullData","gmail","version","auth","authContext","full","response","users","messages","get","userId","format","metadataHeaders","data","error","message","Error","String","InternalError","stack","undefined","headersArray","Array","isArray","payload","Object","fromEntries","map","h","header","name","value","fromHdr","From","from","toAddr","subject","Subject","messageIdHeader","references","References","replySubject","test","inReplyTo","startsWith","refs","filter","Boolean","join","mimeType","lines","raw","threadId","send","requestBody","content","text","JSON","stringify","structuredContent","createTool"],"mappings":"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"}
|
|
@@ -12,7 +12,7 @@ declare const inputSchema: z.ZodObject<{
|
|
|
12
12
|
excludeThreadHistory: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
13
13
|
pageSize: z.ZodDefault<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
|
|
14
14
|
pageToken: z.ZodOptional<z.ZodString>;
|
|
15
|
-
query: z.ZodOptional<z.ZodType<import("../../schemas/gmail-query-schema.js").GmailQuery, unknown, z.core.$ZodTypeInternals<import("../../schemas/gmail-query-schema.js").GmailQuery, unknown>>>;
|
|
15
|
+
query: z.ZodOptional<z.ZodType<string | import("../../schemas/gmail-query-schema.js").GmailQuery, unknown, z.core.$ZodTypeInternals<string | import("../../schemas/gmail-query-schema.js").GmailQuery, unknown>>>;
|
|
16
16
|
fields: z.ZodOptional<z.ZodString>;
|
|
17
17
|
}, z.core.$strip>;
|
|
18
18
|
declare const outputSchema: z.ZodUnion<readonly [z.ZodObject<{
|
|
@@ -73,39 +73,39 @@ declare function handler({ query, pageSize, pageToken, fields, shape, contentTyp
|
|
|
73
73
|
type: "success";
|
|
74
74
|
shape: "objects";
|
|
75
75
|
items: {
|
|
76
|
-
id?: string;
|
|
77
|
-
threadId?: string;
|
|
78
|
-
subject?: string;
|
|
79
|
-
from?: string;
|
|
80
|
-
fromName?: string;
|
|
81
|
-
to?: string;
|
|
82
|
-
cc?: string;
|
|
83
|
-
bcc?: string;
|
|
84
|
-
date?: string;
|
|
85
|
-
receivedDateTime?: string;
|
|
86
|
-
snippet?: string;
|
|
87
|
-
isRead?: boolean;
|
|
88
|
-
isImportant?: boolean;
|
|
89
|
-
hasAttachments?: boolean;
|
|
90
|
-
attachmentCount?: number;
|
|
91
|
-
labelIds?: string[];
|
|
92
|
-
folderName?: string;
|
|
93
|
-
body?: string;
|
|
94
|
-
bodyContentType?: "text" | "html";
|
|
76
|
+
id?: string | undefined;
|
|
77
|
+
threadId?: string | undefined;
|
|
78
|
+
subject?: string | undefined;
|
|
79
|
+
from?: string | undefined;
|
|
80
|
+
fromName?: string | undefined;
|
|
81
|
+
to?: string | undefined;
|
|
82
|
+
cc?: string | undefined;
|
|
83
|
+
bcc?: string | undefined;
|
|
84
|
+
date?: string | undefined;
|
|
85
|
+
receivedDateTime?: string | undefined;
|
|
86
|
+
snippet?: string | undefined;
|
|
87
|
+
isRead?: boolean | undefined;
|
|
88
|
+
isImportant?: boolean | undefined;
|
|
89
|
+
hasAttachments?: boolean | undefined;
|
|
90
|
+
attachmentCount?: number | undefined;
|
|
91
|
+
labelIds?: string[] | undefined;
|
|
92
|
+
folderName?: string | undefined;
|
|
93
|
+
body?: string | undefined;
|
|
94
|
+
bodyContentType?: "text" | "html" | undefined;
|
|
95
95
|
attachments?: {
|
|
96
96
|
id: string;
|
|
97
97
|
filename: string;
|
|
98
98
|
mimeType: string;
|
|
99
|
-
size?: number;
|
|
100
|
-
}[];
|
|
99
|
+
size?: number | undefined;
|
|
100
|
+
}[] | undefined;
|
|
101
101
|
}[];
|
|
102
|
-
nextPageToken?: string;
|
|
102
|
+
nextPageToken?: string | undefined;
|
|
103
103
|
} | {
|
|
104
104
|
type: "success";
|
|
105
105
|
shape: "arrays";
|
|
106
106
|
columns: string[];
|
|
107
107
|
rows: unknown[][];
|
|
108
|
-
nextPageToken?: string;
|
|
108
|
+
nextPageToken?: string | undefined;
|
|
109
109
|
};
|
|
110
110
|
};
|
|
111
111
|
}>;
|
|
@@ -125,7 +125,7 @@ export default function createTool(): {
|
|
|
125
125
|
excludeThreadHistory: z.ZodDefault<z.ZodOptional<z.ZodBoolean>>;
|
|
126
126
|
pageSize: z.ZodDefault<z.ZodOptional<z.ZodCoercedNumber<unknown>>>;
|
|
127
127
|
pageToken: z.ZodOptional<z.ZodString>;
|
|
128
|
-
query: z.ZodOptional<z.ZodType<import("../../schemas/gmail-query-schema.js").GmailQuery, unknown, z.core.$ZodTypeInternals<import("../../schemas/gmail-query-schema.js").GmailQuery, unknown>>>;
|
|
128
|
+
query: z.ZodOptional<z.ZodType<string | import("../../schemas/gmail-query-schema.js").GmailQuery, unknown, z.core.$ZodTypeInternals<string | import("../../schemas/gmail-query-schema.js").GmailQuery, unknown>>>;
|
|
129
129
|
fields: z.ZodOptional<z.ZodString>;
|
|
130
130
|
}, z.core.$strip>;
|
|
131
131
|
readonly outputSchema: z.ZodObject<{
|
|
@@ -8,7 +8,7 @@ import { z } from 'zod';
|
|
|
8
8
|
import { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../../constants.js';
|
|
9
9
|
import { extractBodyFromPayload } from '../../email/parsing/html-processing.js';
|
|
10
10
|
import { executeQuery as executeGmailQuery } from '../../email/querying/execute-query.js';
|
|
11
|
-
import { GmailQueryParameterSchema } from '../../schemas/gmail-query-schema.js';
|
|
11
|
+
import { GmailQueryParameterSchema, parseGmailQueryParameter } from '../../schemas/gmail-query-schema.js';
|
|
12
12
|
const inputSchema = z.object({
|
|
13
13
|
query: GmailQueryParameterSchema.optional().describe('Structured query object or JSON string for filtering messages. Use query-syntax prompt for reference and rawGmailQuery for Gmail syntax.'),
|
|
14
14
|
fields: createFieldsSchema({
|
|
@@ -56,23 +56,24 @@ const config = {
|
|
|
56
56
|
};
|
|
57
57
|
async function handler({ query, pageSize = DEFAULT_PAGE_SIZE, pageToken, fields, shape = 'arrays', contentType = 'text', excludeThreadHistory = false }, extra) {
|
|
58
58
|
const logger = extra.logger;
|
|
59
|
-
const requestedFields = parseFields(fields, EMAIL_FIELDS);
|
|
60
|
-
const includeBody = requestedFields === 'all' || requestedFields.has('body');
|
|
61
|
-
logger.info('gmail.message.search called', {
|
|
62
|
-
query,
|
|
63
|
-
pageSize,
|
|
64
|
-
pageToken: pageToken ? '[provided]' : undefined,
|
|
65
|
-
fields,
|
|
66
|
-
includeBody,
|
|
67
|
-
accountId: extra.authContext.accountId
|
|
68
|
-
});
|
|
69
59
|
try {
|
|
60
|
+
const parsedQuery = parseGmailQueryParameter(query);
|
|
61
|
+
const requestedFields = parseFields(fields, EMAIL_FIELDS);
|
|
62
|
+
const includeBody = requestedFields === 'all' || requestedFields.has('body');
|
|
63
|
+
logger.info('gmail.message.search called', {
|
|
64
|
+
query: parsedQuery,
|
|
65
|
+
pageSize,
|
|
66
|
+
pageToken: pageToken ? '[provided]' : undefined,
|
|
67
|
+
fields,
|
|
68
|
+
includeBody,
|
|
69
|
+
accountId: extra.authContext.accountId
|
|
70
|
+
});
|
|
70
71
|
const gmail = google.gmail({
|
|
71
72
|
version: 'v1',
|
|
72
73
|
auth: extra.authContext.auth
|
|
73
74
|
});
|
|
74
75
|
const started = Date.now();
|
|
75
|
-
const exec = await executeGmailQuery(
|
|
76
|
+
const exec = await executeGmailQuery(parsedQuery, {
|
|
76
77
|
client: gmail,
|
|
77
78
|
logger,
|
|
78
79
|
pageSize,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/mcp-gmail/src/mcp/tools/message-search.ts"],"sourcesContent":["import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\n/** Gmail message search using middleware-based auth pattern */\n\nimport { EMAIL_COMMON_PATTERNS, EMAIL_FIELD_DESCRIPTIONS, EMAIL_FIELDS, EmailContentTypeSchema, type EmailDetail, EmailDetailSchema, ExcludeThreadHistorySchema } from '@mcp-z/email';\nimport type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport { createFieldsSchema, createPaginationSchema, createShapeSchema, filterFields, parseFields, type ToolModule, toColumnarFormat } from '@mcp-z/server';\nimport { type gmail_v1, google } from 'googleapis';\nimport { z } from 'zod';\nimport { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../../constants.ts';\nimport { extractBodyFromPayload } from '../../email/parsing/html-processing.ts';\nimport { executeQuery as executeGmailQuery } from '../../email/querying/execute-query.ts';\nimport { GmailQueryParameterSchema } from '../../schemas/gmail-query-schema.ts';\n\nconst inputSchema = z.object({\n query: GmailQueryParameterSchema.optional().describe('Structured query object or JSON string for filtering messages. Use query-syntax prompt for reference and rawGmailQuery for Gmail syntax.'),\n fields: createFieldsSchema({\n availableFields: EMAIL_FIELDS,\n fieldDescriptions: EMAIL_FIELD_DESCRIPTIONS,\n commonPatterns: EMAIL_COMMON_PATTERNS,\n resourceName: 'email message',\n }),\n ...createPaginationSchema({ defaultPageSize: DEFAULT_PAGE_SIZE, maxPageSize: MAX_PAGE_SIZE, provider: 'gmail' }).shape,\n shape: createShapeSchema(),\n contentType: EmailContentTypeSchema,\n excludeThreadHistory: ExcludeThreadHistorySchema,\n});\n\n// Success branch schemas for different shapes\nconst successObjectsBranchSchema = z.object({\n type: z.literal('success'),\n shape: z.literal('objects'),\n items: z.array(EmailDetailSchema).describe('Matching email messages'),\n nextPageToken: z.string().optional().describe('Token for fetching next page of results'),\n});\n\nconst successArraysBranchSchema = z.object({\n type: z.literal('success'),\n shape: z.literal('arrays'),\n columns: z.array(z.string()).describe('Column names in canonical order'),\n rows: z.array(z.array(z.unknown())).describe('Row data matching column order'),\n nextPageToken: z.string().optional().describe('Token for fetching next page of results'),\n});\n\n// Output schema with auth_required support\n// Using z.union instead of discriminatedUnion since we have two success branches with different shapes\nconst outputSchema = z.union([successObjectsBranchSchema, successArraysBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Search Gmail messages using structured query objects with flexible field selection.',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\nasync function handler({ query, pageSize = DEFAULT_PAGE_SIZE, pageToken, fields, shape = 'arrays', contentType = 'text', excludeThreadHistory = false }: Input, extra: EnrichedExtra) {\n const logger = extra.logger;\n\n const requestedFields = parseFields(fields, EMAIL_FIELDS);\n const includeBody = requestedFields === 'all' || requestedFields.has('body');\n\n logger.info('gmail.message.search called', {\n query,\n pageSize,\n pageToken: pageToken ? '[provided]' : undefined,\n fields,\n includeBody,\n accountId: extra.authContext.accountId, // Available from middleware\n });\n\n try {\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n\n const started = Date.now();\n\n const exec = await executeGmailQuery(\n query,\n {\n client: gmail,\n logger,\n pageSize,\n ...(pageToken !== undefined && { pageToken }),\n includeBody,\n },\n (full: unknown) => {\n // Type-safe property access with guards\n const fullData = full as gmail_v1.Schema$Message;\n\n const headersArray = Array.isArray(fullData?.payload?.headers) ? fullData.payload.headers : [];\n const headers = Object.fromEntries(\n headersArray.map((h: unknown) => {\n const header = h as gmail_v1.Schema$MessagePartHeader;\n return [String(header.name ?? ''), String(header.value ?? '')];\n })\n );\n\n const mapped: Partial<EmailDetail> = {\n id: String(fullData?.id ?? ''),\n threadId: fullData?.threadId ? String(fullData.threadId) : undefined,\n from: headers?.From,\n to: headers?.To,\n subject: headers?.Subject,\n date: headers?.Date,\n snippet: fullData?.snippet ? String(fullData.snippet) : undefined,\n };\n\n if (includeBody) {\n const payload = fullData?.payload;\n // Cast to Schema$MessagePart for extractBodyFromPayload\n const body = payload ? extractBodyFromPayload(payload as gmail_v1.Schema$MessagePart, { contentType, excludeThreadHistory }) : '';\n if (body) {\n mapped.body = body;\n mapped.bodyContentType = contentType;\n }\n }\n return mapped;\n }\n );\n\n const items = exec.items;\n const metadata = exec.metadata;\n const durationMs = Date.now() - started;\n\n // Type-safe field filtering\n const filteredItems = items.map((item) => filterFields(item, requestedFields));\n\n logger.info('gmail.message.search results', { query, pageSize, resultCount: filteredItems.length, fields: fields || 'all' });\n logger.info('gmail.message.search metrics', { durationMs, metadata });\n\n // Type-safe metadata access\n const metadataObj = metadata as { nextPageToken?: string } | undefined;\n\n // Build result based on shape\n const result: Output =\n shape === 'arrays'\n ? {\n type: 'success' as const,\n shape: 'arrays' as const,\n ...toColumnarFormat(filteredItems, requestedFields, EMAIL_FIELDS),\n ...(metadataObj?.nextPageToken && { nextPageToken: metadataObj.nextPageToken }),\n }\n : {\n type: 'success' as const,\n shape: 'objects' as const,\n items: filteredItems,\n ...(metadataObj?.nextPageToken && { nextPageToken: metadataObj.nextPageToken }),\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail.message.search error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error searching messages: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'message-search',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["ErrorCode","McpError","EMAIL_COMMON_PATTERNS","EMAIL_FIELD_DESCRIPTIONS","EMAIL_FIELDS","EmailContentTypeSchema","EmailDetailSchema","ExcludeThreadHistorySchema","schemas","AuthRequiredBranchSchema","createFieldsSchema","createPaginationSchema","createShapeSchema","filterFields","parseFields","toColumnarFormat","google","z","DEFAULT_PAGE_SIZE","MAX_PAGE_SIZE","extractBodyFromPayload","executeQuery","executeGmailQuery","GmailQueryParameterSchema","inputSchema","object","query","optional","describe","fields","availableFields","fieldDescriptions","commonPatterns","resourceName","defaultPageSize","maxPageSize","provider","shape","contentType","excludeThreadHistory","successObjectsBranchSchema","type","literal","items","array","nextPageToken","string","successArraysBranchSchema","columns","rows","unknown","outputSchema","union","config","description","result","handler","pageSize","pageToken","extra","logger","requestedFields","includeBody","has","info","undefined","accountId","authContext","gmail","version","auth","started","Date","now","exec","client","full","fullData","headersArray","Array","isArray","payload","headers","Object","fromEntries","map","h","header","String","name","value","mapped","id","threadId","from","From","to","To","subject","Subject","date","snippet","body","bodyContentType","metadata","durationMs","filteredItems","item","resultCount","length","metadataObj","content","text","JSON","stringify","structuredContent","error","message","Error","InternalError","stack","createTool"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/mcp-gmail/src/mcp/tools/message-search.ts"],"sourcesContent":["import { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\n/** Gmail message search using middleware-based auth pattern */\n\nimport { EMAIL_COMMON_PATTERNS, EMAIL_FIELD_DESCRIPTIONS, EMAIL_FIELDS, EmailContentTypeSchema, type EmailDetail, EmailDetailSchema, ExcludeThreadHistorySchema } from '@mcp-z/email';\nimport type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport { createFieldsSchema, createPaginationSchema, createShapeSchema, filterFields, parseFields, type ToolModule, toColumnarFormat } from '@mcp-z/server';\nimport { type gmail_v1, google } from 'googleapis';\nimport { z } from 'zod';\nimport { DEFAULT_PAGE_SIZE, MAX_PAGE_SIZE } from '../../constants.ts';\nimport { extractBodyFromPayload } from '../../email/parsing/html-processing.ts';\nimport { executeQuery as executeGmailQuery } from '../../email/querying/execute-query.ts';\nimport { GmailQueryParameterSchema, parseGmailQueryParameter } from '../../schemas/gmail-query-schema.ts';\n\nconst inputSchema = z.object({\n query: GmailQueryParameterSchema.optional().describe('Structured query object or JSON string for filtering messages. Use query-syntax prompt for reference and rawGmailQuery for Gmail syntax.'),\n fields: createFieldsSchema({\n availableFields: EMAIL_FIELDS,\n fieldDescriptions: EMAIL_FIELD_DESCRIPTIONS,\n commonPatterns: EMAIL_COMMON_PATTERNS,\n resourceName: 'email message',\n }),\n ...createPaginationSchema({ defaultPageSize: DEFAULT_PAGE_SIZE, maxPageSize: MAX_PAGE_SIZE, provider: 'gmail' }).shape,\n shape: createShapeSchema(),\n contentType: EmailContentTypeSchema,\n excludeThreadHistory: ExcludeThreadHistorySchema,\n});\n\n// Success branch schemas for different shapes\nconst successObjectsBranchSchema = z.object({\n type: z.literal('success'),\n shape: z.literal('objects'),\n items: z.array(EmailDetailSchema).describe('Matching email messages'),\n nextPageToken: z.string().optional().describe('Token for fetching next page of results'),\n});\n\nconst successArraysBranchSchema = z.object({\n type: z.literal('success'),\n shape: z.literal('arrays'),\n columns: z.array(z.string()).describe('Column names in canonical order'),\n rows: z.array(z.array(z.unknown())).describe('Row data matching column order'),\n nextPageToken: z.string().optional().describe('Token for fetching next page of results'),\n});\n\n// Output schema with auth_required support\n// Using z.union instead of discriminatedUnion since we have two success branches with different shapes\nconst outputSchema = z.union([successObjectsBranchSchema, successArraysBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Search Gmail messages using structured query objects with flexible field selection.',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\nasync function handler({ query, pageSize = DEFAULT_PAGE_SIZE, pageToken, fields, shape = 'arrays', contentType = 'text', excludeThreadHistory = false }: Input, extra: EnrichedExtra) {\n const logger = extra.logger;\n\n try {\n const parsedQuery = parseGmailQueryParameter(query);\n const requestedFields = parseFields(fields, EMAIL_FIELDS);\n const includeBody = requestedFields === 'all' || requestedFields.has('body');\n\n logger.info('gmail.message.search called', {\n query: parsedQuery,\n pageSize,\n pageToken: pageToken ? '[provided]' : undefined,\n fields,\n includeBody,\n accountId: extra.authContext.accountId, // Available from middleware\n });\n\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n\n const started = Date.now();\n\n const exec = await executeGmailQuery(\n parsedQuery,\n {\n client: gmail,\n logger,\n pageSize,\n ...(pageToken !== undefined && { pageToken }),\n includeBody,\n },\n (full: unknown) => {\n // Type-safe property access with guards\n const fullData = full as gmail_v1.Schema$Message;\n\n const headersArray = Array.isArray(fullData?.payload?.headers) ? fullData.payload.headers : [];\n const headers = Object.fromEntries(\n headersArray.map((h: unknown) => {\n const header = h as gmail_v1.Schema$MessagePartHeader;\n return [String(header.name ?? ''), String(header.value ?? '')];\n })\n );\n\n const mapped: Partial<EmailDetail> = {\n id: String(fullData?.id ?? ''),\n threadId: fullData?.threadId ? String(fullData.threadId) : undefined,\n from: headers?.From,\n to: headers?.To,\n subject: headers?.Subject,\n date: headers?.Date,\n snippet: fullData?.snippet ? String(fullData.snippet) : undefined,\n };\n\n if (includeBody) {\n const payload = fullData?.payload;\n // Cast to Schema$MessagePart for extractBodyFromPayload\n const body = payload ? extractBodyFromPayload(payload as gmail_v1.Schema$MessagePart, { contentType, excludeThreadHistory }) : '';\n if (body) {\n mapped.body = body;\n mapped.bodyContentType = contentType;\n }\n }\n return mapped;\n }\n );\n\n const items = exec.items;\n const metadata = exec.metadata;\n const durationMs = Date.now() - started;\n\n // Type-safe field filtering\n const filteredItems = items.map((item) => filterFields(item, requestedFields));\n\n logger.info('gmail.message.search results', { query, pageSize, resultCount: filteredItems.length, fields: fields || 'all' });\n logger.info('gmail.message.search metrics', { durationMs, metadata });\n\n // Type-safe metadata access\n const metadataObj = metadata as { nextPageToken?: string } | undefined;\n\n // Build result based on shape\n const result: Output =\n shape === 'arrays'\n ? {\n type: 'success' as const,\n shape: 'arrays' as const,\n ...toColumnarFormat(filteredItems, requestedFields, EMAIL_FIELDS),\n ...(metadataObj?.nextPageToken && { nextPageToken: metadataObj.nextPageToken }),\n }\n : {\n type: 'success' as const,\n shape: 'objects' as const,\n items: filteredItems,\n ...(metadataObj?.nextPageToken && { nextPageToken: metadataObj.nextPageToken }),\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail.message.search error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error searching messages: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'message-search',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["ErrorCode","McpError","EMAIL_COMMON_PATTERNS","EMAIL_FIELD_DESCRIPTIONS","EMAIL_FIELDS","EmailContentTypeSchema","EmailDetailSchema","ExcludeThreadHistorySchema","schemas","AuthRequiredBranchSchema","createFieldsSchema","createPaginationSchema","createShapeSchema","filterFields","parseFields","toColumnarFormat","google","z","DEFAULT_PAGE_SIZE","MAX_PAGE_SIZE","extractBodyFromPayload","executeQuery","executeGmailQuery","GmailQueryParameterSchema","parseGmailQueryParameter","inputSchema","object","query","optional","describe","fields","availableFields","fieldDescriptions","commonPatterns","resourceName","defaultPageSize","maxPageSize","provider","shape","contentType","excludeThreadHistory","successObjectsBranchSchema","type","literal","items","array","nextPageToken","string","successArraysBranchSchema","columns","rows","unknown","outputSchema","union","config","description","result","handler","pageSize","pageToken","extra","logger","parsedQuery","requestedFields","includeBody","has","info","undefined","accountId","authContext","gmail","version","auth","started","Date","now","exec","client","full","fullData","headersArray","Array","isArray","payload","headers","Object","fromEntries","map","h","header","String","name","value","mapped","id","threadId","from","From","to","To","subject","Subject","date","snippet","body","bodyContentType","metadata","durationMs","filteredItems","item","resultCount","length","metadataObj","content","text","JSON","stringify","structuredContent","error","message","Error","InternalError","stack","createTool"],"mappings":"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"}
|
|
@@ -41,9 +41,9 @@ declare function handler(params: Input, extra: EnrichedExtra): Promise<{
|
|
|
41
41
|
id: string;
|
|
42
42
|
sentAt: string;
|
|
43
43
|
recipientCount: number;
|
|
44
|
-
threadId?: string;
|
|
45
|
-
webLink?: string;
|
|
46
|
-
size?: number;
|
|
44
|
+
threadId?: string | undefined;
|
|
45
|
+
webLink?: string | undefined;
|
|
46
|
+
size?: number | undefined;
|
|
47
47
|
};
|
|
48
48
|
};
|
|
49
49
|
};
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/mcp-gmail/src/mcp/tools/message-send.ts"],"sourcesContent":["import { ComposeContentTypeSchema, createEmailRecipientsSchema, createMessageResultSchema } from '@mcp-z/email';\nimport type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport type { ToolModule } from '@mcp-z/server';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\nimport { google } from 'googleapis';\nimport { z } from 'zod';\nimport { buildRfc822FromArgs } from '../../email/composition/rfc822-builder.ts';\nimport { b64url } from '../../lib/base64-encoding.ts';\n\nconst inputSchema = z.object({\n to: createEmailRecipientsSchema('to', true),\n cc: createEmailRecipientsSchema('cc', false),\n bcc: createEmailRecipientsSchema('bcc', false),\n subject: z.string().describe('Message subject').default(''),\n body: z.string().trim().min(1).describe('Email body content (plain text or HTML)'),\n contentType: ComposeContentTypeSchema,\n});\n\n// Success branch schema\nconst successBranchSchema = z.object({\n type: z.literal('success'),\n item: createMessageResultSchema('gmail'),\n});\n\n// Output schema with auth_required support\nconst outputSchema = z.discriminatedUnion('type', [successBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Send an email message through Gmail',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\nasync function handler(params: Input, extra: EnrichedExtra) {\n const { to, cc, bcc, subject, body, contentType } = params;\n const logger = extra.logger;\n logger.info('gmail.message.send called', { to, subject, contentType });\n\n if (!to) {\n throw new McpError(ErrorCode.InvalidParams, 'Missing required field: to');\n }\n\n try {\n // Build RFC822 message - only include optional headers when defined to satisfy\n // exactOptionalPropertyTypes (avoid passing `undefined` into optional props)\n const msgArgs: { to: string; cc?: string; bcc?: string; subject?: string; body: string; contentType: 'text' | 'html' } = { to, body, contentType };\n if (cc !== undefined) msgArgs.cc = cc;\n if (bcc !== undefined) msgArgs.bcc = bcc;\n if (subject !== undefined) msgArgs.subject = subject;\n\n const raw = buildRfc822FromArgs(msgArgs);\n const encodedMessage = b64url(raw);\n\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n\n const sendRes = await gmail.users.messages.send({\n userId: 'me',\n requestBody: { raw: encodedMessage },\n });\n const sendData = sendRes.data;\n const messageId = sendData.id || 'unknown';\n logger.info('Gmail: sent message successfully', { id: messageId });\n logger.info('gmail.message.send result (typed)', { to, subject, messageId });\n\n const totalRecipients = (Array.isArray(to) ? to.length : 1) + (cc ? (Array.isArray(cc) ? cc.length : 1) : 0) + (bcc ? (Array.isArray(bcc) ? bcc.length : 1) : 0);\n const result: Output = {\n type: 'success' as const,\n item: {\n id: messageId,\n sentAt: new Date().toISOString(),\n recipientCount: totalRecipients,\n webLink: `https://mail.google.com/mail/u/0/#sent/${messageId}`,\n },\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail.message.send error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error sending message: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'message-send',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["ComposeContentTypeSchema","createEmailRecipientsSchema","createMessageResultSchema","schemas","AuthRequiredBranchSchema","ErrorCode","McpError","google","z","buildRfc822FromArgs","b64url","inputSchema","object","to","cc","bcc","subject","string","describe","default","body","trim","min","contentType","successBranchSchema","type","literal","item","outputSchema","discriminatedUnion","config","description","result","handler","params","extra","logger","info","InvalidParams","msgArgs","undefined","raw","encodedMessage","gmail","version","auth","authContext","sendRes","users","messages","send","userId","requestBody","sendData","data","messageId","id","totalRecipients","Array","isArray","length","sentAt","Date","toISOString","recipientCount","webLink","content","text","JSON","stringify","structuredContent","error","message","Error","String","InternalError","stack","createTool","name"],"mappings":"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"}
|
|
@@ -2,7 +2,7 @@ import type { EnrichedExtra } from '@mcp-z/oauth-google';
|
|
|
2
2
|
import { z } from 'zod';
|
|
3
3
|
import type { StorageExtra } from '../../types.js';
|
|
4
4
|
declare const inputSchema: z.ZodObject<{
|
|
5
|
-
query: z.ZodOptional<z.ZodType<import("../../types.js").GmailQuery, unknown, z.core.$ZodTypeInternals<import("../../types.js").GmailQuery, unknown>>>;
|
|
5
|
+
query: z.ZodOptional<z.ZodType<string | import("../../types.js").GmailQuery, unknown, z.core.$ZodTypeInternals<string | import("../../types.js").GmailQuery, unknown>>>;
|
|
6
6
|
maxItems: z.ZodDefault<z.ZodNumber>;
|
|
7
7
|
filename: z.ZodDefault<z.ZodString>;
|
|
8
8
|
contentType: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
@@ -55,7 +55,7 @@ export default function createTool(): {
|
|
|
55
55
|
config: {
|
|
56
56
|
readonly description: "Export Gmail messages to CSV with streaming pagination. Returns file URI. Use query-syntax prompt for query reference.";
|
|
57
57
|
readonly inputSchema: z.ZodObject<{
|
|
58
|
-
query: z.ZodOptional<z.ZodType<import("../../types.js").GmailQuery, unknown, z.core.$ZodTypeInternals<import("../../types.js").GmailQuery, unknown>>>;
|
|
58
|
+
query: z.ZodOptional<z.ZodType<string | import("../../types.js").GmailQuery, unknown, z.core.$ZodTypeInternals<string | import("../../types.js").GmailQuery, unknown>>>;
|
|
59
59
|
maxItems: z.ZodDefault<z.ZodNumber>;
|
|
60
60
|
filename: z.ZodDefault<z.ZodString>;
|
|
61
61
|
contentType: z.ZodDefault<z.ZodOptional<z.ZodEnum<{
|
|
@@ -11,7 +11,7 @@ import { z } from 'zod';
|
|
|
11
11
|
import { DEFAULT_PAGE_SIZE } from '../../constants.js';
|
|
12
12
|
import { extractBodyFromPayload } from '../../email/parsing/html-processing.js';
|
|
13
13
|
import { executeQuery as executeGmailQuery } from '../../email/querying/execute-query.js';
|
|
14
|
-
import { GmailQueryParameterSchema } from '../../schemas/gmail-query-schema.js';
|
|
14
|
+
import { GmailQueryParameterSchema, parseGmailQueryParameter } from '../../schemas/gmail-query-schema.js';
|
|
15
15
|
const DEFAULT_MAX_ITEMS = 10000;
|
|
16
16
|
const MAX_EXPORT_ITEMS = 50000;
|
|
17
17
|
const inputSchema = z.object({
|
|
@@ -52,12 +52,6 @@ const config = {
|
|
|
52
52
|
const logger = extra.logger;
|
|
53
53
|
const { storageContext } = extra;
|
|
54
54
|
const { transport, resourceStoreUri, baseUrl } = storageContext;
|
|
55
|
-
logger.info('gmail.messages.export-csv called', {
|
|
56
|
-
query,
|
|
57
|
-
maxItems,
|
|
58
|
-
filename,
|
|
59
|
-
accountId: extra.authContext.accountId
|
|
60
|
-
});
|
|
61
55
|
// Reserve file location for streaming write (creates directory, generates ID, formats filename)
|
|
62
56
|
const reservation = await reserveFile(filename, {
|
|
63
57
|
resourceStoreUri
|
|
@@ -68,6 +62,13 @@ const config = {
|
|
|
68
62
|
maxItems
|
|
69
63
|
});
|
|
70
64
|
try {
|
|
65
|
+
const parsedQuery = parseGmailQueryParameter(query);
|
|
66
|
+
logger.info('gmail.messages.export-csv called', {
|
|
67
|
+
query: parsedQuery,
|
|
68
|
+
maxItems,
|
|
69
|
+
filename,
|
|
70
|
+
accountId: extra.authContext.accountId
|
|
71
|
+
});
|
|
71
72
|
const gmail = google.gmail({
|
|
72
73
|
version: 'v1',
|
|
73
74
|
auth: extra.authContext.auth
|
|
@@ -109,7 +110,7 @@ const config = {
|
|
|
109
110
|
var _exec_metadata;
|
|
110
111
|
const remainingItems = maxItems - totalRows;
|
|
111
112
|
const pageSize = Math.min(remainingItems, DEFAULT_PAGE_SIZE);
|
|
112
|
-
const exec = await executeGmailQuery(
|
|
113
|
+
const exec = await executeGmailQuery(parsedQuery, {
|
|
113
114
|
client: gmail,
|
|
114
115
|
logger,
|
|
115
116
|
pageSize,
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"sources":["/Users/kevin/Dev/Projects/mcp-z/mcp-gmail/src/mcp/tools/messages-export-csv.ts"],"sourcesContent":["import { EmailContentTypeSchema, ExcludeThreadHistorySchema } from '@mcp-z/email';\nimport type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport { getFileUri, reserveFile, type ToolModule } from '@mcp-z/server';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\n\nimport { stringify } from 'csv-stringify/sync';\nimport { createWriteStream } from 'fs';\nimport { unlink } from 'fs/promises';\nimport { type gmail_v1, google } from 'googleapis';\nimport { z } from 'zod';\nimport { DEFAULT_PAGE_SIZE } from '../../constants.ts';\nimport { extractBodyFromPayload } from '../../email/parsing/html-processing.ts';\nimport { executeQuery as executeGmailQuery } from '../../email/querying/execute-query.ts';\nimport { GmailQueryParameterSchema } from '../../schemas/gmail-query-schema.ts';\nimport type { StorageExtra } from '../../types.ts';\n\nconst DEFAULT_MAX_ITEMS = 10000;\nconst MAX_EXPORT_ITEMS = 50000;\n\n/**\n * CSV row format based on EmailDetail\n * All fields are strings (empty string instead of undefined)\n * Includes additional CSV-specific fields: provider and labels\n */\ninterface CsvRow {\n id: string;\n threadId: string;\n from: string;\n to: string;\n cc: string;\n bcc: string;\n subject: string;\n date: string;\n snippet: string;\n body: string;\n provider: string;\n labels: string;\n}\n\nconst inputSchema = z.object({\n query: GmailQueryParameterSchema.optional().describe('Structured query object or JSON string for filtering messages. Use query-syntax prompt for reference and rawGmailQuery for Gmail syntax.'),\n maxItems: z.number().int().positive().max(MAX_EXPORT_ITEMS).default(DEFAULT_MAX_ITEMS).describe(`Maximum messages to export (default: ${DEFAULT_MAX_ITEMS}, max: ${MAX_EXPORT_ITEMS})`),\n filename: z.string().trim().min(1).default('gmail-messages.csv').describe('Output filename (default: gmail-messages.csv)'),\n contentType: EmailContentTypeSchema,\n excludeThreadHistory: ExcludeThreadHistorySchema,\n});\n\nconst successBranchSchema = z.object({\n type: z.literal('success'),\n uri: z.string().describe('File URI (file:// or http://)'),\n filename: z.string().describe('Stored filename'),\n rowCount: z.number().describe('Number of messages exported'),\n truncated: z.boolean().describe('Whether export was truncated at maxItems'),\n});\n\nconst outputSchema = z.discriminatedUnion('type', [successBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Export Gmail messages to CSV with streaming pagination. Returns file URI. Use query-syntax prompt for query reference.',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\n/**\n * Handler for gmail-messages-export-csv tool\n *\n * CRITICAL: Streaming implementation per user requirements\n * - Generate UUID upfront\n * - Write CSV header immediately\n * - Append rows as batches arrive\n * - Delete partial file on error\n * - NO RETRIES (fail fast on error)\n */\nasync function handler({ query, maxItems, filename, contentType, excludeThreadHistory }: Input, extra: EnrichedExtra & StorageExtra) {\n const logger = extra.logger;\n const { storageContext } = extra;\n const { transport, resourceStoreUri, baseUrl } = storageContext;\n\n logger.info('gmail.messages.export-csv called', {\n query,\n maxItems,\n filename,\n accountId: extra.authContext.accountId,\n });\n\n // Reserve file location for streaming write (creates directory, generates ID, formats filename)\n const reservation = await reserveFile(filename, {\n resourceStoreUri,\n });\n const { storedName, fullPath } = reservation;\n\n logger.info('gmail.messages.export-csv starting streaming export', { path: fullPath, maxItems });\n\n try {\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n\n // Create CSV headers (all email fields)\n const csvHeaders = ['id', 'threadId', 'from', 'to', 'cc', 'bcc', 'subject', 'date', 'snippet', 'body', 'provider', 'labels'];\n\n // Create write stream and write headers immediately\n const writeStream = createWriteStream(fullPath, { encoding: 'utf-8' });\n const headerLine = stringify([csvHeaders], { header: false, quoted: true, quote: '\"', escape: '\"' });\n writeStream.write(headerLine);\n\n // Internal pagination loop - append to CSV with each batch\n // NO RETRIES: If any error occurs, fail the whole operation and clean up\n let totalRows = 0;\n let nextPageToken: string | undefined;\n const started = Date.now();\n\n while (totalRows < maxItems) {\n const remainingItems = maxItems - totalRows;\n const pageSize = Math.min(remainingItems, DEFAULT_PAGE_SIZE);\n\n const exec: {\n items: CsvRow[];\n metadata?: { nextPageToken?: string };\n } = await executeGmailQuery(\n query,\n {\n client: gmail,\n logger,\n pageSize,\n ...(nextPageToken !== undefined && { pageToken: nextPageToken }),\n includeBody: true, // Always include body for CSV export\n },\n (full: unknown): CsvRow => {\n // Type-safe property access with guards\n const fullData = full as {\n id?: unknown;\n threadId?: unknown;\n snippet?: unknown;\n labelIds?: unknown[];\n payload?: { headers?: unknown[] };\n };\n\n const headersArray = Array.isArray(fullData?.payload?.headers) ? fullData.payload.headers : [];\n const headers = Object.fromEntries(\n headersArray.map((h: unknown) => {\n const header = h as { name?: unknown; value?: unknown };\n return [String(header.name ?? ''), String(header.value ?? '')];\n })\n );\n\n const payload = fullData?.payload;\n // Cast to Schema$MessagePart for extractBodyFromPayload\n const body = payload ? extractBodyFromPayload(payload as gmail_v1.Schema$MessagePart, { contentType, excludeThreadHistory }) : '';\n\n const labelIds = Array.isArray(fullData?.labelIds) ? fullData.labelIds.map((id) => String(id ?? '')) : [];\n\n return {\n id: String(fullData?.id ?? ''),\n threadId: fullData?.threadId ? String(fullData.threadId) : '',\n from: headers?.From || '',\n to: headers?.To || '',\n cc: headers?.Cc || '',\n bcc: headers?.Bcc || '',\n subject: headers?.Subject || '',\n date: headers?.Date || '',\n snippet: fullData?.snippet ? String(fullData.snippet) : '',\n body,\n provider: 'gmail',\n labels: labelIds.join(';'),\n };\n }\n );\n\n // Type-safe CSV row mapping\n const csvRows = exec.items.map((row) => {\n return [row.id, row.threadId, row.from, row.to, row.cc, row.bcc, row.subject, row.date, row.snippet, row.body, row.provider, row.labels];\n });\n\n // Append rows to CSV file immediately\n if (csvRows.length > 0) {\n const rowsContent = stringify(csvRows, { header: false, quoted: true, quote: '\"', escape: '\"' });\n writeStream.write(rowsContent);\n }\n\n totalRows += exec.items.length;\n nextPageToken = exec.metadata?.nextPageToken;\n\n logger.info('gmail.messages.export-csv batch written', {\n batchSize: exec.items.length,\n totalRows,\n hasMore: Boolean(nextPageToken),\n });\n\n // Exit if no more results or reached maxItems\n if (!nextPageToken || exec.items.length === 0) {\n break;\n }\n }\n\n // Close write stream\n await new Promise<void>((resolve, reject) => {\n writeStream.end(() => resolve());\n writeStream.on('error', reject);\n });\n\n const durationMs = Date.now() - started;\n const truncated = totalRows >= maxItems && Boolean(nextPageToken);\n\n logger.info('gmail.messages.export-csv completed', {\n rowCount: totalRows,\n truncated,\n durationMs,\n filename: storedName,\n });\n\n // Generate URI based on transport type (stdio: file://, HTTP: http://)\n const uri = getFileUri(storedName, transport, {\n resourceStoreUri,\n ...(baseUrl && { baseUrl }),\n endpoint: '/files',\n });\n\n const result: Output = {\n type: 'success' as const,\n uri,\n filename: storedName,\n rowCount: totalRows,\n truncated,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n // CRITICAL: Clean up partial CSV file on error\n try {\n await unlink(fullPath);\n logger.debug('Cleaned up partial CSV file after error', { path: fullPath });\n } catch (_cleanupError) {\n logger.debug('Could not clean up CSV file (may not exist)', { path: fullPath });\n }\n\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail.messages.export-csv error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error exporting messages to CSV: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'messages-export-csv',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["EmailContentTypeSchema","ExcludeThreadHistorySchema","schemas","AuthRequiredBranchSchema","getFileUri","reserveFile","ErrorCode","McpError","stringify","createWriteStream","unlink","google","z","DEFAULT_PAGE_SIZE","extractBodyFromPayload","executeQuery","executeGmailQuery","GmailQueryParameterSchema","DEFAULT_MAX_ITEMS","MAX_EXPORT_ITEMS","inputSchema","object","query","optional","describe","maxItems","number","int","positive","max","default","filename","string","trim","min","contentType","excludeThreadHistory","successBranchSchema","type","literal","uri","rowCount","truncated","boolean","outputSchema","discriminatedUnion","config","description","result","handler","extra","logger","storageContext","transport","resourceStoreUri","baseUrl","info","accountId","authContext","reservation","storedName","fullPath","path","gmail","version","auth","csvHeaders","writeStream","encoding","headerLine","header","quoted","quote","escape","write","totalRows","nextPageToken","started","Date","now","exec","remainingItems","pageSize","Math","client","undefined","pageToken","includeBody","full","fullData","headersArray","Array","isArray","payload","headers","Object","fromEntries","map","h","String","name","value","body","labelIds","id","threadId","from","From","to","To","cc","Cc","bcc","Bcc","subject","Subject","date","snippet","provider","labels","join","csvRows","items","row","length","rowsContent","metadata","batchSize","hasMore","Boolean","Promise","resolve","reject","end","on","durationMs","endpoint","content","text","JSON","structuredContent","error","debug","_cleanupError","message","Error","InternalError","stack","createTool"],"mappings":"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"}
|
|
1
|
+
{"version":3,"sources":["/Users/kevin/Dev/OpenSource/ai/mcp-z/mcp-gmail/src/mcp/tools/messages-export-csv.ts"],"sourcesContent":["import { EmailContentTypeSchema, ExcludeThreadHistorySchema } from '@mcp-z/email';\nimport type { EnrichedExtra } from '@mcp-z/oauth-google';\nimport { schemas } from '@mcp-z/oauth-google';\n\nconst { AuthRequiredBranchSchema } = schemas;\n\nimport { getFileUri, reserveFile, type ToolModule } from '@mcp-z/server';\nimport { ErrorCode, McpError } from '@modelcontextprotocol/sdk/types.js';\n\nimport { stringify } from 'csv-stringify/sync';\nimport { createWriteStream } from 'fs';\nimport { unlink } from 'fs/promises';\nimport { type gmail_v1, google } from 'googleapis';\nimport { z } from 'zod';\nimport { DEFAULT_PAGE_SIZE } from '../../constants.ts';\nimport { extractBodyFromPayload } from '../../email/parsing/html-processing.ts';\nimport { executeQuery as executeGmailQuery } from '../../email/querying/execute-query.ts';\nimport { GmailQueryParameterSchema, parseGmailQueryParameter } from '../../schemas/gmail-query-schema.ts';\nimport type { StorageExtra } from '../../types.ts';\n\nconst DEFAULT_MAX_ITEMS = 10000;\nconst MAX_EXPORT_ITEMS = 50000;\n\n/**\n * CSV row format based on EmailDetail\n * All fields are strings (empty string instead of undefined)\n * Includes additional CSV-specific fields: provider and labels\n */\ninterface CsvRow {\n id: string;\n threadId: string;\n from: string;\n to: string;\n cc: string;\n bcc: string;\n subject: string;\n date: string;\n snippet: string;\n body: string;\n provider: string;\n labels: string;\n}\n\nconst inputSchema = z.object({\n query: GmailQueryParameterSchema.optional().describe('Structured query object or JSON string for filtering messages. Use query-syntax prompt for reference and rawGmailQuery for Gmail syntax.'),\n maxItems: z.number().int().positive().max(MAX_EXPORT_ITEMS).default(DEFAULT_MAX_ITEMS).describe(`Maximum messages to export (default: ${DEFAULT_MAX_ITEMS}, max: ${MAX_EXPORT_ITEMS})`),\n filename: z.string().trim().min(1).default('gmail-messages.csv').describe('Output filename (default: gmail-messages.csv)'),\n contentType: EmailContentTypeSchema,\n excludeThreadHistory: ExcludeThreadHistorySchema,\n});\n\nconst successBranchSchema = z.object({\n type: z.literal('success'),\n uri: z.string().describe('File URI (file:// or http://)'),\n filename: z.string().describe('Stored filename'),\n rowCount: z.number().describe('Number of messages exported'),\n truncated: z.boolean().describe('Whether export was truncated at maxItems'),\n});\n\nconst outputSchema = z.discriminatedUnion('type', [successBranchSchema, AuthRequiredBranchSchema]);\n\nconst config = {\n description: 'Export Gmail messages to CSV with streaming pagination. Returns file URI. Use query-syntax prompt for query reference.',\n inputSchema: inputSchema,\n outputSchema: z.object({\n result: outputSchema,\n }),\n} as const;\n\nexport type Input = z.infer<typeof inputSchema>;\nexport type Output = z.infer<typeof outputSchema>;\n\n/**\n * Handler for gmail-messages-export-csv tool\n *\n * CRITICAL: Streaming implementation per user requirements\n * - Generate UUID upfront\n * - Write CSV header immediately\n * - Append rows as batches arrive\n * - Delete partial file on error\n * - NO RETRIES (fail fast on error)\n */\nasync function handler({ query, maxItems, filename, contentType, excludeThreadHistory }: Input, extra: EnrichedExtra & StorageExtra) {\n const logger = extra.logger;\n const { storageContext } = extra;\n const { transport, resourceStoreUri, baseUrl } = storageContext;\n\n // Reserve file location for streaming write (creates directory, generates ID, formats filename)\n const reservation = await reserveFile(filename, {\n resourceStoreUri,\n });\n const { storedName, fullPath } = reservation;\n\n logger.info('gmail.messages.export-csv starting streaming export', { path: fullPath, maxItems });\n\n try {\n const parsedQuery = parseGmailQueryParameter(query);\n\n logger.info('gmail.messages.export-csv called', {\n query: parsedQuery,\n maxItems,\n filename,\n accountId: extra.authContext.accountId,\n });\n\n const gmail = google.gmail({ version: 'v1', auth: extra.authContext.auth });\n\n // Create CSV headers (all email fields)\n const csvHeaders = ['id', 'threadId', 'from', 'to', 'cc', 'bcc', 'subject', 'date', 'snippet', 'body', 'provider', 'labels'];\n\n // Create write stream and write headers immediately\n const writeStream = createWriteStream(fullPath, { encoding: 'utf-8' });\n const headerLine = stringify([csvHeaders], { header: false, quoted: true, quote: '\"', escape: '\"' });\n writeStream.write(headerLine);\n\n // Internal pagination loop - append to CSV with each batch\n // NO RETRIES: If any error occurs, fail the whole operation and clean up\n let totalRows = 0;\n let nextPageToken: string | undefined;\n const started = Date.now();\n\n while (totalRows < maxItems) {\n const remainingItems = maxItems - totalRows;\n const pageSize = Math.min(remainingItems, DEFAULT_PAGE_SIZE);\n\n const exec: {\n items: CsvRow[];\n metadata?: { nextPageToken?: string };\n } = await executeGmailQuery(\n parsedQuery,\n {\n client: gmail,\n logger,\n pageSize,\n ...(nextPageToken !== undefined && { pageToken: nextPageToken }),\n includeBody: true, // Always include body for CSV export\n },\n (full: unknown): CsvRow => {\n // Type-safe property access with guards\n const fullData = full as {\n id?: unknown;\n threadId?: unknown;\n snippet?: unknown;\n labelIds?: unknown[];\n payload?: { headers?: unknown[] };\n };\n\n const headersArray = Array.isArray(fullData?.payload?.headers) ? fullData.payload.headers : [];\n const headers = Object.fromEntries(\n headersArray.map((h: unknown) => {\n const header = h as { name?: unknown; value?: unknown };\n return [String(header.name ?? ''), String(header.value ?? '')];\n })\n );\n\n const payload = fullData?.payload;\n // Cast to Schema$MessagePart for extractBodyFromPayload\n const body = payload ? extractBodyFromPayload(payload as gmail_v1.Schema$MessagePart, { contentType, excludeThreadHistory }) : '';\n\n const labelIds = Array.isArray(fullData?.labelIds) ? fullData.labelIds.map((id) => String(id ?? '')) : [];\n\n return {\n id: String(fullData?.id ?? ''),\n threadId: fullData?.threadId ? String(fullData.threadId) : '',\n from: headers?.From || '',\n to: headers?.To || '',\n cc: headers?.Cc || '',\n bcc: headers?.Bcc || '',\n subject: headers?.Subject || '',\n date: headers?.Date || '',\n snippet: fullData?.snippet ? String(fullData.snippet) : '',\n body,\n provider: 'gmail',\n labels: labelIds.join(';'),\n };\n }\n );\n\n // Type-safe CSV row mapping\n const csvRows = exec.items.map((row) => {\n return [row.id, row.threadId, row.from, row.to, row.cc, row.bcc, row.subject, row.date, row.snippet, row.body, row.provider, row.labels];\n });\n\n // Append rows to CSV file immediately\n if (csvRows.length > 0) {\n const rowsContent = stringify(csvRows, { header: false, quoted: true, quote: '\"', escape: '\"' });\n writeStream.write(rowsContent);\n }\n\n totalRows += exec.items.length;\n nextPageToken = exec.metadata?.nextPageToken;\n\n logger.info('gmail.messages.export-csv batch written', {\n batchSize: exec.items.length,\n totalRows,\n hasMore: Boolean(nextPageToken),\n });\n\n // Exit if no more results or reached maxItems\n if (!nextPageToken || exec.items.length === 0) {\n break;\n }\n }\n\n // Close write stream\n await new Promise<void>((resolve, reject) => {\n writeStream.end(() => resolve());\n writeStream.on('error', reject);\n });\n\n const durationMs = Date.now() - started;\n const truncated = totalRows >= maxItems && Boolean(nextPageToken);\n\n logger.info('gmail.messages.export-csv completed', {\n rowCount: totalRows,\n truncated,\n durationMs,\n filename: storedName,\n });\n\n // Generate URI based on transport type (stdio: file://, HTTP: http://)\n const uri = getFileUri(storedName, transport, {\n resourceStoreUri,\n ...(baseUrl && { baseUrl }),\n endpoint: '/files',\n });\n\n const result: Output = {\n type: 'success' as const,\n uri,\n filename: storedName,\n rowCount: totalRows,\n truncated,\n };\n\n return {\n content: [\n {\n type: 'text' as const,\n text: JSON.stringify(result),\n },\n ],\n structuredContent: { result },\n };\n } catch (error) {\n // CRITICAL: Clean up partial CSV file on error\n try {\n await unlink(fullPath);\n logger.debug('Cleaned up partial CSV file after error', { path: fullPath });\n } catch (_cleanupError) {\n logger.debug('Could not clean up CSV file (may not exist)', { path: fullPath });\n }\n\n const message = error instanceof Error ? error.message : String(error);\n logger.error('gmail.messages.export-csv error', { error: message });\n\n throw new McpError(ErrorCode.InternalError, `Error exporting messages to CSV: ${message}`, {\n stack: error instanceof Error ? error.stack : undefined,\n });\n }\n}\n\nexport default function createTool() {\n return {\n name: 'messages-export-csv',\n config,\n handler,\n } satisfies ToolModule;\n}\n"],"names":["EmailContentTypeSchema","ExcludeThreadHistorySchema","schemas","AuthRequiredBranchSchema","getFileUri","reserveFile","ErrorCode","McpError","stringify","createWriteStream","unlink","google","z","DEFAULT_PAGE_SIZE","extractBodyFromPayload","executeQuery","executeGmailQuery","GmailQueryParameterSchema","parseGmailQueryParameter","DEFAULT_MAX_ITEMS","MAX_EXPORT_ITEMS","inputSchema","object","query","optional","describe","maxItems","number","int","positive","max","default","filename","string","trim","min","contentType","excludeThreadHistory","successBranchSchema","type","literal","uri","rowCount","truncated","boolean","outputSchema","discriminatedUnion","config","description","result","handler","extra","logger","storageContext","transport","resourceStoreUri","baseUrl","reservation","storedName","fullPath","info","path","parsedQuery","accountId","authContext","gmail","version","auth","csvHeaders","writeStream","encoding","headerLine","header","quoted","quote","escape","write","totalRows","nextPageToken","started","Date","now","exec","remainingItems","pageSize","Math","client","undefined","pageToken","includeBody","full","fullData","headersArray","Array","isArray","payload","headers","Object","fromEntries","map","h","String","name","value","body","labelIds","id","threadId","from","From","to","To","cc","Cc","bcc","Bcc","subject","Subject","date","snippet","provider","labels","join","csvRows","items","row","length","rowsContent","metadata","batchSize","hasMore","Boolean","Promise","resolve","reject","end","on","durationMs","endpoint","content","text","JSON","structuredContent","error","debug","_cleanupError","message","Error","InternalError","stack","createTool"],"mappings":"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"}
|
|
@@ -21,8 +21,9 @@ import { z } from 'zod';
|
|
|
21
21
|
* TypeScript sees the strict GmailQuery type everywhere the schema is used.
|
|
22
22
|
*/
|
|
23
23
|
export declare const GmailQuerySchema: z.ZodType<GmailQuery>;
|
|
24
|
-
export declare const GmailQueryParameterSchema: z.ZodType<GmailQuery>;
|
|
24
|
+
export declare const GmailQueryParameterSchema: z.ZodType<GmailQuery | string>;
|
|
25
25
|
export type GmailQueryParameter = z.infer<typeof GmailQueryParameterSchema>;
|
|
26
|
+
export declare function parseGmailQueryParameter(input: GmailQuery | string | undefined): GmailQuery | undefined;
|
|
26
27
|
export type GmailQuery = BaseEmailQueryFields & {
|
|
27
28
|
$and?: GmailQuery[];
|
|
28
29
|
$or?: GmailQuery[];
|
|
@@ -70,27 +70,24 @@ import { z } from 'zod';
|
|
|
70
70
|
// Raw Gmail query string - escape hatch for advanced syntax
|
|
71
71
|
rawGmailQuery: z.string().min(1).optional().describe('Raw Gmail query syntax for advanced use cases. Bypasses schema validation - use sparingly.')
|
|
72
72
|
}).strict());
|
|
73
|
-
export const GmailQueryParameterSchema = z.
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
code: z.ZodIssueCode.custom,
|
|
82
|
-
message: `Query must be valid JSON. ${message}. Wrap Gmail syntax in {"rawGmailQuery":"<query>"} if needed.`
|
|
83
|
-
});
|
|
84
|
-
return z.NEVER;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
const validated = GmailQuerySchema.safeParse(parsed);
|
|
73
|
+
export const GmailQueryParameterSchema = z.union([
|
|
74
|
+
GmailQuerySchema,
|
|
75
|
+
z.string().min(1)
|
|
76
|
+
]);
|
|
77
|
+
export function parseGmailQueryParameter(input) {
|
|
78
|
+
if (input === undefined) return undefined;
|
|
79
|
+
const raw = typeof input === 'string' ? safeJsonParse(input, 'rawGmailQuery') : input;
|
|
80
|
+
const validated = GmailQuerySchema.safeParse(raw);
|
|
88
81
|
if (!validated.success) {
|
|
89
|
-
|
|
90
|
-
code: z.ZodIssueCode.custom,
|
|
91
|
-
message: `Invalid query JSON: ${validated.error.message}. Use {"rawGmailQuery":"<query>"} for Gmail syntax.`
|
|
92
|
-
});
|
|
93
|
-
return z.NEVER;
|
|
82
|
+
throw new Error(`Invalid query JSON: ${validated.error.message}. Use {"rawGmailQuery":"<query>"} for Gmail syntax.`);
|
|
94
83
|
}
|
|
95
84
|
return validated.data;
|
|
96
|
-
}
|
|
85
|
+
}
|
|
86
|
+
function safeJsonParse(value, rawField) {
|
|
87
|
+
try {
|
|
88
|
+
return JSON.parse(value);
|
|
89
|
+
} catch (error) {
|
|
90
|
+
const message = error instanceof Error ? error.message : 'Invalid JSON';
|
|
91
|
+
throw new Error(`Query must be valid JSON. ${message}. Wrap Gmail syntax in {"${rawField}":"<query>"} if needed.`);
|
|
92
|
+
}
|
|
93
|
+
}
|