UNPKG

uhbarp-gmail-mcp-server

Version:

Gmail MCP Server for managing Gmail through natural language interactions with full OAuth2 authentication support

217 lines (183 loc) 6.63 kB
#!/usr/bin/env node import path from 'path'; import { fileURLToPath } from 'url'; import { spawn } from 'child_process'; import fs from 'fs'; import readline from 'readline'; import os from 'os'; // Debug info - write to a debug file const debugFile = path.join(os.tmpdir(), 'gmail-mcp-debug.log'); const writeDebug = (message) => { try { fs.appendFileSync(debugFile, `${new Date().toISOString()} - ${message}\n`); } catch (err) { // Silently fail if we can't write to the debug file } }; // Start debugging writeDebug('Gmail MCP CLI script started'); writeDebug(`Node version: ${process.version}`); writeDebug(`Platform: ${process.platform}`); writeDebug(`CLI Arguments: ${process.argv.join(' ')}`); writeDebug(`Is stdin a TTY: ${process.stdin.isTTY}`); writeDebug(`Is stdout a TTY: ${process.stdout.isTTY}`); writeDebug(`Process PID: ${process.pid}`); writeDebug(`Executable path: ${process.execPath}`); writeDebug(`Current directory: ${process.cwd()}`); // Print debug file location to stderr (not stdout) console.error(`Debug log: ${debugFile}`); const __filename = fileURLToPath(import.meta.url); const __dirname = path.dirname(__filename); const packageRoot = path.resolve(__dirname, '..'); writeDebug(`__filename: ${__filename}`); writeDebug(`__dirname: ${__dirname}`); writeDebug(`packageRoot: ${packageRoot}`); // Check if bin/cli.js is executable try { const stats = fs.statSync(__filename); const isExecutable = !!(stats.mode & fs.constants.S_IXUSR); writeDebug(`Is CLI executable: ${isExecutable}`); // Make it executable if it's not if (!isExecutable) { fs.chmodSync(__filename, '755'); writeDebug('Made CLI executable'); } } catch (err) { writeDebug(`Error checking/setting executable: ${err.message}`); } // Parse command line arguments const args = process.argv.slice(2); let debug = false; let nonInteractive = false; let setupAuth = false; let resetAuth = false; // Detect if we're running under an MCP context (Claude/ChatGPT/etc.) const isMcpContext = !process.stdin.isTTY || process.env.npm_execpath?.includes('npx') || process.env.CLAUDE_API_KEY || args.includes('--non-interactive') || args.includes('-n'); writeDebug(`Detected MCP context: ${isMcpContext}`); if (isMcpContext) { nonInteractive = true; writeDebug('Setting non-interactive mode due to MCP context detection'); } // Process command line arguments for (let i = 0; i < args.length; i++) { const arg = args[i]; if (arg === '--debug') { debug = true; writeDebug('Debug mode enabled'); } else if (arg === '--non-interactive' || arg === '-n') { nonInteractive = true; writeDebug('Non-interactive mode enabled via flag'); } else if (arg === '--setup-auth') { setupAuth = true; writeDebug('Setup auth mode enabled'); } else if (arg === '--reset-auth') { resetAuth = true; writeDebug('Reset auth mode enabled'); } else if (arg === '--help' || arg === '-h') { console.log(` Gmail MCP Server - Gmail Integration for Claude Desktop Usage: npx gmail-mcp-server [options] Options: --setup-auth Set up Gmail API credentials --reset-auth Clear stored authentication tokens --debug Enable debug output --non-interactive, -n Run in non-interactive mode (no prompt) --help, -h Show this help message Setup: 1. Run: npx gmail-mcp-server --setup-auth 2. Follow the instructions to set up Google API credentials 3. Run: npx gmail-mcp-server to start the server Authentication Setup: The server supports two methods for providing Gmail API credentials: 1. Credentials File: - Download credentials JSON from Google Cloud Console - Save as ~/.gmail-mcp/credentials.json 2. Environment Variables: - GMAIL_CLIENT_ID: Your OAuth2 client ID - GMAIL_CLIENT_SECRET: Your OAuth2 client secret For detailed setup instructions, visit: https://developers.google.com/gmail/api/quickstart/nodejs Examples: npx gmail-mcp-server --setup-auth # Set up authentication npx gmail-mcp-server # Start the server npx gmail-mcp-server --reset-auth # Clear auth tokens `); process.exit(0); } } function startServer() { const serverPath = path.join(packageRoot, 'dist', 'index.js'); // Check if the compiled server exists if (!fs.existsSync(serverPath)) { console.error('Server not found. Building...'); // Try to build the project const buildProcess = spawn('npm', ['run', 'build'], { cwd: packageRoot, stdio: 'inherit' }); buildProcess.on('close', (code) => { if (code === 0) { startServerWithPath(serverPath); } else { console.error('Build failed. Please run "npm run build" manually.'); process.exit(1); } }); } else { startServerWithPath(serverPath); } } function startServerWithPath(serverPath) { writeDebug(`Starting server with path: ${serverPath}`); // Prepare arguments for the server const serverArgs = []; if (setupAuth) serverArgs.push('--setup-auth'); if (resetAuth) serverArgs.push('--reset-auth'); if (debug) serverArgs.push('--debug'); if (nonInteractive) serverArgs.push('--non-interactive'); writeDebug(`Server arguments: ${serverArgs.join(' ')}`); // Start the server process const serverProcess = spawn(process.execPath, [serverPath, ...serverArgs], { stdio: 'inherit', env: { ...process.env, // Ensure environment variables are passed through NODE_PATH: process.env.NODE_PATH || '', } }); // Handle server process events serverProcess.on('error', (err) => { writeDebug(`Server process error: ${err.message}`); console.error('Failed to start Gmail MCP server:', err.message); process.exit(1); }); serverProcess.on('close', (code, signal) => { writeDebug(`Server process closed with code ${code} and signal ${signal}`); if (code !== 0) { console.error(`Gmail MCP server exited with code ${code}`); } process.exit(code || 0); }); // Handle SIGINT and SIGTERM process.on('SIGINT', () => { writeDebug('Received SIGINT, terminating server process'); serverProcess.kill('SIGINT'); }); process.on('SIGTERM', () => { writeDebug('Received SIGTERM, terminating server process'); serverProcess.kill('SIGTERM'); }); } // Special handling for setup and reset modes if (setupAuth || resetAuth) { writeDebug('Running in special mode (setup or reset)'); startServer(); } else { // Normal server startup writeDebug('Starting normal server mode'); startServer(); }