mcp-google-ads 1.4.0 → 1.4.2

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,5 +1,5 @@
1
1
  {
2
- "sha": "94b326c",
3
- "builtAt": "2026-04-18T17:26:41.136Z",
2
+ "sha": "baabc65",
3
+ "builtAt": "2026-04-18T17:46:42.713Z",
4
4
  "embeddedSecrets": true
5
5
  }
@@ -4,9 +4,12 @@ export interface DoctorCheck {
4
4
  status: "pass" | "fail" | "warn";
5
5
  detail: string;
6
6
  }
7
+ export type FetchLatestVersion = () => Promise<string>;
7
8
  export interface DoctorOptions {
8
9
  configPath?: string;
9
10
  credentialsPath?: string;
11
+ fetchLatestVersion?: FetchLatestVersion;
12
+ installedVersion?: string;
10
13
  }
11
14
  export declare function runDoctor(opts?: DoctorOptions): Promise<DoctorCheck[]>;
12
15
  export declare function run(argv?: string[]): Promise<number>;
@@ -1,5 +1,7 @@
1
1
  #!/usr/bin/env node
2
2
  import { existsSync, readFileSync } from "fs";
3
+ import { dirname, join } from "path";
4
+ import { fileURLToPath } from "url";
3
5
  import { credentialsFilePath } from "./credentials.js";
4
6
  import { resolveDefaultConfigPath } from "./install-cli.js";
5
7
  async function runDoctor(opts = {}) {
@@ -69,8 +71,79 @@ async function runDoctor(opts = {}) {
69
71
  status: !hasCustomerId ? "warn" : isMccTerminal ? "warn" : "pass",
70
72
  detail: !hasCustomerId ? "customer_id missing \u2014 cannot check" : isMccTerminal ? `customer_id (${customerId}) equals mcc_customer_id \u2014 this is a Manager account. Most tools need a leaf. Re-run: npx mcp-google-ads-auth and pick a client under the MCC.` : `leaf account (customer_id=${customerId}${mccId ? `, under MCC ${mccId}` : ", direct access"})`
71
73
  });
74
+ checks.push(await checkInstalledIsLatest(opts));
72
75
  return checks;
73
76
  }
77
+ async function checkInstalledIsLatest(opts) {
78
+ const installed = opts.installedVersion ?? readInstalledVersion();
79
+ const fetcher = opts.fetchLatestVersion ?? fetchLatestVersionFromNpm;
80
+ const name = "installed version is up to date";
81
+ if (!installed) {
82
+ return { name, status: "warn", detail: "could not read installed version from package.json" };
83
+ }
84
+ let latest;
85
+ try {
86
+ latest = await fetcher();
87
+ } catch (err) {
88
+ return {
89
+ name,
90
+ status: "warn",
91
+ detail: `could not reach npm registry (${err instanceof Error ? err.message : String(err)}). Installed ${installed}. Skip offline.`
92
+ };
93
+ }
94
+ if (!latest) {
95
+ return { name, status: "warn", detail: `registry returned no version. Installed ${installed}.` };
96
+ }
97
+ if (installed === latest) {
98
+ return { name, status: "pass", detail: `${installed} (latest on npm)` };
99
+ }
100
+ if (semverLt(installed, latest)) {
101
+ return {
102
+ name,
103
+ status: "warn",
104
+ detail: `installed ${installed}, latest on npm is ${latest}. Upgrade: npx -y mcp-google-ads@latest (and fully quit + reopen Claude Desktop).`
105
+ };
106
+ }
107
+ return { name, status: "pass", detail: `${installed} (ahead of npm latest ${latest}; dev build?)` };
108
+ }
109
+ function readInstalledVersion() {
110
+ try {
111
+ const pkgPath = join(dirname(fileURLToPath(import.meta.url)), "..", "package.json");
112
+ const pkg = JSON.parse(readFileSync(pkgPath, "utf8"));
113
+ return typeof pkg.version === "string" ? pkg.version : null;
114
+ } catch {
115
+ return null;
116
+ }
117
+ }
118
+ async function fetchLatestVersionFromNpm() {
119
+ const controller = new AbortController();
120
+ const timer = setTimeout(() => controller.abort(), 5e3);
121
+ try {
122
+ const res = await fetch("https://registry.npmjs.org/mcp-google-ads/latest", {
123
+ signal: controller.signal,
124
+ headers: { Accept: "application/json" }
125
+ });
126
+ if (!res.ok) {
127
+ throw new Error(`HTTP ${res.status}`);
128
+ }
129
+ const body = await res.json();
130
+ if (typeof body.version !== "string") {
131
+ throw new Error("registry response missing version field");
132
+ }
133
+ return body.version;
134
+ } finally {
135
+ clearTimeout(timer);
136
+ }
137
+ }
138
+ function semverLt(a, b) {
139
+ const pa = a.split(".").map((x) => parseInt(x, 10) || 0);
140
+ const pb = b.split(".").map((x) => parseInt(x, 10) || 0);
141
+ for (let i = 0; i < 3; i++) {
142
+ if ((pa[i] ?? 0) < (pb[i] ?? 0)) return true;
143
+ if ((pa[i] ?? 0) > (pb[i] ?? 0)) return false;
144
+ }
145
+ return false;
146
+ }
74
147
  function checkNodeVersion() {
75
148
  const match = process.version.match(/^v(\d+)\./);
76
149
  const major = match ? parseInt(match[1], 10) : 0;
@@ -119,7 +192,6 @@ async function run(argv = process.argv.slice(2)) {
119
192
  process.stdout.write(renderChecks(checks));
120
193
  return checks.some((c) => c.status === "fail") ? 1 : 0;
121
194
  }
122
- import { fileURLToPath } from "url";
123
195
  import { realpathSync } from "fs";
124
196
  function isMainModule() {
125
197
  if (!process.argv[1]) return false;
package/dist/index.js CHANGED
@@ -3,7 +3,7 @@ import { config as dotenvConfig } from "dotenv";
3
3
  import { join, dirname } from "path";
4
4
  import { fileURLToPath } from "url";
5
5
  const __moduleDir = dirname(fileURLToPath(import.meta.url));
6
- dotenvConfig({ path: join(__moduleDir, "..", ".env") });
6
+ dotenvConfig({ path: join(__moduleDir, "..", ".env"), quiet: true });
7
7
  import { Server } from "@modelcontextprotocol/sdk/server/index.js";
8
8
  import { StdioServerTransport } from "@modelcontextprotocol/sdk/server/stdio.js";
9
9
  import {
@@ -54,7 +54,7 @@ try {
54
54
  } catch {
55
55
  console.error(`[build] ${__cliPkg.name}@${__cliPkg.version} (dev mode)`);
56
56
  }
57
- const __minimumSafeVersion = "1.0.5";
57
+ const __minimumSafeVersion = "1.4.1";
58
58
  const __semverLt = (a, b) => {
59
59
  const pa = a.split(".").map(Number), pb = b.split(".").map(Number);
60
60
  for (let i = 0; i < 3; i++) {
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.4.0",
4
+ "version": "1.4.2",
5
5
  "description": "MCP server for Google Ads API with MCC support, 41 tools for campaign management, reporting, and optimization. Read-only by default -- mutating tools require GOOGLE_ADS_MCP_WRITE=true. All creates/updates land PAUSED.",
6
6
  "main": "dist/index.js",
7
7
  "bin": {