#
tokens: 2873/50000 5/5 files
lines: off (toggle) GitHub
raw markdown copy
# Directory Structure

```
├── assets
│   └── images
│       ├── osx-1.png
│       ├── osx-2.png
│       ├── osx-3.png
│       ├── osx-4.png
│       ├── osx-5.png
│       └── osx-6.png
├── build
│   ├── client
│   │   └── gaql-client.js
│   └── index.js
├── package.json
├── README.md
├── src
│   ├── client
│   │   └── gaql-client.ts
│   └── index.ts
└── tsconfig.json
```

# Files

--------------------------------------------------------------------------------
/README.md:
--------------------------------------------------------------------------------

```markdown

# Google Ads MCP (Node.js) - by TrueClicks

Google Ads MCP by TrueClicks enables your AI (like Claude) to securely access and query your Google Ads account data. It supports both Windows and macOS, including Intel and ARM-based systems. It connects AIs to Google Ads using [GAQL.app] as a backend.

This is an *unofficial* Google Ads MCP integration - and as of now, **no official version exists**.

---

## ✅ Why Use This MCP?

Unlike other open-source Multi-Client Processors (MCPs) for Google Ads, this Node.js-based MCP offers **the easiest setup experience available**:

- 🟢 **No Google Cloud Project setup required**
- 🟢 No OAuth credentials
- 🟢 No Developer Tokens
- 🟢 No Client IDs
- 🟢 No authentication hassles

Instead, it uses free **[GAQL.app](https://gaql.app)**, which securely handles authentication and query execution behind the scenes.

This makes it ideal for users who want to get up and running **within minutes**.

---

## 🛠️ Setup Guide

### 1. Install Node.js

Ensure you have Node.js installed on your system.

- On Windows, open **Command Prompt** (search for "cmd" in the Start menu) and run:

```sh
winget install nodejs
```

- On macOS, download and install Node.js from [https://nodejs.org](https://nodejs.org). If you need help, please refer to the screenshots at the end of this document. 

### 2. Get Your GPT Token

1. Go to [https://gaql.app](https://gaql.app)
2. Log in using your Google account to authorize Google Ads access.
3. Click the **Copy GPT Token** button in the top-right corner.

### 3. Configure Your AI (Claude)

The application is configured via a JSON file named `claude_desktop_config.json`.

1. Open the **Claude** desktop application.

2. Press:

   - `CTRL + ,` (Control key and comma) on Windows/Linux
   - `Command + ,` (Command key and comma) on macOS

3. In the left sidebar, click **Developer**.

4. Open  **Edit config** and open `claude_desktop_config.json` for editing.

5. Paste the following JSON into your configuration file:

   ```json
   {
     "mcpServers": {
       "gads": {
         "command": "npx",
         "args": [
           "-y",
           "@trueclicks/google-ads-mcp-js",
           "--token=YOUR_GPT_TOKEN_HERE"
         ]
       }
     }
   }
   ```

   > **Important:** Replace `YOUR_GPT_TOKEN_HERE` with the token copied from GAQL.app.

6. Exit Claude completely:

   - On Windows/Linux: **Hamburger menu > File > Exit**
   - On macOS: Right-click the Claude icon in the top-right panel and click **Quit**

7. Restart Claude.

You’re now ready to use Claude AI to query your Google Ads accounts.

---

## 💬 Example Prompts

- `List my Google Ads accounts`
- `What is the cost for account XYZ in the past 30 days?`
- `What are the top 5 setting recommendations for my campaigns?`

---

## 🆚 Comparison with Other Google Ads MCPs

| Feature                    | Google Ads MCP (Node.js) | Other MCPs (Python/etc.)  |
| -------------------------- | ------------------------ | ------------------------- |
| Google Cloud project setup | 🟢 No                    | 🔧 Yes                    |
| OAuth Client ID required   | 🟢 No                    | 🔧 Yes                    |
| Developer Token needed     | 🟢 No                    | 🔧 Yes                    |
| Google Ads API familiarity | 🟢 No                    | ⚠️ Yes                    |
| Setup complexity           | 🎉 Very low              | 🟠 Moderate to high       |
| Backend service            | ☁️ Hosted via GAQL.app   | 🔧 Direct API integration |

---

## 📸 Setup Screenshots (macOS)

1. **Node.js download:** Screenshot of downloading Node.js from the official site.
![Node.js download](https://github.com/TrueClicks/google-ads-mcp-js/blob/main/assets/images/osx-1.png)

2. **Claude Developer section:** Screenshot of accessing the Developer section in Claude.
![Claude developer section](https://github.com/TrueClicks/google-ads-mcp-js/blob/main/assets/images/osx-2.png)

3. **Open configuration file for editing:** Screenshot showing how to open `claude_desktop_config.json`.
![Claude config file](https://github.com/TrueClicks/google-ads-mcp-js/blob/main/assets/images/osx-3.png)

4. **Editing configuration JSON:** Screenshot of the JSON configuration being edited.
![Content in claude_desktop_config.json](https://github.com/TrueClicks/google-ads-mcp-js/blob/main/assets/images/osx-4.png)

5. **Restarting Claude:** Screenshot showing how to quit and restart Claude from the top-right corner.
![How to hard restart Claude](https://github.com/TrueClicks/google-ads-mcp-js/blob/main/assets/images/osx-5.png)

6. **Example prompt:** Screenshot showing an example prompt using Claude with Google Ads integration.
![Example prompting Google Ads with Claude](https://github.com/TrueClicks/google-ads-mcp-js/blob/main/assets/images/osx-6.png)

---

For issues or questions, please contact [[email protected]](mailto:[email protected]).

---

```

