mcp-ga4 1.0.1 → 2.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 drak-marketing
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md CHANGED
@@ -1,102 +1,169 @@
1
1
  # mcp-ga4
2
2
 
3
- Ask Google Analytics 4 questions in plain English using Claude.
3
+ MCP server for Google Analytics 4 -- run reports, realtime data, custom dimensions, and property management via Claude.
4
4
 
5
- ## Quickstart (2 steps)
5
+ ## Features
6
6
 
7
- ### 1. Setup
7
+ - 9 tools covering reporting, realtime data, custom dimensions/metrics, data streams, and feedback
8
+ - Two configuration modes: single-property (env vars) and multi-client (config.json)
9
+ - Supports both service account and OAuth credentials
10
+ - Relative date support (today, yesterday, 7daysAgo, 30daysAgo, 90daysAgo)
11
+ - Built on official Google SDKs with resilience patterns
12
+
13
+ ## Installation
8
14
 
9
15
  ```bash
10
- npx mcp-ga4-setup
16
+ npm install mcp-ga4
11
17
  ```
12
18
 
13
- This will:
14
- - Ask for your GA4 property ID (find it in GA4 > Admin > Property Details)
15
- - Open your browser to sign in with Google
16
- - Verify the connection works
17
- - Generate the Claude Code configuration snippet
19
+ Or clone the repository:
20
+
21
+ ```bash
22
+ git clone https://github.com/drak-marketing/mcp-ga4.git
23
+ cd mcp-ga4
24
+ npm install
25
+ npm run build
26
+ ```
18
27
 
19
- **Note:** You may see a "Google hasn't verified this app" warning during sign-in.
20
- Click **Advanced** then **Go to mcp-ga4 (unsafe)** to continue. This is safe --
21
- the app only requests read-only access to your analytics data.
28
+ ## Configuration
22
29
 
23
- ### 2. Configure Claude Code
30
+ ### Mode 1: Single Property (env vars)
24
31
 
25
- Copy the JSON snippet from the setup wizard into your Claude Code MCP config.
32
+ Set environment variables to connect to a single GA4 property:
33
+
34
+ ```bash
35
+ GA4_PROPERTY_ID=123456789
36
+ GOOGLE_APPLICATION_CREDENTIALS=/path/to/service-account.json
37
+ ```
26
38
 
27
- The snippet looks like:
39
+ For OAuth credentials instead of a service account:
40
+
41
+ ```bash
42
+ GA4_PROPERTY_ID=123456789
43
+ GA4_CREDENTIALS_FILE=/path/to/oauth-credentials.json
44
+ ```
45
+
46
+ ### Mode 2: Multi-Client (config.json)
47
+
48
+ Create a `config.json` in the project root to map multiple GA4 properties to project directories. The server auto-detects which property to use based on the caller's working directory.
28
49
 
29
50
  ```json
30
51
  {
31
- "ga4": {
32
- "command": "npx",
33
- "args": ["-y", "mcp-ga4"],
34
- "env": {
35
- "GA4_PROPERTY_ID": "YOUR_PROPERTY_ID",
36
- "GOOGLE_APPLICATION_CREDENTIALS": "~/.config/mcp-ga4/credentials.json"
52
+ "credentials_file": "/path/to/oauth-credentials.json",
53
+ "clients": {
54
+ "client-a": {
55
+ "name": "Client A",
56
+ "folder": "/path/to/client-a/project",
57
+ "property_id": "123456789"
58
+ },
59
+ "client-b": {
60
+ "name": "Client B",
61
+ "folder": "/path/to/client-b/project",
62
+ "property_id": "987654321"
37
63
  }
38
64
  }
39
65
  }
40
66
  ```
41
67
 
42
- - **Claude Desktop (Mac):** `~/Library/Application Support/Claude/claude_desktop_config.json`
43
- - **Claude Code CLI:** `.mcp.json` in your project directory
68
+ ## Usage
44
69
 
45
- That's it. No Python, no pip, no venv. Just `npx`.
70
+ ### Claude Code (.mcp.json)
46
71
 
47
- ## Usage
72
+ Single-property mode:
73
+
74
+ ```json
75
+ {
76
+ "mcpServers": {
77
+ "ga4": {
78
+ "command": "npx",
79
+ "args": ["mcp-ga4"],
80
+ "env": {
81
+ "GA4_PROPERTY_ID": "123456789",
82
+ "GOOGLE_APPLICATION_CREDENTIALS": "/path/to/credentials.json"
83
+ }
84
+ }
85
+ }
86
+ }
87
+ ```
88
+
89
+ Multi-client mode:
48
90
 
49
- Once configured, just ask Claude questions about your analytics:
91
+ ```json
92
+ {
93
+ "mcpServers": {
94
+ "ga4": {
95
+ "command": "node",
96
+ "args": ["/path/to/mcp-ga4/dist/index.js"]
97
+ }
98
+ }
99
+ }
100
+ ```
50
101
 
51
- - "What were my top 10 pages last week?"
52
- - "Show me traffic by source for the past 30 days"
53
- - "What's my bounce rate trend this month?"
54
- - "Which campaigns drove the most conversions?"
55
- - "What devices are my users on?"
56
- - "Show me real-time active users"
102
+ ## Common Query Patterns
57
103
 
