Skip to main content
Glama

Google Ads MCP Server

by seovimalraj
gsc.ts4.76 kB
import { google, webmasters_v3 } from 'googleapis'; type SearchAnalyticsRow = webmasters_v3.Schema$ApiDataRow; type Dimension = 'query' | 'page'; type DimensionFilterOperator = 'contains' | 'equals' | 'notEquals'; export interface FetchGscRowsOptions { siteUrl: string; timeRange?: string; startDate?: string; endDate?: string; rowLimit?: number; dimensions?: Dimension[]; filters?: Array<{ dimension: Dimension; operator: DimensionFilterOperator; expression: string; }>; } export interface GscRow { query: string; page: string; clicks: number; impressions: number; ctr: number; position: number; } const SEARCH_CONSOLE_SCOPES = ['https://www.googleapis.com/auth/webmasters.readonly']; let authClient: webmasters_v3.Options['auth']; async function getAuthClient(): Promise<webmasters_v3.Options['auth']> { if (authClient) { return authClient; } const clientEmail = process.env.GOOGLE_CLIENT_EMAIL ?? process.env.GOOGLE_SERVICE_ACCOUNT_EMAIL ?? process.env.GOOGLE_SEARCH_CONSOLE_CLIENT_EMAIL; const privateKeyRaw = process.env.GOOGLE_PRIVATE_KEY ?? process.env.GOOGLE_SERVICE_ACCOUNT_PRIVATE_KEY ?? process.env.GOOGLE_SEARCH_CONSOLE_PRIVATE_KEY ?? process.env.GOOGLE_SERVICE_ACCOUNT_KEY; if (!clientEmail || !privateKeyRaw) { throw new Error( 'Search Console credentials are not configured. Set GOOGLE_CLIENT_EMAIL and GOOGLE_PRIVATE_KEY.', ); } const privateKey = privateKeyRaw.replace(/\\n/g, '\n'); authClient = new google.auth.JWT({ email: clientEmail, key: privateKey, scopes: SEARCH_CONSOLE_SCOPES, }); return authClient; } function formatDate(date: Date): string { return date.toISOString().slice(0, 10); } function resolveDateRange(options: FetchGscRowsOptions): { startDate: string; endDate: string } { if (options.startDate && options.endDate) { return { startDate: options.startDate, endDate: options.endDate }; } const range = options.timeRange ?? 'last_90_days'; const end = new Date(); end.setUTCHours(0, 0, 0, 0); const start = new Date(end); const lastDaysMatch = /^last_(\d+)_days$/i.exec(range); if (lastDaysMatch) { const days = Math.max(1, Number.parseInt(lastDaysMatch[1], 10)); start.setUTCDate(end.getUTCDate() - (days - 1)); return { startDate: formatDate(start), endDate: formatDate(end) }; } const lastMonthsMatch = /^last_(\d+)_months$/i.exec(range); if (lastMonthsMatch) { const months = Math.max(1, Number.parseInt(lastMonthsMatch[1], 10)); start.setUTCMonth(end.getUTCMonth() - months); return { startDate: formatDate(start), endDate: formatDate(end) }; } const customMatch = /^custom:(\d{4}-\d{2}-\d{2}):(\d{4}-\d{2}-\d{2})$/i.exec(range); if (customMatch) { return { startDate: customMatch[1], endDate: customMatch[2] }; } switch (range) { case 'last_28_days': start.setUTCDate(end.getUTCDate() - 27); break; case 'last_180_days': start.setUTCDate(end.getUTCDate() - 179); break; case 'last_12_months': start.setUTCMonth(end.getUTCMonth() - 12); break; default: start.setUTCDate(end.getUTCDate() - 89); break; } return { startDate: formatDate(start), endDate: formatDate(end) }; } function mapRows(rows: SearchAnalyticsRow[] = []): GscRow[] { return rows .map((row) => { const keys = Array.isArray(row.keys) ? row.keys : []; const [query = '', page = ''] = keys; return { query, page, clicks: row.clicks ?? 0, impressions: row.impressions ?? 0, ctr: row.ctr ?? 0, position: row.position ?? 0, } satisfies GscRow; }) .filter((row) => row.query.length > 0); } export async function fetchGscRows(options: FetchGscRowsOptions): Promise<GscRow[]> { if (!options.siteUrl) { throw new Error('siteUrl is required to query Search Console.'); } const auth = await getAuthClient(); const webmasters = google.webmasters({ version: 'v3', auth }); const { startDate, endDate } = resolveDateRange(options); const requestBody: webmasters_v3.Schema$SearchAnalyticsQueryRequest = { startDate, endDate, dimensions: options.dimensions ?? ['query', 'page'], rowLimit: Math.min(options.rowLimit ?? 5000, 25000), }; if (options.filters?.length) { requestBody.dimensionFilterGroups = [ { filters: options.filters.map((filter) => ({ dimension: filter.dimension, operator: filter.operator, expression: filter.expression, })), }, ]; } const response = await webmasters.searchanalytics.query({ siteUrl: options.siteUrl, requestBody, }); return mapRows(response.data.rows); }

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

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