--------------------------------------------------------------------------------
/tsconfig.json:
--------------------------------------------------------------------------------

```json
{
    "compilerOptions": {
      "target": "ES2022",
      "module": "Node16",
      "moduleResolution": "Node16",
      "outDir": "./build",
      "rootDir": "./src",
      "strict": true,
      "esModuleInterop": true,
      "skipLibCheck": true,
      "forceConsistentCasingInFileNames": true
    },
    "include": ["src/**/*"],
    "exclude": ["node_modules"]
  }
```

--------------------------------------------------------------------------------
/package.json:
--------------------------------------------------------------------------------

```json
{
  "name": "@trueclicks/google-ads-mcp-js",
  "version": "0.3.7",
  "description": "Gogle Ads MCP by TrueClicks enables your GPT (like Claude) to securely access and query your Google Ads account data via GAQL.app.",
  "license": "MIT",
  "author": "TrueClicks",
  "repository": {
    "type": "git",
    "url": "https://github.com/TrueClicks/google-ads-mcp-js.git"
  },
  "homepage": "https://www.trueclicks.com",
  "bugs": {
    "url": "https://github.com/TrueClicks/google-ads-mcp-js"
  },
  "type": "module",
  "bin": {
    "google-ads-mcp-js": "build/index.js"
  },
  "files": [
    "build"
  ],
  "scripts": {
    "build": "npx tsc && npx shx chmod +x build/*.js",
    "prepare": "npm run build",
    "watch": "npx tsc --watch"
  },
  "dependencies": {
    "@modelcontextprotocol/sdk": "^1.8.0",
    "zod": "^3.24.2"
  },
  "devDependencies": {
    "@types/node": "^22.14.0",
    "typescript": "^5.8.2",
    "shx": "^0.4.0"
  },
  "main": "index.js"
}
```

--------------------------------------------------------------------------------
/src/client/gaql-client.ts:
--------------------------------------------------------------------------------

