uhbarp-gmail-mcp-server
Version:
Gmail MCP Server for managing Gmail through natural language interactions with full OAuth2 authentication support
1,201 lines (1,197 loc) ⢠65 kB
JavaScript
/**
* Gmail MCP Server
* A comprehensive server implementation using Model Context Protocol for Gmail API
*/
import { Server } from "@modelcontextprotocol/sdk/server/index.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { CallToolRequestSchema, ListResourcesRequestSchema, ListToolsRequestSchema, ReadResourceRequestSchema } from "@modelcontextprotocol/sdk/types.js";
import { gmailAuth } from "./utils/gmail-auth.js";
import { gmailOperations } from "./utils/gmail-operations.js";
import { multiUserAuth } from "./utils/multi-user-auth.js";
import { logger } from './utils/api.js';
let gmailConfig;
function parseArgs() {
const args = process.argv.slice(2);
const config = {
setupAuth: false,
resetAuth: false,
debug: false,
nonInteractive: false,
multiUser: false
};
for (let i = 0; i < args.length; i++) {
const arg = args[i];
if (arg === '--setup-auth') {
config.setupAuth = true;
}
else if (arg === '--reset-auth') {
config.resetAuth = true;
}
else if (arg === '--debug' || arg === '-d') {
config.debug = true;
}
else if (arg === '--non-interactive' || arg === '-n') {
config.nonInteractive = true;
}
else if (arg === '--multi-user') {
config.multiUser = true;
}
}
return config;
}
const server = new Server({
name: "gmail-mcp-server",
version: "1.0.0"
}, {
capabilities: {
resources: {
read: true,
list: true
},
tools: {
list: true,
call: true
}
}
});
// Set up the resource listing request handler
server.setRequestHandler(ListResourcesRequestSchema, async () => {
logger.log('Received list resources request');
return { resources: [] };
});
/**
* Handler for reading resource information.
*/
server.setRequestHandler(ReadResourceRequestSchema, async (request) => {
logger.log('Received read resource request: ' + JSON.stringify(request));
throw new Error("Resource reading not implemented");
});
/**
* List available tools for interacting with Gmail.
*/
server.setRequestHandler(ListToolsRequestSchema, async () => {
const baseTools = [
{
name: "send_email",
description: "Send an email message with support for HTML content, attachments, and international characters. Supports both plain text and HTML emails with proper multipart structure.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
to: {
type: "array",
items: { type: "string" },
description: "List of recipient email addresses"
},
cc: {
type: "array",
items: { type: "string" },
description: "List of CC recipient email addresses (optional)"
},
bcc: {
type: "array",
items: { type: "string" },
description: "List of BCC recipient email addresses (optional)"
},
subject: {
type: "string",
description: "Email subject line with full support for international characters"
},
text: {
type: "string",
description: "Plain text content of the email (optional if html is provided)"
},
html: {
type: "string",
description: "HTML content of the email (optional if text is provided)"
},
replyTo: {
type: "string",
description: "Reply-to email address (optional)"
},
attachments: {
type: "array",
items: {
type: "object",
properties: {
filename: {
type: "string",
description: "Name of the attachment file"
},
content: {
type: "string",
description: "Base64 encoded content of the attachment or file path"
},
contentType: {
type: "string",
description: "MIME type of the attachment (optional, auto-detected if not provided)"
}
},
required: ["filename", "content"]
},
description: "List of email attachments (optional)"
}
},
required: ["to", "subject"],
additionalProperties: false
}
},
{
name: "read_email",
description: "Read email message by ID with advanced MIME structure handling. Returns full email content including headers, body, and attachment information.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageId: {
type: "string",
description: "Gmail message ID to retrieve"
},
format: {
type: "string",
enum: ["minimal", "full", "raw", "metadata"],
description: "Level of detail to retrieve (default: full)",
default: "full"
},
includeContent: {
type: "boolean",
description: "Whether to extract and include email content (text/html)",
default: true
}
},
required: ["messageId"],
additionalProperties: false
}
},
{
name: "search_emails",
description: "Search emails with various criteria including subject, sender, date range, and advanced Gmail search syntax. Supports complex queries and filtering.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
query: {
type: "string",
description: "General search query using Gmail search syntax (e.g., 'is:unread has:attachment')"
},
from: {
type: "string",
description: "Search for emails from specific sender"
},
to: {
type: "string",
description: "Search for emails to specific recipient"
},
subject: {
type: "string",
description: "Search for emails with specific subject"
},
after: {
type: "string",
description: "Search for emails after date (format: YYYY/MM/DD)"
},
before: {
type: "string",
description: "Search for emails before date (format: YYYY/MM/DD)"
},
hasAttachment: {
type: "boolean",
description: "Filter emails that have attachments"
},
label: {
type: "string",
description: "Search within specific label/folder"
},
isUnread: {
type: "boolean",
description: "Filter for unread emails only"
},
maxResults: {
type: "number",
description: "Maximum number of results to return (default: 100, max: 500)",
minimum: 1,
maximum: 500,
default: 100
}
},
additionalProperties: false
}
},
{
name: "list_emails",
description: "List emails in inbox, sent, or custom label/folder. Returns basic email information including subjects, senders, and snippets.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
labelId: {
type: "string",
description: "Gmail label ID or name (default: INBOX). Common values: INBOX, SENT, DRAFT, TRASH, SPAM",
default: "INBOX"
},
maxResults: {
type: "number",
description: "Maximum number of emails to retrieve (default: 50, max: 500)",
minimum: 1,
maximum: 500,
default: 50
}
},
additionalProperties: false
}
},
{
name: "mark_email",
description: "Mark email as read or unread. Useful for managing email status and organizing your inbox.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageId: {
type: "string",
description: "Gmail message ID to mark"
},
read: {
type: "boolean",
description: "True to mark as read, false to mark as unread"
}
},
required: ["messageId", "read"],
additionalProperties: false
}
},
{
name: "move_email",
description: "Move email to different label/folder. Can add labels and remove existing ones in a single operation.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageId: {
type: "string",
description: "Gmail message ID to move"
},
labelId: {
type: "string",
description: "Label ID to add to the email"
},
removeLabelIds: {
type: "array",
items: { type: "string" },
description: "List of label IDs to remove from the email (optional)"
}
},
required: ["messageId", "labelId"],
additionalProperties: false
}
},
{
name: "delete_email",
description: "Permanently delete an email message. Use with caution as this action cannot be undone.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageId: {
type: "string",
description: "Gmail message ID to delete"
}
},
required: ["messageId"],
additionalProperties: false
}
},
{
name: "get_labels",
description: "List all available Gmail labels including system labels (Inbox, Sent, etc.) and user-defined labels.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
}
},
additionalProperties: false
}
},
{
name: "create_label",
description: "Create a new Gmail label/folder for organizing emails.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
name: {
type: "string",
description: "Name of the new label"
},
visible: {
type: "boolean",
description: "Whether the label should be visible in the label list (default: true)",
default: true
}
},
required: ["name"],
additionalProperties: false
}
},
{
name: "update_label",
description: "Update an existing Gmail label's name or visibility settings.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
labelId: {
type: "string",
description: "ID of the label to update"
},
name: {
type: "string",
description: "New name for the label (optional)"
},
visible: {
type: "boolean",
description: "Whether the label should be visible (optional)"
}
},
required: ["labelId"],
additionalProperties: false
}
},
{
name: "delete_label",
description: "Delete a Gmail label. Note: This will remove the label from all emails but won't delete the emails themselves.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
labelId: {
type: "string",
description: "ID of the label to delete"
}
},
required: ["labelId"],
additionalProperties: false
}
},
{
name: "get_attachment",
description: "Download and retrieve attachment information from an email. Returns attachment data, filename, and metadata.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageId: {
type: "string",
description: "Gmail message ID containing the attachment"
},
attachmentId: {
type: "string",
description: "Attachment ID to download"
}
},
required: ["messageId", "attachmentId"],
additionalProperties: false
}
},
{
name: "batch_mark_read",
description: "Mark multiple emails as read in a single batch operation for efficiency.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageIds: {
type: "array",
items: { type: "string" },
description: "List of Gmail message IDs to mark as read"
}
},
required: ["messageIds"],
additionalProperties: false
}
},
{
name: "batch_move_emails",
description: "Move multiple emails to a label/folder in a single batch operation.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageIds: {
type: "array",
items: { type: "string" },
description: "List of Gmail message IDs to move"
},
labelId: {
type: "string",
description: "Label ID to add to all emails"
},
removeLabelIds: {
type: "array",
items: { type: "string" },
description: "List of label IDs to remove from all emails (optional)"
}
},
required: ["messageIds", "labelId"],
additionalProperties: false
}
},
{
name: "batch_delete_emails",
description: "Delete multiple emails in a single batch operation. Use with caution as this action cannot be undone.",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "User ID for multi-user authentication (required if using multi-user mode)"
},
messageIds: {
type: "array",
items: { type: "string" },
description: "List of Gmail message IDs to delete"
}
},
required: ["messageIds"],
additionalProperties: false
}
}
];
// Add multi-user specific tools
const multiUserTools = [
{
name: "authenticate_user",
description: "Start authentication flow for a new user. Returns authentication URL that user needs to visit.",
inputSchema: {
type: "object",
properties: {
userEmail: {
type: "string",
description: "User's email address (optional, for identification)"
}
},
additionalProperties: false
}
},
{
name: "get_my_session_info",
description: "Get information about your own authentication session (user-specific, secure).",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "Your User ID to get information for"
}
},
required: ["userId"],
additionalProperties: false
}
},
{
name: "remove_my_session",
description: "Remove your own authentication session (user-specific, secure).",
inputSchema: {
type: "object",
properties: {
userId: {
type: "string",
description: "Your User ID to remove"
}
},
required: ["userId"],
additionalProperties: false
}
}
];
// Add one-time authentication tool for single-user mode
const oneTimeAuthTools = [
{
name: "quick_authenticate",
description: "One-time Gmail authentication for immediate access. Opens browser automatically and completes authentication flow.",
inputSchema: {
type: "object",
properties: {
force: {
type: "boolean",
description: "Force re-authentication even if already authenticated (default: false)",
default: false
}
},
additionalProperties: false
}
},
{
name: "get_auth_link",
description: "Get a clickable Gmail authentication link without auto-opening browser. Perfect for manual authentication control.",
inputSchema: {
type: "object",
properties: {
force: {
type: "boolean",
description: "Force new authentication link even if already authenticated (default: false)",
default: false
}
},
additionalProperties: false
}
}
];
if (gmailConfig.multiUser) {
return { tools: [...baseTools, ...multiUserTools] };
}
// For single-user mode, add quick authentication if not already authenticated
if (!await gmailAuth.isAuthenticated()) {
return { tools: [...baseTools, ...oneTimeAuthTools] };
}
return { tools: baseTools };
});
/**
* Handle tool calls to the Gmail API.
*/
server.setRequestHandler(CallToolRequestSchema, async (request) => {
logger.log('Received call tool request: ' + JSON.stringify(request));
switch (request.params.name) {
case "quick_authenticate":
try {
const args = request.params.arguments || {};
const { force = false } = args;
// Check if already authenticated and not forcing re-auth
if (!force && await gmailAuth.isAuthenticated()) {
return {
content: [
{
type: "text",
text: "ā
You're already authenticated! Gmail access is ready.\n\nYou can now use all Gmail tools. If you want to re-authenticate, use the 'force' option."
}
]
};
}
// Check if credentials are configured
if (!await gmailAuth.isConfigured()) {
return {
content: [
{
type: "text",
text: "ā Gmail credentials not configured.\n\nPlease set up your OAuth2 credentials first:\n1. Set environment variables GMAIL_CLIENT_ID and GMAIL_CLIENT_SECRET\n2. Or run: gmail-mcp-server --setup-auth"
}
],
isError: true
};
}
try {
// Perform authentication
await gmailAuth.authenticate();
return {
content: [
{
type: "text",
text: "š Gmail authentication successful!\n\nā
You can now use all Gmail tools:\n- send_email\n- read_email\n- search_emails\n- list_emails\n- get_labels\n- And more!\n\nAuthentication complete and ready for use."
}
]
};
}
catch (authError) {
const errorMessage = authError instanceof Error ? authError.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `ā Authentication failed: ${errorMessage}\n\nPlease try again or check your OAuth2 credentials.`
}
],
isError: true
};
}
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error during authentication: ${errorMessage}`
}
],
isError: true
};
}
case "authenticate_user":
try {
if (!gmailConfig.multiUser) {
throw new Error("Multi-user mode not enabled. Restart server with --multi-user flag.");
}
const args = request.params.arguments || {};
const { userEmail } = args;
const authResult = await multiUserAuth.authenticateNewUser(userEmail);
return {
content: [
{
type: "text",
text: `š Authentication started for user!
User ID: ${authResult.userId}
Authentication URL: ${authResult.authUrl}
Server Port: ${authResult.port}
š Please visit the authentication URL to complete the process.
The authentication will timeout in 10 minutes.
After successful authentication, use this User ID for all Gmail operations: ${authResult.userId}`
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error starting authentication: ${errorMessage}`
}
],
isError: true
};
}
case "get_my_session_info":
try {
if (!gmailConfig.multiUser) {
throw new Error("Multi-user mode not enabled. Restart server with --multi-user flag.");
}
const args = request.params.arguments || {};
const { userId } = args;
if (!userId) {
throw new Error("'userId' is required");
}
const session = multiUserAuth.getUserSession(userId);
if (!session) {
return {
content: [
{
type: "text",
text: `ā User session ${userId} not found.`
}
]
};
}
const responseText = `š User Session Info:
User ID: ${session.userId}
Email: ${session.userEmail || 'Unknown'}
Authenticated: ${session.authenticated ? 'ā
' : 'ā'}
Token Expires: ${new Date(session.expiryDate).toLocaleString()}
Status: ${session.authenticated ? 'Active' : 'Inactive'}`;
return {
content: [
{
type: "text",
text: responseText
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error getting user info: ${errorMessage}`
}
],
isError: true
};
}
case "remove_my_session":
try {
if (!gmailConfig.multiUser) {
throw new Error("Multi-user mode not enabled. Restart server with --multi-user flag.");
}
const args = request.params.arguments || {};
const { userId } = args;
if (!userId) {
throw new Error("'userId' is required");
}
const removed = multiUserAuth.removeUser(userId);
return {
content: [
{
type: "text",
text: removed
? `ā
User session ${userId} removed successfully.`
: `ā User session ${userId} not found.`
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error removing user session: ${errorMessage}`
}
],
isError: true
};
}
case "send_email":
try {
const args = request.params.arguments || {};
const { userId, to, cc, bcc, subject, text, html, replyTo, attachments } = args;
if (!to || !Array.isArray(to) || to.length === 0) {
throw new Error("'to' field is required and must be a non-empty array");
}
if (!subject) {
throw new Error("'subject' field is required");
}
if (!text && !html) {
throw new Error("Either 'text' or 'html' content is required");
}
const emailMessage = {
to,
cc,
bcc,
subject,
text,
html,
replyTo,
attachments: attachments?.map((att) => ({
filename: att.filename,
content: att.content,
contentType: att.contentType,
encoding: 'base64'
}))
};
let result;
if (gmailConfig.multiUser) {
if (!userId) {
throw new Error("'userId' is required in multi-user mode");
}
// Get user-specific Gmail client
const gmail = await multiUserAuth.getGmailClientForUser(userId);
// Use the multi-user Gmail operations (would need to be created)
result = await gmailOperations.sendEmail(emailMessage);
}
else {
result = await gmailOperations.sendEmail(emailMessage);
}
return {
content: [
{
type: "text",
text: `Email sent successfully!\nMessage ID: ${result.id}\nThread ID: ${result.threadId}\nRecipients: ${to.join(', ')}\nSubject: ${subject}`
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error sending email: ${errorMessage}`
}
],
isError: true
};
}
case "read_email":
try {
const args = request.params.arguments || {};
const { userId, messageId, format = 'full', includeContent = true } = args;
if (!messageId) {
throw new Error("'messageId' is required");
}
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
const email = await gmailOperations.getEmail(messageId, format);
let responseText = `Email ID: ${email.id}\nThread ID: ${email.threadId}\nSnippet: ${email.snippet}\nLabels: ${email.labelIds?.join(', ') || 'None'}\n`;
if (email.payload && email.payload.headers) {
const headers = email.payload.headers;
const fromHeader = headers.find((h) => h.name === 'From');
const toHeader = headers.find((h) => h.name === 'To');
const subjectHeader = headers.find((h) => h.name === 'Subject');
const dateHeader = headers.find((h) => h.name === 'Date');
responseText += `From: ${fromHeader?.value || 'Unknown'}\n`;
responseText += `To: ${toHeader?.value || 'Unknown'}\n`;
responseText += `Subject: ${subjectHeader?.value || 'No Subject'}\n`;
responseText += `Date: ${dateHeader?.value || 'Unknown'}\n`;
}
if (includeContent && email.payload) {
const content = gmailOperations.extractEmailContent(email.payload);
if (content.text) {
responseText += `\n--- Text Content ---\n${content.text}\n`;
}
if (content.html) {
responseText += `\n--- HTML Content ---\n${content.html}\n`;
}
if (content.attachments.length > 0) {
responseText += `\n--- Attachments ---\n`;
content.attachments.forEach((att, index) => {
responseText += `${index + 1}. ${att.filename} (${att.mimeType}, ${att.size} bytes)\n`;
});
}
}
return {
content: [
{
type: "text",
text: responseText
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error reading email: ${errorMessage}`
}
],
isError: true
};
}
case "search_emails":
try {
const args = request.params.arguments || {};
const { userId, ...searchArgs } = args;
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
const searchCriteria = {
query: searchArgs.query,
from: searchArgs.from,
to: searchArgs.to,
subject: searchArgs.subject,
after: searchArgs.after,
before: searchArgs.before,
hasAttachment: searchArgs.hasAttachment,
label: searchArgs.label,
isUnread: searchArgs.isUnread,
maxResults: searchArgs.maxResults || 100
};
const result = await gmailOperations.searchEmails(searchCriteria);
if (result.messages.length === 0) {
return {
content: [
{
type: "text",
text: "No emails found matching the search criteria."
}
]
};
}
let responseText = `Found ${result.messages.length} emails:\n\n`;
result.messages.forEach((email, index) => {
const fromHeader = email.payload?.headers?.find((h) => h.name === 'From');
const subjectHeader = email.payload?.headers?.find((h) => h.name === 'Subject');
const dateHeader = email.payload?.headers?.find((h) => h.name === 'Date');
responseText += `${index + 1}. ID: ${email.id}\n`;
responseText += ` From: ${fromHeader?.value || 'Unknown'}\n`;
responseText += ` Subject: ${subjectHeader?.value || 'No Subject'}\n`;
responseText += ` Date: ${dateHeader?.value || 'Unknown'}\n`;
responseText += ` Snippet: ${email.snippet}\n`;
responseText += ` Labels: ${email.labelIds?.join(', ') || 'None'}\n\n`;
});
if (result.nextPageToken) {
responseText += `Note: More results available. Use pagination to retrieve additional emails.`;
}
return {
content: [
{
type: "text",
text: responseText
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error searching emails: ${errorMessage}`
}
],
isError: true
};
}
case "list_emails":
try {
const args = request.params.arguments || {};
const { userId, labelId = 'INBOX', maxResults = 50 } = args;
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
const result = await gmailOperations.listEmails(labelId, maxResults);
if (result.messages.length === 0) {
return {
content: [
{
type: "text",
text: `No emails found in label: ${labelId}`
}
]
};
}
let responseText = `Found ${result.messages.length} emails in ${labelId}:\n\n`;
result.messages.forEach((email, index) => {
const fromHeader = email.payload?.headers?.find((h) => h.name === 'From');
const subjectHeader = email.payload?.headers?.find((h) => h.name === 'Subject');
const dateHeader = email.payload?.headers?.find((h) => h.name === 'Date');
responseText += `${index + 1}. ID: ${email.id}\n`;
responseText += ` From: ${fromHeader?.value || 'Unknown'}\n`;
responseText += ` Subject: ${subjectHeader?.value || 'No Subject'}\n`;
responseText += ` Date: ${dateHeader?.value || 'Unknown'}\n`;
responseText += ` Snippet: ${email.snippet}\n\n`;
});
return {
content: [
{
type: "text",
text: responseText
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error listing emails: ${errorMessage}`
}
],
isError: true
};
}
case "mark_email":
try {
const args = request.params.arguments || {};
const { userId, messageId, read } = args;
if (!messageId) {
throw new Error("'messageId' is required");
}
if (typeof read !== 'boolean') {
throw new Error("'read' must be a boolean value");
}
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
await gmailOperations.markEmail(messageId, read);
return {
content: [
{
type: "text",
text: `Email ${messageId} marked as ${read ? 'read' : 'unread'} successfully.`
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error marking email: ${errorMessage}`
}
],
isError: true
};
}
case "move_email":
try {
const args = request.params.arguments || {};
const { userId, messageId, labelId, removeLabelIds } = args;
if (!messageId) {
throw new Error("'messageId' is required");
}
if (!labelId) {
throw new Error("'labelId' is required");
}
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
await gmailOperations.moveToLabel(messageId, labelId, removeLabelIds);
return {
content: [
{
type: "text",
text: `Email ${messageId} moved to label ${labelId} successfully.`
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error moving email: ${errorMessage}`
}
],
isError: true
};
}
case "delete_email":
try {
const args = request.params.arguments || {};
const { userId, messageId } = args;
if (!messageId) {
throw new Error("'messageId' is required");
}
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
await gmailOperations.deleteEmail(messageId);
return {
content: [
{
type: "text",
text: `Email ${messageId} deleted successfully.`
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error deleting email: ${errorMessage}`
}
],
isError: true
};
}
case "get_labels":
try {
const args = request.params.arguments || {};
const { userId } = args;
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
const labels = await gmailOperations.getLabels();
let responseText = `Gmail Labels (${labels.length} total):\n\n`;
// Separate system and user labels
const systemLabels = labels.filter(label => label.type === 'system');
const userLabels = labels.filter(label => label.type === 'user');
if (systemLabels.length > 0) {
responseText += "System Labels:\n";
systemLabels.forEach(label => {
responseText += `- ${label.name} (ID: ${label.id})\n`;
});
responseText += "\n";
}
if (userLabels.length > 0) {
responseText += "User Labels:\n";
userLabels.forEach(label => {
responseText += `- ${label.name} (ID: ${label.id})\n`;
});
}
return {
content: [
{
type: "text",
text: responseText
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error retrieving labels: ${errorMessage}`
}
],
isError: true
};
}
case "create_label":
try {
const args = request.params.arguments || {};
const { userId, name, visible = true } = args;
if (!name) {
throw new Error("'name' is required");
}
if (gmailConfig.multiUser && !userId) {
throw new Error("'userId' is required in multi-user mode");
}
const label = await gmailOperations.createLabel(name, visible);
return {
content: [
{
type: "text",
text: `Label "${name}" created successfully!\nLabel ID: ${label.id}\nVisible: ${visible}`
}
]
};
}
catch (error) {
const errorMessage = error instanceof Error ? error.message : 'Unknown error';
return {
content: [
{
type: "text",
text: `Error creating label: ${errorMessage}`
}
],
isError: true
};
}
case "update_label":
try {
c