@channel47/google-ads-mcp 1.1.0 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +30 -6
- package/package.json +1 -1
- package/server/auth.js +3 -2
- package/server/index.js +13 -1
- package/server/tools/gaql-query.js +1 -1
- package/server/tools/list-accounts.js +9 -7
- package/server/tools/mutate.js +2 -1
package/README.md
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
|
|
6
6
|
MCP server for Google Ads API access via GAQL (Google Ads Query Language). Built by a practitioner managing 25+ ad accounts daily — not a demo.
|
|
7
7
|
|
|
8
|
-
Part of [
|
|
8
|
+
Part of [channel47](https://channel47.dev), the open-source ecosystem of profession plugins for Claude Code. [Get the newsletter](https://channel47.dev/subscribe) for weekly skill breakdowns from production use.
|
|
9
9
|
|
|
10
10
|
## What It Does
|
|
11
11
|
|
|
@@ -18,7 +18,7 @@ Part of [Channel 47](https://channel47.dev), the open-source ecosystem of profes
|
|
|
18
18
|
|
|
19
19
|
### For Claude Code Plugin Users
|
|
20
20
|
|
|
21
|
-
Bundled with the [media-buyer plugin](https://github.com/channel47/
|
|
21
|
+
Bundled with the [media-buyer plugin](https://github.com/channel47/plugins). No manual install required.
|
|
22
22
|
|
|
23
23
|
### Standalone Use
|
|
24
24
|
|
|
@@ -50,6 +50,30 @@ google-ads-mcp
|
|
|
50
50
|
|----------|-------------|
|
|
51
51
|
| `GOOGLE_ADS_LOGIN_CUSTOMER_ID` | MCC Account ID (10 digits, no dashes) |
|
|
52
52
|
| `GOOGLE_ADS_DEFAULT_CUSTOMER_ID` | Default account ID for queries |
|
|
53
|
+
| `GOOGLE_ADS_READ_ONLY` | Set to `true` to hide the mutate tool entirely — query and list only |
|
|
54
|
+
|
|
55
|
+
### MCP Config Example
|
|
56
|
+
|
|
57
|
+
```json
|
|
58
|
+
{
|
|
59
|
+
"mcpServers": {
|
|
60
|
+
"google-ads": {
|
|
61
|
+
"command": "npx",
|
|
62
|
+
"args": ["-y", "@channel47/google-ads-mcp@latest"],
|
|
63
|
+
"env": {
|
|
64
|
+
"GOOGLE_ADS_DEVELOPER_TOKEN": "your-developer-token",
|
|
65
|
+
"GOOGLE_ADS_CLIENT_ID": "your-client-id",
|
|
66
|
+
"GOOGLE_ADS_CLIENT_SECRET": "your-client-secret",
|
|
67
|
+
"GOOGLE_ADS_REFRESH_TOKEN": "your-refresh-token",
|
|
68
|
+
"GOOGLE_ADS_LOGIN_CUSTOMER_ID": "1234567890",
|
|
69
|
+
"GOOGLE_ADS_READ_ONLY": "true"
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
}
|
|
73
|
+
}
|
|
74
|
+
```
|
|
75
|
+
|
|
76
|
+
Remove `GOOGLE_ADS_READ_ONLY` or set to `false` to enable the mutate tool.
|
|
53
77
|
|
|
54
78
|
## Tools
|
|
55
79
|
|
|
@@ -241,15 +265,15 @@ npm install # workspaces — installs all servers
|
|
|
241
265
|
npm test # runs all server tests
|
|
242
266
|
```
|
|
243
267
|
|
|
244
|
-
|
|
268
|
+
3 core tools. Dry-run by default. OAuth 2.0 with environment-based credentials.
|
|
245
269
|
|
|
246
|
-
Pairs with the [media-buyer plugin](https://github.com/channel47/
|
|
270
|
+
Pairs with the [media-buyer plugin](https://github.com/channel47/plugins), which adds skills, mutation validation hooks, and GAQL reference docs on top of this server.
|
|
247
271
|
|
|
248
272
|
## Links
|
|
249
273
|
|
|
250
|
-
- [
|
|
274
|
+
- [channel47](https://channel47.dev) — open-source profession plugins for Claude Code
|
|
251
275
|
- [Build Notes](https://channel47.dev/subscribe) — weekly skill breakdowns from production use
|
|
252
|
-
- [Media Buyer Plugin](https://github.com/channel47/
|
|
276
|
+
- [Media Buyer Plugin](https://github.com/channel47/plugins) — the paid-search toolkit this MCP powers
|
|
253
277
|
- [NPM Package](https://www.npmjs.com/package/@channel47/google-ads-mcp)
|
|
254
278
|
- [Google Ads API](https://developers.google.com/google-ads/api/docs/start) / [GAQL Reference](https://developers.google.com/google-ads/api/docs/query/overview)
|
|
255
279
|
- [X](https://x.com/ctrlswing) / [LinkedIn](https://www.linkedin.com/in/jackson-d-9979a7a0/) / [GitHub](https://github.com/channel47)
|
package/package.json
CHANGED
package/server/auth.js
CHANGED
|
@@ -61,14 +61,15 @@ export function initializeGoogleAdsClient() {
|
|
|
61
61
|
/**
|
|
62
62
|
* Get authenticated customer client for a specific account
|
|
63
63
|
* @param {string} customerId - Google Ads account ID (without dashes)
|
|
64
|
+
* @param {string} [loginCustomerId] - MCC account ID to authenticate through (overrides env var)
|
|
64
65
|
* @returns {Customer}
|
|
65
66
|
*/
|
|
66
|
-
export function getCustomerClient(customerId) {
|
|
67
|
+
export function getCustomerClient(customerId, loginCustomerId) {
|
|
67
68
|
const client = initializeGoogleAdsClient();
|
|
68
69
|
|
|
69
70
|
return client.Customer({
|
|
70
71
|
customer_id: customerId,
|
|
71
72
|
refresh_token: process.env.GOOGLE_ADS_REFRESH_TOKEN,
|
|
72
|
-
login_customer_id: process.env.GOOGLE_ADS_LOGIN_CUSTOMER_ID,
|
|
73
|
+
login_customer_id: loginCustomerId || process.env.GOOGLE_ADS_LOGIN_CUSTOMER_ID,
|
|
73
74
|
});
|
|
74
75
|
}
|
package/server/index.js
CHANGED
|
@@ -26,7 +26,7 @@ import { getPromptsList, renderPrompt } from './prompts/templates.js';
|
|
|
26
26
|
|
|
27
27
|
// Server metadata
|
|
28
28
|
const SERVER_NAME = 'google-ads-mcp';
|
|
29
|
-
const SERVER_VERSION = '1.
|
|
29
|
+
const SERVER_VERSION = '1.1.1';
|
|
30
30
|
|
|
31
31
|
// Validate environment on startup
|
|
32
32
|
const { valid, missing } = validateEnvironment();
|
|
@@ -61,6 +61,10 @@ const ALL_TOOLS = [
|
|
|
61
61
|
inputSchema: {
|
|
62
62
|
type: 'object',
|
|
63
63
|
properties: {
|
|
64
|
+
login_customer_id: {
|
|
65
|
+
type: 'string',
|
|
66
|
+
description: 'MCC account ID to authenticate through (overrides GOOGLE_ADS_LOGIN_CUSTOMER_ID env var). Use when accessing accounts under a different MCC than the default.'
|
|
67
|
+
},
|
|
64
68
|
include_manager_accounts: {
|
|
65
69
|
type: 'boolean',
|
|
66
70
|
description: 'Include manager (MCC) accounts in results',
|
|
@@ -79,6 +83,10 @@ const ALL_TOOLS = [
|
|
|
79
83
|
type: 'string',
|
|
80
84
|
description: 'Google Ads account ID (optional, uses default if not specified)'
|
|
81
85
|
},
|
|
86
|
+
login_customer_id: {
|
|
87
|
+
type: 'string',
|
|
88
|
+
description: 'MCC account ID to authenticate through (overrides GOOGLE_ADS_LOGIN_CUSTOMER_ID env var). Use when accessing accounts under a different MCC than the default.'
|
|
89
|
+
},
|
|
82
90
|
query: {
|
|
83
91
|
type: 'string',
|
|
84
92
|
description: 'Full GAQL query string (SELECT only)'
|
|
@@ -123,6 +131,10 @@ For IMAGE/VIDEO assets, use 'image_file_path' or 'video_file_path' with absolute
|
|
|
123
131
|
type: 'string',
|
|
124
132
|
description: 'Google Ads customer ID (optional, uses default if not specified)'
|
|
125
133
|
},
|
|
134
|
+
login_customer_id: {
|
|
135
|
+
type: 'string',
|
|
136
|
+
description: 'MCC account ID to authenticate through (overrides GOOGLE_ADS_LOGIN_CUSTOMER_ID env var). Use when accessing accounts under a different MCC than the default.'
|
|
137
|
+
},
|
|
126
138
|
operations: {
|
|
127
139
|
type: 'array',
|
|
128
140
|
description: 'Array of mutation operations. Supports standard format ({ create/update/remove: ... }) or Opteo format ({ entity, operation, resource }).',
|
|
@@ -12,18 +12,20 @@ export async function listAccounts(params = {}) {
|
|
|
12
12
|
try {
|
|
13
13
|
const includeManagers = params.include_manager_accounts || false;
|
|
14
14
|
|
|
15
|
-
//
|
|
16
|
-
|
|
17
|
-
|
|
15
|
+
// Resolve MCC to enumerate — broader fallback than query/mutate because
|
|
16
|
+
// list_accounts always needs an MCC, while the others target specific accounts
|
|
17
|
+
const loginCustomerId = params.login_customer_id ||
|
|
18
|
+
process.env.GOOGLE_ADS_LOGIN_CUSTOMER_ID ||
|
|
19
|
+
process.env.GOOGLE_ADS_DEFAULT_CUSTOMER_ID;
|
|
18
20
|
|
|
19
|
-
if (!
|
|
21
|
+
if (!loginCustomerId) {
|
|
20
22
|
throw new Error(
|
|
21
|
-
'Either GOOGLE_ADS_LOGIN_CUSTOMER_ID or GOOGLE_ADS_DEFAULT_CUSTOMER_ID must be set'
|
|
23
|
+
'Either login_customer_id parameter, GOOGLE_ADS_LOGIN_CUSTOMER_ID, or GOOGLE_ADS_DEFAULT_CUSTOMER_ID must be set'
|
|
22
24
|
);
|
|
23
25
|
}
|
|
24
26
|
|
|
25
|
-
// Get customer client
|
|
26
|
-
const customer = getCustomerClient(
|
|
27
|
+
// Get customer client — login_customer_id serves as both the query target and auth MCC
|
|
28
|
+
const customer = getCustomerClient(loginCustomerId, loginCustomerId);
|
|
27
29
|
|
|
28
30
|
// Build query with optional manager filter
|
|
29
31
|
const filters = includeManagers ? '' : "WHERE customer_client.manager = false";
|
package/server/tools/mutate.js
CHANGED
|
@@ -17,6 +17,7 @@ import { normalizeOperations } from '../utils/operation-transform.js';
|
|
|
17
17
|
export async function mutate(params) {
|
|
18
18
|
const {
|
|
19
19
|
customer_id = process.env.GOOGLE_ADS_CUSTOMER_ID,
|
|
20
|
+
login_customer_id,
|
|
20
21
|
operations,
|
|
21
22
|
partial_failure = true,
|
|
22
23
|
dry_run = true
|
|
@@ -31,7 +32,7 @@ export async function mutate(params) {
|
|
|
31
32
|
return formatError(new Error('operations array is required and must contain at least one operation'));
|
|
32
33
|
}
|
|
33
34
|
|
|
34
|
-
const customer = getCustomerClient(customer_id);
|
|
35
|
+
const customer = getCustomerClient(customer_id, login_customer_id);
|
|
35
36
|
let response;
|
|
36
37
|
let partialFailureErrors = [];
|
|
37
38
|
|