```typescript
// src/client/gaql-client.ts

const API_BASE = "https://api.gaql.app";
const USER_AGENT = "qaql-tool-client/1.0";

export interface GaqlQueryRequest {
    query: string;
    customerId: number;
    loginCustomerId: number;
    reportAggregation: string;
}

export class GaqlClient {
    private token: string;

    constructor(token: string) {
        if (!token) {
            throw new Error("GAQL token is required");
        }

        this.token = token;
    }

    async getAccounts(): Promise<any> {
        const url = `${API_BASE}/api/gpt/google-ads/get-accounts?gptToken=${this.token}`;
        
        const response = await fetch(url, {
            headers: {
                "User-Agent": USER_AGENT
            }
        });

        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`GAQL error ${response.status}: ${errorText}`);
        }

        return response.json();
    }

    async executeGaqlQuery(query: GaqlQueryRequest): Promise<string> {
        const url = `${API_BASE}/api/gpt/google-ads/execute-query?gptToken=${this.token}`;
        
        const response = await fetch(url, {
            method: "POST",
            headers: {
                "Content-Type": "application/json",
                "User-Agent": USER_AGENT
            },
            body: JSON.stringify(query)
        });

        if (!response.ok) {
            const errorText = await response.text();
            throw new Error(`GAQL error ${response.status}: ${errorText}`);
        }

        const json = await response.json();
        return JSON.stringify(json, null, 2); // pretty print
    }
}

```

--------------------------------------------------------------------------------
/src/index.ts:
--------------------------------------------------------------------------------

```typescript
#!/usr/bin/env node
import { McpServer } from "@modelcontextprotocol/sdk/server/mcp.js";
import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
import { z } from "zod";
import { GaqlClient, GaqlQueryRequest } from "./client/gaql-client.js";

// CLI token parsing
console.error("Process argv:", process.argv);
const tokenArg = process.argv.find(arg => arg.startsWith("--token="));
const token = tokenArg?.substring("--token=".length);

if (!token) {
    console.error("❌ Error: Missing --token argument.");
    process.exit(1);
} else {
    console.error("✅ Parsed token.");
}

const gaqlClient = new GaqlClient(token);

const server = new McpServer({
    name: "Google Ads MCP",
    version: "1.0.0"
});

// Tool: get-accounts
server.tool(
    "get-accounts",
    "Gets Google Ads accounts.",
    async () => {
        console.error("🛠 get-accounts called");
        try {
            const accounts = await gaqlClient.getAccounts();
            return {
                content: [{
                    type: "text",
                    text: `Accounts:\n${JSON.stringify(accounts, null, 2)}`
                }]
            };
        } catch (err: any) {
            console.error("❌ Error in get-accounts:", err);

            return {
                content: [{
                    type: "text",
                    text: `Failed to fetch accounts: ${err.message}`
                }]
            };
        }
    }
);

// Tool: execute-gaql-query
server.tool(
    "execute-gaql-query",
    "Executes a GAQL query and returns the result as a formatted JSON string.",
    {
        query: z.string().describe("GAQL query"),
        customerId: z.number().describe("Customer ID"),
        loginCustomerId: z.number().describe("Login Customer ID"),
        reportAggregation: z.string().describe("Report aggregation (for Microsoft Advertising only)")
    },
    async (args: GaqlQueryRequest) => {
        console.error("🛠 execute-gaql-query called with:", args);
        try {
            const result = await gaqlClient.executeGaqlQuery(args);

            return {
                content: [{
                    type: "text",
                    text: `Query result:\n${result}`
                }]
            };
        } catch (err: any) {
            console.error("❌ Error in execute-gaql-query:", err);

            return {
                content: [{
                    type: "text",
                    text: `Failed to execute query: ${err.message}`
                }]
            };
        }
    }
);

// Start server
async function main() {
    console.error("🚀 Connecting to MCP server...");
    const transport = new StdioServerTransport();
    await server.connect(transport);
    console.error("✅ MCP server is running.");
}

main().catch((err) => {
    console.error("🔥 Fatal error in main():", err);
    process.exit(1);
});

```