Spaces:
Sleeping
Sleeping
File size: 3,848 Bytes
7dc28be | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 | import type { FastMCP } from 'fastmcp';
import { UserError } from 'fastmcp';
import { z } from 'zod';
import { gmail_v1 } from 'googleapis';
import { getGmailClient } from '../../clients.js';
import { findHeaderValue, extractMessageBody } from './helpers.js';
function collectAttachments(payload?: gmail_v1.Schema$MessagePart): Array<{
partId: string | null;
filename: string;
mimeType: string;
size: number;
attachmentId: string | null;
}> {
const out: ReturnType<typeof collectAttachments> = [];
if (!payload) return out;
const walk = (part: gmail_v1.Schema$MessagePart) => {
if (part.filename && part.body?.attachmentId) {
out.push({
partId: part.partId ?? null,
filename: part.filename,
mimeType: part.mimeType ?? 'application/octet-stream',
size: part.body.size ?? 0,
attachmentId: part.body.attachmentId ?? null,
});
}
if (part.parts) for (const sub of part.parts) walk(sub);
};
walk(payload);
return out;
}
export function register(server: FastMCP) {
server.addTool({
name: 'getMessage',
description:
'Fetches a single Gmail message by ID with headers, decoded plain-text body, HTML body, and a list of attachments (metadata only). Use listMessages to discover message IDs.',
parameters: z.strictObject({
messageId: z.string().describe('The Gmail message ID, typically from listMessages results.'),
format: z
.enum(['full', 'metadata', 'minimal'])
.optional()
.default('full')
.describe(
'"full" returns headers + body + attachments; "metadata" returns headers only; "minimal" returns just labels/snippet.'
),
}),
execute: async (args, { log }) => {
const gmail = await getGmailClient();
log.info(`Getting Gmail message ${args.messageId} (format=${args.format})`);
try {
const response = await gmail.users.messages.get({
userId: 'me',
id: args.messageId,
format: args.format,
});
const msg = response.data;
const headers = msg.payload?.headers;
const base = {
id: msg.id,
threadId: msg.threadId,
labelIds: msg.labelIds ?? [],
snippet: msg.snippet ?? '',
historyId: msg.historyId,
internalDate: msg.internalDate,
sizeEstimate: msg.sizeEstimate,
};
if (args.format === 'minimal') {
return JSON.stringify(base, null, 2);
}
const headerSummary = {
from: findHeaderValue(headers, 'From'),
to: findHeaderValue(headers, 'To'),
cc: findHeaderValue(headers, 'Cc'),
bcc: findHeaderValue(headers, 'Bcc'),
subject: findHeaderValue(headers, 'Subject'),
date: findHeaderValue(headers, 'Date'),
messageIdHeader: findHeaderValue(headers, 'Message-Id'),
};
if (args.format === 'metadata') {
return JSON.stringify({ ...base, headers: headerSummary }, null, 2);
}
const { text, html } = extractMessageBody(msg.payload);
const attachments = collectAttachments(msg.payload);
return JSON.stringify(
{
...base,
headers: headerSummary,
body: { text, html },
attachments,
},
null,
2
);
} catch (error: any) {
log.error(`Error getting Gmail message: ${error.message || error}`);
if (error.code === 404)
throw new UserError(`Gmail message not found (ID: ${args.messageId}).`);
if (error.code === 403)
throw new UserError('Permission denied. Confirm the gmail.modify scope was granted.');
throw new UserError(`Failed to get Gmail message: ${error.message || 'Unknown error'}`);
}
},
});
}
|