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.
@@ -1 +1 @@
1
- {"sha":"29cbd4f","builtAt":"2026-04-09T21:28:40.127Z"}
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(`Rate limited, retry after ${retryAfterMs}ms`);
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(`Auth failed: ${message}. Check credentials.`, error);
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 = process.env.GA4_PROPERTY_ID;
43
- const credsFile = process.env.GOOGLE_APPLICATION_CREDENTIALS || "";
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);
@@ -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.5",
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.0.0"
33
+ "node": ">=18.18.0"
34
34
  },
35
35
  "dependencies": {
36
36
  "@google-analytics/admin": "^9.0.1",