mcp-ga4 2.0.5 → 2.0.7
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/errors.d.ts +4 -0
- package/dist/errors.js +10 -2
- package/dist/index.js +11 -3
- package/dist/resilience.js +1 -1
- package/package.json +2 -2
package/dist/build-info.json
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"sha":"
|
|
1
|
+
{"sha":"dc01d5b","builtAt":"2026-04-09T21:47:51.653Z"}
|
package/dist/errors.d.ts
CHANGED
|
@@ -10,4 +10,8 @@ export declare class Ga4ServiceError extends Error {
|
|
|
10
10
|
readonly cause?: unknown | undefined;
|
|
11
11
|
constructor(message: string, cause?: unknown | undefined);
|
|
12
12
|
}
|
|
13
|
+
export declare function validateCredentials(): {
|
|
14
|
+
valid: boolean;
|
|
15
|
+
missing: string[];
|
|
16
|
+
};
|
|
13
17
|
export declare function classifyError(error: any): Error;
|
package/dist/errors.js
CHANGED
|
@@ -9,7 +9,7 @@ export class Ga4AuthError extends Error {
|
|
|
9
9
|
export class Ga4RateLimitError extends Error {
|
|
10
10
|
retryAfterMs;
|
|
11
11
|
constructor(retryAfterMs, cause) {
|
|
12
|
-
super(`
|
|
12
|
+
super(`GA4 rate limited, retry after ${retryAfterMs}ms`);
|
|
13
13
|
this.retryAfterMs = retryAfterMs;
|
|
14
14
|
this.name = "Ga4RateLimitError";
|
|
15
15
|
this.cause = cause;
|
|
@@ -23,13 +23,21 @@ export class Ga4ServiceError extends Error {
|
|
|
23
23
|
this.name = "Ga4ServiceError";
|
|
24
24
|
}
|
|
25
25
|
}
|
|
26
|
+
export function validateCredentials() {
|
|
27
|
+
const missing = [];
|
|
28
|
+
if (!process.env.GA4_PROPERTY_ID?.trim())
|
|
29
|
+
missing.push("GA4_PROPERTY_ID");
|
|
30
|
+
if (!process.env.GOOGLE_APPLICATION_CREDENTIALS?.trim())
|
|
31
|
+
missing.push("GOOGLE_APPLICATION_CREDENTIALS");
|
|
32
|
+
return { valid: missing.length === 0, missing };
|
|
33
|
+
}
|
|
26
34
|
export function classifyError(error) {
|
|
27
35
|
const message = error?.message || String(error);
|
|
28
36
|
const code = error?.code || error?.status;
|
|
29
37
|
if (code === 401 || code === 403 || code === 7 || code === 16 ||
|
|
30
38
|
message.includes("PERMISSION_DENIED") || message.includes("UNAUTHENTICATED") ||
|
|
31
39
|
message.includes("invalid_grant")) {
|
|
32
|
-
return new Ga4AuthError(`
|
|
40
|
+
return new Ga4AuthError(`GA4 auth failed: ${message}. Check credentials.`, error);
|
|
33
41
|
}
|
|
34
42
|
if (code === 429 || code === 8 || message.includes("RESOURCE_EXHAUSTED") || message.includes("rateLimitExceeded")) {
|
|
35
43
|
return new Ga4RateLimitError(60_000, error);
|
package/dist/index.js
CHANGED
|
@@ -37,10 +37,14 @@ if (process.argv.includes("--version") || process.argv.includes("-v")) {
|
|
|
37
37
|
console.error(__cliPkg.version);
|
|
38
38
|
process.exit(0);
|
|
39
39
|
}
|
|
40
|
+
// ============================================
|
|
41
|
+
// ENV VAR TRIMMING
|
|
42
|
+
// ============================================
|
|
43
|
+
const envTrimmed = (key) => (process.env[key] || "").trim().replace(/^["']|["']$/g, "");
|
|
40
44
|
function loadConfig() {
|
|
41
45
|
// Single-property mode via env vars
|
|
42
|
-
const propertyId =
|
|
43
|
-
const credsFile =
|
|
46
|
+
const propertyId = envTrimmed("GA4_PROPERTY_ID");
|
|
47
|
+
const credsFile = envTrimmed("GOOGLE_APPLICATION_CREDENTIALS");
|
|
44
48
|
if (propertyId) {
|
|
45
49
|
return {
|
|
46
50
|
credentials_file: credsFile,
|
|
@@ -407,6 +411,7 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
407
411
|
error: true,
|
|
408
412
|
error_type: error.name,
|
|
409
413
|
message: error.message,
|
|
414
|
+
server: __cliPkg.name,
|
|
410
415
|
};
|
|
411
416
|
if (error instanceof Ga4AuthError) {
|
|
412
417
|
response.action_required = "Check service account or OAuth credentials and GA4 property access.";
|
|
@@ -422,8 +427,8 @@ server.setRequestHandler(CallToolRequestSchema, async (request) => {
|
|
|
422
427
|
response.details = rawError.stack;
|
|
423
428
|
}
|
|
424
429
|
return {
|
|
425
|
-
content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
|
|
426
430
|
isError: true,
|
|
431
|
+
content: [{ type: "text", text: JSON.stringify(response, null, 2) }],
|
|
427
432
|
};
|
|
428
433
|
}
|
|
429
434
|
});
|
|
@@ -455,4 +460,7 @@ process.on("SIGINT", () => {
|
|
|
455
460
|
process.on("SIGPIPE", () => {
|
|
456
461
|
// Client disconnected -- expected during shutdown
|
|
457
462
|
});
|
|
463
|
+
process.on("unhandledRejection", (reason) => {
|
|
464
|
+
console.error("[error] Unhandled promise rejection:", reason);
|
|
465
|
+
});
|
|
458
466
|
main().catch(console.error);
|
package/dist/resilience.js
CHANGED
|
@@ -2,7 +2,7 @@ import { retry, circuitBreaker, wrap, handleWhen, timeout, TimeoutStrategy, Expo
|
|
|
2
2
|
import pino from "pino";
|
|
3
3
|
export const logger = pino({
|
|
4
4
|
level: process.env.LOG_LEVEL || "info",
|
|
5
|
-
...(process.env.NODE_ENV !== "test" && {
|
|
5
|
+
...(process.env.NODE_ENV !== "test" && process.stderr.isTTY && {
|
|
6
6
|
transport: {
|
|
7
7
|
target: "pino-pretty",
|
|
8
8
|
options: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "mcp-ga4",
|
|
3
3
|
"mcpName": "io.github.mharnett/ga4",
|
|
4
|
-
"version": "2.0.
|
|
4
|
+
"version": "2.0.7",
|
|
5
5
|
"description": "MCP server for Google Analytics 4 - query GA4 data with natural language via Claude.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"bin": {
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"author": "drak-marketing",
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"engines": {
|
|
33
|
-
"node": ">=18.
|
|
33
|
+
"node": ">=18.18.0"
|
|
34
34
|
},
|
|
35
35
|
"dependencies": {
|
|
36
36
|
"@google-analytics/admin": "^9.0.1",
|