mcp-google-gsc 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/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # mcp-gsc
1
+ # mcp-google-gsc
2
2
 
3
3
  MCP server for Google Search Console -- search analytics, URL inspection, and site management via Claude.
4
4
 
@@ -14,14 +14,14 @@ MCP server for Google Search Console -- search analytics, URL inspection, and si
14
14
  ### From npm
15
15
 
16
16
  ```bash
17
- npm install mcp-gsc
17
+ npm install mcp-google-gsc
18
18
  ```
19
19
 
20
20
  ### From source
21
21
 
22
22
  ```bash
23
- git clone https://github.com/drak-marketing/mcp-gsc.git
24
- cd mcp-gsc
23
+ git clone https://github.com/mharnett/mcp-search-console.git
24
+ cd mcp-google-gsc
25
25
  npm install
26
26
  npm run build
27
27
  ```
@@ -76,7 +76,7 @@ Or if installed globally:
76
76
  "mcpServers": {
77
77
  "gsc": {
78
78
  "command": "npx",
79
- "args": ["mcp-gsc"]
79
+ "args": ["mcp-google-gsc"]
80
80
  }
81
81
  }
82
82
  }
@@ -1 +1 @@
1
- {"sha":"def225f","builtAt":"2026-04-09T18:53:28.788Z"}
1
+ {"sha":"2dbde9f","builtAt":"2026-04-09T21:18:50.062Z"}
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-search-console`);
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 (for multi-client setups)
22
38
  const configPath = join(dirname(new URL(import.meta.url).pathname), "..", "config.json");
@@ -230,8 +246,8 @@ class GscManager {
230
246
  const config = loadConfig();
231
247
  const gscManager = new GscManager(config);
232
248
  const server = new Server({
233
- name: "mcp-gsc",
234
- version: "1.0.0",
249
+ name: __cliPkg.name,
250
+ version: __cliPkg.version,
235
251
  }, {
236
252
  capabilities: {
237
253
  tools: {},
@@ -362,4 +378,12 @@ async function main() {
362
378
  await server.connect(transport);
363
379
  console.error("[startup] MCP GSC server running");
364
380
  }
381
+ process.on("SIGTERM", () => {
382
+ console.error("[shutdown] SIGTERM received, exiting");
383
+ process.exit(0);
384
+ });
385
+ process.on("SIGINT", () => {
386
+ console.error("[shutdown] SIGINT received, exiting");
387
+ process.exit(0);
388
+ });
365
389
  main().catch(console.error);
@@ -1,4 +1,4 @@
1
- import { retry, circuitBreaker, wrap, handleAll, timeout, TimeoutStrategy, ExponentialBackoff, ConsecutiveBreaker, } from "cockatiel";
1
+ import { retry, circuitBreaker, wrap, handleWhen, timeout, TimeoutStrategy, ExponentialBackoff, ConsecutiveBreaker, } from "cockatiel";
2
2
  import pino from "pino";
3
3
  // ============================================
4
4
  // LOGGER
@@ -21,25 +21,38 @@ export const logger = pino({
21
21
  // ============================================
22
22
  const MAX_RESPONSE_SIZE = 200_000; // 200KB
23
23
  export function safeResponse(data, context) {
24
- const jsonStr = JSON.stringify(data);
25
- const sizeBytes = Buffer.byteLength(jsonStr, "utf-8");
26
- if (sizeBytes > MAX_RESPONSE_SIZE) {
27
- logger.warn({ sizeBytes, maxSize: MAX_RESPONSE_SIZE, context }, `Response exceeds size limit, truncating`);
28
- if (Array.isArray(data)) {
29
- const truncated = data.slice(0, Math.max(1, Math.floor(data.length * 0.5)));
30
- return truncated;
24
+ let current = data;
25
+ for (let pass = 0; pass < 10; pass++) {
26
+ const jsonStr = JSON.stringify(current);
27
+ const sizeBytes = Buffer.byteLength(jsonStr, "utf-8");
28
+ if (sizeBytes <= MAX_RESPONSE_SIZE)
29
+ return current;
30
+ logger.warn({ sizeBytes, maxSize: MAX_RESPONSE_SIZE, context, pass }, "Response exceeds size limit, truncating");
31
+ if (Array.isArray(current)) {
32
+ current = current.slice(0, Math.max(1, Math.floor(current.length * 0.5)));
33
+ continue;
31
34
  }
32
- if (typeof data === "object" && data !== null) {
33
- const obj = data;
34
- for (const key of ["items", "results", "data", "rows"]) {
35
- if (Array.isArray(obj[key])) {
35
+ if (typeof current === "object" && current !== null) {
36
+ const obj = current;
37
+ let truncated = false;
38
+ for (const key of ["items", "results", "data", "rows", "tags", "triggers", "variables"]) {
39
+ if (Array.isArray(obj[key]) && obj[key].length > 1) {
36
40
  obj[key] = obj[key].slice(0, Math.max(1, Math.floor(obj[key].length * 0.5)));
37
- return obj;
41
+ if ("count" in obj)
42
+ obj.count = obj[key].length;
43
+ if ("row_count" in obj)
44
+ obj.row_count = obj[key].length;
45
+ obj.truncated = true;
46
+ truncated = true;
47
+ break;
38
48
  }
39
49
  }
50
+ if (truncated)
51
+ continue;
40
52
  }
53
+ break;
41
54
  }
42
- return data;
55
+ return current;
43
56
  }
44
57
  // ============================================
45
58
  // RETRY + CIRCUIT BREAKER + TIMEOUT
@@ -48,11 +61,24 @@ const backoff = new ExponentialBackoff({
48
61
  initialDelay: 100,
49
62
  maxDelay: 5_000,
50
63
  });
51
- const retryPolicy = retry(handleAll, {
64
+ const isTransient = handleWhen((err) => {
65
+ const msg = (err?.message || "").toLowerCase();
66
+ const code = err?.code || err?.status;
67
+ if (code === 401 || code === 403 || code === 7 || code === 16)
68
+ return false;
69
+ if (msg.includes("unauthenticated") || msg.includes("permission_denied") || msg.includes("invalid_grant"))
70
+ return false;
71
+ if (code === 429 || msg.includes("rate"))
72
+ return true;
73
+ if (code >= 400 && code < 500)
74
+ return false;
75
+ return true;
76
+ });
77
+ const retryPolicy = retry(isTransient, {
52
78
  maxAttempts: 3,
53
79
  backoff,
54
80
  });
55
- const circuitBreakerPolicy = circuitBreaker(handleAll, {
81
+ const circuitBreakerPolicy = circuitBreaker(isTransient, {
56
82
  halfOpenAfter: 60_000,
57
83
  breaker: new ConsecutiveBreaker(5),
58
84
  });
package/package.json CHANGED
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "mcp-google-gsc",
3
3
  "mcpName": "io.github.mharnett/google-gsc",
4
- "version": "1.0.3",
4
+ "version": "1.0.5",
5
5
  "description": "MCP server for Google Search Console API with multi-client support, search analytics, and URL inspection.",
6
6
  "main": "dist/index.js",
7
7
  "bin": {