authenticate_gmail
Authenticate Gmail access by opening a web browser to authorize connection for managing your inbox with natural language commands.
Instructions
Authenticate Gmail access via web browser (opens browser automatically)
Input Schema
TableJSON Schema
| Name | Required | Description | Default |
|---|---|---|---|
No arguments | |||
Implementation Reference
- src/lib.ts:52-109 (handler)Special case handler for 'authenticate_gmail' tool: obtains OAuth client, performs web-based authentication, and returns success message upon completion.if (req.params.name === 'authenticate_gmail') { // Always get fresh OAuth client let oauth2Client = await getOAuthClient(); if (!oauth2Client) { throw new Error(`Gmail OAuth Setup Required Please complete the following steps: 1. Create Google Cloud Project Visit: https://console.cloud.google.com/projectcreate 2. Enable Gmail API Visit: https://console.cloud.google.com/apis/api/gmail.googleapis.com/metrics 3. Create OAuth Credentials Visit: https://console.cloud.google.com/auth/clients Choose "Desktop app" type Download as gcp-oauth.keys.json 4. Add Required Scope Visit: https://console.cloud.google.com/auth/scopes Add: https://mail.google.com/ 5. Add Test User Visit: https://console.cloud.google.com/auth/audience Add your Google email as test user 6. Save the file to project directory and restart Claude Desktop Expected OAuth file location: ${process.env.GMAIL_OAUTH_PATH || 'project directory/gcp-oauth.keys.json'}`); } try { await authenticateWeb(oauth2Client); // Reinitialize Gmail service after successful authentication let gmailService = new GmailService(oauth2Client); return { content: [{ type: "text", text: `Authentication Successful! Gmail Manager is now connected to your Gmail account! You can now use all Gmail tools: - Search and filter emails - Delete emails in bulk - Create and manage labels - Organize your inbox Ready to start managing your inbox!` }] }; } catch (error) { throw new Error(`Authentication failed: ${error instanceof Error ? error.message : String(error)}`); } }
- src/tools.ts:32-32 (schema)Input schema for authenticate_gmail tool (empty object, no parameters required).authenticate_gmail: z.object({})
- src/tools.ts:50-55 (registration)getToolDefinitions exports tool definitions including 'authenticate_gmail' schema and description for MCP tool listing.export const getToolDefinitions = () => Object.entries(schemas).map(([name, schema]) => ({ name, description: toolDescriptions[name], inputSchema: zodToJsonSchema(schema) }));
- src/lib.ts:48-48 (registration)Registers the tool list handler which includes 'authenticate_gmail' via getToolDefinitions().server.setRequestHandler(ListToolsRequestSchema, async () => ({ tools: getToolDefinitions() }));
- src/auth.ts:601-831 (helper)Core helper function authenticateWeb that performs the OAuth2 web flow: starts local HTTP server, generates auth URL, opens browser, handles callback, saves tokens.export async function authenticateWeb(oauth2Client: OAuth2Client, credentialsPath?: string): Promise<void> { const creds = credentialsPath || path.join(CONFIG_DIR, 'credentials.json'); return new Promise((resolve, reject) => { let server: http.Server; let port: number; let redirectUri: string; server = http.createServer(async (req, res) => { try { // Get the actual server address for URL parsing const serverAddress = server.address(); const currentPort = serverAddress && typeof serverAddress === 'object' ? serverAddress.port : port; const url = new URL(req.url!, `http://localhost:${currentPort}`); if (url.pathname === '/oauth/callback') { const code = url.searchParams.get('code'); if (code) { // Create OAuth client with current redirect URI const currentWebOAuth2Client = new OAuth2Client( (oauth2Client as any)._clientId, (oauth2Client as any)._clientSecret, redirectUri ); const { tokens } = await currentWebOAuth2Client.getToken(code); oauth2Client.setCredentials(tokens); // Ensure the directory exists const credsDir = path.dirname(creds); if (!fs.existsSync(credsDir)) { fs.mkdirSync(credsDir, { recursive: true }); } // Save credentials fs.writeFileSync(creds, JSON.stringify(tokens, null, 2)); res.writeHead(200, { 'Content-Type': 'text/html' }); res.end(getAuthSuccessHTML()); server.close(); resolve(); } else { res.writeHead(400, { 'Content-Type': 'text/html' }); res.end(getAuthFailedHTML()); server.close(); reject(new Error('No authorization code received')); } } else if (url.pathname === '/') { // Landing page - redirect to Google OAuth // Create OAuth client with current redirect URI for auth URL generation const currentWebOAuth2Client = new OAuth2Client( (oauth2Client as any)._clientId, (oauth2Client as any)._clientSecret, redirectUri ); const currentAuthUrl = currentWebOAuth2Client.generateAuthUrl({ access_type: 'offline', scope: ['https://mail.google.com/'] }); res.writeHead(302, { 'Location': currentAuthUrl }); res.end(); } else if (url.pathname.startsWith('/images/')) { // Serve static images const imagePath = path.join(projectRoot, 'public', url.pathname); try { if (fs.existsSync(imagePath)) { const imageData = fs.readFileSync(imagePath); const ext = path.extname(imagePath).toLowerCase(); let contentType = 'application/octet-stream'; if (ext === '.gif') contentType = 'image/gif'; else if (ext === '.png') contentType = 'image/png'; else if (ext === '.jpg' || ext === '.jpeg') contentType = 'image/jpeg'; res.writeHead(200, { 'Content-Type': contentType }); res.end(imageData); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Image not found'); } } catch (error) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error serving image'); } } else if (url.pathname.startsWith('/css/')) { // Serve CSS files const cssPath = path.join(projectRoot, 'public', url.pathname); try { if (fs.existsSync(cssPath) && path.extname(cssPath).toLowerCase() === '.css') { const cssData = fs.readFileSync(cssPath, 'utf8'); res.writeHead(200, { 'Content-Type': 'text/css' }); res.end(cssData); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('CSS file not found'); } } catch (error) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error serving CSS file'); } } else if (url.pathname.startsWith('/data/')) { // Serve JSON data files const dataPath = path.join(projectRoot, 'public', url.pathname); try { if (fs.existsSync(dataPath) && path.extname(dataPath).toLowerCase() === '.json') { const jsonData = fs.readFileSync(dataPath, 'utf8'); res.writeHead(200, { 'Content-Type': 'application/json' }); res.end(jsonData); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Data file not found'); } } catch (error) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error serving data file'); } } else if (url.pathname.startsWith('/js/')) { // Serve JavaScript files const jsPath = path.join(projectRoot, 'public', url.pathname); try { if (fs.existsSync(jsPath) && path.extname(jsPath).toLowerCase() === '.js') { const jsData = fs.readFileSync(jsPath, 'utf8'); res.writeHead(200, { 'Content-Type': 'application/javascript' }); res.end(jsData); } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('JavaScript file not found'); } } catch (error) { res.writeHead(500, { 'Content-Type': 'text/plain' }); res.end('Error serving JavaScript file'); } } else { res.writeHead(404, { 'Content-Type': 'text/plain' }); res.end('Not Found'); } } catch (error: any) { // Only send error response if headers haven't been sent yet if (!res.headersSent) { res.writeHead(500, { 'Content-Type': 'text/html' }); res.end(`<h1>Authentication Error</h1><p>${error instanceof Error ? error.message : 'Unknown error'}</p>`); } server.close(); reject(error); } }); server.listen(0, async () => { // Get the actual port that was assigned const address = server.address(); if (!address || typeof address === 'string') { reject(new Error('Failed to start OAuth server')); return; } port = address.port; redirectUri = `http://localhost:${port}/oauth/callback`; // Update the OAuth client with the dynamic redirect URI const webOAuth2Client = new OAuth2Client( (oauth2Client as any)._clientId, (oauth2Client as any)._clientSecret, redirectUri ); const authUrl = webOAuth2Client.generateAuthUrl({ access_type: 'offline', scope: ['https://mail.google.com/'] }); console.error(`\nOpening authentication in your browser...`); console.error(`\nIf the browser doesn't open automatically, please visit:`); console.error(`\n${authUrl}\n`); // Open browser (platform-agnostic) const platform = os.platform(); // Check if we're in WSL const isWSL = fs.existsSync('/proc/version') && fs.readFileSync('/proc/version', 'utf8').toLowerCase().includes('microsoft'); if (isWSL) { // In WSL, use Windows' cmd.exe to open the browser exec(`cmd.exe /c start "${authUrl}"`, (error: any) => { if (error) { // Try PowerShell as fallback exec(`powershell.exe -Command "Start-Process '${authUrl}'"`, (error2: any) => { if (error2) { console.error('Could not open browser automatically. Please open the URL manually.'); } }); } }); } else if (platform === 'darwin') { exec(`open "${authUrl}"`, (error: any) => { if (error) { console.error('Could not open browser automatically. Please open the URL manually.'); } }); } else if (platform === 'win32') { exec(`cmd.exe /c start "" "${authUrl}"`, (error: any) => { if (error) { console.error('Could not open browser automatically. Please open the URL manually.'); } }); } else { // Linux exec(`xdg-open "${authUrl}"`, (error: any) => { if (error) { // Try alternative methods exec(`sensible-browser "${authUrl}"`, (error2: any) => { if (error2) { console.error('Could not open browser automatically. Please open the URL manually.'); } }); } }); } }); server.on('error', (error) => { if ((error as any).code === 'EADDRINUSE') { // Port 3000 is in use } reject(error); }); }); }