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 +5 -5
- package/dist/build-info.json +1 -1
- package/dist/index.js +26 -2
- package/dist/resilience.js +42 -16
- package/package.json +1 -1
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/
|
|
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
|
}
|
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"sha":"
|
|
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:
|
|
234
|
-
version:
|
|
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);
|
package/dist/resilience.js
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { retry, circuitBreaker, wrap,
|
|
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
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
if (
|
|
29
|
-
|
|
30
|
-
|
|
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
|
|
33
|
-
const obj =
|
|
34
|
-
|
|
35
|
-
|
|
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
|
-
|
|
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
|
|
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
|
|
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(
|
|
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.
|
|
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": {
|