58
- ## Feedback
104
+ **Top pages:**
105
+ dimensions=`pagePath`, metrics=`screenPageViews`, order_by=`screenPageViews`
59
106
 
60
- If a query doesn't return what you expected, tell Claude! It will log the
61
- pattern using the built-in `ga4_suggest_improvement` tool so the tool can
62
- be improved for everyone.
107
+ **Traffic sources:**
108
+ dimensions=`sessionSource,sessionMedium`, metrics=`sessions,totalUsers`
63
109
 
64
- You can also say "send feedback" to Claude to report bugs or request features.
110
+ **Daily trend:**
111
+ dimensions=`date`, metrics=`sessions,totalUsers`
65
112
 
66
- ## Troubleshooting
113
+ **Campaign performance:**
114
+ dimensions=`sessionCampaignName`, metrics=`sessions,conversions`
67
115
 
68
- **"No refresh token received"**
69
- Go to https://myaccount.google.com/permissions, remove "mcp-ga4", then re-run `npx mcp-ga4-setup`.
116
+ **Device breakdown:**
117
+ dimensions=`deviceCategory`, metrics=`sessions,totalUsers`
70
118
 
71
- **"Connection failed" / "Permission denied"**
72
- Your Google account may not have access to the GA4 property. Check your access at
73
- GA4 > Admin > Account Access Management.
119
+ ## Tools
74
120
 
75
- **Token expired**
76
- Re-run `npx mcp-ga4-setup` to refresh your credentials.
121
+ | Tool | Description |
122
+ |------|-------------|
123
+ | `ga4_get_client_context` | Returns the active GA4 property ID and client name |
124
+ | `ga4_run_report` | Run a standard GA4 report with dimensions, metrics, date range, and filters |
125
+ | `ga4_realtime_report` | Query realtime data (last 30 minutes) |
126
+ | `ga4_list_custom_dimensions` | List all custom dimensions for the property |
127
+ | `ga4_create_custom_dimension` | Create a new custom dimension |
128
+ | `ga4_list_custom_metrics` | List all custom metrics for the property |
129
+ | `ga4_list_data_streams` | List web/app data streams and their measurement IDs |
130
+ | `ga4_send_feedback` | Submit feedback on a query result |
131
+ | `ga4_suggest_improvement` | Suggest a new query pattern or improvement |
77
132
 
78
- ## Advanced: Multi-Property Setup
133
+ ## Date Formats
79
134
 
80
- For managing multiple GA4 properties, create `~/.config/mcp-ga4/config.json`:
135
+ Use `YYYY-MM-DD` for absolute dates, or these relative shortcuts:
81
136
 
82
- ```json
83
- {
84
- "credentials_file": "~/.config/mcp-ga4/credentials.json",
85
- "clients": {
86
- "my-site": {
87
- "name": "My Website",
88
- "folder": "/path/to/project",
89
- "property_id": "123456789"
90
- },
91
- "other-site": {
92
- "name": "Other Site",
93
- "folder": "/path/to/other",
94
- "property_id": "987654321"
95
- }
96
- }
97
- }
98
- ```
137
+ - `today`
138
+ - `yesterday`
139
+ - `7daysAgo`
140
+ - `30daysAgo`
141
+ - `90daysAgo`
142
+
143
+ ## Common Dimensions and Metrics
144
+
145
+ **Dimensions:** date, dateHour, eventName, pagePath, pageTitle, sessionSource, sessionMedium, sessionCampaignName, country, city, deviceCategory, browser, operatingSystem, landingPage, pageReferrer, newVsReturning, firstUserSource, firstUserMedium, firstUserCampaignName
146
+
147
+ **Metrics:** sessions, totalUsers, newUsers, activeUsers, screenPageViews, eventCount, conversions, engagedSessions, engagementRate, averageSessionDuration, bounceRate, sessionsPerUser, screenPageViewsPerSession, userEngagementDuration
148
+
149
+ ## Data Freshness
150
+
151
+ - Standard reports: 24-48 hour delay
152
+ - Realtime reports: last 30 minutes only
153
+
154
+ ## Architecture
155
+
156
+ Built on:
157
+
158
+ - `@google-analytics/data` -- GA4 Data API for reports
159
+ - `@google-analytics/admin` -- GA4 Admin API for property management
160
+ - `cockatiel` -- resilience (retry, circuit breaker)
161
+ - `pino` -- structured logging
99
162
 
100
163
  ## License
101
164
 
102
165
  MIT
