mcp-google-ads 1.0.8 → 1.0.10

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.
@@ -1 +1 @@
1
- {"sha":"bdd46f2","builtAt":"2026-04-09T21:28:07.536Z"}
1
+ {"sha":"fd2b513","builtAt":"2026-04-09T21:47:18.505Z"}
package/dist/errors.js CHANGED
@@ -12,7 +12,7 @@ export class GoogleAdsAuthError extends Error {
12
12
  export class GoogleAdsRateLimitError extends Error {
13
13
  retryAfterMs;
14
14
  constructor(retryAfterMs, cause) {
15
- super(`Rate limited, retry after ${retryAfterMs}ms`);
15
+ super(`Google Ads rate limited, retry after ${retryAfterMs}ms`);
16
16
  this.retryAfterMs = retryAfterMs;
17
17
  this.name = "GoogleAdsRateLimitError";
18
18
  this.cause = cause;
@@ -89,7 +89,7 @@ export function classifyError(error) {
89
89
  message.includes("refresh token") ||
90
90
  code?.authentication_error ||
91
91
  code?.authorization_error) {
92
- return new GoogleAdsAuthError(`Auth failed: ${message}. Check your refresh token and OAuth credentials.`, error);
92
+ return new GoogleAdsAuthError(`Google Ads auth failed: ${message}. Check your refresh token and OAuth credentials.`, error);
93
93
  }
94
94
  // Rate limiting
95
95
  if (status === 429 ||
package/dist/index.js CHANGED
@@ -34,6 +34,10 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
34
34
  console.error(__cliPkg.version);
35
35
  process.exit(0);
36
36
  }
37
+ // ============================================
38
+ // ENV VAR TRIMMING
39
+ // ============================================
40
+ const envTrimmed = (key) => (process.env[key] || "").trim().replace(/^["']|["']$/g, "");
37
41
  function loadConfig() {
38
42
  // Try config.json first (for multi-client setups)
39
43
  const configPath = join(dirname(new URL(import.meta.url).pathname), "..", "config.json");
@@ -41,12 +45,12 @@ function loadConfig() {
41
45
  return JSON.parse(readFileSync(configPath, "utf-8"));
42
46
  }
43
47
  // Fall back to env vars (single-client mode)
44
- const clientId = process.env.GOOGLE_ADS_CLIENT_ID;
45
- const clientSecret = process.env.GOOGLE_ADS_CLIENT_SECRET;
46
- const developerToken = process.env.GOOGLE_ADS_DEVELOPER_TOKEN;
47
- const refreshToken = process.env.GOOGLE_ADS_REFRESH_TOKEN;
48
- const customerId = process.env.GOOGLE_ADS_CUSTOMER_ID;
49
- const mccId = process.env.GOOGLE_ADS_MCC_CUSTOMER_ID;
48
+ const clientId = envTrimmed("GOOGLE_ADS_CLIENT_ID");
49
+ const clientSecret = envTrimmed("GOOGLE_ADS_CLIENT_SECRET");
50
+ const developerToken = envTrimmed("GOOGLE_ADS_DEVELOPER_TOKEN");
51
+ const refreshToken = envTrimmed("GOOGLE_ADS_REFRESH_TOKEN");
52
+ const customerId = envTrimmed("GOOGLE_ADS_CUSTOMER_ID");
53
+ const mccId = envTrimmed("GOOGLE_ADS_MCC_CUSTOMER_ID");
50
54
  if (!clientId || !clientSecret || !developerToken || !refreshToken) {
51
55
  throw new Error("No configuration found. Either:\n" +
52
56
  " 1. Set env vars: GOOGLE_ADS_CLIENT_ID, GOOGLE_ADS_CLIENT_SECRET, GOOGLE_ADS_DEVELOPER_TOKEN, GOOGLE_ADS_REFRESH_TOKEN, GOOGLE_ADS_CUSTOMER_ID\n" +
@@ -113,11 +117,11 @@ class GoogleAdsManager {
113
117
  throw new GoogleAdsAuthError(msg);
114
118
  }
115
119
  logger.info("Credentials validated: all required env vars present");
116
- this.defaultRefreshToken = process.env.GOOGLE_ADS_REFRESH_TOKEN;
120
+ this.defaultRefreshToken = envTrimmed("GOOGLE_ADS_REFRESH_TOKEN");
117
121
  this.api = new GoogleAdsApi({
118
- client_id: process.env.GOOGLE_ADS_CLIENT_ID,
119
- client_secret: process.env.GOOGLE_ADS_CLIENT_SECRET,
120
- developer_token: process.env.GOOGLE_ADS_DEVELOPER_TOKEN,
122
+ client_id: envTrimmed("GOOGLE_ADS_CLIENT_ID"),
123
+ client_secret: envTrimmed("GOOGLE_ADS_CLIENT_SECRET"),
124
+ developer_token: envTrimmed("GOOGLE_ADS_DEVELOPER_TOKEN"),
121
125
  });
122
126
  }
123
127
  getClientForCustomerId(customerId) {
@@ -1759,6 +1763,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1759
1763
  error: true,
1760
1764
  error_type: error.name,
1761
1765
  message: error.message,
1766
+ server: __cliPkg.name,
1762
1767
  };
1763
1768
  if (error instanceof GoogleAdsAuthError) {
1764
1769
  response.action_required = "Re-authenticate: check refresh token in macOS Keychain. Token may be expired or revoked.";
@@ -1775,11 +1780,11 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
1775
1780
  response.details = rawError.errors || rawError.stack;
1776
1781
  }
1777
1782
  return {
1783
+ isError: true,
1778
1784
  content: [{
1779
1785
  type: "text",
1780
1786
  text: JSON.stringify(response, null, 2),
1781
1787
  }],
1782
- isError: true,
1783
1788
  };
1784
1789
  }
1785
1790
  });
@@ -1818,4 +1823,7 @@ process.on("SIGINT", () => {
1818
1823
  process.on("SIGPIPE", () => {
1819
1824
  // Client disconnected -- expected during shutdown
1820
1825
  });
1826
+ process.on("unhandledRejection", (reason) => {
1827
+ console.error("[error] Unhandled promise rejection:", reason);
1828
+ });
1821
1829
  main().catch((err) => logger.error({ error: err.message, stack: err.stack }, "Fatal startup error"));
@@ -5,7 +5,7 @@ import pino from "pino";
5
5
  // ============================================
6
6
  export const logger = pino({
7
7
  level: process.env.LOG_LEVEL || "info",
8
- ...(process.env.NODE_ENV !== "test" && {
8
+ ...(process.env.NODE_ENV !== "test" && process.stderr.isTTY && {
9
9
  transport: {
10
10
  target: "pino-pretty",
11
11
  options: {
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.8",
4
+ "version": "1.0.10",
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": {
@@ -53,7 +53,7 @@
53
53
  },
54
54
  "homepage": "https://github.com/mharnett/mcp-google-ads#readme",
55
55
  "engines": {
56
- "node": ">=18.0.0"
56
+ "node": ">=18.18.0"
57
57
  },
58
58
  "dependencies": {
59
59
  "@anthropic-ai/sdk": "^0.24.0",