mcp-google-ads 1.0.3 → 1.0.5
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/index.js +37 -10
- package/package.json +1 -1
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"sha":"
|
|
1
|
+
{"sha":"82b0bbb","builtAt":"2026-04-09T19:59:25.007Z"}
|
package/dist/index.js
CHANGED
|
@@ -17,6 +17,22 @@ try {
|
|
|
17
17
|
catch {
|
|
18
18
|
// build-info.json not present (dev mode)
|
|
19
19
|
}
|
|
20
|
+
// CLI flags
|
|
21
|
+
const __cliPkg = JSON.parse(readFileSync(join(dirname(new URL(import.meta.url).pathname), "..", "package.json"), "utf-8"));
|
|
22
|
+
if (process.argv.includes("--help") || process.argv.includes("-h")) {
|
|
23
|
+
console.log(`${__cliPkg.name} v${__cliPkg.version}\n`);
|
|
24
|
+
console.log(`Usage: ${__cliPkg.name} [options]\n`);
|
|
25
|
+
console.log("MCP server communicating via stdio. Configure in your .mcp.json.\n");
|
|
26
|
+
console.log("Options:");
|
|
27
|
+
console.log(" --help, -h Show this help message");
|
|
28
|
+
console.log(" --version, -v Show version number");
|
|
29
|
+
console.log(`\nDocumentation: https://github.com/mharnett/mcp-google-ads`);
|
|
30
|
+
process.exit(0);
|
|
31
|
+
}
|
|
32
|
+
if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
33
|
+
console.log(__cliPkg.version);
|
|
34
|
+
process.exit(0);
|
|
35
|
+
}
|
|
20
36
|
function loadConfig() {
|
|
21
37
|
// Try config.json first (for multi-client setups)
|
|
22
38
|
const configPath = join(dirname(new URL(import.meta.url).pathname), "..", "config.json");
|
|
@@ -64,6 +80,17 @@ function getClientFromWorkingDir(config, cwd) {
|
|
|
64
80
|
return null;
|
|
65
81
|
}
|
|
66
82
|
// ============================================
|
|
83
|
+
// GAQL SANITIZATION
|
|
84
|
+
// ============================================
|
|
85
|
+
/** Strip non-numeric characters from IDs used in GAQL WHERE clauses. */
|
|
86
|
+
function sanitizeNumericId(id) {
|
|
87
|
+
return id.replace(/[^0-9]/g, "");
|
|
88
|
+
}
|
|
89
|
+
/** Escape single quotes in strings used in GAQL WHERE clauses. */
|
|
90
|
+
function escapeGaqlString(s) {
|
|
91
|
+
return s.replace(/'/g, "\\'");
|
|
92
|
+
}
|
|
93
|
+
// ============================================
|
|
67
94
|
// TYPED ERRORS & VALIDATION (extracted to errors.ts)
|
|
68
95
|
// ============================================
|
|
69
96
|
import { GoogleAdsAuthError, GoogleAdsRateLimitError, GoogleAdsServiceError, validateCredentials, classifyError, } from "./errors.js";
|
|
@@ -177,7 +204,7 @@ class GoogleAdsManager {
|
|
|
177
204
|
WHERE ad_group.status != 'REMOVED'
|
|
178
205
|
`;
|
|
179
206
|
if (campaignId) {
|
|
180
|
-
query += ` AND campaign.id = ${campaignId}`;
|
|
207
|
+
query += ` AND campaign.id = ${sanitizeNumericId(campaignId)}`;
|
|
181
208
|
}
|
|
182
209
|
query += ` ORDER BY campaign.name, ad_group.name`;
|
|
183
210
|
const result = await withResilience(() => customer.query(query), "listAdGroups");
|
|
@@ -203,7 +230,7 @@ class GoogleAdsManager {
|
|
|
203
230
|
WHERE ad_group_ad.status != 'REMOVED'
|
|
204
231
|
`;
|
|
205
232
|
if (options.campaignId) {
|
|
206
|
-
query += ` AND campaign.id = ${options.campaignId}`;
|
|
233
|
+
query += ` AND campaign.id = ${sanitizeNumericId(options.campaignId)}`;
|
|
207
234
|
}
|
|
208
235
|
if (options.adGroupId) {
|
|
209
236
|
query += ` AND ad_group.id = ${options.adGroupId}`;
|
|
@@ -281,7 +308,7 @@ class GoogleAdsManager {
|
|
|
281
308
|
const existing = await withResilience(() => customer.query(`
|
|
282
309
|
SELECT label.resource_name, label.name
|
|
283
310
|
FROM label
|
|
284
|
-
WHERE label.name = '${labelName}'
|
|
311
|
+
WHERE label.name = '${escapeGaqlString(labelName)}'
|
|
285
312
|
AND label.status = 'ENABLED'
|
|
286
313
|
`), "ensureLabelExists.query");
|
|
287
314
|
if (existing.length > 0) {
|
|
@@ -292,7 +319,7 @@ class GoogleAdsManager {
|
|
|
292
319
|
if (result.existing) {
|
|
293
320
|
// Race condition: re-query
|
|
294
321
|
const requery = await withResilience(() => customer.query(`
|
|
295
|
-
SELECT label.resource_name FROM label WHERE label.name = '${labelName}' AND label.status = 'ENABLED'
|
|
322
|
+
SELECT label.resource_name FROM label WHERE label.name = '${escapeGaqlString(labelName)}' AND label.status = 'ENABLED'
|
|
296
323
|
`), "ensureLabelExists.requery");
|
|
297
324
|
return requery[0].label.resource_name;
|
|
298
325
|
}
|
|
@@ -695,7 +722,7 @@ class GoogleAdsManager {
|
|
|
695
722
|
query += ` AND ad_group_criterion.keyword.text LIKE '%${options.keywordTextContains}%'`;
|
|
696
723
|
}
|
|
697
724
|
if (options.campaignIds && options.campaignIds.length > 0) {
|
|
698
|
-
query += ` AND campaign.id IN (${options.campaignIds.join(",")})`;
|
|
725
|
+
query += ` AND campaign.id IN (${options.campaignIds.map(sanitizeNumericId).join(",")})`;
|
|
699
726
|
}
|
|
700
727
|
if (options.adGroupIds && options.adGroupIds.length > 0) {
|
|
701
728
|
query += ` AND ad_group.id IN (${options.adGroupIds.join(",")})`;
|
|
@@ -735,7 +762,7 @@ class GoogleAdsManager {
|
|
|
735
762
|
query += ` AND ad_group_criterion.keyword.text LIKE '%${options.keywordTextContains}%'`;
|
|
736
763
|
}
|
|
737
764
|
if (options.campaignIds && options.campaignIds.length > 0) {
|
|
738
|
-
query += ` AND campaign.id IN (${options.campaignIds.join(",")})`;
|
|
765
|
+
query += ` AND campaign.id IN (${options.campaignIds.map(sanitizeNumericId).join(",")})`;
|
|
739
766
|
}
|
|
740
767
|
if (options.adGroupIds && options.adGroupIds.length > 0) {
|
|
741
768
|
query += ` AND ad_group.id IN (${options.adGroupIds.join(",")})`;
|
|
@@ -773,7 +800,7 @@ class GoogleAdsManager {
|
|
|
773
800
|
query += ` AND search_term_view.search_term LIKE '%${options.searchTermContains}%'`;
|
|
774
801
|
}
|
|
775
802
|
if (options.campaignIds && options.campaignIds.length > 0) {
|
|
776
|
-
query += ` AND campaign.id IN (${options.campaignIds.join(",")})`;
|
|
803
|
+
query += ` AND campaign.id IN (${options.campaignIds.map(sanitizeNumericId).join(",")})`;
|
|
777
804
|
}
|
|
778
805
|
if (options.adGroupIds && options.adGroupIds.length > 0) {
|
|
779
806
|
query += ` AND ad_group.id IN (${options.adGroupIds.join(",")})`;
|
|
@@ -809,7 +836,7 @@ class GoogleAdsManager {
|
|
|
809
836
|
query += ` AND search_term_view.search_term LIKE '%${options.searchTermContains}%'`;
|
|
810
837
|
}
|
|
811
838
|
if (options.campaignIds && options.campaignIds.length > 0) {
|
|
812
|
-
query += ` AND campaign.id IN (${options.campaignIds.join(",")})`;
|
|
839
|
+
query += ` AND campaign.id IN (${options.campaignIds.map(sanitizeNumericId).join(",")})`;
|
|
813
840
|
}
|
|
814
841
|
if (options.adGroupIds && options.adGroupIds.length > 0) {
|
|
815
842
|
query += ` AND ad_group.id IN (${options.adGroupIds.join(",")})`;
|
|
@@ -854,7 +881,7 @@ class GoogleAdsManager {
|
|
|
854
881
|
AND ad_group_ad.status != 'REMOVED'
|
|
855
882
|
`;
|
|
856
883
|
if (options.campaignIds && options.campaignIds.length > 0) {
|
|
857
|
-
query += ` AND campaign.id IN (${options.campaignIds.join(",")})`;
|
|
884
|
+
query += ` AND campaign.id IN (${options.campaignIds.map(sanitizeNumericId).join(",")})`;
|
|
858
885
|
}
|
|
859
886
|
if (options.adGroupIds && options.adGroupIds.length > 0) {
|
|
860
887
|
query += ` AND ad_group.id IN (${options.adGroupIds.join(",")})`;
|
|
@@ -897,7 +924,7 @@ class GoogleAdsManager {
|
|
|
897
924
|
AND ad_group_ad.status != 'REMOVED'
|
|
898
925
|
`;
|
|
899
926
|
if (options.campaignIds && options.campaignIds.length > 0) {
|
|
900
|
-
query += ` AND campaign.id IN (${options.campaignIds.join(",")})`;
|
|
927
|
+
query += ` AND campaign.id IN (${options.campaignIds.map(sanitizeNumericId).join(",")})`;
|
|
901
928
|
}
|
|
902
929
|
if (options.adGroupIds && options.adGroupIds.length > 0) {
|
|
903
930
|
query += ` AND ad_group.id IN (${options.adGroupIds.join(",")})`;
|
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.5",
|
|
5
5
|
"description": "MCP server for Google Ads API with MCC support, 35 tools for campaign management, reporting, and optimization. Safe by default — all changes created PAUSED.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|