Skip to main content
Glama

Google Ads MCP Server

by martechery
auth.ts4.42 kB
// Lightweight auth abstraction to enable unit testing and future ADC integration. // For now, prefer reading an explicit token from env for tests/dev. export type AuthType = 'env' | 'adc' | 'gcloud_cli' | 'oauth' | 'service_account' | 'runtime'; export type AccessToken = { token: string; type: AuthType; quotaProjectId?: string; developerToken?: string; }; import { getConnection, touchConnection, refreshAccessTokenForSession } from './utils/connection-manager.js'; export async function getAccessToken(sessionKey?: string): Promise<AccessToken> { if (process.env.ENABLE_RUNTIME_CREDENTIALS === 'true') { if (!sessionKey) { throw new Error('ERR_NO_SESSION_KEY: session_key required in multi-tenant mode'); } const ctx = getConnection(sessionKey); if (!ctx) { throw new Error('ERR_NO_CREDENTIALS: No credentials provided for this session'); } touchConnection(sessionKey); const cred = ctx.credentials; // Preemptive refresh if expiring within 5 minutes const now = Date.now(); const expiresAt = cred.expires_at ?? 0; const needsRefresh = !!cred.refresh_token && expiresAt > 0 && expiresAt - now < 5 * 60 * 1000; if (needsRefresh) { try { await refreshAccessTokenForSession(sessionKey); } catch (e) { // If refresh fails, fall through to use existing token or surface error // Connection manager will purge session on invalid_grant } } return { token: (getConnection(sessionKey)?.credentials.access_token) || cred.access_token, type: 'runtime', quotaProjectId: cred.quota_project_id, developerToken: cred.developer_token, }; } const envToken = process.env.GOOGLE_ADS_ACCESS_TOKEN; if (envToken) { return { token: envToken, type: 'env', quotaProjectId: process.env.GOOGLE_ADS_QUOTA_PROJECT_ID || undefined, }; } // Fallbacks: if GOOGLE_APPLICATION_CREDENTIALS is not set, check for local ADC file and well-known path if (!process.env.GOOGLE_APPLICATION_CREDENTIALS) { try { const fs = await import('node:fs'); const path = await import('node:path'); const os = await import('node:os'); const localPath = path.resolve(process.cwd(), '.auth', 'adc.json'); const exists = (p?: string) => !!p && fs.existsSync(p); let wellKnown: string | undefined; if (process.platform === 'win32') { const appData = process.env.APPDATA || path.join(os.homedir(), 'AppData', 'Roaming'); wellKnown = path.join(appData, 'gcloud', 'application_default_credentials.json'); } else { wellKnown = path.join(os.homedir(), '.config', 'gcloud', 'application_default_credentials.json'); } if (exists(localPath)) process.env.GOOGLE_APPLICATION_CREDENTIALS = localPath; else if (exists(wellKnown)) process.env.GOOGLE_APPLICATION_CREDENTIALS = wellKnown; } catch { // ignore fs errors } } // Attempt Application Default Credentials via google-auth-library const { GoogleAuth } = await import('google-auth-library'); const auth = new GoogleAuth({ scopes: ['https://www.googleapis.com/auth/adwords'], }); try { const client = await auth.getClient(); const token = await client.getAccessToken(); const quotaProjectId = (auth as any).quotaProjectId || process.env.GOOGLE_ADS_QUOTA_PROJECT_ID || undefined; if (!token || !token.token) { const hasClientCreds = !!process.env.GOOGLE_OAUTH_CLIENT_ID && !!process.env.GOOGLE_OAUTH_CLIENT_SECRET; if (hasClientCreds) { throw new Error( 'ADC not configured. GOOGLE_OAUTH_CLIENT_ID/SECRET present. Hint: run manage_auth with { "action": "oauth_login" } to complete OAuth and create an ADC file.' ); } throw new Error('ADC returned no access token. Run: gcloud auth application-default login --scopes=https://www.googleapis.com/auth/cloud-platform,https://www.googleapis.com/auth/adwords'); } return { token: token.token, type: 'adc', quotaProjectId }; } catch (e: any) { const hasClientCreds = !!process.env.GOOGLE_OAUTH_CLIENT_ID && !!process.env.GOOGLE_OAUTH_CLIENT_SECRET; if (hasClientCreds) { throw new Error( 'ADC not configured. GOOGLE_OAUTH_CLIENT_ID/SECRET present. Hint: run manage_auth with { "action": "oauth_login" } to complete OAuth and create an ADC file.' ); } throw e; } }

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/martechery/mcp-google-ads-ts'

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