166
+
167
+ ## Author
168
+
169
+ Built by Mark Harnett / [drak-marketing](https://github.com/drak-marketing)
@@ -0,0 +1,10 @@
1
+ {
2
+ "credentials_file": "/path/to/credentials.json",
3
+ "clients": {
4
+ "my-site": {
5
+ "name": "My Website",
6
+ "folder": "/path/to/project",
7
+ "property_id": "123456789"
8
+ }
9
+ }
10
+ }
@@ -0,0 +1 @@
1
+ {"sha":"38406d3","builtAt":"2026-04-03T23:26:52.380Z"}
package/dist/errors.d.ts CHANGED
@@ -1,15 +1,12 @@
1
- /**
2
- * Typed errors and classification for GA4 API calls.
3
- */
4
- export declare class GA4AuthError extends Error {
1
+ export declare class Ga4AuthError extends Error {
5
2
  readonly cause?: unknown | undefined;
6
3
  constructor(message: string, cause?: unknown | undefined);
7
4
  }
8
- export declare class GA4RateLimitError extends Error {
5
+ export declare class Ga4RateLimitError extends Error {
9
6
  readonly retryAfterMs: number;
10
7
  constructor(retryAfterMs: number, cause?: unknown);
11
8
  }
12
- export declare class GA4ServiceError extends Error {
9
+ export declare class Ga4ServiceError extends Error {
13
10
  readonly cause?: unknown | undefined;
14
11
  constructor(message: string, cause?: unknown | undefined);
15
12
  }
package/dist/errors.js CHANGED
@@ -1,59 +1,42 @@
1
- /**
2
- * Typed errors and classification for GA4 API calls.
3
- */
4
- export class GA4AuthError extends Error {
1
+ export class Ga4AuthError extends Error {
5
2
  cause;
6
3
  constructor(message, cause) {
7
4
  super(message);
8
5
  this.cause = cause;
9
- this.name = "GA4AuthError";
6
+ this.name = "Ga4AuthError";
10
7
  }
11
8
  }
12
- export class GA4RateLimitError extends Error {
9
+ export class Ga4RateLimitError extends Error {
13
10
  retryAfterMs;
14
11
  constructor(retryAfterMs, cause) {
15
12
  super(`Rate limited, retry after ${retryAfterMs}ms`);
16
- this.name = "GA4RateLimitError";
17
13
  this.retryAfterMs = retryAfterMs;
14
+ this.name = "Ga4RateLimitError";
18
15
  this.cause = cause;
19
16
  }
20
17
  }
21
- export class GA4ServiceError extends Error {
18
+ export class Ga4ServiceError extends Error {
22
19
  cause;
23
20
  constructor(message, cause) {
24
21
  super(message);
25
22
  this.cause = cause;
26
- this.name = "GA4ServiceError";
23
+ this.name = "Ga4ServiceError";
27
24
  }
28
25
  }
29
26
  export function classifyError(error) {
30
- const message = typeof error?.message === "string" ? error.message : String(error);
31
- const status = error?.status || error?.code;
32
- // Auth failures
33
- if (status === 401 ||
34
- status === 403 ||
35
- message.includes("invalid_grant") ||
36
- message.includes("Token has been expired") ||
37
- message.includes("refresh token") ||
38
- message.includes("UNAUTHENTICATED") ||
39
- message.includes("PERMISSION_DENIED")) {
40
- return new GA4AuthError(`Auth failed: ${message}. Re-run mcp-ga4-setup to refresh credentials.`, error);
27
+ const message = error?.message || String(error);
28
+ const code = error?.code || error?.status;
29
+ if (code === 401 || code === 403 || code === 7 || code === 16 ||
30
+ message.includes("PERMISSION_DENIED") || message.includes("UNAUTHENTICATED") ||
31
+ message.includes("invalid_grant")) {
32
+ return new Ga4AuthError(`Auth failed: ${message}. Check credentials.`, error);
41
33
  }
42
- // Rate limiting
43
- if (status === 429 || message.includes("RESOURCE_EXHAUSTED")) {
44
- const retryMs = 60_000;
45
- return new GA4RateLimitError(retryMs, error);
34
+ if (code === 429 || code === 8 || message.includes("RESOURCE_EXHAUSTED") || message.includes("rateLimitExceeded")) {
35
+ return new Ga4RateLimitError(60_000, error);
46
36
  }
47
- // Server errors
48
- if ((typeof status === "number" && status >= 500) ||
49
- message.includes("INTERNAL")) {
50
- return new GA4ServiceError(`GA4 API server error: ${message}`, error);
51
- }
52
- if (!(error instanceof Error)) {
53
- const wrapped = new Error(message);
54
- wrapped.name = "GA4Error";
55
- wrapped.cause = error;
56
- return wrapped;
37
+ if (code >= 500 || code === 13 || code === 14 ||
38
+ message.includes("INTERNAL") || message.includes("UNAVAILABLE")) {
39
+ return new Ga4ServiceError(`GA4 API server error: ${message}`, error);
57
40
  }
58
41
  return error;
59
42
  }
package/dist/index.d.ts CHANGED
@@ -1,9 +1,2 @@
1
1
  #!/usr/bin/env node
2
- /**
3
- * mcp-ga4 -- MCP server for querying Google Analytics 4 via natural language.
4
- *
5
- * Supports two config modes:
6
- * 1. Single-property (env vars): GA4_PROPERTY_ID + GOOGLE_APPLICATION_CREDENTIALS
7
- * 2. Multi-client (config.json): MCP_GA4_CONFIG or ~/.config/mcp-ga4/config.json
8
- */
9
2
  export {};