Skip to main content
Glama

Google Ads MCP Server

by seovimalraj

Google Ads MCP Server

A Next.js App Router deployment that exposes a Model Context Protocol (MCP) server backed by the Google Ads Keyword Planner. The server provides three tools—get_keyword_ideas, get_historical_metrics, and get_forecast—with secure OAuth, encrypted credential storage, in-flight validation, and lightweight rate limiting. It is optimised for Vercel (Node.js runtime) but runs locally with the same feature set.

Features

  • Next.js 15 App Router using strict TypeScript, ESLint, and Prettier.

  • MCP tools for keyword discovery, historical metrics, and forecasts.

  • Google OAuth 2.0 flow with AES-256-GCM encrypted refresh token storage.

  • KV persistence via Vercel KV with an automatic in-memory fallback for local development.

  • Zod validation for every tool input plus environment assertions.

  • Token bucket rate limiting (10 requests/minute per user/IP).

  • Structured logging & error mapping for predictable MCP responses.

Requirements

  • Node.js 18.18 or newer (Next.js 15 canary requires ≥18.18).

  • pnpm or npm for dependency management.

  • A Google Ads Manager account with API access.

  • Google Cloud OAuth client (type: Web application) authorised for the deployment URL.

  • Vercel KV database (or compatible Upstash REST endpoint).

Environment variables

Create a .env file (or configure Vercel project secrets) that includes all variables from .env.example.

Variable

Required

Description

GADS_CLIENT_ID

OAuth client ID created in Google Cloud Console.

GADS_CLIENT_SECRET

OAuth client secret that matches the client ID.

GADS_DEV_TOKEN

Google Ads developer token approved for production.

GADS_LOGIN_CUSTOMER_ID

⚪️

Optional manager account ID used for scoped access.

ENCRYPTION_KEY

Base64 encoded 32-byte key for AES-256-GCM. Generate with

openssl rand -base64 32

.

KV_REST_API_URL

⚪️

Vercel KV REST endpoint. Omit to use in-memory storage locally.

KV_REST_API_TOKEN

⚪️

Vercel KV REST token with read/write permissions.

KV_REST_API_READ_ONLY_TOKEN

⚪️

Optional read-only token for GET operations.

NEXT_PUBLIC_APP_NAME

Display name shown on the OAuth confirmation page.

NEXTAUTH_URL

⚪️

Optional explicit base URL. When omitted the server uses the incoming request origin.

ℹ️ If any required variable is missing the server throws an explicit error at startup, ensuring you see actionable messages in local development and on Vercel logs.

Project structure

app/ api/ auth/ start/route.ts # Initiates Google OAuth consent callback/route.ts # Handles OAuth callback and refresh token storage mcp/route.ts # MCP endpoint (GET health, POST invoke) lib/ ads.ts # Google Ads client and helpers crypto.ts # AES-256-GCM encrypt/decrypt utilities kv.ts # Vercel KV / in-memory abstraction mcp.ts # Tool registration, validation, rate limiting ratelimit.ts # Simple token-bucket implementation schemas.ts # Zod schemas for env + tool payloads types/index.d.ts # Shared TypeScript types (ToolResponse, AuthToken)

Getting started locally

pnpm install pnpm dev

The development server defaults to http://localhost:3000. Ensure your Google OAuth client has that origin and callback (http://localhost:3000/api/auth/callback) added to the authorised redirect URIs.

Running without Vercel KV

If KV_REST_API_URL or KV_REST_API_TOKEN is missing the server logs a warning and stores tokens in-memory. This is ideal for local testing but not for production because refresh tokens will be lost on redeploys.

OAuth flow

  1. Direct the browser (or an HTTP client) to /api/auth/start?userId=<USER>&customerId=<CUSTOMER_ID>.

  2. Google prompts for consent with the https://www.googleapis.com/auth/adwords scope.

  3. On success Google redirects to /api/auth/callback. The server exchanges the code for tokens, encrypts the refresh token with AES-256-GCM, and persists it in KV.

  4. The callback endpoint renders a simple confirmation page. Tokens are now available to all MCP tools via the stored userId.

The state parameter is encrypted to prevent tampering and includes the user/customer IDs plus a timestamp.

MCP tools

All tools share the same POST endpoint: POST /api/mcp with JSON payload {"tool": "<name>", "input": { ... } }. Responses follow the ToolResponse shape ({ ok: true, data } or { ok: false, error }).

ping

{ "tool": "ping", "input": { "userId": "demo" } }

Result:

{ "ok": true, "data": { "status": "ok", "kv": "memory" }, "meta": { "ip": "127.0.0.1" } }

get_keyword_ideas

{ "tool": "get_keyword_ideas", "input": { "userId": "demo", "customerId": "1234567890", "keywords": ["toroidal transformer"], "locationIds": ["2356"], "languageId": "1000", "network": "GOOGLE_SEARCH_AND_PARTNERS", "pageSize": 30 } }

