Skip to main content
Glama

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
NameRequiredDescriptionDefault

No arguments

Implementation Reference

  • 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)}`); } }
  • 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() }));
  • 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); }); }); }

Latest Blog Posts

MCP directory API

We provide all the information about MCP servers via our MCP API.

curl -X GET 'https://glama.ai/api/mcp/v1/servers/muammar-yacoob/GMail-Manager-MCP'

If you have feedback or need assistance with the MCP directory API, please join our Discord server