mcp-google-ads 1.0.12 → 1.0.13
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/dist/build-info.json +1 -1
- package/dist/errors.js +5 -0
- package/dist/index.js +27 -52
- package/dist/resilience.js +1 -0
- package/dist/tools.js +47 -13
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"sha":"
|
|
1
|
+
{"sha":"00a842b","builtAt":"2026-04-09T22:23:35.553Z"}
|
package/dist/errors.js
CHANGED
|
@@ -37,6 +37,11 @@ export function validateCredentials() {
|
|
|
37
37
|
"GOOGLE_ADS_REFRESH_TOKEN",
|
|
38
38
|
];
|
|
39
39
|
const missing = required.filter((key) => !process.env[key] || process.env[key].trim() === "");
|
|
40
|
+
// Basic format validation: credentials should have reasonable length > 10 chars
|
|
41
|
+
const malformed = required.filter((key) => process.env[key] && process.env[key].trim().length > 0 && process.env[key].trim().length < 10);
|
|
42
|
+
if (malformed.length > 0) {
|
|
43
|
+
missing.push(...malformed.map(k => `${k} (format: too short, expected length > 10)`));
|
|
44
|
+
}
|
|
40
45
|
return { valid: missing.length === 0, missing };
|
|
41
46
|
}
|
|
42
47
|
/**
|
package/dist/index.js
CHANGED
|
@@ -8,6 +8,7 @@ import { CallToolRequestSchema, ListToolsRequestSchema, } from "@modelcontextpro
|
|
|
8
8
|
import { tools } from "./tools.js";
|
|
9
9
|
import { GoogleAdsApi, enums } from "google-ads-api";
|
|
10
10
|
import { readFileSync, existsSync } from "fs";
|
|
11
|
+
import v8 from "v8";
|
|
11
12
|
// CLI package info
|
|
12
13
|
const __cliPkg = JSON.parse(readFileSync(join(dirname(new URL(import.meta.url).pathname), "..", "package.json"), "utf-8"));
|
|
13
14
|
// Log build fingerprint at startup
|
|
@@ -39,6 +40,15 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
|
39
40
|
console.error(__cliPkg.version);
|
|
40
41
|
process.exit(0);
|
|
41
42
|
}
|
|
43
|
+
// Startup: detect npx vs direct node
|
|
44
|
+
if (process.argv[1]?.includes('.npm/_npx')) {
|
|
45
|
+
console.error("[startup] Running via npx -- first run may be slow due to package resolution");
|
|
46
|
+
}
|
|
47
|
+
// Startup: check heap size
|
|
48
|
+
const heapLimit = v8.getHeapStatistics().heap_size_limit;
|
|
49
|
+
if (heapLimit < 256 * 1024 * 1024) {
|
|
50
|
+
console.error(`[startup] WARNING: Heap limit is ${Math.round(heapLimit / 1024 / 1024)}MB`);
|
|
51
|
+
}
|
|
42
52
|
// ============================================
|
|
43
53
|
// ENV VAR TRIMMING
|
|
44
54
|
// ============================================
|
|
@@ -96,9 +106,9 @@ function getClientFromWorkingDir(config, cwd) {
|
|
|
96
106
|
function sanitizeNumericId(id) {
|
|
97
107
|
return id.replace(/[^0-9]/g, "");
|
|
98
108
|
}
|
|
99
|
-
/** Escape single quotes in strings used in GAQL WHERE clauses. */
|
|
109
|
+
/** Escape single quotes and backslashes in strings used in GAQL WHERE clauses. */
|
|
100
110
|
function escapeGaqlString(s) {
|
|
101
|
-
return s.replace(/'/g, "\\'");
|
|
111
|
+
return s.replace(/\\/g, "\\\\").replace(/'/g, "\\'");
|
|
102
112
|
}
|
|
103
113
|
// ============================================
|
|
104
114
|
// TYPED ERRORS & VALIDATION (extracted to errors.ts)
|
|
@@ -315,12 +325,8 @@ class GoogleAdsManager {
|
|
|
315
325
|
const customer = this.getCustomer(customerId);
|
|
316
326
|
const cleanId = customerId.replace(/-/g, "");
|
|
317
327
|
// Check if label already exists
|
|
318
|
-
const
|
|
319
|
-
SELECT label.resource_name, label.name
|
|
320
|
-
FROM label
|
|
321
|
-
WHERE label.name = '${escapeGaqlString(labelName)}'
|
|
322
|
-
AND label.status = 'ENABLED'
|
|
323
|
-
`), "ensureLabelExists.query");
|
|
328
|
+
const safeName = escapeGaqlString(labelName);
|
|
329
|
+
const existing = await withResilience(() => customer.query(`SELECT label.resource_name, label.name FROM label WHERE label.name = '` + safeName + `' AND label.status = 'ENABLED'`), "ensureLabelExists.query");
|
|
324
330
|
if (existing.length > 0) {
|
|
325
331
|
return existing[0].label.resource_name;
|
|
326
332
|
}
|
|
@@ -328,9 +334,7 @@ class GoogleAdsManager {
|
|
|
328
334
|
const result = await this.createLabel(customerId, labelName);
|
|
329
335
|
if (result.existing) {
|
|
330
336
|
// Race condition: re-query
|
|
331
|
-
const requery = await withResilience(() => customer.query(`
|
|
332
|
-
SELECT label.resource_name FROM label WHERE label.name = '${escapeGaqlString(labelName)}' AND label.status = 'ENABLED'
|
|
333
|
-
`), "ensureLabelExists.requery");
|
|
337
|
+
const requery = await withResilience(() => customer.query(`SELECT label.resource_name FROM label WHERE label.name = '` + safeName + `' AND label.status = 'ENABLED'`), "ensureLabelExists.requery");
|
|
334
338
|
return requery[0].label.resource_name;
|
|
335
339
|
}
|
|
336
340
|
return result.results[0].resource_name;
|
|
@@ -1011,37 +1015,18 @@ class GoogleAdsManager {
|
|
|
1011
1015
|
// Get search term category insights for a campaign (with trend comparison)
|
|
1012
1016
|
async getSearchTermInsights(customerId, options) {
|
|
1013
1017
|
const customer = this.getCustomer(customerId);
|
|
1018
|
+
const safeCampaignId = sanitizeNumericId(options.campaignId);
|
|
1019
|
+
const safeStartDate = escapeGaqlString(options.startDate);
|
|
1020
|
+
const safeEndDate = escapeGaqlString(options.endDate);
|
|
1014
1021
|
// Current period - get categories with metrics
|
|
1015
|
-
const currentQuery = `
|
|
1016
|
-
SELECT
|
|
1017
|
-
campaign_search_term_insight.campaign_id,
|
|
1018
|
-
campaign_search_term_insight.category_label,
|
|
1019
|
-
campaign_search_term_insight.id,
|
|
1020
|
-
metrics.clicks,
|
|
1021
|
-
metrics.impressions,
|
|
1022
|
-
metrics.conversions,
|
|
1023
|
-
metrics.conversions_value
|
|
1024
|
-
FROM campaign_search_term_insight
|
|
1025
|
-
WHERE campaign_search_term_insight.campaign_id = '${options.campaignId}'
|
|
1026
|
-
AND segments.date BETWEEN '${options.startDate}' AND '${options.endDate}'
|
|
1027
|
-
`;
|
|
1022
|
+
const currentQuery = `SELECT campaign_search_term_insight.campaign_id, campaign_search_term_insight.category_label, campaign_search_term_insight.id, metrics.clicks, metrics.impressions, metrics.conversions, metrics.conversions_value FROM campaign_search_term_insight WHERE campaign_search_term_insight.campaign_id = '` + safeCampaignId + `' AND segments.date BETWEEN '` + safeStartDate + `' AND '` + safeEndDate + `'`;
|
|
1028
1023
|
const currentResults = await withResilience(() => customer.query(currentQuery), "getSearchTermInsights.current");
|
|
1029
1024
|
// If comparison dates provided, get previous period too
|
|
1030
1025
|
let previousResults = [];
|
|
1031
1026
|
if (options.compareStartDate && options.compareEndDate) {
|
|
1032
|
-
const
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
campaign_search_term_insight.category_label,
|
|
1036
|
-
campaign_search_term_insight.id,
|
|
1037
|
-
metrics.clicks,
|
|
1038
|
-
metrics.impressions,
|
|
1039
|
-
metrics.conversions,
|
|
1040
|
-
metrics.conversions_value
|
|
1041
|
-
FROM campaign_search_term_insight
|
|
1042
|
-
WHERE campaign_search_term_insight.campaign_id = '${options.campaignId}'
|
|
1043
|
-
AND segments.date BETWEEN '${options.compareStartDate}' AND '${options.compareEndDate}'
|
|
1044
|
-
`;
|
|
1027
|
+
const safeCompStart = escapeGaqlString(options.compareStartDate);
|
|
1028
|
+
const safeCompEnd = escapeGaqlString(options.compareEndDate);
|
|
1029
|
+
const prevQuery = `SELECT campaign_search_term_insight.campaign_id, campaign_search_term_insight.category_label, campaign_search_term_insight.id, metrics.clicks, metrics.impressions, metrics.conversions, metrics.conversions_value FROM campaign_search_term_insight WHERE campaign_search_term_insight.campaign_id = '` + safeCampaignId + `' AND segments.date BETWEEN '` + safeCompStart + `' AND '` + safeCompEnd + `'`;
|
|
1045
1030
|
previousResults = await withResilience(() => customer.query(prevQuery), "getSearchTermInsights.previous");
|
|
1046
1031
|
}
|
|
1047
1032
|
// Build previous period lookup by category label
|
|
@@ -1110,21 +1095,11 @@ class GoogleAdsManager {
|
|
|
1110
1095
|
// Get individual search terms within a specific insight category
|
|
1111
1096
|
async getSearchTermInsightTerms(customerId, options) {
|
|
1112
1097
|
const customer = this.getCustomer(customerId);
|
|
1113
|
-
const
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
campaign_search_term_insight.id,
|
|
1118
|
-
segments.search_term,
|
|
1119
|
-
metrics.clicks,
|
|
1120
|
-
metrics.impressions,
|
|
1121
|
-
metrics.conversions,
|
|
1122
|
-
metrics.conversions_value
|
|
1123
|
-
FROM campaign_search_term_insight
|
|
1124
|
-
WHERE campaign_search_term_insight.campaign_id = '${options.campaignId}'
|
|
1125
|
-
AND campaign_search_term_insight.id = '${options.insightId}'
|
|
1126
|
-
AND segments.date BETWEEN '${options.startDate}' AND '${options.endDate}'
|
|
1127
|
-
`;
|
|
1098
|
+
const safeCampId = sanitizeNumericId(options.campaignId);
|
|
1099
|
+
const safeInsId = sanitizeNumericId(options.insightId);
|
|
1100
|
+
const safeStart = escapeGaqlString(options.startDate);
|
|
1101
|
+
const safeEnd = escapeGaqlString(options.endDate);
|
|
1102
|
+
const query = `SELECT campaign_search_term_insight.campaign_id, campaign_search_term_insight.category_label, campaign_search_term_insight.id, segments.search_term, metrics.clicks, metrics.impressions, metrics.conversions, metrics.conversions_value FROM campaign_search_term_insight WHERE campaign_search_term_insight.campaign_id = '` + safeCampId + `' AND campaign_search_term_insight.id = '` + safeInsId + `' AND segments.date BETWEEN '` + safeStart + `' AND '` + safeEnd + `'`;
|
|
1128
1103
|
const result = await withResilience(() => customer.query(query), "getSearchTermInsightTerms");
|
|
1129
1104
|
return safeResponse(result, "getSearchTermInsightTerms");
|
|
1130
1105
|
}
|
package/dist/resilience.js
CHANGED
|
@@ -5,6 +5,7 @@ import pino from "pino";
|
|
|
5
5
|
// ============================================
|
|
6
6
|
export const logger = pino({
|
|
7
7
|
level: process.env.LOG_LEVEL || "info",
|
|
8
|
+
redact: ["access_token", "refresh_token", "client_secret", "*.access_token", "*.refresh_token", "*.client_secret"],
|
|
8
9
|
...(process.env.NODE_ENV !== "test" && process.stderr.isTTY && {
|
|
9
10
|
transport: {
|
|
10
11
|
target: "pino-pretty",
|
package/dist/tools.js
CHANGED
|
@@ -3,6 +3,7 @@ export const tools = [
|
|
|
3
3
|
name: "google_ads_get_client_context",
|
|
4
4
|
description: "Get the current client context and health status based on working directory. Call this first to confirm which Google Ads account you're working with.",
|
|
5
5
|
inputSchema: {
|
|
6
|
+
additionalProperties: false,
|
|
6
7
|
type: "object",
|
|
7
8
|
properties: {
|
|
8
9
|
working_directory: {
|
|
@@ -17,6 +18,7 @@ export const tools = [
|
|
|
17
18
|
name: "google_ads_list_campaigns",
|
|
18
19
|
description: "List all campaigns for the current client account",
|
|
19
20
|
inputSchema: {
|
|
21
|
+
additionalProperties: false,
|
|
20
22
|
type: "object",
|
|
21
23
|
properties: {
|
|
22
24
|
customer_id: {
|
|
@@ -30,6 +32,7 @@ export const tools = [
|
|
|
30
32
|
name: "google_ads_list_ad_groups",
|
|
31
33
|
description: "List ad groups, optionally filtered by campaign",
|
|
32
34
|
inputSchema: {
|
|
35
|
+
additionalProperties: false,
|
|
33
36
|
type: "object",
|
|
34
37
|
properties: {
|
|
35
38
|
customer_id: { type: "string" },
|
|
@@ -41,10 +44,11 @@ export const tools = [
|
|
|
41
44
|
name: "google_ads_get_campaign_tracking",
|
|
42
45
|
description: "Get campaign tracking parameters including tracking URL template, final URL suffix, and custom URL parameters",
|
|
43
46
|
inputSchema: {
|
|
47
|
+
additionalProperties: false,
|
|
44
48
|
type: "object",
|
|
45
49
|
properties: {
|
|
46
50
|
customer_id: { type: "string" },
|
|
47
|
-
campaign_id: { type: "string", description: "The campaign ID to get tracking info for" },
|
|
51
|
+
campaign_id: { type: "string", description: "The numeric string campaign ID to get tracking info for" },
|
|
48
52
|
},
|
|
49
53
|
required: ["campaign_id"],
|
|
50
54
|
},
|
|
@@ -53,6 +57,7 @@ export const tools = [
|
|
|
53
57
|
name: "google_ads_list_pending_changes",
|
|
54
58
|
description: "List all pending changes (paused items with claude- label) awaiting review",
|
|
55
59
|
inputSchema: {
|
|
60
|
+
additionalProperties: false,
|
|
56
61
|
type: "object",
|
|
57
62
|
properties: {
|
|
58
63
|
customer_id: { type: "string" },
|
|
@@ -63,6 +68,7 @@ export const tools = [
|
|
|
63
68
|
name: "google_ads_validate_ad",
|
|
64
69
|
description: "Validate an RSA without creating it. Use this to check for errors before creating.",
|
|
65
70
|
inputSchema: {
|
|
71
|
+
additionalProperties: false,
|
|
66
72
|
type: "object",
|
|
67
73
|
properties: {
|
|
68
74
|
headlines: {
|
|
@@ -87,6 +93,7 @@ export const tools = [
|
|
|
87
93
|
name: "google_ads_create_campaign",
|
|
88
94
|
description: "Create a new campaign (will be PAUSED until approved). Returns campaign ID.",
|
|
89
95
|
inputSchema: {
|
|
96
|
+
additionalProperties: false,
|
|
90
97
|
type: "object",
|
|
91
98
|
properties: {
|
|
92
99
|
customer_id: { type: "string" },
|
|
@@ -100,6 +107,7 @@ export const tools = [
|
|
|
100
107
|
name: "google_ads_create_ad_group",
|
|
101
108
|
description: "Create a new ad group (will be PAUSED until approved). Returns ad group ID.",
|
|
102
109
|
inputSchema: {
|
|
110
|
+
additionalProperties: false,
|
|
103
111
|
type: "object",
|
|
104
112
|
properties: {
|
|
105
113
|
customer_id: { type: "string" },
|
|
@@ -114,6 +122,7 @@ export const tools = [
|
|
|
114
122
|
name: "google_ads_create_responsive_search_ad",
|
|
115
123
|
description: "Create a responsive search ad (will be PAUSED until approved). Validates before creating. Headlines/descriptions can be plain strings or objects with pinned_position (1-3 for headlines, 1-2 for descriptions).",
|
|
116
124
|
inputSchema: {
|
|
125
|
+
additionalProperties: false,
|
|
117
126
|
type: "object",
|
|
118
127
|
properties: {
|
|
119
128
|
customer_id: { type: "string" },
|
|
@@ -161,6 +170,7 @@ export const tools = [
|
|
|
161
170
|
name: "google_ads_create_keywords",
|
|
162
171
|
description: "Create keywords for an ad group (will be PAUSED until approved). Keywords are auto-labeled for easy discovery via google_ads_list_pending_changes.",
|
|
163
172
|
inputSchema: {
|
|
173
|
+
additionalProperties: false,
|
|
164
174
|
type: "object",
|
|
165
175
|
properties: {
|
|
166
176
|
customer_id: { type: "string" },
|
|
@@ -188,6 +198,7 @@ export const tools = [
|
|
|
188
198
|
name: "google_ads_enable_items",
|
|
189
199
|
description: "Enable paused campaigns, ad groups, or ads. REQUIRES USER APPROVAL. Use after reviewing in Google Ads UI.",
|
|
190
200
|
inputSchema: {
|
|
201
|
+
additionalProperties: false,
|
|
191
202
|
type: "object",
|
|
192
203
|
properties: {
|
|
193
204
|
customer_id: { type: "string" },
|
|
@@ -201,6 +212,7 @@ export const tools = [
|
|
|
201
212
|
name: "google_ads_pause_items",
|
|
202
213
|
description: "Pause enabled campaigns, ad groups, or ads. REQUIRES USER APPROVAL. This will stop items from serving.",
|
|
203
214
|
inputSchema: {
|
|
215
|
+
additionalProperties: false,
|
|
204
216
|
type: "object",
|
|
205
217
|
properties: {
|
|
206
218
|
customer_id: { type: "string" },
|
|
@@ -214,6 +226,7 @@ export const tools = [
|
|
|
214
226
|
name: "google_ads_create_shared_set",
|
|
215
227
|
description: "Create a new shared negative keyword list at account level. Returns the new shared set ID.",
|
|
216
228
|
inputSchema: {
|
|
229
|
+
additionalProperties: false,
|
|
217
230
|
type: "object",
|
|
218
231
|
properties: {
|
|
219
232
|
customer_id: { type: "string" },
|
|
@@ -226,11 +239,12 @@ export const tools = [
|
|
|
226
239
|
name: "google_ads_link_shared_set",
|
|
227
240
|
description: "Link a shared negative keyword list to one or more campaigns. Once linked, all negatives in the list will apply to those campaigns.",
|
|
228
241
|
inputSchema: {
|
|
242
|
+
additionalProperties: false,
|
|
229
243
|
type: "object",
|
|
230
244
|
properties: {
|
|
231
245
|
customer_id: { type: "string" },
|
|
232
246
|
shared_set_id: { type: "string", description: "The shared set ID to link" },
|
|
233
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "
|
|
247
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Numeric string campaign IDs to link the shared set to" },
|
|
234
248
|
},
|
|
235
249
|
required: ["shared_set_id", "campaign_ids"],
|
|
236
250
|
},
|
|
@@ -239,11 +253,12 @@ export const tools = [
|
|
|
239
253
|
name: "google_ads_unlink_shared_set",
|
|
240
254
|
description: "Unlink a shared negative keyword list from one or more campaigns. The list's negatives will no longer apply to those campaigns.",
|
|
241
255
|
inputSchema: {
|
|
256
|
+
additionalProperties: false,
|
|
242
257
|
type: "object",
|
|
243
258
|
properties: {
|
|
244
259
|
customer_id: { type: "string" },
|
|
245
260
|
shared_set_id: { type: "string", description: "The shared set ID to unlink" },
|
|
246
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "
|
|
261
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Numeric string campaign IDs to unlink the shared set from" },
|
|
247
262
|
},
|
|
248
263
|
required: ["shared_set_id", "campaign_ids"],
|
|
249
264
|
},
|
|
@@ -252,6 +267,7 @@ export const tools = [
|
|
|
252
267
|
name: "google_ads_add_shared_negatives",
|
|
253
268
|
description: "Add negative keywords to a shared negative keyword list. Keywords will immediately block matching queries across all campaigns the list is applied to.",
|
|
254
269
|
inputSchema: {
|
|
270
|
+
additionalProperties: false,
|
|
255
271
|
type: "object",
|
|
256
272
|
properties: {
|
|
257
273
|
customer_id: { type: "string" },
|
|
@@ -275,6 +291,7 @@ export const tools = [
|
|
|
275
291
|
name: "google_ads_remove_shared_negatives",
|
|
276
292
|
description: "Remove negative keywords from a shared negative keyword list by their resource names. Get resource names from a GAQL query on shared_criterion.",
|
|
277
293
|
inputSchema: {
|
|
294
|
+
additionalProperties: false,
|
|
278
295
|
type: "object",
|
|
279
296
|
properties: {
|
|
280
297
|
customer_id: { type: "string" },
|
|
@@ -291,6 +308,7 @@ export const tools = [
|
|
|
291
308
|
name: "google_ads_add_campaign_negatives",
|
|
292
309
|
description: "Add negative keywords at the campaign level. Use for campaign-specific negatives that shouldn't be in a shared list.",
|
|
293
310
|
inputSchema: {
|
|
311
|
+
additionalProperties: false,
|
|
294
312
|
type: "object",
|
|
295
313
|
properties: {
|
|
296
314
|
customer_id: { type: "string" },
|
|
@@ -314,6 +332,7 @@ export const tools = [
|
|
|
314
332
|
name: "google_ads_remove_campaign_negatives",
|
|
315
333
|
description: "Remove campaign-level negative keywords by their resource names. Get resource names from a GAQL query on campaign_criterion.",
|
|
316
334
|
inputSchema: {
|
|
335
|
+
additionalProperties: false,
|
|
317
336
|
type: "object",
|
|
318
337
|
properties: {
|
|
319
338
|
customer_id: { type: "string" },
|
|
@@ -330,6 +349,7 @@ export const tools = [
|
|
|
330
349
|
name: "google_ads_remove_adgroup_negatives",
|
|
331
350
|
description: "Remove ad-group-level negative keywords by their resource names. Get resource names from a GAQL query on ad_group_criterion.",
|
|
332
351
|
inputSchema: {
|
|
352
|
+
additionalProperties: false,
|
|
333
353
|
type: "object",
|
|
334
354
|
properties: {
|
|
335
355
|
customer_id: { type: "string" },
|
|
@@ -346,6 +366,7 @@ export const tools = [
|
|
|
346
366
|
name: "google_ads_pause_keywords",
|
|
347
367
|
description: "Pause active keywords by their criterion resource names.",
|
|
348
368
|
inputSchema: {
|
|
369
|
+
additionalProperties: false,
|
|
349
370
|
type: "object",
|
|
350
371
|
properties: {
|
|
351
372
|
customer_id: { type: "string" },
|
|
@@ -358,10 +379,11 @@ export const tools = [
|
|
|
358
379
|
name: "google_ads_update_campaign_tracking",
|
|
359
380
|
description: "Update campaign tracking parameters: final URL suffix, tracking URL template, and/or custom URL parameters. Use google_ads_get_campaign_tracking first to see current values.",
|
|
360
381
|
inputSchema: {
|
|
382
|
+
additionalProperties: false,
|
|
361
383
|
type: "object",
|
|
362
384
|
properties: {
|
|
363
385
|
customer_id: { type: "string" },
|
|
364
|
-
campaign_id: { type: "string", description: "The campaign ID to update" },
|
|
386
|
+
campaign_id: { type: "string", description: "The numeric string campaign ID to update" },
|
|
365
387
|
final_url_suffix: { type: "string", description: "New final URL suffix (appended to landing page URLs). Set to empty string to clear." },
|
|
366
388
|
tracking_url_template: { type: "string", description: "New tracking URL template. Set to empty string to clear." },
|
|
367
389
|
url_custom_parameters: {
|
|
@@ -387,13 +409,14 @@ export const tools = [
|
|
|
387
409
|
name: "google_ads_keyword_performance",
|
|
388
410
|
description: "Get keyword performance report with metrics including impressions, clicks, cost, conversions, quality score components (quality score, expected CTR, ad relevance, landing page experience), and impression share metrics.",
|
|
389
411
|
inputSchema: {
|
|
412
|
+
additionalProperties: false,
|
|
390
413
|
type: "object",
|
|
391
414
|
properties: {
|
|
392
415
|
customer_id: { type: "string" },
|
|
393
416
|
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
394
417
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
395
418
|
keyword_text_contains: { type: "string", description: "Filter keywords containing this text" },
|
|
396
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by campaign IDs" },
|
|
419
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by numeric string campaign IDs" },
|
|
397
420
|
ad_group_ids: { type: "array", items: { type: "string" }, description: "Filter by ad group IDs" },
|
|
398
421
|
},
|
|
399
422
|
required: ["start_date", "end_date"],
|
|
@@ -403,13 +426,14 @@ export const tools = [
|
|
|
403
426
|
name: "google_ads_keyword_performance_by_conversion",
|
|
404
427
|
description: "Get keyword performance broken down by conversion action. Shows which keywords drive which conversion types (e.g., form fills, MQLs, etc.).",
|
|
405
428
|
inputSchema: {
|
|
429
|
+
additionalProperties: false,
|
|
406
430
|
type: "object",
|
|
407
431
|
properties: {
|
|
408
432
|
customer_id: { type: "string" },
|
|
409
433
|
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
410
434
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
411
435
|
keyword_text_contains: { type: "string", description: "Filter keywords containing this text" },
|
|
412
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by campaign IDs" },
|
|
436
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by numeric string campaign IDs" },
|
|
413
437
|
ad_group_ids: { type: "array", items: { type: "string" }, description: "Filter by ad group IDs" },
|
|
414
438
|
},
|
|
415
439
|
required: ["start_date", "end_date"],
|
|
@@ -419,6 +443,7 @@ export const tools = [
|
|
|
419
443
|
name: "google_ads_search_term_report",
|
|
420
444
|
description: "Get search term report showing actual search queries that triggered ads, with the keyword they matched to.",
|
|
421
445
|
inputSchema: {
|
|
446
|
+
additionalProperties: false,
|
|
422
447
|
type: "object",
|
|
423
448
|
properties: {
|
|
424
449
|
customer_id: { type: "string" },
|
|
@@ -426,7 +451,7 @@ export const tools = [
|
|
|
426
451
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
427
452
|
keyword_text_contains: { type: "string", description: "Filter by keyword text" },
|
|
428
453
|
search_term_contains: { type: "string", description: "Filter search terms containing this text" },
|
|
429
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by campaign IDs" },
|
|
454
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by numeric string campaign IDs" },
|
|
430
455
|
ad_group_ids: { type: "array", items: { type: "string" }, description: "Filter by ad group IDs" },
|
|
431
456
|
},
|
|
432
457
|
required: ["start_date", "end_date"],
|
|
@@ -436,6 +461,7 @@ export const tools = [
|
|
|
436
461
|
name: "google_ads_search_term_report_by_conversion",
|
|
437
462
|
description: "Get search term report broken down by conversion action. Shows which search queries drive which conversion types.",
|
|
438
463
|
inputSchema: {
|
|
464
|
+
additionalProperties: false,
|
|
439
465
|
type: "object",
|
|
440
466
|
properties: {
|
|
441
467
|
customer_id: { type: "string" },
|
|
@@ -443,7 +469,7 @@ export const tools = [
|
|
|
443
469
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
444
470
|
keyword_text_contains: { type: "string", description: "Filter by keyword text" },
|
|
445
471
|
search_term_contains: { type: "string", description: "Filter search terms containing this text" },
|
|
446
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by campaign IDs" },
|
|
472
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by numeric string campaign IDs" },
|
|
447
473
|
ad_group_ids: { type: "array", items: { type: "string" }, description: "Filter by ad group IDs" },
|
|
448
474
|
},
|
|
449
475
|
required: ["start_date", "end_date"],
|
|
@@ -453,12 +479,13 @@ export const tools = [
|
|
|
453
479
|
name: "google_ads_ad_performance",
|
|
454
480
|
description: "Get ad performance report with metrics, ad copy (headlines/descriptions), final URLs, and ad strength rating.",
|
|
455
481
|
inputSchema: {
|
|
482
|
+
additionalProperties: false,
|
|
456
483
|
type: "object",
|
|
457
484
|
properties: {
|
|
458
485
|
customer_id: { type: "string" },
|
|
459
486
|
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
460
487
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
461
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by campaign IDs" },
|
|
488
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by numeric string campaign IDs" },
|
|
462
489
|
ad_group_ids: { type: "array", items: { type: "string" }, description: "Filter by ad group IDs" },
|
|
463
490
|
},
|
|
464
491
|
required: ["start_date", "end_date"],
|
|
@@ -468,12 +495,13 @@ export const tools = [
|
|
|
468
495
|
name: "google_ads_ad_performance_by_conversion",
|
|
469
496
|
description: "Get ad performance broken down by conversion action. Shows which ads drive which conversion types.",
|
|
470
497
|
inputSchema: {
|
|
498
|
+
additionalProperties: false,
|
|
471
499
|
type: "object",
|
|
472
500
|
properties: {
|
|
473
501
|
customer_id: { type: "string" },
|
|
474
502
|
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
475
503
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
476
|
-
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by campaign IDs" },
|
|
504
|
+
campaign_ids: { type: "array", items: { type: "string" }, description: "Filter by numeric string campaign IDs" },
|
|
477
505
|
ad_group_ids: { type: "array", items: { type: "string" }, description: "Filter by ad group IDs" },
|
|
478
506
|
},
|
|
479
507
|
required: ["start_date", "end_date"],
|
|
@@ -483,6 +511,7 @@ export const tools = [
|
|
|
483
511
|
name: "google_ads_list_conversion_actions",
|
|
484
512
|
description: "List all available conversion actions (e.g., form fills, MQLs, etc.) to understand what conversion tracking is set up.",
|
|
485
513
|
inputSchema: {
|
|
514
|
+
additionalProperties: false,
|
|
486
515
|
type: "object",
|
|
487
516
|
properties: {
|
|
488
517
|
customer_id: { type: "string" },
|
|
@@ -493,10 +522,11 @@ export const tools = [
|
|
|
493
522
|
name: "google_ads_search_term_insights",
|
|
494
523
|
description: "Get search term category insights for a campaign. Shows the search categories your ads appeared against (like the Insights panel in the Google Ads UI). Must query one campaign at a time. Optionally compares two date ranges to show trends.",
|
|
495
524
|
inputSchema: {
|
|
525
|
+
additionalProperties: false,
|
|
496
526
|
type: "object",
|
|
497
527
|
properties: {
|
|
498
528
|
customer_id: { type: "string" },
|
|
499
|
-
campaign_id: { type: "string", description: "The campaign ID to get insights for (required, one campaign at a time)" },
|
|
529
|
+
campaign_id: { type: "string", description: "The numeric string campaign ID to get insights for (required, one campaign at a time)" },
|
|
500
530
|
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
501
531
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
502
532
|
compare_start_date: { type: "string", description: "Comparison period start date (optional, for trend calculation)" },
|
|
@@ -509,10 +539,11 @@ export const tools = [
|
|
|
509
539
|
name: "google_ads_search_term_insight_terms",
|
|
510
540
|
description: "Drill into a specific search term category to see the individual search terms within it. Requires an insight_id from google_ads_search_term_insights.",
|
|
511
541
|
inputSchema: {
|
|
542
|
+
additionalProperties: false,
|
|
512
543
|
type: "object",
|
|
513
544
|
properties: {
|
|
514
545
|
customer_id: { type: "string" },
|
|
515
|
-
campaign_id: { type: "string", description: "The campaign ID" },
|
|
546
|
+
campaign_id: { type: "string", description: "The numeric string campaign ID" },
|
|
516
547
|
insight_id: { type: "string", description: "The insight category ID from google_ads_search_term_insights results" },
|
|
517
548
|
start_date: { type: "string", description: "Start date in YYYY-MM-DD format" },
|
|
518
549
|
end_date: { type: "string", description: "End date in YYYY-MM-DD format" },
|
|
@@ -524,10 +555,11 @@ export const tools = [
|
|
|
524
555
|
name: "google_ads_update_campaign_budget",
|
|
525
556
|
description: "Update the daily budget for a campaign. Can update the existing budget amount or create a new solo budget and reassign the campaign to it (useful for breaking shared budgets).",
|
|
526
557
|
inputSchema: {
|
|
558
|
+
additionalProperties: false,
|
|
527
559
|
type: "object",
|
|
528
560
|
properties: {
|
|
529
561
|
customer_id: { type: "string" },
|
|
530
|
-
campaign_id: { type: "string", description: "The campaign ID to update" },
|
|
562
|
+
campaign_id: { type: "string", description: "The numeric string campaign ID to update" },
|
|
531
563
|
daily_budget: { type: "number", description: "New daily budget in dollars" },
|
|
532
564
|
create_new_budget: { type: "boolean", description: "If true, creates a new budget and reassigns this campaign to it (breaks shared budgets). If false, updates the existing budget amount in place (affects all campaigns sharing it)." },
|
|
533
565
|
},
|
|
@@ -538,6 +570,7 @@ export const tools = [
|
|
|
538
570
|
name: "google_ads_gaql_query",
|
|
539
571
|
description: "Execute a raw GAQL (Google Ads Query Language) query. Use this for custom reports or accessing any Google Ads API resource not covered by other tools. See https://developers.google.com/google-ads/api/docs/query/overview for GAQL syntax.",
|
|
540
572
|
inputSchema: {
|
|
573
|
+
additionalProperties: false,
|
|
541
574
|
type: "object",
|
|
542
575
|
properties: {
|
|
543
576
|
customer_id: { type: "string" },
|
|
@@ -550,6 +583,7 @@ export const tools = [
|
|
|
550
583
|
name: "google_ads_keyword_volume",
|
|
551
584
|
description: "Get historical search volume estimates for a list of keywords using the Google Ads Keyword Planner. Returns avg monthly searches, competition level, and CPC bid range for each keyword.",
|
|
552
585
|
inputSchema: {
|
|
586
|
+
additionalProperties: false,
|
|
553
587
|
type: "object",
|
|
554
588
|
properties: {
|
|
555
589
|
customer_id: { type: "string", description: "The Google Ads customer ID" },
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-google-ads",
|
|
3
3
|
"mcpName": "io.github.mharnett/google-ads",
|
|
4
|
-
"version": "1.0.
|
|
4
|
+
"version": "1.0.13",
|
|
5
5
|
"description": "MCP server for Google Ads API with MCC support, 34 tools for campaign management, reporting, and optimization. Safe by default -- all changes created PAUSED.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|