get_historical_metrics

{ "tool": "get_historical_metrics", "input": { "userId": "demo", "customerId": "1234567890", "keywords": ["toroidal transformer"], "locationIds": ["2356"], "languageId": "1000" } }

get_forecast

{ "tool": "get_forecast", "input": { "userId": "demo", "customerId": "1234567890", "keywords": ["toroidal transformer"], "locationIds": ["2356"], "languageId": "1000", "cpcBidMicros": 1500000, "dailyBudgetMicros": 2500000 } }

All tool inputs are validated by Zod. Errors return HTTP 400 and a structured payload:

{ "ok": false, "error": { "code": "INVALID_ARGUMENT", "message": "Input validation failed.", "details": { "fieldErrors": { "keywords": ["Provide either keywords[] or urlSeed."] } } } }

Rate limiting & errors

  • Rate limiting: 10 requests per minute per user (falls back to IP if userId missing). Exceeding the budget returns 429 with code: RATE_LIMIT_EXCEEDED.

  • Authentication errors: Missing or corrupt refresh tokens map to AUTHENTICATION_ERROR (HTTP 401).

  • Permission issues: Google Ads permission denials map to PERMISSION_DENIED (HTTP 403).

  • Quota problems: API quota issues map to QUOTA_EXCEEDED (HTTP 429).

  • Unexpected failures: Return 500 with code: UNKNOWN but never leak secrets.

Structured logs (via console.log / console.error) include tool name, user ID, duration, and error codes to help diagnose incidents quickly.

Deployment on Vercel

  1. Push the repository to GitHub and create a Vercel project targeting this repo.

  2. Set the production environment variables listed above. Remember to generate a strong ENCRYPTION_KEY and configure KV credentials.

  3. Deploy. Vercel automatically builds the Next.js app in the Node.js runtime.

  4. After deployment, run the OAuth flow (https://<app>.vercel.app/api/auth/start?...) and confirm the callback stores the token (check logs or /api/mcp ping).

  5. Smoke test each tool with curl or the MCP client using the production URL.

ChatGPT Developer Mode integration

Add a remote MCP server in ChatGPT using the following configuration:

  • Server URL: https://<your-app>.vercel.app/api/mcp

  • Tools: Automatically discovered (ping, get_keyword_ideas, get_historical_metrics, get_forecast).

  • Auth flow: Run /api/auth/start in a browser before invoking any tool so the refresh token is available.

Example invocation in ChatGPT (after auth):

{ "tool": "get_keyword_ideas", "input": { "userId": "demo", "customerId": "1234567890", "keywords": ["toroidal transformer"], "locationIds": ["2356"], "languageId": "1000" } }

Expect a JSON array of keyword ideas with text, competition, and bid ranges.

Troubleshooting

Symptom

Resolution

Invalid environment configuration

error on boot

Verify

.env

matches

.env.example

and the encryption key is 32 bytes base64.

/api/auth/start

returns 400

Both

userId

and

customerId

must be present in the query string.

Google callback lacks

refresh_token

Ensure you append

prompt=consent&access_type=offline

(the start endpoint does this automatically). Remove the app from Google account permissions and retry.

MCP tools return

AUTHENTICATION_ERROR

The stored refresh token may be corrupted or encrypted with an old key. Regenerate

ENCRYPTION_KEY

, re-run OAuth, and redeploy.

RATE_LIMIT_EXCEEDED

Wait until the window resets (~60 seconds) or reduce tool frequency.

PERMISSION_DENIED

from Google

Confirm the user has access to the specified

customerId

and the developer token is approved.

Acceptance checklist

  • App Router structure with TypeScript, ESLint, Prettier, and strict mode.

  • OAuth start/callback routes using AES-256-GCM encrypted refresh tokens.

  • KV abstraction with Vercel + in-memory modes.

  • Google Ads helpers for keyword ideas, historical metrics, and forecasts.

  • MCP tools with validation, rate limiting, and error handling.

  • README covering setup, usage, deployment, and troubleshooting.

-
security - not tested
A
license - permissive license
-
quality - not tested

hybrid server

The server is able to function both locally and remotely, depending on the configuration or use case.

Enables interaction with Google Ads through secure OAuth authentication to discover keyword ideas, analyze historical search metrics, and generate forecasts for advertising campaigns. Built as a Next.js application with encrypted credential storage and rate limiting.

  1. Features
    1. Requirements
      1. Environment variables
        1. Project structure
          1. Getting started locally
            1. Running without Vercel KV
          2. OAuth flow
            1. MCP tools
              1. ping
              2. get_keyword_ideas
              3. get_historical_metrics
              4. get_forecast
            2. Rate limiting & errors
              1. Deployment on Vercel
                1. ChatGPT Developer Mode integration
                  1. Troubleshooting
                    1. Acceptance checklist

                      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